Add behavioral types system

Implement behavioral properties for functions including:
- Property annotations: is pure, is total, is idempotent, is deterministic, is commutative
- Where clause constraints: where F is pure
- Result refinements: where result >= 0 (parsing only, not enforced)

Key changes:
- AST: BehavioralProperty enum, WhereClause enum, updated FunctionDecl
- Lexer: Added keywords (is, pure, total, idempotent, deterministic, commutative, where, assume)
- Parser: parse_behavioral_properties(), parse_where_clauses(), parse_single_property()
- Types: PropertySet for tracking function properties, updated Function type
- Typechecker: Verify pure functions don't have effects, validate where clause type params

Properties are informational/guarantees rather than type constraints - a pure
function can be used anywhere a function is expected. Property requirements
are meant to be enforced via where clauses (future work: call-site checking).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 03:30:51 -05:00
parent 15e5ccb064
commit 66132779cc
6 changed files with 624 additions and 4 deletions

View File

@@ -788,4 +788,66 @@ c")"#;
"#;
assert_eq!(eval(source).unwrap(), "0");
}
// ============ Behavioral Types Tests ============
#[test]
fn test_behavioral_pure_function() {
// A pure function with no effects should work
let source = r#"
fn double(x: Int): Int is pure = x * 2
let result = double(21)
"#;
assert_eq!(eval(source).unwrap(), "42");
}
#[test]
fn test_behavioral_total_function() {
// A total function should work
let source = r#"
fn always42(): Int is total = 42
let result = always42()
"#;
assert_eq!(eval(source).unwrap(), "42");
}
#[test]
fn test_behavioral_idempotent_function() {
// An idempotent function
let source = r#"
fn clamp(x: Int): Int is idempotent = if x < 0 then 0 else x
let result = clamp(clamp(-5))
"#;
assert_eq!(eval(source).unwrap(), "0");
}
#[test]
fn test_behavioral_multiple_properties() {
// A function with multiple properties
let source = r#"
fn identity(x: Int): Int is pure, is total = x
let result = identity(100)
"#;
assert_eq!(eval(source).unwrap(), "100");
}
#[test]
fn test_behavioral_deterministic() {
let source = r#"
fn square(x: Int): Int is deterministic = x * x
let result = square(7)
"#;
assert_eq!(eval(source).unwrap(), "49");
}
#[test]
fn test_behavioral_pure_with_effects_error() {
// A pure function with effects should produce a type error
let source = r#"
fn bad(x: Int): Int with {Logger} is pure = x
"#;
let result = eval(source);
assert!(result.is_err());
assert!(result.unwrap_err().contains("pure but has effects"));
}
}