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:
@@ -9,8 +9,8 @@ use crate::ast::{
|
||||
};
|
||||
use crate::modules::ModuleLoader;
|
||||
use crate::types::{
|
||||
self, unify, EffectDef, EffectOpDef, EffectSet, HandlerDef, Type, TypeEnv, TypeScheme,
|
||||
VariantDef, VariantFieldsDef,
|
||||
self, unify, EffectDef, EffectOpDef, EffectSet, HandlerDef, PropertySet, Type, TypeEnv,
|
||||
TypeScheme, VariantDef, VariantFieldsDef,
|
||||
};
|
||||
|
||||
/// Type checking error
|
||||
@@ -234,6 +234,55 @@ impl TypeChecker {
|
||||
span: func.span,
|
||||
});
|
||||
}
|
||||
|
||||
// Check behavioral properties
|
||||
let properties = PropertySet::from_ast(&func.properties);
|
||||
|
||||
// Pure functions cannot have effects
|
||||
if properties.is_pure() && !func.effects.is_empty() {
|
||||
self.errors.push(TypeError {
|
||||
message: format!(
|
||||
"Function '{}' is declared as pure but has effects: {{{}}}",
|
||||
func.name.name,
|
||||
func.effects
|
||||
.iter()
|
||||
.map(|e| e.name.as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
),
|
||||
span: func.span,
|
||||
});
|
||||
}
|
||||
|
||||
// Check where clause property constraints
|
||||
for where_clause in &func.where_clauses {
|
||||
match where_clause {
|
||||
ast::WhereClause::PropertyConstraint {
|
||||
type_param,
|
||||
property,
|
||||
span,
|
||||
} => {
|
||||
// Record the constraint for later checking when the function is called
|
||||
// For now, we just validate that the type parameter exists
|
||||
if !func.type_params.iter().any(|p| p.name == type_param.name)
|
||||
&& !func.params.iter().any(|p| p.name.name == type_param.name)
|
||||
{
|
||||
self.errors.push(TypeError {
|
||||
message: format!(
|
||||
"Unknown type parameter '{}' in where clause",
|
||||
type_param.name
|
||||
),
|
||||
span: *span,
|
||||
});
|
||||
}
|
||||
}
|
||||
ast::WhereClause::ResultRefinement { predicate, span } => {
|
||||
// Result refinements are checked at runtime or via SMT solver
|
||||
// For now, we just type-check the predicate expression
|
||||
// (would need 'result' in scope, which we don't have yet)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_let_decl(&mut self, let_decl: &LetDecl) {
|
||||
@@ -1089,8 +1138,9 @@ impl TypeChecker {
|
||||
|
||||
let return_type = self.resolve_type(&func.return_type);
|
||||
let effects = EffectSet::from_iter(func.effects.iter().map(|e| e.name.clone()));
|
||||
let properties = PropertySet::from_ast(&func.properties);
|
||||
|
||||
Type::function_with_effects(param_types, return_type, effects)
|
||||
Type::function_with_properties(param_types, return_type, effects, properties)
|
||||
}
|
||||
|
||||
fn effect_def(&self, effect: &EffectDecl) -> EffectDef {
|
||||
|
||||
Reference in New Issue
Block a user