added bindingkind verification
This commit is contained in:
parent
6a1362390c
commit
b959e11e51
11 changed files with 448 additions and 98 deletions
3
simple.sui
Normal file
3
simple.sui
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fn simple() -> int do
|
||||
42
|
||||
end
|
||||
13
src/ast.rs
13
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<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)>),
|
||||
|
|
|
|||
|
|
@ -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, ¶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<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)?;
|
||||
|
|
|
|||
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
5
test_affine.sui
Normal 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
5
test_binding.sui
Normal 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
4
test_linear.sui
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
fn test_linear() -> int do
|
||||
let once y = 10
|
||||
5
|
||||
end
|
||||
4
test_linear_ok.sui
Normal file
4
test_linear_ok.sui
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
fn test_linear_ok() -> int do
|
||||
let once z = 10
|
||||
z + 5
|
||||
end
|
||||
7
test_scoping.sui
Normal file
7
test_scoping.sui
Normal 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
5
test_shadow.sui
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fn test_shadow() -> int do
|
||||
let x = 5
|
||||
let x = 10
|
||||
x
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue