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

@@ -41,6 +41,16 @@ pub enum TokenKind {
From, // from (for migrations)
Latest, // latest (for @latest version constraint)
// Behavioral type keywords
Is, // is (for behavioral properties)
Pure, // pure
Total, // total
Idempotent, // idempotent
Deterministic, // deterministic
Commutative, // commutative
Where, // where (for constraints)
Assume, // assume (for unverified properties)
// Operators
Plus, // +
Minus, // -
@@ -108,6 +118,14 @@ impl fmt::Display for TokenKind {
TokenKind::As => write!(f, "as"),
TokenKind::From => write!(f, "from"),
TokenKind::Latest => write!(f, "latest"),
TokenKind::Is => write!(f, "is"),
TokenKind::Pure => write!(f, "pure"),
TokenKind::Total => write!(f, "total"),
TokenKind::Idempotent => write!(f, "idempotent"),
TokenKind::Deterministic => write!(f, "deterministic"),
TokenKind::Commutative => write!(f, "commutative"),
TokenKind::Where => write!(f, "where"),
TokenKind::Assume => write!(f, "assume"),
TokenKind::True => write!(f, "true"),
TokenKind::False => write!(f, "false"),
TokenKind::Plus => write!(f, "+"),
@@ -532,6 +550,14 @@ impl<'a> Lexer<'a> {
"as" => TokenKind::As,
"from" => TokenKind::From,
"latest" => TokenKind::Latest,
"is" => TokenKind::Is,
"pure" => TokenKind::Pure,
"total" => TokenKind::Total,
"idempotent" => TokenKind::Idempotent,
"deterministic" => TokenKind::Deterministic,
"commutative" => TokenKind::Commutative,
"where" => TokenKind::Where,
"assume" => TokenKind::Assume,
"true" => TokenKind::Bool(true),
"false" => TokenKind::Bool(false),
_ => TokenKind::Ident(ident.to_string()),