diff --git a/Cargo.lock b/Cargo.lock index 78eebd3..895adbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,19 +72,12 @@ name = "boring-lang" version = "0.0.1" dependencies = [ "clap", - "inkwell", "lalrpop", "lalrpop-util", "regex", "thiserror", ] -[[package]] -name = "cc" -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" - [[package]] name = "cfg-if" version = "1.0.0" @@ -196,39 +189,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "inkwell" -version = "0.1.0" -source = "git+https://github.com/TheDan64/inkwell?branch=llvm7-0#ef1f5e491fd599d84ba67f82b87e55cb7be4b0b8" -dependencies = [ - "either", - "inkwell_internals", - "libc", - "llvm-sys", - "once_cell", - "parking_lot", - "regex", -] - -[[package]] -name = "inkwell_internals" -version = "0.2.0" -source = "git+https://github.com/TheDan64/inkwell?branch=llvm7-0#ef1f5e491fd599d84ba67f82b87e55cb7be4b0b8" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "instant" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" -dependencies = [ - "cfg-if", -] - [[package]] name = "itertools" version = "0.10.1" @@ -282,28 +242,6 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" -[[package]] -name = "llvm-sys" -version = "70.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673300127ec17878e6f7fee4e851ba6fd36b08c26b2d6258732d699d1b3e3fcd" -dependencies = [ - "cc", - "lazy_static", - "libc", - "regex", - "semver", -] - -[[package]] -name = "lock_api" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" -dependencies = [ - "scopeguard", -] - [[package]] name = "log" version = "0.4.14" @@ -331,31 +269,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" -[[package]] -name = "parking_lot" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - [[package]] name = "petgraph" version = "0.5.1" @@ -448,39 +361,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "siphasher" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "729a25c17d72b06c68cb47955d44fda88ad2d3e7d77e025663fdd69b93dd71a1" -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - [[package]] name = "string_cache" version = "0.8.1" diff --git a/Cargo.toml b/Cargo.toml index 1d3631a..1d8be69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,6 @@ features = ["lexer"] [dependencies] lalrpop-util = "0.19.6" regex = "1" -inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" } +# inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm7-0" } clap = "2.33.0" thiserror = "1" diff --git a/examples/generics.bl b/examples/generics.bl new file mode 100644 index 0000000..5979201 --- /dev/null +++ b/examples/generics.bl @@ -0,0 +1,27 @@ +type MyTrait trait {} + +type Pair[K, V: MyTrait] struct { + k: K, + v: V, +} + +type Value struct { + value: i64, +} + +impl MyTrait for Value {} + + +impl [K, V: MyTrait] Pair[K, V] { + fn get_value[T](self: Self, a: T): V { + return self.v; + } +} + +fn main(): i64 { + let a = Pair{ + k: 4, + v: Value{value: 6}, + }; + return a.get_value(999); +} diff --git a/src/ast.rs b/src/ast.rs index 358c69f..8037f56 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,17 +1,19 @@ use std::cell::RefCell; +#[derive(Debug, Clone, PartialEq, Eq)] pub struct IdGenerator { + id_key: String, counter: RefCell, } impl IdGenerator { - pub fn new() -> Self { - IdGenerator { counter: RefCell::new(0) } + pub fn new(key: &str) -> Self { + IdGenerator { id_key: key.to_string(), counter: RefCell::new(0) } } pub fn next(&self) -> String { *self.counter.borrow_mut() += 1; - ("S".to_owned() + &self.counter.borrow().to_string()).to_string() + (self.id_key.to_owned() + &self.counter.borrow().to_string()).to_string() } } @@ -68,11 +70,18 @@ pub struct UnknownTypeUsage { pub name: String, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum NamespaceTypeUsage { + Type(NamedTypeUsage) + // Module +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TypeUsage { Function(FunctionTypeUsage), Named(NamedTypeUsage), Unknown(UnknownTypeUsage), + Namespace(NamespaceTypeUsage) } impl TypeUsage { @@ -200,8 +209,8 @@ pub struct Operation { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct VariableUsage { - pub type_parameters: GenericUsage, pub name: Identifier, + pub type_parameters: GenericUsage, pub type_: TypeUsage, } diff --git a/src/builtins.rs b/src/builtins.rs new file mode 100644 index 0000000..473368c --- /dev/null +++ b/src/builtins.rs @@ -0,0 +1 @@ +use std::collections::HashMap; diff --git a/src/errors.rs b/src/errors.rs index c1359e7..928369c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -47,6 +47,14 @@ pub enum TypingError { }, #[error("cannot use type as an expression")] TypeIsNotAnExpression { type_name: ast::Identifier }, + #[error("wrong number of type parameters")] + WrongNumberOfTypeParameters { + // TODO: add position + }, + #[error("invalid use of alias")] + InvalidUseofAlias, + #[error("type cannot be used for generic")] + InvalidTypeForGeneric, #[error("multiple errors")] MultipleErrors { errors: Vec }, } diff --git a/src/grammar.lalrpop b/src/grammar.lalrpop index ef2566a..8dd4b13 100644 --- a/src/grammar.lalrpop +++ b/src/grammar.lalrpop @@ -117,8 +117,8 @@ pub StructGetter: ast::StructGetter = { pub VariableUsage: ast::VariableUsage = { => match gu { - Some(tp) => ast::VariableUsage{type_parameters: tp, name: identifier, type_: ast::TypeUsage::new_unknown(&id_generator)}, - None => ast::VariableUsage{type_parameters: ast::GenericUsage::Unknown, name: identifier, type_: ast::TypeUsage::new_unknown(&id_generator)}, + Some(tp) => ast::VariableUsage{name: identifier, type_parameters: tp.clone(), type_: ast::TypeUsage::new_unknown(&id_generator)}, + None => ast::VariableUsage{name: identifier, type_parameters: ast::GenericUsage::Unknown, type_: ast::TypeUsage::new_unknown(&id_generator)}, } }; diff --git a/src/main.rs b/src/main.rs index 421aa59..bcbfd4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,11 +37,18 @@ fn main() { let _output = matches.value_of("OUTPUT").unwrap_or(default_output); let contents = fs::read_to_string(input).expect("input file not found"); - let unknown_id_gen = ast::IdGenerator::new(); + let unknown_id_gen = ast::IdGenerator::new("S"); let module_ast = grammar::ModuleParser::new().parse(&unknown_id_gen, &contents).unwrap(); //TODO: convert to error // println!("ast: {:#?}", &module_ast); let alias_resolver = type_alias_resolution::TypeAliasResolver {}; - let resolved_ast = alias_resolver.with_module(&module_ast); + let resolved_ast = match alias_resolver.with_module(&module_ast) { + Ok(r) => r, + Err(err) => { + println!("type checking error: {:#?}", &err); + panic!("bad alias"); + } + }; + // println!("resolved ast: {:#?}", &resolved_ast); let trait_checker = trait_checking::TraitChecker {}; match trait_checker.with_module(&resolved_ast) { @@ -76,7 +83,7 @@ fn main() { #[test] fn grammar() { - let id_gen = ast::IdGenerator::new(); + let id_gen = ast::IdGenerator::new("S"); assert!(grammar::LiteralIntParser::new().parse(&id_gen, "22").is_ok()); assert!(grammar::IdentifierParser::new().parse(&id_gen, "foo").is_ok()); assert!(grammar::LiteralIntParser::new().parse(&id_gen, "2a").is_err()); diff --git a/src/type_alias_resolution.rs b/src/type_alias_resolution.rs index 7c29d59..17e8673 100644 --- a/src/type_alias_resolution.rs +++ b/src/type_alias_resolution.rs @@ -1,4 +1,7 @@ use crate::ast; +use crate::errors; + +pub type Result = std::result::Result; #[derive(Debug, Clone, PartialEq, Eq, Hash)] struct Context { @@ -26,19 +29,38 @@ fn resolve_type(ctx: &Context, type_: &ast::NamedTypeUsage) -> ast::TypeUsage { return result; } -fn process_type(ctx: &Context, type_: &ast::TypeUsage) -> ast::TypeUsage { +fn process_type(ctx: &Context, type_: &ast::TypeUsage) -> Result { match type_ { ast::TypeUsage::Named(named) => { - return resolve_type(ctx, named); + return Ok(resolve_type(ctx, named)); } ast::TypeUsage::Function(function) => { - return ast::TypeUsage::Function(ast::FunctionTypeUsage { - arguments: function.arguments.iter().map(|a| process_type(ctx, &a.clone())).collect(), - return_type: Box::new(process_type(ctx, &function.return_type.clone())), - }); + let mut arguments = vec!(); + for a in function.arguments.iter() { + arguments.push(process_type(ctx, &a.clone())?); + } + return Ok(ast::TypeUsage::Function(ast::FunctionTypeUsage { + arguments: arguments, + return_type: Box::new(process_type(ctx, &function.return_type.clone())?), + })); } ast::TypeUsage::Unknown(unknown) => { - return ast::TypeUsage::Unknown(unknown.clone()); + return Ok(ast::TypeUsage::Unknown(unknown.clone())); + }, + ast::TypeUsage::Namespace(namespace) => { + match namespace { + ast::NamespaceTypeUsage::Type(named_type)=> { + let result = resolve_type(ctx, named_type); + match result { + ast::TypeUsage::Named(named) => { + return Ok(ast::TypeUsage::Namespace(ast::NamespaceTypeUsage::Type(named))); + }, + _ => { + return Err(errors::TypingError::InvalidUseofAlias); + } + } + } + } } } } @@ -46,7 +68,7 @@ fn process_type(ctx: &Context, type_: &ast::TypeUsage) -> ast::TypeUsage { pub struct TypeAliasResolver {} impl TypeAliasResolver { - pub fn with_module(self: &Self, module: &ast::Module) -> ast::Module { + pub fn with_module(self: &Self, module: &ast::Module) -> Result { let mut ctx = Context { type_aliases: vec![] }; for item in module.items.iter() { match item { @@ -57,77 +79,78 @@ impl TypeAliasResolver { } } - return ast::Module { - items: module - .items - .iter() - .map(|item| match item { - ast::ModuleItem::Function(function) => ast::ModuleItem::Function(self.with_function(&ctx, function)), - ast::ModuleItem::TypeDeclaration(type_declaration) => { - ast::ModuleItem::TypeDeclaration(self.with_type_declaration(&ctx, type_declaration)) - } - ast::ModuleItem::Impl(impl_) => ast::ModuleItem::Impl(self.with_impl(&ctx, impl_)), - }) - .collect(), - }; + let mut items = vec!(); + for item in module.items.iter() { + items.push(match item { + ast::ModuleItem::Function(function) => ast::ModuleItem::Function(self.with_function(&ctx, function)?), + ast::ModuleItem::TypeDeclaration(type_declaration) => { + ast::ModuleItem::TypeDeclaration(self.with_type_declaration(&ctx, type_declaration)?) + } + ast::ModuleItem::Impl(impl_) => ast::ModuleItem::Impl(self.with_impl(&ctx, impl_)?), + }); + } + + return Ok(ast::Module { + items: items, + }); } - fn with_function(self: &Self, ctx: &Context, function: &ast::Function) -> ast::Function { - return ast::Function { - declaration: self.with_function_declaration(ctx, &function.declaration), - block: self.with_block(ctx, &function.block), - }; + fn with_function(self: &Self, ctx: &Context, function: &ast::Function) -> Result { + return Ok(ast::Function { + declaration: self.with_function_declaration(ctx, &function.declaration)?, + block: self.with_block(ctx, &function.block)?, + }); } - fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> ast::FunctionDeclaration { - return ast::FunctionDeclaration { + fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> Result { + let mut arguments = vec!(); + for arg in declaration.arguments.iter() { + arguments.push(ast::VariableDeclaration { + name: arg.name.clone(), + type_: process_type(ctx, &arg.type_)?, + }); + } + return Ok(ast::FunctionDeclaration { name: declaration.name.clone(), generic: declaration.generic.clone(), - arguments: declaration - .arguments - .iter() - .map(|arg| ast::VariableDeclaration { - name: arg.name.clone(), - type_: process_type(ctx, &arg.type_), - }) - .collect(), - return_type: process_type(ctx, &declaration.return_type), - }; + arguments: arguments, + return_type: process_type(ctx, &declaration.return_type)?, + }); } - fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> ast::TypeDeclaration { + fn with_type_declaration(self: &Self, ctx: &Context, type_declaration: &ast::TypeDeclaration) -> Result { match type_declaration { ast::TypeDeclaration::Struct(struct_) => { - return ast::TypeDeclaration::Struct(self.with_struct_declaration(ctx, struct_)); + return Ok(ast::TypeDeclaration::Struct(self.with_struct_declaration(ctx, struct_)?)); } ast::TypeDeclaration::Primitive(primitive) => { - return ast::TypeDeclaration::Primitive(primitive.clone()); + return Ok(ast::TypeDeclaration::Primitive(primitive.clone())); } ast::TypeDeclaration::Alias(alias) => { - return ast::TypeDeclaration::Alias(alias.clone()); + return Ok(ast::TypeDeclaration::Alias(alias.clone())); } ast::TypeDeclaration::Trait(trait_) => { - return ast::TypeDeclaration::Trait(self.with_trait(ctx, trait_)); + return Ok(ast::TypeDeclaration::Trait(self.with_trait(ctx, trait_)?)); } } } - fn with_struct_declaration(self: &Self, ctx: &Context, struct_: &ast::StructTypeDeclaration) -> ast::StructTypeDeclaration { - return ast::StructTypeDeclaration { + fn with_struct_declaration(self: &Self, ctx: &Context, struct_: &ast::StructTypeDeclaration) -> Result { + let mut fields = vec!(); + for field in struct_.fields.iter() { + fields.push(ast::StructField { + name: field.name.clone(), + type_: process_type(ctx, &field.type_)?, + }); + } + return Ok(ast::StructTypeDeclaration { generic: struct_.generic.clone(), name: struct_.name.clone(), - fields: struct_ - .fields - .iter() - .map(|field| ast::StructField { - name: field.name.clone(), - type_: process_type(ctx, &field.type_), - }) - .collect(), - }; + fields: fields, + }); } - fn with_trait(self: &Self, ctx: &Context, trait_: &ast::TraitTypeDeclaration) -> ast::TraitTypeDeclaration { + fn with_trait(self: &Self, ctx: &Context, trait_: &ast::TraitTypeDeclaration) -> Result { let mut trait_ctx = ctx.clone(); trait_ctx.type_aliases.push(ast::AliasTypeDeclaration { name: ast::Identifier { @@ -141,23 +164,23 @@ impl TypeAliasResolver { name: trait_.name.clone(), }), }); - return ast::TraitTypeDeclaration { + let mut functions = vec!(); + for f in trait_.functions.iter() { + functions.push(match f { + ast::TraitItem::Function(function) => ast::TraitItem::Function(self.with_function(&trait_ctx, function)?), + ast::TraitItem::FunctionDeclaration(function_declaration) => { + ast::TraitItem::FunctionDeclaration(self.with_function_declaration(&trait_ctx, function_declaration)?) + } + }); + } + return Ok(ast::TraitTypeDeclaration { generic: trait_.generic.clone(), name: trait_.name.clone(), - functions: trait_ - .functions - .iter() - .map(|f| match f { - ast::TraitItem::Function(function) => ast::TraitItem::Function(self.with_function(&trait_ctx, function)), - ast::TraitItem::FunctionDeclaration(function_declaration) => { - ast::TraitItem::FunctionDeclaration(self.with_function_declaration(&trait_ctx, function_declaration)) - } - }) - .collect(), - }; + functions: functions, + }); } - fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> ast::Impl { + fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result { let mut impl_ctx = ctx.clone(); impl_ctx.type_aliases.push(ast::AliasTypeDeclaration { name: ast::Identifier { @@ -168,85 +191,93 @@ impl TypeAliasResolver { }, replaces: ast::TypeUsage::Named(impl_.struct_.clone()), }); - return ast::Impl { + let mut functions = vec!(); + for f in impl_.functions.iter() { + functions.push(self.with_function(&impl_ctx, f)?); + } + return Ok(ast::Impl { generic: impl_.generic.clone(), trait_: impl_.trait_.clone(), struct_: impl_.struct_.clone(), - functions: impl_.functions.iter().map(|f| self.with_function(&impl_ctx, f)).collect(), - }; + functions: functions, + }); } - fn with_block(self: &Self, ctx: &Context, block: &ast::Block) -> ast::Block { - return ast::Block { - statements: block.statements.iter().map(|s| self.with_statement(ctx, s)).collect(), - type_: process_type(ctx, &block.type_), - }; + fn with_block(self: &Self, ctx: &Context, block: &ast::Block) -> Result { + let mut statements = vec!(); + for s in block.statements.iter() { + statements.push(self.with_statement(ctx, s)?); + } + return Ok(ast::Block { + statements: statements, + type_: process_type(ctx, &block.type_)?, + }); } - fn with_statement(self: &Self, ctx: &Context, statement: &ast::Statement) -> ast::Statement { + fn with_statement(self: &Self, ctx: &Context, statement: &ast::Statement) -> Result { match statement { ast::Statement::Return(return_statement) => { - return ast::Statement::Return(self.with_return_statement(ctx, return_statement)); + return Ok(ast::Statement::Return(self.with_return_statement(ctx, return_statement)?)); } ast::Statement::Let(let_statement) => { - return ast::Statement::Let(self.with_let_statement(ctx, let_statement)); + return Ok(ast::Statement::Let(self.with_let_statement(ctx, let_statement)?)); } ast::Statement::Assignment(assignment_statement) => { - return ast::Statement::Assignment(self.with_assignment_statement(ctx, assignment_statement)); + return Ok(ast::Statement::Assignment(self.with_assignment_statement(ctx, assignment_statement)?)); } ast::Statement::Expression(expression) => { - return ast::Statement::Expression(self.with_expression(ctx, expression)); + return Ok(ast::Statement::Expression(self.with_expression(ctx, expression)?)); } } } - fn with_return_statement(self: &Self, ctx: &Context, statement: &ast::ReturnStatement) -> ast::ReturnStatement { - return ast::ReturnStatement { - source: self.with_expression(ctx, &statement.source), - }; + fn with_return_statement(self: &Self, ctx: &Context, statement: &ast::ReturnStatement) -> Result { + return Ok(ast::ReturnStatement { + source: self.with_expression(ctx, &statement.source)?, + }); } - fn with_let_statement(self: &Self, ctx: &Context, statement: &ast::LetStatement) -> ast::LetStatement { - return ast::LetStatement { + fn with_let_statement(self: &Self, ctx: &Context, statement: &ast::LetStatement) -> Result { + return Ok(ast::LetStatement { variable_name: statement.variable_name.clone(), - expression: self.with_expression(ctx, &statement.expression), - type_: process_type(ctx, &statement.type_), - }; + expression: self.with_expression(ctx, &statement.expression)?, + type_: process_type(ctx, &statement.type_)?, + }); } - fn with_assignment_statement(self: &Self, ctx: &Context, statement: &ast::AssignmentStatement) -> ast::AssignmentStatement { - return ast::AssignmentStatement { + fn with_assignment_statement(self: &Self, ctx: &Context, statement: &ast::AssignmentStatement) -> Result { + return Ok(ast::AssignmentStatement { source: match &statement.source { ast::AssignmentTarget::Variable(variable) => ast::AssignmentTarget::Variable(ast::VariableUsage { type_parameters: variable.type_parameters.clone(), name: variable.name.clone(), - type_: process_type(ctx, &variable.type_), + type_: process_type(ctx, &variable.type_)?, }), ast::AssignmentTarget::StructAttr(struct_attr) => ast::AssignmentTarget::StructAttr(ast::StructGetter { type_parameters: struct_attr.type_parameters.clone(), - source: self.with_expression(ctx, &struct_attr.source), + source: self.with_expression(ctx, &struct_attr.source)?, attribute: struct_attr.attribute.clone(), - type_: process_type(ctx, &struct_attr.type_), + type_: process_type(ctx, &struct_attr.type_)?, }), }, - expression: self.with_expression(ctx, &statement.expression), - }; + expression: self.with_expression(ctx, &statement.expression)?, + }); } - fn with_expression(self: &Self, ctx: &Context, expression: &ast::Expression) -> ast::Expression { - return ast::Expression { + fn with_expression(self: &Self, ctx: &Context, expression: &ast::Expression) -> Result { + return Ok(ast::Expression { subexpression: Box::new(match &*expression.subexpression { ast::Subexpression::LiteralInt(literal_int) => ast::Subexpression::LiteralInt(ast::LiteralInt { value: literal_int.value.clone(), - type_: process_type(ctx, &literal_int.type_), + type_: process_type(ctx, &literal_int.type_)?, }), ast::Subexpression::LiteralFloat(literal_float) => ast::Subexpression::LiteralFloat(ast::LiteralFloat { value: literal_float.value.clone(), - type_: process_type(ctx, &literal_float.type_), + type_: process_type(ctx, &literal_float.type_)?, }), ast::Subexpression::LiteralBool(literal_bool) => ast::Subexpression::LiteralBool(ast::LiteralBool { value: literal_bool.value.clone(), - type_: process_type(ctx, &literal_bool.type_), + type_: process_type(ctx, &literal_bool.type_)?, }), ast::Subexpression::LiteralStruct(literal_struct) => { let result = resolve_type( @@ -260,50 +291,56 @@ impl TypeAliasResolver { ast::TypeUsage::Named(named) => named.name.clone(), _ => panic!("LiteralStruct resolved to non-named-type"), }; + let mut fields = vec!(); + for field in literal_struct.fields.iter() { + fields.push((field.0.clone(), self.with_expression(ctx, &field.1)?)); + } ast::Subexpression::LiteralStruct(ast::LiteralStruct { type_parameters: literal_struct.type_parameters.clone(), name: new_name.clone(), - fields: literal_struct - .fields - .iter() - .map(|field| (field.0.clone(), self.with_expression(ctx, &field.1))) - .collect(), - type_: process_type(ctx, &literal_struct.type_), + fields: fields, + type_: process_type(ctx, &literal_struct.type_)?, }) } - 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_: process_type(ctx, &function_call.type_), - }), + ast::Subexpression::FunctionCall(function_call) => { + let mut arguments = vec!(); + for arg in function_call.arguments.iter() { + arguments.push(self.with_expression(ctx, arg)?); + } + ast::Subexpression::FunctionCall(ast::FunctionCall { + source: self.with_expression(ctx, &function_call.source)?, + arguments: arguments, + type_: process_type(ctx, &function_call.type_)?, + }) + }, ast::Subexpression::VariableUsage(variable_usage) => ast::Subexpression::VariableUsage(ast::VariableUsage { - type_parameters: variable_usage.type_parameters.clone(), name: variable_usage.name.clone(), - type_: process_type(ctx, &variable_usage.type_), + type_parameters: variable_usage.type_parameters.clone(), + type_: process_type(ctx, &variable_usage.type_)?, }), ast::Subexpression::If(if_expression) => ast::Subexpression::If(ast::IfExpression { - condition: self.with_expression(ctx, &if_expression.condition), - block: self.with_block(ctx, &if_expression.block), + condition: self.with_expression(ctx, &if_expression.condition)?, + block: self.with_block(ctx, &if_expression.block)?, else_: match &if_expression.else_ { - Some(else_) => Some(self.with_block(ctx, else_)), + Some(else_) => Some(self.with_block(ctx, else_)?), None => None, }, - type_: process_type(ctx, &if_expression.type_), + type_: process_type(ctx, &if_expression.type_)?, }), ast::Subexpression::StructGetter(struct_getter) => ast::Subexpression::StructGetter(ast::StructGetter { type_parameters: struct_getter.type_parameters.clone(), - source: self.with_expression(ctx, &struct_getter.source), + source: self.with_expression(ctx, &struct_getter.source)?, attribute: struct_getter.attribute.clone(), - type_: process_type(ctx, &struct_getter.type_), + type_: process_type(ctx, &struct_getter.type_)?, }), - ast::Subexpression::Block(block) => ast::Subexpression::Block(self.with_block(ctx, &block)), + ast::Subexpression::Block(block) => ast::Subexpression::Block(self.with_block(ctx, &block)?), ast::Subexpression::Op(op) => ast::Subexpression::Op(ast::Operation { - left: self.with_expression(ctx, &op.left), + left: self.with_expression(ctx, &op.left)?, op: op.op.clone(), - right: self.with_expression(ctx, &op.right), + right: self.with_expression(ctx, &op.right)?, }), }), - type_: process_type(ctx, &expression.type_), - }; + type_: process_type(ctx, &expression.type_)?, + }); } } diff --git a/src/type_checking.rs b/src/type_checking.rs index 1a72625..badde40 100644 --- a/src/type_checking.rs +++ b/src/type_checking.rs @@ -1,15 +1,121 @@ use crate::ast; use crate::errors; use std::collections::HashMap; +use std::rc::Rc; pub type SubstitutionMap = HashMap; pub type Result = std::result::Result; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TypeConstructor { + generic: ast::Generic, + type_usage: ast::TypeUsage, +} + +fn type_meets_trait_bounds(ctx: &Context, type_: &ast::TypeUsage, bounds: &Vec) -> bool { + for bound in bounds.iter() { + let named = match type_ { + ast::TypeUsage::Named(named) => named, + ast::TypeUsage::Function(_) => { + return false; + }, + ast::TypeUsage::Unknown(_) => { + return true; // unknown this pass, skip, test once known + }, + ast::TypeUsage::Namespace(_) => { + panic!("impossible"); + } + }; + match &ctx.environment[&named.name.name.value] { + NamedEntity::NamedType(named_type) => { + println!("env value: {:?}", &ctx.environment[&named.name.name.value]); + let mut found = false; + for impl_ in named_type.impls.iter() { + println!("for: {:?}", &named.name.name.value); + println!("bounds: {:?}", &bound.name.value); + println!("trait: {:?}", &impl_.trait_); + if impl_.trait_ == Some(bound.name.value.to_string()) { + found = true; + break; + } + } + if found == false { + return false; + } + }, + _ => { + panic!("type is a variable, this should not happen"); + } + } + } + return true; +} + +fn replace_generic_with_concrete(replace: &ast::Identifier, with_type: &ast::TypeUsage, in_type: &ast::TypeUsage) -> ast::TypeUsage { + match in_type { + ast::TypeUsage::Named(named) => { + if named.name.name.value == replace.name.value { + return with_type.clone(); + } + return in_type.clone(); + }, + ast::TypeUsage::Function(func) => { + return ast::TypeUsage::Function(ast::FunctionTypeUsage{ + arguments: func.arguments.iter().map(|arg| { + replace_generic_with_concrete(replace, with_type, arg) + }).collect(), + return_type: Box::new(replace_generic_with_concrete(replace, with_type, &func.return_type)), + }); + }, + _ => panic!("unknown in a generic, this should not happen") + }; +} + +impl TypeConstructor { + fn from_declaration(declaration: &ast::FunctionDeclaration) -> TypeConstructor { + return TypeConstructor{ + generic: declaration.generic.clone(), + type_usage: declaration.to_type(), + } + } + fn construct(&self, ctx: &Context, usage: &ast::GenericUsage) -> Result { + match usage { + ast::GenericUsage::Known(known_usage) => { + let mut result = self.type_usage.clone(); + if known_usage.parameters.len() != self.generic.parameters.len() { + return Err(errors::TypingError::WrongNumberOfTypeParameters{}); + } + // 1. for arg in args, assert arg matches self.generic traits via impl name + // 2. replace type usage names with arg types + for i in 0..known_usage.parameters.len() { + println!("test: {:?}\n{:?}", &known_usage.parameters[i], &self.generic.parameters[i].bounds); + if !type_meets_trait_bounds(ctx, &known_usage.parameters[i], &self.generic.parameters[i].bounds) { + panic!("InvalidTypeForGeneric"); + return Err(errors::TypingError::InvalidTypeForGeneric); + } + result = replace_generic_with_concrete(&self.generic.parameters[i].name, &known_usage.parameters[i], &self.type_usage); + } + return Ok(result); + }, + ast::GenericUsage::Unknown => { + // generate new Unknown Types for matching + let mut result = self.type_usage.clone(); + for param in self.generic.parameters.iter() { + let id = ctx.id_generator.next(); + let unknown = ast::TypeUsage::Unknown(ast::UnknownTypeUsage{name: id}); + result = replace_generic_with_concrete(¶m.name, &unknown, &self.type_usage); + } + return Ok(result); + } + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnvImpl { trait_: Option, - functions: HashMap, + functions: HashMap, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -21,6 +127,7 @@ pub enum TypeType { #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnvType { + generic: ast::Generic, is_a: TypeType, fields: HashMap, impls: Vec, @@ -29,6 +136,7 @@ pub struct EnvType { impl EnvType { fn from_struct(struct_: &ast::StructTypeDeclaration) -> EnvType { return EnvType { + generic: struct_.generic.clone(), is_a: TypeType::Struct, fields: struct_ .fields @@ -44,42 +152,65 @@ impl EnvType { for func in trait_.functions.iter() { match func { ast::TraitItem::FunctionDeclaration(fd) => { - functions.insert(fd.name.name.value.to_string(), fd.to_type()); + functions.insert(fd.name.name.value.to_string(), TypeConstructor::from_declaration(&fd)); } ast::TraitItem::Function(f) => { - functions.insert(f.declaration.name.name.value.to_string(), f.declaration.to_type()); + functions.insert(f.declaration.name.name.value.to_string(), TypeConstructor::from_declaration(&f.declaration)); } } } let impl_ = EnvImpl { - trait_: None, + trait_: Some(trait_.name.name.value.to_string()), functions: functions, }; return EnvType { + generic: trait_.generic.clone(), is_a: TypeType::Trait, fields: HashMap::new(), impls: vec![impl_], }; } + + fn type_construct(&self, ctx: &Context, usage: &ast::GenericUsage) -> Result { + let mut fields = HashMap::new(); + for (k, v) in self.fields.iter() { + let type_usage = TypeConstructor{generic: self.generic.clone(), type_usage: v.clone()}.construct(ctx, usage)?; + fields.insert(k.clone(), type_usage); + } + let mut impls = vec!(); + for impl_ in self.impls.iter() { + let mut functions = HashMap::new(); + for (name, func) in impl_.functions.iter() { + let type_usage = TypeConstructor{generic: self.generic.clone(), type_usage: func.type_usage.clone()}.construct(ctx, usage)?; + functions.insert(name.clone(), TypeConstructor{generic: func.generic.clone(), type_usage: type_usage}); + } + impls.push(EnvImpl{ + trait_: impl_.trait_.clone(), + functions: functions, + }); + } + return Ok(EnvType{ + generic: ast::Generic{parameters: vec!()}, + is_a: self.is_a.clone(), + fields: fields, + impls: impls, + }); + } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum NamedEntity { NamedType(EnvType), + Function(TypeConstructor), Variable(ast::TypeUsage), } -#[derive(Debug, Clone, PartialEq, Eq)] -struct Context { - pub current_function_return: Option, - pub environment: HashMap, -} - fn create_builtins() -> HashMap { let mut result = HashMap::new(); result.insert( "i8".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -88,6 +219,7 @@ fn create_builtins() -> HashMap { result.insert( "i16".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -96,6 +228,7 @@ fn create_builtins() -> HashMap { result.insert( "i32".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -104,6 +237,7 @@ fn create_builtins() -> HashMap { result.insert( "i64".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -112,6 +246,7 @@ fn create_builtins() -> HashMap { result.insert( "isize".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -121,6 +256,7 @@ fn create_builtins() -> HashMap { result.insert( "u8".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -129,6 +265,7 @@ fn create_builtins() -> HashMap { result.insert( "u16".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -137,6 +274,7 @@ fn create_builtins() -> HashMap { result.insert( "u32".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -145,6 +283,7 @@ fn create_builtins() -> HashMap { result.insert( "u64".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -153,6 +292,7 @@ fn create_builtins() -> HashMap { result.insert( "usize".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -162,6 +302,7 @@ fn create_builtins() -> HashMap { result.insert( "f32".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -170,6 +311,7 @@ fn create_builtins() -> HashMap { result.insert( "f64".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -179,6 +321,7 @@ fn create_builtins() -> HashMap { result.insert( "bool".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -187,6 +330,7 @@ fn create_builtins() -> HashMap { result.insert( "!".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -195,6 +339,7 @@ fn create_builtins() -> HashMap { result.insert( "unit".to_string(), NamedEntity::NamedType(EnvType { + generic: ast::Generic{parameters: vec!()}, is_a: TypeType::Scalar, fields: HashMap::new(), impls: vec![], @@ -205,20 +350,24 @@ fn create_builtins() -> HashMap { enum StructAttr { Field(ast::TypeUsage), - Method(ast::TypeUsage), + Method(TypeConstructor), } -fn apply_self(type_name: &str, type_: &ast::TypeUsage) -> ast::TypeUsage { - match type_ { +fn apply_self(type_name: &str, constructor: &TypeConstructor) -> TypeConstructor { + match &constructor.type_usage { ast::TypeUsage::Function(func) => { if func.arguments.len() > 0 { match &func.arguments[0] { ast::TypeUsage::Named(named) => { if type_name == named.name.name.value { - return ast::TypeUsage::Function(ast::FunctionTypeUsage { + let result = ast::TypeUsage::Function(ast::FunctionTypeUsage { arguments: func.arguments[1..func.arguments.len()].iter().map(|arg| arg.clone()).collect(), return_type: func.return_type.clone(), }); + return TypeConstructor{ + generic: constructor.generic.clone(), + type_usage: result, + } } } _ => {} @@ -227,7 +376,7 @@ fn apply_self(type_name: &str, type_: &ast::TypeUsage) -> ast::TypeUsage { } _ => {} } - return type_.clone(); + return constructor.clone(); } fn get_attr(ctx: &Context, get_from: &NamedEntity, attribute: &ast::Identifier) -> Result { @@ -256,7 +405,12 @@ fn get_attr(ctx: &Context, get_from: &NamedEntity, attribute: &ast::Identifier) } NamedEntity::Variable(type_) => match type_ { ast::TypeUsage::Named(named) => { - let attr = get_attr(ctx, &ctx.environment[&named.name.name.value], attribute)?; + let env_type = match &ctx.environment[&named.name.name.value] { + NamedEntity::NamedType(env_type) => env_type, + _ => panic!("variable has non-type as type"), + }; + let type_ = env_type.type_construct(ctx, &named.type_parameters)?; + let attr = get_attr(ctx, &NamedEntity::NamedType(type_), attribute)?; let method = match attr { StructAttr::Field(field) => return Ok(StructAttr::Field(field)), StructAttr::Method(method) => method, @@ -269,9 +423,21 @@ fn get_attr(ctx: &Context, get_from: &NamedEntity, attribute: &ast::Identifier) }); } }, + NamedEntity::Function(_) => { + return Err(errors::TypingError::AttributeOfNonstruct { + identifier: attribute.clone(), + }); + } } } +#[derive(Debug, Clone, PartialEq, Eq)] +struct Context { + pub current_function_return: Option, + pub environment: HashMap, + pub id_generator: Rc, +} + impl Context { fn add_variable(&self, name: String, type_usage: &ast::TypeUsage) -> Context { let mut ctx = self.clone(); @@ -289,11 +455,16 @@ impl Context { let mut ctx = self.clone(); for parameter in generic.parameters.iter() { let mut env_type = EnvType{ + generic: ast::Generic{ + parameters: vec!(), + }, is_a: TypeType::Trait, fields: HashMap::new(), impls: vec!(), }; for bound in parameter.bounds.iter() { + println!("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"); + println!("found bound: {:?}", bound); if !self.environment.contains_key(&bound.name.value) { return Err(errors::TypingError::TypeDoesNotExist { identifier: bound.clone(), @@ -314,7 +485,7 @@ impl Context { fn add_impl(&self, impl_: &ast::Impl, traits: &HashMap) -> Result { let mut functions = HashMap::new(); for func in impl_.functions.iter() { - functions.insert(func.declaration.name.name.value.to_string(), func.declaration.to_type()); + functions.insert(func.declaration.name.name.value.to_string(), TypeConstructor::from_declaration(&func.declaration)); } // fill out defaults match &impl_.trait_ { @@ -330,7 +501,7 @@ impl Context { if !functions.contains_key(&default_function.declaration.name.name.value) { functions.insert( default_function.declaration.name.name.value.to_string(), - default_function.declaration.to_type(), + TypeConstructor::from_declaration(&default_function.declaration), ); } } @@ -356,7 +527,12 @@ impl Context { .insert(impl_.struct_.name.name.value.to_string(), NamedEntity::NamedType(env_type.clone())); } NamedEntity::Variable(_) => { - return Err(errors::TypingError::TypeDoesNotExist { + return Err(errors::TypingError::IdentifierIsNotType { + identifier: impl_.struct_.name.clone(), + }); + } + NamedEntity::Function(_) => { + return Err(errors::TypingError::IdentifierIsNotType { identifier: impl_.struct_.name.clone(), }); } @@ -369,6 +545,7 @@ fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> { let result = match type_ { ast::TypeUsage::Named(named) => { if !ctx.environment.contains_key(&named.name.name.value) { + panic!("foo"); return Err(errors::TypingError::TypeDoesNotExist { identifier: named.name.clone(), }); @@ -385,6 +562,7 @@ fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> { } } ast::TypeUsage::Unknown(_) => {} // do nothing + ast::TypeUsage::Namespace(_) => {} ast::TypeUsage::Function(function) => { let mut errs = vec![]; for arg in function.arguments.iter() { @@ -415,6 +593,9 @@ fn apply_substitution(ctx: &Context, substitution: &SubstitutionMap, type_: &ast ast::TypeUsage::Unknown(unknown.clone()) } } + ast::TypeUsage::Namespace(namespace) => { + ast::TypeUsage::Namespace(namespace.clone()) + } ast::TypeUsage::Function(function) => { let mut arguments = vec![]; for arg in function.arguments.iter() { @@ -507,6 +688,7 @@ fn contains(t: &ast::TypeUsage, name: &str) -> bool { match t { ast::TypeUsage::Named(_) => return false, ast::TypeUsage::Unknown(unknown) => unknown.name == name, + ast::TypeUsage::Namespace(_) => return false, ast::TypeUsage::Function(f) => { if contains(&*f.return_type, name) { return true; @@ -528,6 +710,7 @@ impl TypeChecker { let mut ctx = Context { environment: create_builtins(), current_function_return: None, + id_generator: Rc::new(ast::IdGenerator::new("T")), }; let mut traits = HashMap::new(); @@ -554,7 +737,10 @@ impl TypeChecker { }; ctx.environment.insert( function.declaration.name.name.value.to_string(), - NamedEntity::Variable(ast::TypeUsage::Function(function_type)), + NamedEntity::Function(TypeConstructor{ + generic: function.declaration.generic.clone(), + type_usage: ast::TypeUsage::Function(function_type), + }), ); } _ => {} @@ -743,6 +929,7 @@ impl TypeChecker { &impl_ctx, &ast::TypeUsage::new_named(&impl_.struct_.name.clone(), &ast::GenericUsage::Unknown), )?; + println!("env {:?}", impl_ctx); let mut functions = vec![]; for function in impl_.functions.iter() { let (result, function_subs) = self.with_function(&impl_ctx, &substitutions, function)?; @@ -900,8 +1087,8 @@ impl TypeChecker { ast::AssignmentTarget::Variable(variable) => { substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable.type_, &expr.type_)?)?; ast::AssignmentTarget::Variable(ast::VariableUsage { - type_parameters: variable.type_parameters.clone(), name: variable.name.clone(), + type_parameters: variable.type_parameters.clone(), type_: apply_substitution(ctx, &substitution, &variable.type_)?, }) } @@ -1070,7 +1257,7 @@ impl TypeChecker { let mut substitution = substitution.clone(); let struct_type = match &ctx.environment[&literal_struct.name.name.value] { NamedEntity::NamedType(env_type) => match &env_type.is_a { - TypeType::Struct => env_type.clone(), + TypeType::Struct => env_type.type_construct(ctx, &literal_struct.type_parameters)?, _ => { return Err(errors::TypingError::NotAStructLiteral { identifier: literal_struct.name.clone(), @@ -1177,19 +1364,21 @@ impl TypeChecker { ) -> Result<(ast::VariableUsage, SubstitutionMap)> { let mut substitution = substitution.clone(); match &ctx.environment[&variable_usage.name.name.value] { - NamedEntity::NamedType(_) => { - return Err(errors::TypingError::TypeIsNotAnExpression { - type_name: variable_usage.name.clone(), - }); + NamedEntity::NamedType(named_type) => { + let type_ = ast::TypeUsage::Namespace(ast::NamespaceTypeUsage::Type(ast::NamedTypeUsage{name: variable_usage.name.clone(), type_parameters: variable_usage.type_parameters.clone()})); + substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &type_)?)?; } NamedEntity::Variable(variable) => { substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &variable)?)?; } + NamedEntity::Function(function) => { + substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &function.construct(ctx, &variable_usage.type_parameters)?)?)?; + } } Ok(( ast::VariableUsage { - type_parameters: variable_usage.type_parameters.clone(), name: variable_usage.name.clone(), + type_parameters: variable_usage.type_parameters.clone(), // Redundant to type type_: apply_substitution(ctx, &substitution, &variable_usage.type_)?, }, substitution, @@ -1290,11 +1479,12 @@ impl TypeChecker { ) -> Result<(ast::StructGetter, SubstitutionMap)> { let mut substitution = substitution.clone(); let (source, subst) = self.with_expression(ctx, &substitution, &struct_getter.source)?; + println!("source: {:?}", &source); substitution = compose_substitutions(ctx, &substitution, &subst)?; let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_getter.attribute)? { StructAttr::Field(type_) => type_, - StructAttr::Method(type_) => type_, + StructAttr::Method(constructor) => constructor.construct(ctx, &struct_getter.type_parameters)?, }; substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &struct_getter.type_, &field_type)?)?;