added bindingkind verification

This commit is contained in:
Masashi 2025-12-15 17:02:43 +05:30
commit b959e11e51
11 changed files with 448 additions and 98 deletions

3
simple.sui Normal file
View file

@ -0,0 +1,3 @@
fn simple() -> int do
42
end

View file

@ -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<Parameter>,
pub args: Vec<(String, Option<TypeAnnot>)>,
pub args: Vec<(BindingId, String, Option<TypeAnnot>)>,
pub return_type: Option<TypeAnnot>,
pub body: TypedExpr,
pub ty: Type,
@ -395,8 +398,8 @@ pub enum TypedExprKind {
Dot(Box<TypedExpr>, String),
EarlyReturn(Option<Box<TypedExpr>>),
OptionalChain(Option<Box<TypedExpr>>, String),
Lambda(Vec<(String, Option<TypeAnnot>)>, Box<TypedExpr>),
Let(String, BindingKind, Option<TypeAnnot>, Box<TypedExpr>),
Lambda(Vec<(BindingId, String, Option<TypeAnnot>)>, Box<TypedExpr>),
Let(BindingId, String, BindingKind, Option<TypeAnnot>, Box<TypedExpr>),
Assign(Box<TypedExpr>, Box<TypedExpr>),
Cast(Box<TypedExpr>, TypeAnnot),
If(Box<TypedExpr>, Box<TypedExpr>, Option<Box<TypedExpr>>),
@ -405,7 +408,7 @@ pub enum TypedExprKind {
Do(Vec<TypedExpr>),
BinOp(Box<TypedExpr>, BinOp, Box<TypedExpr>),
UnOp(UnOp, Box<TypedExpr>),
For(String, Box<TypedExpr>, Box<TypedExpr>),
For(BindingId, String, Box<TypedExpr>, Box<TypedExpr>),
Range(Box<TypedExpr>, Box<TypedExpr>),
Return(Option<Box<TypedExpr>>),
Break,
@ -422,7 +425,7 @@ pub struct TypedPattern {
#[derive(Debug, Clone)]
pub enum TypedPatternKind {
Wildcard,
Variable(String),
Variable(BindingId, String),
Literal(String),
Tuple(Vec<TypedPattern>),
Struct(String, Vec<(String, TypedPattern)>),

View file

@ -17,6 +17,133 @@ impl LambdaLowerer {
}
}
fn collect_free_vars(&self, expr: &Expr, lambda_params: &[String]) -> std::collections::HashSet<String> {
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<String>, local_scope: &mut std::collections::HashSet<String>) {
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<String> = 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, &param_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<Vec<ASTNode>, String> {
let mut lowered_nodes = Vec::new();
@ -59,37 +186,47 @@ impl LambdaLowerer {
fn lower_expr(&self, expr: &Expr) -> Result<Expr, String> {
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<String> = 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)?;

View file

@ -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)?;
}

View file

@ -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<String, (Type, BindingKind)>,
vars: HashMap<crate::ast::BindingId, VarInfo>,
name_to_id: HashMap<String, crate::ast::BindingId>,
types: HashMap<String, TypeInfo>,
functions: HashMap<String, FunctionType>,
traits: HashMap<String, TraitInfo>,
impls: Vec<ImplInfo>,
type_vars: HashMap<String, Type>,
scopes: Vec<Vec<crate::ast::BindingId>>,
}
#[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<Vec<TypedASTNode>, 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<TypedFunction, TypeError> {
// 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(),
)
}

5
test_affine.sui Normal file
View file

@ -0,0 +1,5 @@
fn test_affine() -> int do
let uniq x = 5
x + 1
x + 2
end

5
test_binding.sui Normal file
View file

@ -0,0 +1,5 @@
fn test_binding_id() -> int do
let x = 5
let x = x + 1 # shadowing
x
end

4
test_linear.sui Normal file
View file

@ -0,0 +1,4 @@
fn test_linear() -> int do
let once y = 10
5
end

4
test_linear_ok.sui Normal file
View file

@ -0,0 +1,4 @@
fn test_linear_ok() -> int do
let once z = 10
z + 5
end

7
test_scoping.sui Normal file
View file

@ -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

5
test_shadow.sui Normal file
View file

@ -0,0 +1,5 @@
fn test_shadow() -> int do
let x = 5
let x = 10
x
end