From b959e11e51f6d337dfefb618f46e69f56f37df26 Mon Sep 17 00:00:00 2001 From: Masashi Date: Mon, 15 Dec 2025 17:02:43 +0530 Subject: [PATCH] added bindingkind verification --- simple.sui | 3 + src/ast.rs | 13 +- src/lambda_lower.rs | 191 ++++++++++++++++++++++++----- src/monomorphize.rs | 19 +-- src/typechecker.rs | 288 +++++++++++++++++++++++++++++++++++--------- test_affine.sui | 5 + test_binding.sui | 5 + test_linear.sui | 4 + test_linear_ok.sui | 4 + test_scoping.sui | 7 ++ test_shadow.sui | 5 + 11 files changed, 447 insertions(+), 97 deletions(-) create mode 100644 simple.sui create mode 100644 test_affine.sui create mode 100644 test_binding.sui create mode 100644 test_linear.sui create mode 100644 test_linear_ok.sui create mode 100644 test_scoping.sui create mode 100644 test_shadow.sui diff --git a/simple.sui b/simple.sui new file mode 100644 index 0000000..173f651 --- /dev/null +++ b/simple.sui @@ -0,0 +1,3 @@ +fn simple() -> int do + 42 +end \ No newline at end of file diff --git a/src/ast.rs b/src/ast.rs index d4a52d6..fe65eb3 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,6 +1,9 @@ use crate::typechecker::Type; use std::ops::Range; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BindingId(pub usize); + #[derive(Debug, Clone)] pub enum TypeAnnot { Var(String), @@ -306,7 +309,7 @@ pub enum TypedASTNodeKind { pub struct TypedFunction { pub name: String, pub parameters: Vec, - pub args: Vec<(String, Option)>, + pub args: Vec<(BindingId, String, Option)>, pub return_type: Option, pub body: TypedExpr, pub ty: Type, @@ -395,8 +398,8 @@ pub enum TypedExprKind { Dot(Box, String), EarlyReturn(Option>), OptionalChain(Option>, String), - Lambda(Vec<(String, Option)>, Box), - Let(String, BindingKind, Option, Box), + Lambda(Vec<(BindingId, String, Option)>, Box), + Let(BindingId, String, BindingKind, Option, Box), Assign(Box, Box), Cast(Box, TypeAnnot), If(Box, Box, Option>), @@ -405,7 +408,7 @@ pub enum TypedExprKind { Do(Vec), BinOp(Box, BinOp, Box), UnOp(UnOp, Box), - For(String, Box, Box), + For(BindingId, String, Box, Box), Range(Box, Box), Return(Option>), Break, @@ -422,7 +425,7 @@ pub struct TypedPattern { #[derive(Debug, Clone)] pub enum TypedPatternKind { Wildcard, - Variable(String), + Variable(BindingId, String), Literal(String), Tuple(Vec), Struct(String, Vec<(String, TypedPattern)>), diff --git a/src/lambda_lower.rs b/src/lambda_lower.rs index 6c4a132..6094c16 100644 --- a/src/lambda_lower.rs +++ b/src/lambda_lower.rs @@ -17,6 +17,133 @@ impl LambdaLowerer { } } + + + + + fn collect_free_vars(&self, expr: &Expr, lambda_params: &[String]) -> std::collections::HashSet { + let mut free_vars = std::collections::HashSet::new(); + let mut local_scope = std::collections::HashSet::new(); + self.collect_free_vars_expr(expr, lambda_params, &mut free_vars, &mut local_scope); + free_vars + } + + fn collect_free_vars_expr(&self, expr: &Expr, lambda_params: &[String], free_vars: &mut std::collections::HashSet, local_scope: &mut std::collections::HashSet) { + match &expr.kind { + ExprKind::Variable(name) => { + if !lambda_params.contains(name) && !local_scope.contains(name) { + free_vars.insert(name.clone()); + } + } + ExprKind::Lambda(args, body) => { + let param_names: Vec = args.iter().map(|(name, _)| name.clone()).collect(); + // For nested lambdas, we don't enter a new scope here since we're just collecting free vars + self.collect_free_vars_expr(body, ¶m_names, free_vars, local_scope); + } + ExprKind::Let(name, _, _, body) => { + // Collect from body before adding the binding + self.collect_free_vars_expr(body, lambda_params, free_vars, local_scope); + // Add to local scope + local_scope.insert(name.clone()); + } + ExprKind::Call(func, args) => { + self.collect_free_vars_expr(func, lambda_params, free_vars, local_scope); + for arg in args { + self.collect_free_vars_expr(arg, lambda_params, free_vars, local_scope); + } + } + // Handle other expression types that contain subexpressions + ExprKind::If(cond, then_expr, else_expr) => { + self.collect_free_vars_expr(cond, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(then_expr, lambda_params, free_vars, local_scope); + if let Some(else_expr) = else_expr { + self.collect_free_vars_expr(else_expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::Match(scrutinee, arms) => { + self.collect_free_vars_expr(scrutinee, lambda_params, free_vars, local_scope); + for (_, expr) in arms { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::While(cond, body) => { + self.collect_free_vars_expr(cond, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(body, lambda_params, free_vars, local_scope); + } + ExprKind::For(_, iter, body) => { + self.collect_free_vars_expr(iter, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(body, lambda_params, free_vars, local_scope); + } + ExprKind::Do(exprs) => { + for expr in exprs { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::BinOp(left, _, right) => { + self.collect_free_vars_expr(left, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(right, lambda_params, free_vars, local_scope); + } + ExprKind::UnOp(_, operand) => { + self.collect_free_vars_expr(operand, lambda_params, free_vars, local_scope); + } + ExprKind::Assign(target, value) => { + self.collect_free_vars_expr(target, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(value, lambda_params, free_vars, local_scope); + } + ExprKind::Cast(operand, _) => { + self.collect_free_vars_expr(operand, lambda_params, free_vars, local_scope); + } + ExprKind::Index(obj, index) => { + self.collect_free_vars_expr(obj, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(index, lambda_params, free_vars, local_scope); + } + ExprKind::Dot(obj, _) => { + self.collect_free_vars_expr(obj, lambda_params, free_vars, local_scope); + } + ExprKind::EarlyReturn(expr) => { + if let Some(expr) = expr { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::OptionalChain(obj, _) => { + if let Some(obj) = obj { + self.collect_free_vars_expr(obj, lambda_params, free_vars, local_scope); + } + } + ExprKind::Return(expr) => { + if let Some(expr) = expr { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::Array(exprs) => { + for expr in exprs { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::Tuple(exprs) => { + for expr in exprs { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::StructLit(_, fields) => { + for (_, expr) in fields { + self.collect_free_vars_expr(expr, lambda_params, free_vars, local_scope); + } + } + ExprKind::EnumLit(_, _, args) => { + for arg in args { + self.collect_free_vars_expr(arg, lambda_params, free_vars, local_scope); + } + } + ExprKind::Range(start, end) => { + self.collect_free_vars_expr(start, lambda_params, free_vars, local_scope); + self.collect_free_vars_expr(end, lambda_params, free_vars, local_scope); + } + // Terminal expressions don't contain variables + ExprKind::Int(_) | ExprKind::Float(_) | ExprKind::Bool(_) | ExprKind::String(_) | ExprKind::Break | ExprKind::Continue => {} + } + } + /// Lower all lambdas in a program by hoisting them to functions pub fn lower_program(&self, nodes: &[ASTNode]) -> Result, String> { let mut lowered_nodes = Vec::new(); @@ -59,37 +186,47 @@ impl LambdaLowerer { fn lower_expr(&self, expr: &Expr) -> Result { let new_kind = match &expr.kind { ExprKind::Lambda(args, body) => { - // Generate a unique name for this lambda function - let lambda_id = { - let mut counter = self.lambda_counter.borrow_mut(); - *counter += 1; - *counter - }; - let lambda_name = format!("__suic_gen_lambda_{}", lambda_id); + // Collect free variables (captured variables) + let lambda_params: Vec = args.iter().map(|(name, _)| name.clone()).collect(); + let free_vars = self.collect_free_vars(body, &lambda_params); - // Lower the lambda body recursively - let lowered_body = self.lower_expr(body)?; + if free_vars.is_empty() { + // No captures - hoist to function like the original implementation + let lambda_id = { + let mut counter = self.lambda_counter.borrow_mut(); + *counter += 1; + *counter + }; + let lambda_name = format!("__suic_gen_lambda_{}", lambda_id); - // Create a new function for this lambda - let lambda_func = ASTNode { - kind: ASTNodeKind::Function(Function { - name: lambda_name.clone(), - parameters: Vec::new(), // No type parameters for now - args: args.clone(), - return_type: None, // Let typechecker infer return type - body: lowered_body, - }), - span: expr.span.clone(), - attributes: Vec::new(), - }; + // Lower the lambda body recursively + let lowered_body = self.lower_expr(body)?; - // Store the generated function - self.generated_functions - .borrow_mut() - .push(lambda_func); + // Create a new function for this lambda + let lambda_func = ASTNode { + kind: ASTNodeKind::Function(Function { + name: lambda_name.clone(), + parameters: Vec::new(), // No type parameters for now + args: args.clone(), + return_type: None, // Let typechecker infer return type + body: lowered_body, + }), + span: expr.span.clone(), + attributes: Vec::new(), + }; - // Replace the lambda with a reference to the generated function - ExprKind::Variable(lambda_name) + // Store the generated function + self.generated_functions + .borrow_mut() + .push(lambda_func); + + // Replace the lambda with a reference to the generated function + ExprKind::Variable(lambda_name) + } else { + // Has captures - keep as lambda, but recursively lower the body + let lowered_body = self.lower_expr(body)?; + ExprKind::Lambda(args.clone(), Box::new(lowered_body)) + } } ExprKind::Call(func, args) => { let lowered_func = self.lower_expr(func)?; diff --git a/src/monomorphize.rs b/src/monomorphize.rs index a0f5280..bbd9e0e 100644 --- a/src/monomorphize.rs +++ b/src/monomorphize.rs @@ -439,10 +439,11 @@ impl Monomorphizer { TypedExprKind::Lambda(params.clone(), Box::new(new_body)) } - TypedExprKind::Let(name, binding_kind, ty_annot, expr) => { + TypedExprKind::Let(id, name, binding_kind, ty_annot, expr) => { let (new_expr, expr_needs) = self.monomorphize_expr(expr)?; needs.extend(expr_needs); TypedExprKind::Let( + *id, name.clone(), binding_kind.clone(), ty_annot.clone(), @@ -527,12 +528,12 @@ impl Monomorphizer { TypedExprKind::UnOp(op.clone(), Box::new(new_operand)) } - TypedExprKind::For(var, iter_expr, body) => { + TypedExprKind::For(id, var, iter_expr, body) => { let (new_iter, iter_needs) = self.monomorphize_expr(iter_expr)?; let (new_body, body_needs) = self.monomorphize_expr(body)?; needs.extend(iter_needs); needs.extend(body_needs); - TypedExprKind::For(var.clone(), Box::new(new_iter), Box::new(new_body)) + TypedExprKind::For(*id, var.clone(), Box::new(new_iter), Box::new(new_body)) } TypedExprKind::Range(start, end) => { @@ -682,7 +683,7 @@ impl Monomorphizer { let mut new_args = Vec::new(); let mut needs = Vec::new(); - for (arg_name, arg_ty_opt) in &generic_func.args { + for (arg_id, arg_name, arg_ty_opt) in &generic_func.args { let new_arg_ty = if let Some(arg_ty) = arg_ty_opt { let ty = self.substitute_in_type_annot(arg_ty, &subst_map)?; self.collect_needs_from_type(&ty, &mut needs); @@ -690,7 +691,7 @@ impl Monomorphizer { } else { None }; - new_args.push((arg_name.clone(), new_arg_ty)); + new_args.push((*arg_id, arg_name.clone(), new_arg_ty)); } // Specialize return type @@ -986,7 +987,7 @@ fn check_function_for_typevars(func: &TypedFunction) -> Result<(), Monomorphizat )); } - for (_, ty_opt) in &func.args { + for (_, _, ty_opt) in &func.args { if let Some(ty) = ty_opt { if has_typevars_in_type_annot(ty) { return Err(MonomorphizationError::new( @@ -1056,7 +1057,7 @@ fn check_enum_for_typevars(e: &TypedEnum) -> Result<(), MonomorphizationError> { fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError> { match &expr.kind { TypedExprKind::Lambda(params, body) => { - for (_, ty_opt) in params { + for (_, _, ty_opt) in params { if let Some(ty) = ty_opt { if has_typevars_in_type_annot(ty) { return Err(MonomorphizationError::new( @@ -1068,7 +1069,7 @@ fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError } check_expr_for_typevars(body)?; } - TypedExprKind::Let(_, _, ty_opt, expr) => { + TypedExprKind::Let(_, _, _, ty_opt, expr) => { if let Some(ty) = ty_opt { if has_typevars_in_type_annot(ty) { return Err(MonomorphizationError::new( @@ -1160,7 +1161,7 @@ fn check_expr_for_typevars(expr: &TypedExpr) -> Result<(), MonomorphizationError TypedExprKind::UnOp(_, operand) => { check_expr_for_typevars(operand)?; } - TypedExprKind::For(_, iter, body) => { + TypedExprKind::For(_, _, iter, body) => { check_expr_for_typevars(iter)?; check_expr_for_typevars(body)?; } diff --git a/src/typechecker.rs b/src/typechecker.rs index 118d3d3..ae4b82d 100644 --- a/src/typechecker.rs +++ b/src/typechecker.rs @@ -84,14 +84,25 @@ pub enum TypeErrorKind { Other(String), } +#[derive(Clone)] +struct VarInfo { + ty: Type, + kind: BindingKind, + name: String, + usage: usize, + span: Span, +} + #[derive(Clone)] struct TypeEnv { - vars: HashMap, + vars: HashMap, + name_to_id: HashMap, types: HashMap, functions: HashMap, traits: HashMap, impls: Vec, type_vars: HashMap, + scopes: Vec>, } #[derive(Clone, Debug)] @@ -130,31 +141,100 @@ impl TypeEnv { fn new() -> Self { TypeEnv { vars: HashMap::new(), + name_to_id: HashMap::new(), types: HashMap::new(), functions: HashMap::new(), traits: HashMap::new(), impls: Vec::new(), type_vars: HashMap::new(), + scopes: Vec::new(), } } - fn enter_scope(&self) -> Self { - TypeEnv { - vars: self.vars.clone(), - types: self.types.clone(), - functions: self.functions.clone(), - traits: self.traits.clone(), - impls: self.impls.clone(), - type_vars: self.type_vars.clone(), + fn enter_scope(&mut self) { + self.scopes.push(Vec::new()); + } + + fn exit_scope(&mut self) -> Result<(), TypeError> { + if let Some(scope) = self.scopes.pop() { + for &id in &scope { + if let Some(var_info) = self.vars.get(&id) { + match var_info.kind { + BindingKind::Linear => { + if var_info.usage != 1 { + return Err(TypeError { + kind: TypeErrorKind::LinearityError(format!( + "Linear variable '{}' used {} times, must be exactly 1", + var_info.name, var_info.usage + )), + span: var_info.span.clone(), + }); + } + } + BindingKind::Affine => { + if var_info.usage > 1 { + return Err(TypeError { + kind: TypeErrorKind::LinearityError(format!( + "Affine variable '{}' used {} times, must be at most 1", + var_info.name, var_info.usage + )), + span: var_info.span.clone(), + }); + } + } + _ => {} + } + let name = var_info.name.clone(); + self.vars.remove(&id); + self.name_to_id.remove(&name); + } + } + } + Ok(()) + } + + fn add_var( + &mut self, + id: crate::ast::BindingId, + name: String, + ty: Type, + kind: BindingKind, + span: Span, + ) { + let var_info = VarInfo { + ty, + kind, + name: name.clone(), + usage: 0, + span, + }; + self.vars.insert(id, var_info); + if let Some(current) = self.scopes.last_mut() { + current.push(id); + } + self.name_to_id.insert(name, id); + } + + fn get_var(&self, id: &crate::ast::BindingId) -> Option<&VarInfo> { + self.vars.get(id) + } + + fn get_var_by_name(&self, name: &str) -> Option<(crate::ast::BindingId, &VarInfo)> { + if let Some(id) = self.name_to_id.get(name) { + if let Some(var_info) = self.vars.get(id) { + Some((*id, var_info)) + } else { + None + } + } else { + None } } - fn add_var(&mut self, name: String, ty: Type, kind: BindingKind) { - self.vars.insert(name, (ty, kind)); - } - - fn get_var(&self, name: &str) -> Option<&(Type, BindingKind)> { - self.vars.get(name) + fn increment_usage(&mut self, id: &crate::ast::BindingId) { + if let Some(var_info) = self.vars.get_mut(id) { + var_info.usage += 1; + } } fn add_type(&mut self, name: String, info: TypeInfo) { @@ -176,15 +256,23 @@ impl TypeEnv { pub struct TypeChecker { env: TypeEnv, + binding_id_counter: usize, } impl TypeChecker { pub fn new() -> Self { TypeChecker { env: TypeEnv::new(), + binding_id_counter: 0, } } + fn next_binding_id(&mut self) -> crate::ast::BindingId { + let id = crate::ast::BindingId(self.binding_id_counter); + self.binding_id_counter += 1; + id + } + pub fn typecheck_program(&mut self, nodes: &[ASTNode]) -> Result, TypeError> { // First pass: collect all type definitions, function signatures, etc. for node in nodes { @@ -192,10 +280,12 @@ impl TypeChecker { } // Second pass: typecheck everything + self.env.enter_scope(); let mut typed_nodes = Vec::new(); for node in nodes { typed_nodes.push(self.typecheck_node(node)?); } + self.env.exit_scope()?; Ok(typed_nodes) } @@ -240,7 +330,13 @@ impl TypeChecker { .return_type .as_ref() .map(|t| self.type_annot_to_type(t)) - .unwrap_or(Type::Unit); + .unwrap_or_else(|| { + if f.name.starts_with("__suic_gen_lambda_") { + Type::Unknown + } else { + Type::Unit + } + }); let func_type = FunctionType { type_params: f.parameters.iter().map(|p| p.name.clone()).collect(), @@ -325,11 +421,15 @@ impl TypeChecker { kind: TypedASTNodeKind::Struct(TypedStruct { name: s.name.clone(), parameters: s.parameters.clone(), - fields: s.fields.iter().map(|f| TypedField { - name: f.name.clone(), - field_type: f.field_type.clone(), - span: f.span.clone(), - }).collect(), + fields: s + .fields + .iter() + .map(|f| TypedField { + name: f.name.clone(), + field_type: f.field_type.clone(), + span: f.span.clone(), + }) + .collect(), }), span: node.span.clone(), attributes: node.attributes.clone(), @@ -342,11 +442,15 @@ impl TypeChecker { kind: TypedASTNodeKind::Enum(TypedEnum { name: e.name.clone(), parameters: e.parameters.clone(), - variants: e.variants.iter().map(|v| TypedVariant { - name: v.name.clone(), - fields: v.fields.clone(), - span: v.span.clone(), - }).collect(), + variants: e + .variants + .iter() + .map(|v| TypedVariant { + name: v.name.clone(), + fields: v.fields.clone(), + span: v.span.clone(), + }) + .collect(), }), span: node.span.clone(), attributes: node.attributes.clone(), @@ -424,11 +528,8 @@ impl TypeChecker { } fn typecheck_function(&mut self, func: &Function) -> Result { - // Save the original environment - let original_env = self.env.clone(); - // Enter new scope for function - self.env = self.env.enter_scope(); + self.env.enter_scope(); // Add type parameters to environment for param in &func.parameters { @@ -439,28 +540,41 @@ impl TypeChecker { // Add function parameters to environment let mut param_types = Vec::new(); + let mut typed_args = Vec::new(); for (arg_name, arg_type_annot) in &func.args { + let arg_id = self.next_binding_id(); let arg_type = arg_type_annot .as_ref() .map(|t| self.type_annot_to_type(t)) .unwrap_or(Type::Unknown); param_types.push(arg_type.clone()); - self.env - .add_var(arg_name.clone(), arg_type, BindingKind::Default); + self.env.add_var( + arg_id, + arg_name.clone(), + arg_type, + BindingKind::Default, + func.body.span.clone(), + ); + typed_args.push((arg_id, arg_name.clone(), arg_type_annot.clone())); } // Typecheck function body let typed_body = self.typecheck_expr(&func.body)?; - // Restore the original environment - self.env = original_env; + // Exit scope, checking usages + self.env.exit_scope()?; // Check return type - let expected_return = func - .return_type - .as_ref() - .map(|t| self.type_annot_to_type(t)) - .unwrap_or(Type::Unit); + let expected_return = + if func.name.starts_with("__suic_gen_lambda_") && func.return_type.is_none() { + // For generated lambda functions, infer return type from body + typed_body.ty.clone() + } else { + func.return_type + .as_ref() + .map(|t| self.type_annot_to_type(t)) + .unwrap_or(Type::Unit) + }; if !self.types_compatible(&typed_body.ty, &expected_return) { return Err(TypeError { @@ -474,7 +588,7 @@ impl TypeChecker { Ok(TypedFunction { name: func.name.clone(), parameters: func.parameters.clone(), - args: func.args.clone(), + args: typed_args, return_type: func.return_type.clone(), body: typed_body, ty: func_type, @@ -490,8 +604,10 @@ impl TypeChecker { ExprKind::Variable(name) => { // First check if it's a variable - if let Some((t, _)) = self.env.get_var(name) { - (TypedExprKind::Variable(name.clone()), t.clone()) + if let Some((id, var_info)) = self.env.get_var_by_name(name) { + let ty = var_info.ty.clone(); + self.env.increment_usage(&id); + (TypedExprKind::Variable(name.clone()), ty) } else if let Some(func_type) = self.env.get_function(name) { // If not a variable, check if it's a function let func_type_clone = func_type.clone(); @@ -689,11 +805,18 @@ impl TypeChecker { typed_value.ty.clone() }; - self.env - .add_var(name.clone(), var_type.clone(), binding_kind.clone()); + let var_id = self.next_binding_id(); + self.env.add_var( + var_id, + name.clone(), + var_type.clone(), + binding_kind.clone(), + expr.span.clone(), + ); ( TypedExprKind::Let( + var_id, name.clone(), binding_kind.clone(), type_annot.clone(), @@ -776,13 +899,25 @@ impl TypeChecker { _ => Type::Unknown, }; - self.env = self.env.enter_scope(); - self.env - .add_var(var.clone(), element_type, BindingKind::Default); + self.env.enter_scope(); + let var_id = self.next_binding_id(); + self.env.add_var( + var_id, + var.clone(), + element_type, + BindingKind::Default, + expr.span.clone(), + ); let typed_body = self.typecheck_expr(body)?; + self.env.exit_scope()?; ( - TypedExprKind::For(var.clone(), Box::new(typed_iterable), Box::new(typed_body)), + TypedExprKind::For( + var_id, + var.clone(), + Box::new(typed_iterable), + Box::new(typed_body), + ), Type::Unit, ) } @@ -1007,8 +1142,10 @@ impl TypeChecker { let mut result_type = Type::Unknown; for (i, (pattern, body)) in arms.iter().enumerate() { + self.env.enter_scope(); let typed_pattern = self.typecheck_pattern(pattern, &typed_scrutinee.ty)?; let typed_body = self.typecheck_expr(body)?; + self.env.exit_scope()?; if i == 0 { result_type = typed_body.ty.clone(); @@ -1029,29 +1166,62 @@ impl TypeChecker { } ExprKind::Lambda(params, body) => { - self.env = self.env.enter_scope(); - + // Enter scope for lambda parameters + self.env.enter_scope(); let mut param_types = Vec::new(); + let mut typed_params = Vec::new(); for (param_name, param_type_annot) in params { + let param_id = self.next_binding_id(); let param_type = param_type_annot .as_ref() .map(|t| self.type_annot_to_type(t)) .unwrap_or(Type::Unknown); param_types.push(param_type.clone()); - self.env - .add_var(param_name.clone(), param_type, BindingKind::Default); + self.env.add_var( + param_id, + param_name.clone(), + param_type, + BindingKind::Default, + expr.span.clone(), + ); + typed_params.push((param_id, param_name.clone(), param_type_annot.clone())); } let typed_body = self.typecheck_expr(body)?; let func_type = Type::Function(param_types, Box::new(typed_body.ty.clone())); + // Exit scope, checking usages + self.env.exit_scope()?; + ( - TypedExprKind::Lambda(params.clone(), Box::new(typed_body)), + TypedExprKind::Lambda(typed_params, Box::new(typed_body)), func_type, ) } ExprKind::Assign(lhs, rhs) => { + // Check if lhs is a mutable variable + if let ExprKind::Variable(name) = &lhs.kind { + if let Some((_, var_info)) = self.env.get_var_by_name(name) { + if var_info.kind != BindingKind::Mutable { + return Err(TypeError { + kind: TypeErrorKind::MutableityError(format!( + "Cannot assign to immutable variable '{}'", + name + )), + span: lhs.span.clone(), + }); + } + } + } else { + return Err(TypeError { + kind: TypeErrorKind::MutableityError( + "Invalid left-hand side of assignment".to_string(), + ), + span: lhs.span.clone(), + }); + } + let typed_lhs = self.typecheck_expr(lhs)?; let typed_rhs = self.typecheck_expr(rhs)?; @@ -1170,10 +1340,16 @@ impl TypeChecker { PatternKind::Wildcard => (TypedPatternKind::Wildcard, scrutinee_type.clone()), PatternKind::Variable(name) => { - self.env - .add_var(name.clone(), scrutinee_type.clone(), BindingKind::Default); + let var_id = self.next_binding_id(); + self.env.add_var( + var_id, + name.clone(), + scrutinee_type.clone(), + BindingKind::Default, + pattern.span.clone(), + ); ( - TypedPatternKind::Variable(name.clone()), + TypedPatternKind::Variable(var_id, name.clone()), scrutinee_type.clone(), ) } diff --git a/test_affine.sui b/test_affine.sui new file mode 100644 index 0000000..fdb5645 --- /dev/null +++ b/test_affine.sui @@ -0,0 +1,5 @@ +fn test_affine() -> int do + let uniq x = 5 + x + 1 + x + 2 +end \ No newline at end of file diff --git a/test_binding.sui b/test_binding.sui new file mode 100644 index 0000000..8761b1d --- /dev/null +++ b/test_binding.sui @@ -0,0 +1,5 @@ +fn test_binding_id() -> int do + let x = 5 + let x = x + 1 # shadowing + x +end \ No newline at end of file diff --git a/test_linear.sui b/test_linear.sui new file mode 100644 index 0000000..c663c16 --- /dev/null +++ b/test_linear.sui @@ -0,0 +1,4 @@ +fn test_linear() -> int do + let once y = 10 + 5 +end \ No newline at end of file diff --git a/test_linear_ok.sui b/test_linear_ok.sui new file mode 100644 index 0000000..c96a794 --- /dev/null +++ b/test_linear_ok.sui @@ -0,0 +1,4 @@ +fn test_linear_ok() -> int do + let once z = 10 + z + 5 +end \ No newline at end of file diff --git a/test_scoping.sui b/test_scoping.sui new file mode 100644 index 0000000..6f3438f --- /dev/null +++ b/test_scoping.sui @@ -0,0 +1,7 @@ +fn test_scoping() -> int do + let x = 5 + let f = lambda (y) y + 1 # simple lambda + f = lambda (z) z+1 + let x = 10 # shadow x + f(3) # should work +end diff --git a/test_shadow.sui b/test_shadow.sui new file mode 100644 index 0000000..ffbb6ac --- /dev/null +++ b/test_shadow.sui @@ -0,0 +1,5 @@ +fn test_shadow() -> int do + let x = 5 + let x = 10 + x +end \ No newline at end of file