diff --git a/examples/math/main.bl b/examples/math/main.bl index faf0e91..5e4962b 100644 --- a/examples/math/main.bl +++ b/examples/math/main.bl @@ -1,14 +1,14 @@ // adds a and b, but also 4 for some reason -fn add(a: I32, b: I32): I32 { +fn add(a: I64, b: I64): I64 { let foo = 4; // because I feel like it - let test_float: F32 = { + let test_float: F64 = { 10.2 }; test_float = 5.0; a + b + foo } -fn subtract(a: I32, b: I32): I32 { +fn subtract(a: I64, b: I64): I64 { a - b } @@ -25,10 +25,10 @@ fn i_hate_this(a: F64): F64 { } fn unit_function() { - let a: I32 = 4; + let a: I64 = 4; } -fn main(): I32 { +fn main(): I64 { add(4, subtract(5, 2)) } @@ -39,28 +39,28 @@ fn returns_user(): User { }; } -fn get_user_id(): U64 { +fn get_user_id(): I64 { let user = returns_user(); user.id = 5; return user.id; } -fn use_method(user: User): U64 { +fn use_method(user: User): I64 { return user.get_id(); } type User struct { - id: U64, + id: I64, } impl User { - fn new(id: U64): Self { + fn new(id: I64): Self { return Self{ id: id, }; } - fn get_id(self: Self): U64 { + fn get_id(self: Self): I64 { return self.id; } } diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index c8b9473..a169b72 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -37,7 +37,7 @@ pub LiteralInt: String = { }; pub SpannedLiteralInt: ast::LiteralInt = { - > => ast::LiteralInt{value: literal_int, type_: ast::TypeUsage::new_builtin("i64".to_string())} + > => ast::LiteralInt{value: literal_int, type_: ast::TypeUsage::new_builtin("I64".to_string())} }; pub LiteralFloat: String = { @@ -45,7 +45,7 @@ pub LiteralFloat: String = { }; pub SpannedLiteralFloat: ast::LiteralFloat = { - > => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("f64".to_string())} + > => ast::LiteralFloat{value: literal_float, type_: ast::TypeUsage::new_builtin("F64".to_string())} }; pub Identifier: String = { @@ -149,7 +149,7 @@ pub Statement: ast::Statement = { pub Block: ast::Block = { "{" )*> "}" => match e { - None => ast::Block{statements: v, type_: ast::new_never()}, + None => ast::Block{statements: v, type_: ast::TypeUsage::new_unknown(&id_generator)}, Some(e) => { let mut v = v; v.push(ast::Statement::Expression(e)); @@ -160,7 +160,7 @@ pub Block: ast::Block = { pub TypeUsage: ast::TypeUsage = { => ast::TypeUsage::Named(ast::NamedTypeUsage{name: n}), - "fn" "(" > ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::TypeUsage::new_unknown(&id_generator))}), + "fn" "(" > ")" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(ast::new_unit())}), "fn" "(" > ")" ":" => ast::TypeUsage::Function(ast::FunctionTypeUsage{arguments: args, return_type: Box::new(rt)}), }; @@ -169,7 +169,7 @@ pub VariableDeclaration: ast::VariableDeclaration = { }; pub FunctionDeclaration: ast::FunctionDeclaration = { - "fn" "(" > ")" => ast::FunctionDeclaration{name: n, arguments: args, return_type: ast::TypeUsage::new_unknown(&id_generator)}, + "fn" "(" > ")" => ast::FunctionDeclaration{name: n, arguments: args, return_type: ast::new_unit()}, "fn" "(" > ")" ":" => ast::FunctionDeclaration{name: n, arguments: args, return_type: rt}, }; diff --git a/src/main.rs b/src/main.rs index ebc436a..e3948be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,7 +48,7 @@ fn main() { println!("resolved ast: {:#?}", &resolved_ast); let type_checker = type_checking::TypeChecker{}; let (checked_ast, subst) = type_checker.with_module(&resolved_ast); - println!("checked ast: {:#?}", &resolved_ast); + println!("checked ast: {:#?}", &checked_ast); println!("substitutions: {:#?}", &subst); // let context = Context::create(); diff --git a/src/type_alias_resolution.rs b/src/type_alias_resolution.rs index a40a80a..9e46052 100644 --- a/src/type_alias_resolution.rs +++ b/src/type_alias_resolution.rs @@ -212,8 +212,13 @@ impl TypeAliasResolver { }) }, ast::Subexpression::LiteralStruct(literal_struct) => { + let result = resolve_type(ctx, &ast::NamedTypeUsage{name: literal_struct.name.clone()}); + let new_name = match &result { + ast::TypeUsage::Named(named) => { named.name.clone() }, + _ => panic!("LiteralStruct resolved to non-named-type"), + }; ast::Subexpression::LiteralStruct(ast::LiteralStruct{ - name: literal_struct.name.clone(), + name: new_name.clone(), fields: literal_struct.fields.iter().map(|field|{ (field.0.clone(), self.with_expression(ctx, &field.1)) }).collect(), diff --git a/src/type_checking.rs b/src/type_checking.rs index 2c8808f..563756f 100644 --- a/src/type_checking.rs +++ b/src/type_checking.rs @@ -4,34 +4,36 @@ use crate::ast; pub type SubstitutionMap = HashMap; +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum NamedEntity { TypeDeclaration(ast::TypeDeclaration), Variable(ast::TypeUsage), } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq)] struct Context { pub current_function_return: Option, - pub environment: HashMap, + pub impls: HashMap, + pub environment: HashMap, } impl Context { fn add_variable(&self, name: String, type_usage: &ast::TypeUsage) -> Context { let mut ctx = self.clone(); - ctx.environment[name] = NamedEntity::Variable(type_usage.clone()); + ctx.environment.insert(name.to_string(), NamedEntity::Variable(type_usage.clone())); return ctx; } fn add_type(&self, name: String, type_decl: &ast::TypeDeclaration) -> Context { let mut ctx = self.clone(); - ctx.environment[name] = NamedEntity::TypeDeclaration(type_decl.clone()); + ctx.environment.insert(name.to_string(), NamedEntity::TypeDeclaration(type_decl.clone())); return ctx; } - fn set_current_function_return(&self, function: ast::TypeUsage) -> Context { + fn set_current_function_return(&self, function: &ast::TypeUsage) -> Context { let mut ctx = self.clone(); - ctx.current_function_return = Some(function); + ctx.current_function_return = Some(function.clone()); return ctx; } } @@ -40,75 +42,90 @@ fn apply_substitution(substitution: &SubstitutionMap, type_: &ast::TypeUsage) -> match type_ { ast::TypeUsage::Named(named) => ast::TypeUsage::Named(named.clone()), ast::TypeUsage::Unknown(unknown) => { - if substitution.contains_key(unknown.name) { - ast::TypeUsage::Unknown(substitution[unknown.name].clone()) + if substitution.contains_key(&unknown.name) { + substitution[&unknown.name].clone() } else { ast::TypeUsage::Unknown(unknown.clone()) } }, ast::TypeUsage::Function(function) => { - ast::TypeUsage::Function(FunctionTypeUsage{ + ast::TypeUsage::Function(ast::FunctionTypeUsage{ arguments: function.arguments.iter().map(|arg| { apply_substitution(substitution, arg) }).collect(), - return_type: apply_substitution(substitution, function.return_type), + return_type: Box::new(apply_substitution(substitution, &function.return_type)), }) } } } -fn compose_substitutions(s1: SubstitutionMap, s2: SubstitutionMap) -> SubstitutionMap { +fn compose_substitutions(s1: &SubstitutionMap, s2: &SubstitutionMap) -> SubstitutionMap { let mut result = SubstitutionMap::new(); for k in s2.keys() { - result[k] = apply_substitution(s1, s2[k]); + result.insert(k.to_string(), apply_substitution(s1, &s2[k])); } - return s1.into_iter().chain(result).collect(); + return s1.into_iter().map(|(k, v)| (k.clone(), v.clone())).chain(result).collect(); } -fn unify(t1: ast::TypeUsage, t2: ast::TypeUsage) -> SubstitutionMap { +fn unify(t1: &ast::TypeUsage, t2: &ast::TypeUsage) -> SubstitutionMap { match (t1, t2) { (ast::TypeUsage::Named(named1), ast::TypeUsage::Named(named2)) => { + // if named1.name.name.value == "!" || named2.name.name.value == "!" { + // return SubstitutionMap::new(); // never matches with everything + // } if named1.name.name.value == named2.name.name.value { - return SubstitutionMap::new() + return SubstitutionMap::new(); } }, _ => {}, } - if let ast::TypeUsage::Unknown(unknown) = t1 { - return var_bind(unknown.name, t2); + match t1 { + ast::TypeUsage::Unknown(unknown) => { + return var_bind(&unknown.name, t2); + }, + _ => {}, } - if let ast::TypeUsage::Unknown(unknown) = t2 { - return var_bind(unknown.name, t1); + match t2 { + ast::TypeUsage::Unknown(unknown) => { + return var_bind(&unknown.name, t1); + }, + _ => {}, } match (t1, t2) { (ast::TypeUsage::Function(f1), ast::TypeUsage::Function(f2)) => { - let mut result = unify(f1.return_type, f2.return_type); + let mut result = unify(&*f1.return_type, &*f2.return_type); if f1.arguments.len() != f2.arguments.len() { panic!("Argument lengths don't match"); } for (i, _) in f1.arguments.iter().enumerate() { - result = compose_substitutions(result, unify(apply_substitution(result, f1.arguments[i]), apply_substitution(result, f2.arguments[i]))); + result = compose_substitutions(&result, &unify(&apply_substitution(&result, &f1.arguments[i]), &apply_substitution(&result, &f2.arguments[i]))); } return result; }, _ => {}, } + println!("problem:\n{:?}\n{:?}", t1, t2); panic!("Mismatched unification types"); } -fn var_bind(name: &str, t: ast::TypeUsage) -> SubstitutionMap { - if let ast::TypeUsage::Unknown(unknown) = t && name == unknown.name { - return SubstitutionMap::new(); +fn var_bind(name: &str, t: &ast::TypeUsage) -> SubstitutionMap { + match t { + ast::TypeUsage::Unknown(unknown) => { + if name == unknown.name { + return SubstitutionMap::new(); + } + }, + _ => {} } if contains(t, name) { panic!("Type contains a reference to itself") } let mut substitution = SubstitutionMap::new(); - substitution[name] = t; + substitution.insert(name.to_string(), t.clone()); return substitution; } -fn contains(t: ast::TypeUsage, name: &str) -> bool { +fn contains(t: &ast::TypeUsage, name: &str) -> bool { match t { ast::TypeUsage::Named(_) => { return false @@ -117,11 +134,11 @@ fn contains(t: ast::TypeUsage, name: &str) -> bool { unknown.name == name }, ast::TypeUsage::Function(f) => { - if contains(f.return_type, name) { + if contains(&*f.return_type, name) { return true; } for arg in f.arguments.iter() { - if contains(arg, name) { + if contains(&arg.clone(), name) { return true; } } @@ -137,46 +154,77 @@ impl TypeChecker { pub fn with_module(self: &Self, module: &ast::Module) -> (ast::Module, SubstitutionMap) { let mut ctx = Context{ environment: HashMap::new(), //TODO: builtins + impls: HashMap::new(), + current_function_return: None, }; for item in module.items.iter() { match item { ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => { - ctx.declarations.push(ast::NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone()))); + ctx.environment.insert(struct_.name.name.value.to_string(), NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone()))); }, ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => { - ctx.declarations.push(ast::NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone()))); + ctx.environment.insert(alias.name.name.value.to_string(), NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone()))); + }, + ast::ModuleItem::Function(function) => { + let function_type = ast::FunctionTypeUsage{ + arguments: function.declaration.arguments.iter().map(|arg|{arg.type_.clone()}).collect(), + return_type: Box::new(function.declaration.return_type.clone()), + }; + ctx.environment.insert(function.declaration.name.name.value.to_string(), NamedEntity::Variable(ast::TypeUsage::Function(function_type))); + }, + ast::ModuleItem::Impl(impl_) => { + ctx.impls.insert(impl_.struct_name.name.value.to_string(), impl_.clone()); }, _ => {}, } } - return ast::Module{ + let mut subst = SubstitutionMap::new(); + let result = ast::Module{ items: module.items.iter().map(|item|{ match item { ast::ModuleItem::Function(function) => { - ast::ModuleItem::Function(self.with_function(&ctx, function)) + let (func, fn_subst) = self.with_function(&ctx, &subst, function); + subst = compose_substitutions(&subst, &fn_subst); + ast::ModuleItem::Function(func) }, ast::ModuleItem::TypeDeclaration(type_declaration) => { - ast::ModuleItem::TypeDeclaration(self.with_type_declaration(&ctx, type_declaration)) + let (ty_decl, ty_subst) = self.with_type_declaration(&ctx, type_declaration); + subst = compose_substitutions(&subst, &ty_subst); + ast::ModuleItem::TypeDeclaration(ty_decl) }, ast::ModuleItem::Impl(impl_) => { - ast::ModuleItem::Impl(self.with_impl(&ctx, impl_)) + let (impl_result, impl_subst) = self.with_impl(&ctx, &subst, impl_); + subst = compose_substitutions(&subst, &impl_subst); + ast::ModuleItem::Impl(impl_result) }, } }).collect() }; + return (result, subst); } - fn with_function(self: &Self, ctx: &Context, function: &ast::Function) -> (ast::Function, SubstitutionMap) { + fn with_function(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, function: &ast::Function) -> (ast::Function, SubstitutionMap) { // add args to env - let mut function_ctx = ctx.set_current_function_return(function.declaration.return_type.clone()); + let mut function_ctx = ctx.set_current_function_return(&function.declaration.return_type.clone()); for arg in function.declaration.arguments.iter() { - function_ctx = function_ctx.add_variable(arg.name.to_string(), arg.type_.clone()); + function_ctx = function_ctx.add_variable(arg.name.name.value.to_string(), &arg.type_.clone()); + } + + let (block, substitution) = self.with_block(&function_ctx, incoming_substitutions, &function.block); + let mut substitution = compose_substitutions(incoming_substitutions, &substitution); + match &block.type_ { + ast::TypeUsage::Named(named) => { + if named.name.name.value != "!" { + substitution = compose_substitutions(&substitution, &unify(&function.declaration.return_type, &block.type_)); + } + }, + _ => { + substitution = compose_substitutions(&substitution, &unify(&function.declaration.return_type, &block.type_)); + } } - let (block, substitution) = self.with_block(function_ctx, &function.block); - let substitution = unify(block.type_, function.declaration.return_type); return (ast::Function{ declaration: ast::FunctionDeclaration{ @@ -193,8 +241,8 @@ impl TypeChecker { fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> (ast::TypeDeclaration, SubstitutionMap) { match type_declaration { ast::TypeDeclaration::Struct(struct_) => { - let (result, substitution) = self.with_struct_declaration(ctx, struct_); - return (ast::TypeDeclaration::Struct(result), substitution); + let result = self.with_struct_declaration(ctx, struct_); + return (ast::TypeDeclaration::Struct(result), SubstitutionMap::new()); }, ast::TypeDeclaration::Primitive(primitive) => { return (ast::TypeDeclaration::Primitive(primitive.clone()), SubstitutionMap::new()); @@ -217,174 +265,327 @@ impl TypeChecker { }; } - fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> (ast::Impl, SubstitutionMap) { - let mut substitutions = SubstitutionMap::new(); + fn with_impl(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, impl_: &ast::Impl) -> (ast::Impl, SubstitutionMap) { + let mut substitutions = incoming_substitutions.clone(); return (ast::Impl{ struct_name: impl_.struct_name.clone(), functions: impl_.functions.iter().map(|f|{ - let (result, function_subs) = self.with_function(&ctx, f); - substitutions = compose_substitutions(substitutions, function_subs) + let (result, function_subs) = self.with_function(&ctx, &substitutions, f); + substitutions = compose_substitutions(&substitutions, &function_subs); result }).collect(), }, substitutions); } - fn with_block(self: &Self, ctx: &Context, block: &ast::Block) -> (ast::Block, SubstitutionMap) { - let mut substitutions = SubstitutionMap::new(); + fn with_block(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, block: &ast::Block) -> (ast::Block, SubstitutionMap) { + let mut substitutions = incoming_substitutions.clone(); let mut block_ctx = ctx.clone(); - return ast::Block{ - statements: block.statements.iter().map(|s| { - let (statement_ctx, result, statement_substitutions) = self.with_statement(block_ctx, s); - block_ctx = statement_ctx - substitutions = compose_substitutions(substitutions, statement_substitutions); - result - }).collect(), - type_: block.type_.clone(), + // if return it's always never + // if last is expression it's that else unit + let mut has_return = false; + for statement in block.statements.iter() { + match statement { + ast::Statement::Return(_) => { + has_return = true; + }, + _ => {} + } + } + let statements = block.statements.iter().map(|s| { + let (statement_ctx, result, statement_substitutions) = self.with_statement(&block_ctx, &substitutions, s); + block_ctx = statement_ctx; + substitutions = compose_substitutions(&substitutions, &statement_substitutions); + result + }).collect(); + if !has_return { + match block.statements.last().unwrap() { + ast::Statement::Expression(expr) => { + substitutions = compose_substitutions(&substitutions, &unify(&block.type_, &expr.type_)); + }, + _ => { + substitutions = compose_substitutions(&substitutions, &unify(&block.type_, &ast::new_unit())); + } + } + } + let result_type = if has_return { + ast::new_never() + } else { + apply_substitution(&substitutions, &block.type_) }; + let block_result = ast::Block{ + statements: statements, + type_: result_type, + }; + return (block_result, substitutions); } - fn with_statement(self: &Self, ctx: &Context, statement: &ast::Statement) -> (Context, ast::Statement, SubstitutionMap) { - + fn with_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::Statement) -> (Context, ast::Statement, SubstitutionMap) { match statement { ast::Statement::Return(return_statement) => { - let (result, subst) = self.with_return_statement(ctx, return_statement); + let (result, subst) = self.with_return_statement(ctx, incoming_substitutions, return_statement); + let subst = compose_substitutions(&incoming_substitutions, &subst); return (ctx.clone(), ast::Statement::Return(result), subst); }, ast::Statement::Let(let_statement) => { - let (let_ctx, result, subst) = self.with_let_statement(ctx, let_statement); + let (let_ctx, result, subst) = self.with_let_statement(ctx, incoming_substitutions, let_statement); + let subst = compose_substitutions(&incoming_substitutions, &subst); return (let_ctx, ast::Statement::Let(result), subst); }, ast::Statement::Assignment(assignment_statement) => { - let (result, subst) = self.with_assignment_statement(ctx, assignment_statement); + let (result, subst) = self.with_assignment_statement(ctx, incoming_substitutions, assignment_statement); + let subst = compose_substitutions(&incoming_substitutions, &subst); return (ctx.clone(), ast::Statement::Assignment(result), subst); }, ast::Statement::Expression(expression) => { - let (result, subst) = self.with_expression(ctx, expression); + let (result, subst) = self.with_expression(ctx, incoming_substitutions, expression); + let subst = compose_substitutions(&incoming_substitutions, &subst); return (ctx.clone(), ast::Statement::Expression(result), subst); }, } } - fn with_return_statement(self: &Self, ctx: &Context, statement: &ast::ReturnStatement) -> (ast::ReturnStatement, SubstitutionMap) { - let (result, subst) = self.with_expression(ctx, &statement.source); - let substitution = compose_substitutions(subst, unify(result.type_, ctx.current_function_return)); + fn with_return_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::ReturnStatement) -> (ast::ReturnStatement, SubstitutionMap) { + let (result, subst) = self.with_expression(ctx, incoming_substitutions, &statement.source); + let mut substitution = compose_substitutions(&incoming_substitutions, &subst); + let mut is_never = false; + match &result.type_ { + ast::TypeUsage::Named(named) => { + if named.name.name.value == "!" { + is_never = true; + } + }, + _ => {}, + } + if !is_never { + substitution = compose_substitutions(&subst, &unify(&ctx.current_function_return.as_ref().unwrap(), &result.type_)); + } + return (ast::ReturnStatement{ source: result, }, substitution); } - fn with_let_statement(self: &Self, ctx: &Context, statement: &ast::LetStatement) -> (Context, ast::LetStatement, SubstitutionMap) { - let (result, subst) = self.with_expression(ctx, &statement.expression); - let let_ctx = ctx.add_variable(statement.variable_name.clone(), result.type_); - let substitution = compose_substitutions(subst, unify(&statement.type_, result.type_)); + fn with_let_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::LetStatement) -> (Context, ast::LetStatement, SubstitutionMap) { + let (result, subst) = self.with_expression(ctx, incoming_substitutions, &statement.expression); + let let_ctx = ctx.add_variable(statement.variable_name.name.value.clone(), &result.type_); + let substitution = compose_substitutions(&subst, &unify(&statement.type_, &result.type_)); return (let_ctx, ast::LetStatement{ variable_name: statement.variable_name.clone(), expression: result, - type_: &statement.type_.clone(), + type_: apply_substitution(&substitution, &statement.type_), }, substitution); } - fn with_assignment_statement(self: &Self, ctx: &Context, statement: &ast::AssignmentStatement) -> ast::AssignmentStatement { + fn with_assignment_statement(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, statement: &ast::AssignmentStatement) -> (ast::AssignmentStatement, SubstitutionMap) { + let (expr, subst) = self.with_expression(ctx, incoming_substitutions, &statement.expression); + let mut substitution = compose_substitutions(&incoming_substitutions, &subst); - return ast::AssignmentStatement{ + let result_as = ast::AssignmentStatement{ source: match &statement.source { ast::AssignmentTarget::Variable(variable) => { - let assignment_subs = compose_substitutions(subs, unify(&expr.type_, &variable.type_)); - (ast::AssignmentTarget::Variable(ast::VariableUsage{ + substitution = compose_substitutions(&substitution, &unify(&variable.type_, &expr.type_)); + ast::AssignmentTarget::Variable(ast::VariableUsage{ name: variable.name.clone(), - type_: &variable.type_.clone(), - }), assignment_subs) + type_: apply_substitution(&substitution, &variable.type_), + }) }, ast::AssignmentTarget::StructAttr(struct_attr) => { - // let assignment_subs = compose_substitutions(subs, unify(&expr.type_, &struct_attr.type_)); - let (expr, subst) = self.with_expression(ctx, &struct_attr.source); - - (ast::AssignmentTarget::StructAttr(ast::StructGetter{ - source: expr, + let (source, subst) = self.with_expression(ctx, &substitution, &struct_attr.source); + // TODO: match source attr with type + let substitution = compose_substitutions(&compose_substitutions(&substitution, &subst), &unify(&struct_attr.type_, &expr.type_)); + ast::AssignmentTarget::StructAttr(ast::StructGetter{ + source: source, attribute: struct_attr.attribute.clone(), - type_: &struct_attr.type_.clone(), - }), subst) + type_: apply_substitution(&substitution, &struct_attr.type_), + }) }, }, expression: expr, - } + }; + return (result_as, substitution); } - fn with_expression(self: &Self, ctx: &Context, expression: &ast::Expression) -> (ast::Expression, SubstitutionMap) { - let mut substitution = SubstitutionMap::new(); - let expr = ast::Expression{ - subexpression: Box::new(match &*expression.subexpression { - ast::Subexpression::LiteralInt(literal_int) => { - ast::Subexpression::LiteralInt(ast::LiteralInt{ - value: literal_int.value.clone(), - type_: literal_int.type_.clone(), - }) - }, - ast::Subexpression::LiteralFloat(literal_float) => { - ast::Subexpression::LiteralFloat(ast::LiteralFloat{ - value: literal_float.value.clone(), - type_: literal_float.type_.clone(), - }) - }, - ast::Subexpression::LiteralStruct(literal_struct) => { - ast::Subexpression::LiteralStruct(ast::LiteralStruct{ - name: literal_struct.name.clone(), - fields: literal_struct.fields.iter().map(|field|{ + fn with_expression(self: &Self, ctx: &Context, incoming_substitutions: &SubstitutionMap, expression: &ast::Expression) -> (ast::Expression, SubstitutionMap) { + let mut substitution = incoming_substitutions.clone(); + let subexpression = Box::new(match &*expression.subexpression { + ast::Subexpression::LiteralInt(literal_int) => { + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &literal_int.type_)); + ast::Subexpression::LiteralInt(ast::LiteralInt{ + value: literal_int.value.clone(), + type_: apply_substitution(&substitution, &literal_int.type_), + }) + }, + ast::Subexpression::LiteralFloat(literal_float) => { + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &literal_float.type_)); + ast::Subexpression::LiteralFloat(ast::LiteralFloat{ + value: literal_float.value.clone(), + type_: apply_substitution(&substitution, &literal_float.type_), + }) + }, + ast::Subexpression::LiteralStruct(literal_struct) => { + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &literal_struct.type_)); + let type_declaration = match &ctx.environment[&literal_struct.name.name.value] { + NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => { + type_declaration + }, + _ => {panic!("literal struct used with non struct name")} + }; + ast::Subexpression::LiteralStruct(ast::LiteralStruct{ + name: literal_struct.name.clone(), + fields: literal_struct.fields.iter().map(|field|{ + let (result, subst) = self.with_expression(ctx, &substitution, &field.1); - // substitution = compose_substitutions(substitution, ); - (field.0.clone(), self.with_expression(ctx, &field.1)) - }).collect(), - type_: literal_struct.type_.clone(), - }) - }, - ast::Subexpression::FunctionCall(function_call) => { - ast::Subexpression::FunctionCall(ast::FunctionCall{ - source: self.with_expression(ctx, &function_call.source), - arguments: function_call.arguments.iter().map(|arg| {self.with_expression(ctx, arg)}).collect(), - type_: function_call.type_.clone(), - }) - }, - ast::Subexpression::VariableUsage(variable_usage) => { - match ctx.environment[variable_usage.name] { - NamedEntity::TypeDeclaration(_) => { - panic!("Using types not yet supported"); - }, - NamedEntity::Variable(variable) => { - substitution = compose_substitutions(substitution, unify(variable, expression.type_)); - }, - } - ast::Subexpression::VariableUsage(ast::VariableUsage{ - name: variable_usage.name.clone(), - type_: variable_usage.type_.clone(), - }) - }, - ast::Subexpression::StructGetter(struct_getter) => { - ast::Subexpression::StructGetter(ast::StructGetter{ - source: self.with_expression(ctx, &struct_getter.source), - attribute: struct_getter.attribute.clone(), - type_: struct_getter.type_.clone(), - }) - }, - ast::Subexpression::Block(block) => { - let (result, substitution) = self.with_block(ctx, &block); - substitution = compose_substitutions(substitution, unify(expression.type_, block.type_)); - ast::Subexpression::Block(result) - }, - ast::Subexpression::Op(op) => { - let expr_left, subst_left = self.with_expression(ctx, &op.left); - let expr_right, subst_right = self.with_expression(ctx, &op.right); - substitution = compose_substitutions(substitution, subst_left); - substitution = compose_substitutions(substitution, subst_right); - substitution = compose_substitutions(substitution, unify(expression.type_, expr_left.type_)); - substitution = compose_substitutions(substitution, unify(expression.type_, expr_right.type_)); - ast::Subexpression::Op(ast::Operation{ - left: expr_left, - op: op.op.clone(), - right: expr_right, - }) - }, - }), - type_: expression.type_.clone(), + for type_field in type_declaration.fields.iter() { + if type_field.name.name.value == field.0.name.value { + substitution = compose_substitutions(&substitution, &unify(&type_field.type_, &result.type_)); + } + } + + substitution = compose_substitutions(&substitution, &subst); + (field.0.clone(), result) + }).collect(), + type_: apply_substitution(&substitution, &literal_struct.type_), + }) + }, + ast::Subexpression::FunctionCall(function_call) => { + let (source, subst) = self.with_expression(ctx, &substitution, &function_call.source); + substitution = compose_substitutions(&substitution, &subst); + match &source.type_ { + ast::TypeUsage::Function(fn_type) => { + substitution = compose_substitutions(&substitution, &unify(&function_call.type_, &*fn_type.return_type)); + if function_call.arguments.len() != fn_type.arguments.len() { + panic!("mismatched function argument count"); + } + }, + ast::TypeUsage::Named(_) => panic!("FunctionCall doesn't have function type."), + _ => {}, + } + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &function_call.type_)); + ast::Subexpression::FunctionCall(ast::FunctionCall{ + source: source.clone(), + arguments: function_call.arguments.iter().enumerate().map(|(i, arg)| { + let (result, subst) = self.with_expression(ctx, &substitution, arg); + substitution = compose_substitutions(&substitution, &subst); + + match &source.type_ { + ast::TypeUsage::Function(fn_type) => { + substitution = compose_substitutions(&substitution, &unify(&fn_type.arguments[i], &result.type_)); + }, + ast::TypeUsage::Named(_) => panic!("FunctionCall doesn't have function type."), + _ => {}, + } + result + }).collect(), + type_: apply_substitution(&substitution, &function_call.type_), + }) + }, + ast::Subexpression::VariableUsage(variable_usage) => { + match &ctx.environment[&variable_usage.name.name.value] { + NamedEntity::TypeDeclaration(_) => { + panic!("Using types not yet supported"); + }, + NamedEntity::Variable(variable) => { + substitution = compose_substitutions(&substitution, &unify(&variable_usage.type_, &variable)); + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &variable_usage.type_)); + }, + } + ast::Subexpression::VariableUsage(ast::VariableUsage{ + name: variable_usage.name.clone(), + type_: apply_substitution(&substitution, &variable_usage.type_), + }) + }, + ast::Subexpression::StructGetter(struct_getter) => { + let (source, subst) = self.with_expression(ctx, &substitution, &struct_getter.source); + substitution = compose_substitutions(&substitution, &subst); + + match &source.type_ { + ast::TypeUsage::Named(named) => { + match &ctx.environment[&named.name.name.value] { + NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => { + let mut found = false; + for field in type_declaration.fields.iter() { + if field.name.name.value == struct_getter.attribute.name.value { + found = true; + substitution = compose_substitutions(&substitution, &unify(&struct_getter.type_, &field.type_)); + } + } + if !found { + println!("foo: {:?} {:?}", &type_declaration.name.name.value, struct_getter.attribute.name.value); + for method in ctx.impls[&type_declaration.name.name.value].functions.iter() { + println!("foo: {:?} {:?}", &method.declaration.name.name.value, struct_getter.attribute.name.value); + if method.declaration.name.name.value == struct_getter.attribute.name.value { + let mut function_type = ast::FunctionTypeUsage{ + arguments: method.declaration.arguments.iter().map(|arg|{arg.type_.clone()}).collect(), + return_type: Box::new(method.declaration.return_type.clone()), + }; + // if the name of the type of the first argument == the class, remove the first arg + if function_type.arguments.len() > 0 { + match &function_type.arguments[0] { + ast::TypeUsage::Named(named) => { + if named.name.name.value == type_declaration.name.name.value { + function_type = ast::FunctionTypeUsage{ + arguments: method.declaration.arguments[1..method.declaration.arguments.len()].iter().map(|arg|{arg.type_.clone()}).collect(), + return_type: Box::new(method.declaration.return_type.clone()), + }; + } + }, + _ => {}, + }; + } + + println!("found: {:?}", &function_type); + substitution = compose_substitutions(&substitution, &unify(&struct_getter.type_, &ast::TypeUsage::Function(function_type))); + found = true; + } + } + } + if !found { + panic!("unknown field name") + } + + }, + _ => panic!("struct getter being used on non-struct") + } + }, + ast::TypeUsage::Function(_) => { + panic!("function used with attr") + }, + _ => {} // skip unifying if struct type is unknown1 + } + + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &struct_getter.type_)); + + ast::Subexpression::StructGetter(ast::StructGetter{ + source: source, + attribute: struct_getter.attribute.clone(), + type_: apply_substitution(&substitution, &struct_getter.type_), + }) + }, + ast::Subexpression::Block(block) => { + let (result, subst) = self.with_block(ctx, &substitution, &block); + substitution = compose_substitutions(&substitution, &subst); + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &result.type_)); + println!("foo {:?} {:?}", &expression.type_, &block.type_); + ast::Subexpression::Block(result) + }, + ast::Subexpression::Op(op) => { + let (expr_left, subst_left) = self.with_expression(ctx, &substitution, &op.left); + let (expr_right, subst_right) = self.with_expression(ctx, &substitution, &op.right); + substitution = compose_substitutions(&substitution, &subst_left); + substitution = compose_substitutions(&substitution, &subst_right); + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &expr_left.type_)); + substitution = compose_substitutions(&substitution, &unify(&expression.type_, &expr_right.type_)); + ast::Subexpression::Op(ast::Operation{ + left: expr_left, + op: op.op.clone(), + right: expr_right, + }) + }, + }); + + let expr = ast::Expression{ + subexpression: subexpression, + type_: apply_substitution(&substitution, &expression.type_), }; return (expr, substitution); }