diff --git a/src/ast.rs b/src/ast.rs index 052da18..1f25b8f 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -17,7 +17,7 @@ impl IdGenerator { pub fn new_unit() -> TypeUsage { TypeUsage::Named(NamedTypeUsage { - type_parameters: GenericUsage::Known(GenericInstantiation{parameters: vec!()}), + type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }), name: Identifier { name: Spanned { span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these @@ -29,7 +29,7 @@ pub fn new_unit() -> TypeUsage { pub fn new_never() -> TypeUsage { TypeUsage::Named(NamedTypeUsage { - type_parameters: GenericUsage::Known(GenericInstantiation{parameters: vec!()}), + type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }), name: Identifier { name: Spanned { span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these @@ -81,12 +81,15 @@ impl TypeUsage { } pub fn new_named(identifier: &Identifier, generic_usage: &GenericUsage) -> TypeUsage { - return TypeUsage::Named(NamedTypeUsage { type_parameters: generic_usage.clone(), name: identifier.clone() }); + return TypeUsage::Named(NamedTypeUsage { + type_parameters: generic_usage.clone(), + name: identifier.clone(), + }); } pub fn new_builtin(name: String) -> TypeUsage { TypeUsage::Named(NamedTypeUsage { - type_parameters: GenericUsage::Known(GenericInstantiation{parameters: vec!()}), + type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }), name: Identifier { name: Spanned { span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these @@ -128,16 +131,12 @@ pub enum GenericUsage { impl GenericUsage { pub fn new(type_parameters: &[TypeUsage]) -> Self { - GenericUsage::Known(GenericInstantiation{ - parameters: type_parameters.iter().map(|tp| { - tp.clone() - }).collect(), + GenericUsage::Known(GenericInstantiation { + parameters: type_parameters.iter().map(|tp| tp.clone()).collect(), }) } } - - #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Operator { Mul, diff --git a/src/errors.rs b/src/errors.rs index 3b8c90f..c1359e7 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -27,10 +27,7 @@ pub enum TypingError { #[error("name is not a struct, cannot instaniate")] NotAStructLiteral { identifier: ast::Identifier }, #[error("struct literal fields mismatch")] - StructLiteralFieldsMismatch { - struct_name: ast::Identifier, - struct_definition_name: ast::Identifier, - }, + StructLiteralFieldsMismatch { struct_name: ast::Identifier }, #[error("missing trait function")] MissingTraitFunction { struct_name: ast::Identifier, @@ -48,6 +45,8 @@ pub enum TypingError { IfConditionMustBeBool { // TODO: add position }, + #[error("cannot use type as an expression")] + TypeIsNotAnExpression { type_name: ast::Identifier }, #[error("multiple errors")] MultipleErrors { errors: Vec }, } diff --git a/src/interpreter.rs b/src/interpreter.rs index 2f1265f..5c03620 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -95,12 +95,10 @@ impl Context { ast::ModuleItem::Function(function) => { ctx.environment.insert( function.declaration.name.name.value.to_string(), - NamedEntity::Variable(Value::Function( - Function{ - partial: vec!(), - ref_: FunctionRef::User(function.clone()), - } - )), + NamedEntity::Variable(Value::Function(Function { + partial: vec![], + ref_: FunctionRef::User(function.clone()), + })), ); } ast::ModuleItem::Impl(impl_) => { @@ -225,11 +223,9 @@ impl TreeWalkInterpreter { let mut ctx = Context::from_module(module); let main = match &ctx.environment["main"] { - NamedEntity::Variable(Value::Function(func)) => { - match &func.ref_ { - FunctionRef::User(ref_) => ref_.clone(), - _ => panic!("main should be a user defined function"), - } + NamedEntity::Variable(Value::Function(func)) => match &func.ref_ { + FunctionRef::User(ref_) => ref_.clone(), + _ => panic!("main should be a user defined function"), }, _ => panic!("main should be a user defined function"), }; @@ -378,26 +374,35 @@ impl TreeWalkInterpreter { argument_values.push(argument_value); } match &source { - Value::Function(function) => { - match &function.ref_ { - FunctionRef::User(user_function) => { - let mut fn_ctx = ctx.new_env(); - let mut i = 0; - for partial_arg in &function.partial { - fn_ctx.set_variable(user_function.declaration.arguments[i].name.name.value.to_string(), &partial_arg.clone()); - i = i + 1; - } - for argument_value in &argument_values { - fn_ctx.set_variable(user_function.declaration.arguments[i].name.name.value.to_string(), &argument_value.clone()); - } - return ExpressionResult::Value(self.with_function(&mut fn_ctx, user_function)); + Value::Function(function) => match &function.ref_ { + FunctionRef::User(user_function) => { + let mut fn_ctx = ctx.new_env(); + let mut i = 0; + for partial_arg in &function.partial { + fn_ctx.set_variable( + user_function.declaration.arguments[i].name.name.value.to_string(), + &partial_arg.clone(), + ); + i = i + 1; } - FunctionRef::Builtin(builtin_function) => { - let all_values = function.partial.iter().map(|val| {val.clone()}).chain(argument_values.into_iter()).collect(); - return ExpressionResult::Value(builtin_function(all_values)); + for argument_value in &argument_values { + fn_ctx.set_variable( + user_function.declaration.arguments[i].name.name.value.to_string(), + &argument_value.clone(), + ); } + return ExpressionResult::Value(self.with_function(&mut fn_ctx, user_function)); } - } + FunctionRef::Builtin(builtin_function) => { + let all_values = function + .partial + .iter() + .map(|val| val.clone()) + .chain(argument_values.into_iter()) + .collect(); + return ExpressionResult::Value(builtin_function(all_values)); + } + }, _ => panic!("type error: function call source must be a function"), } } @@ -454,17 +459,17 @@ impl TreeWalkInterpreter { match &method.declaration.arguments[0].type_ { ast::TypeUsage::Named(arg_named) => { if arg_named.name.name.value == s.source.name.name.value { - return ExpressionResult::Value(Value::Function(Function{ - partial: vec!(source.clone()), + return ExpressionResult::Value(Value::Function(Function { + partial: vec![source.clone()], ref_: FunctionRef::User(method.clone()), })); } } - _ => {}, + _ => {} } } - return ExpressionResult::Value(Value::Function(Function{ - partial: vec!(), + return ExpressionResult::Value(Value::Function(Function { + partial: vec![], ref_: FunctionRef::User(method.clone()), })); } diff --git a/src/main.rs b/src/main.rs index 368e1bb..421aa59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod ast; mod errors; mod interpreter; +mod trait_checking; mod type_alias_resolution; mod type_checking; #[macro_use] @@ -42,6 +43,14 @@ fn main() { let alias_resolver = type_alias_resolution::TypeAliasResolver {}; let resolved_ast = alias_resolver.with_module(&module_ast); // println!("resolved ast: {:#?}", &resolved_ast); + let trait_checker = trait_checking::TraitChecker {}; + match trait_checker.with_module(&resolved_ast) { + Ok(_) => {} + Err(err) => { + println!("type checking error: {:#?}", &err); + return; + } + } let type_checker = type_checking::TypeChecker {}; let type_checking_result = type_checker.with_module(&resolved_ast); match &type_checking_result { diff --git a/src/trait_checking.rs b/src/trait_checking.rs new file mode 100644 index 0000000..0c1fa06 --- /dev/null +++ b/src/trait_checking.rs @@ -0,0 +1,171 @@ +use crate::ast; +use crate::errors; +use std::collections::HashMap; + +pub type Result = std::result::Result; + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Context { + pub environment_traits: HashMap, +} + +fn create_builtins() -> HashMap { + let mut result = HashMap::::new(); + return result; +} + +fn compare_struct_trait( + struct_: &ast::TypeUsage, + trait_: &ast::TypeUsage, + struct_name: &ast::Identifier, + trait_name: &ast::Identifier, +) -> Result<()> { + match struct_ { + ast::TypeUsage::Named(named) => match trait_ { + ast::TypeUsage::Named(trait_named) => { + if named.name.name.value == trait_named.name.name.value + || (named.name.name.value == struct_name.name.value && trait_named.name.name.value == trait_name.name.value) + { + return Ok(()); + } + return Err(errors::TypingError::TypeMismatch { + type_one: struct_.clone(), + type_two: trait_.clone(), + }); + } + ast::TypeUsage::Function(_) => { + return Err(errors::TypingError::TypeMismatch { + type_one: struct_.clone(), + type_two: trait_.clone(), + }); + } + _ => panic!("Unknown in function definition"), + }, + ast::TypeUsage::Function(function) => match trait_ { + ast::TypeUsage::Named(_) => { + return Err(errors::TypingError::TypeMismatch { + type_one: struct_.clone(), + type_two: trait_.clone(), + }); + } + ast::TypeUsage::Function(trait_function) => { + if function.arguments.len() != trait_function.arguments.len() { + return Err(errors::TypingError::TypeMismatch { + type_one: struct_.clone(), + type_two: trait_.clone(), + }); + } + for (i, _) in function.arguments.iter().enumerate() { + compare_struct_trait(&function.arguments[i], &trait_function.arguments[i], struct_name, trait_name)?; + } + compare_struct_trait(&function.return_type, &trait_function.return_type, struct_name, trait_name)?; + return Ok(()); + } + _ => panic!("Unknown in function definition"), + }, + _ => panic!("Unknown in function definition"), + } +} + +pub struct TraitChecker {} + +impl TraitChecker { + pub fn with_module(self: &Self, module: &ast::Module) -> Result<()> { + let mut ctx = Context { + environment_traits: create_builtins(), + }; + + for item in module.items.iter() { + match item { + ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => { + ctx.environment_traits.insert(trait_.name.name.value.to_string(), trait_.clone()); + } + _ => {} + } + } + + for item in module.items.iter() { + match item { + ast::ModuleItem::Impl(impl_) => { + self.with_impl(&ctx, impl_)?; + } + _ => {} + } + } + return Ok(()); + } + + fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result<()> { + // See if trait actually matches + match &impl_.trait_ { + Some(trait_) => { + // assert trait functions satisfied + if !ctx.environment_traits.contains_key(&trait_.name.value) { + return Err(errors::TypingError::TypeDoesNotExist { + identifier: trait_.clone(), + }); + } + let trait_declaration = &ctx.environment_traits[&trait_.name.value]; + for trait_item in trait_declaration.functions.iter() { + match trait_item { + ast::TraitItem::FunctionDeclaration(declaration) => { + let mut found = false; + for impl_function in impl_.functions.iter() { + if impl_function.declaration.name.name.value == declaration.name.name.value { + found = true; + compare_struct_trait( + &impl_function.declaration.to_type(), + &declaration.to_type(), + &impl_.struct_name, + &trait_, + )?; + } + } + if found == false { + return Err(errors::TypingError::MissingTraitFunction { + struct_name: impl_.struct_name.clone(), + function_name: declaration.name.clone(), + }); + } + } + ast::TraitItem::Function(function) => { + // skip found check because it has a default + for impl_function in impl_.functions.iter() { + if impl_function.declaration.name.name.value == function.declaration.name.name.value { + compare_struct_trait( + &impl_function.declaration.to_type(), + &function.declaration.to_type(), + &impl_.struct_name, + &trait_, + )?; + } + } + } + } + } + // assert all functions are in trait + for impl_function in impl_.functions.iter() { + let mut found = false; + for trait_item in trait_declaration.functions.iter() { + let declaration = match trait_item { + ast::TraitItem::Function(function) => &function.declaration, + ast::TraitItem::FunctionDeclaration(declaration) => &declaration, + }; + if impl_function.declaration.name.name.value == declaration.name.name.value { + found = true; + break; + } + } + if found == false { + return Err(errors::TypingError::FunctionNotInTrait { + function_name: impl_function.declaration.name.clone(), + }); + } + } + } + None => {} + } + // TODO: check for duplicate functions + return Ok(()); + } +} diff --git a/src/type_alias_resolution.rs b/src/type_alias_resolution.rs index f2900c5..cd8187c 100644 --- a/src/type_alias_resolution.rs +++ b/src/type_alias_resolution.rs @@ -138,7 +138,7 @@ impl TypeAliasResolver { }, replaces: ast::TypeUsage::Named(ast::NamedTypeUsage { type_parameters: ast::GenericUsage::Unknown, - name: trait_.name.clone() + name: trait_.name.clone(), }), }); return ast::TraitTypeDeclaration { @@ -148,9 +148,7 @@ impl TypeAliasResolver { .functions .iter() .map(|f| match f { - ast::TraitItem::Function(function) => { - ast::TraitItem::Function(self.with_function(&trait_ctx, function)) - }, + 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)) } diff --git a/src/type_checking.rs b/src/type_checking.rs index c2aea6d..14ce7b3 100644 --- a/src/type_checking.rs +++ b/src/type_checking.rs @@ -6,16 +6,72 @@ pub type SubstitutionMap = HashMap; pub type Result = std::result::Result; -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EnvImpl { + trait_: Option, + functions: HashMap, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TypeType { + Scalar, + Trait, + Struct, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EnvType { + is_a: TypeType, + fields: HashMap, + impls: Vec, +} + +impl EnvType { + fn from_struct(struct_: &ast::StructTypeDeclaration) -> EnvType { + return EnvType { + is_a: TypeType::Struct, + fields: struct_ + .fields + .iter() + .map(|field| (field.name.name.value.to_string(), field.type_.clone())) + .collect(), + impls: vec![], + }; + } + + fn from_trait(trait_: &ast::TraitTypeDeclaration) -> EnvType { + let mut functions = HashMap::new(); + for func in trait_.functions.iter() { + match func { + ast::TraitItem::FunctionDeclaration(fd) => { + functions.insert(fd.name.name.value.to_string(), fd.to_type()); + } + ast::TraitItem::Function(f) => { + functions.insert(f.declaration.name.name.value.to_string(), f.declaration.to_type()); + } + } + } + let impl_ = EnvImpl { + trait_: None, + functions: functions, + }; + return EnvType { + is_a: TypeType::Trait, + fields: HashMap::new(), + impls: vec![impl_], + }; + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] pub enum NamedEntity { - TypeDeclaration(ast::TypeDeclaration), + NamedType(EnvType), Variable(ast::TypeUsage), } #[derive(Debug, Clone, PartialEq, Eq)] struct Context { pub current_function_return: Option, - pub impls: Vec, pub environment: HashMap, } @@ -23,99 +79,127 @@ fn create_builtins() -> HashMap { let mut result = HashMap::new(); result.insert( "i8".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "i8".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "i16".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "i16".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "i32".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "i32".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "i64".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "i64".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "isize".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "isize".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "u8".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "u8".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "u16".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "u16".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "u32".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "u32".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "u64".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "u64".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "usize".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "usize".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "f32".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "f32".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "f64".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "f64".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "bool".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "bool".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); - result.insert( "!".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "!".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); result.insert( "unit".to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration { - name: "unit".to_string(), - })), + NamedEntity::NamedType(EnvType { + is_a: TypeType::Scalar, + fields: HashMap::new(), + impls: vec![], + }), ); - return result; } @@ -124,167 +208,67 @@ enum StructAttr { Method(ast::TypeUsage), } -fn get_struct_attr(ctx: &Context, struct_declaration: &ast::StructTypeDeclaration, attribute: &ast::Identifier) -> Result { - for field in struct_declaration.fields.iter() { - if field.name.name.value == attribute.name.value { - return Ok(StructAttr::Field(field.type_.clone())); - } - } - - let mut result = Vec::new(); - for impl_ in ctx.impls.iter() { - if &struct_declaration.name.name.value != &impl_.struct_name.name.value { - continue; - } - for method in impl_.functions.iter() { - if method.declaration.name.name.value == attribute.name.value { - let mut function_type = method.declaration.to_type(); - - // if the name of the type of the first argument == the class, remove the first arg - if method.declaration.arguments.len() > 0 { - match &method.declaration.arguments[0].type_ { - ast::TypeUsage::Named(named) => { - if named.name.name.value == struct_declaration.name.name.value { - function_type = method.declaration.to_method_type(); - } - } - _ => {} - }; - } - result.push(function_type); - } - } - } - // TODO: default trait impls - if result.len() == 0 { - return Err(errors::TypingError::UnknownFieldName { - identifier: attribute.clone(), - }); - } - if result.len() > 1 { - return Err(errors::TypingError::MultipleFieldName { - identifier: attribute.clone(), - }); - } - return Ok(StructAttr::Method(result[0].clone())); -} - -fn get_trait_attr(ctx: &Context, trait_declaration: &ast::TraitTypeDeclaration, attribute: &ast::Identifier) -> Result { - let mut result = Vec::new(); - for trait_item in trait_declaration.functions.iter() { - let declaration = match trait_item { - ast::TraitItem::Function(function) => &function.declaration, - ast::TraitItem::FunctionDeclaration(declaration) => declaration, - }; - if declaration.name.name.value == attribute.name.value { - let mut function_type = declaration.to_type(); - println!("foo: {:?}", declaration); - // if the name of the type of the first argument == the class, remove the first arg - if declaration.arguments.len() > 0 { - match &declaration.arguments[0].type_ { +fn apply_self(type_name: &str, type_: &ast::TypeUsage) -> ast::TypeUsage { + match type_ { + ast::TypeUsage::Function(func) => { + if func.arguments.len() > 0 { + match &func.arguments[0] { ast::TypeUsage::Named(named) => { - if named.name.name.value == trait_declaration.name.name.value { - function_type = declaration.to_method_type(); + if type_name == named.name.name.value { + return 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 type_.clone(); +} + +fn get_attr(ctx: &Context, get_from: &NamedEntity, attribute: &ast::Identifier) -> Result { + match get_from { + NamedEntity::NamedType(env_type) => { + if env_type.fields.contains_key(&attribute.name.value) { + return Ok(StructAttr::Field(env_type.fields[&attribute.name.value].clone())); + } + let mut result = Vec::new(); + for impl_ in env_type.impls.iter() { + if impl_.functions.contains_key(&attribute.name.value) { + result.push(impl_.functions[&attribute.name.value].clone()) + } + } + if result.len() == 0 { + return Err(errors::TypingError::UnknownFieldName { + identifier: attribute.clone(), + }); + } + if result.len() > 1 { + return Err(errors::TypingError::MultipleFieldName { + identifier: attribute.clone(), + }); + } + return Ok(StructAttr::Method(result[0].clone())); + } + NamedEntity::Variable(type_) => match type_ { + ast::TypeUsage::Named(named) => { + let attr = get_attr(ctx, &ctx.environment[&named.name.name.value], attribute)?; + let method = match attr { + StructAttr::Field(field) => return Ok(StructAttr::Field(field)), + StructAttr::Method(method) => method, }; + return Ok(StructAttr::Method(apply_self(&named.name.name.value, &method))); } - result.push(function_type); - } - } - if result.len() == 0 { - return Err(errors::TypingError::UnknownFieldName { - identifier: attribute.clone(), - }); - } - if result.len() > 1 { - return Err(errors::TypingError::MultipleFieldName { - identifier: attribute.clone(), - }); - } - return Ok(StructAttr::Method(result[0].clone())); -} - -fn get_attr(ctx: &Context, source_type: &ast::TypeUsage, attribute: &ast::Identifier) -> Result { - match source_type { - ast::TypeUsage::Named(named) => { - match &ctx.environment[&named.name.name.value] { - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => { - return get_struct_attr(ctx, type_declaration, attribute); - } - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Trait(type_declaration)) => { - return get_trait_attr(ctx, type_declaration, attribute); - } - _ => { - return Err(errors::TypingError::AttributeOfNonstruct { - identifier: attribute.clone(), - }); - // TODO: support builtins - float, int, etc. - } - } - } - ast::TypeUsage::Function(_) => { - return Err(errors::TypingError::NotAStructLiteral { - identifier: attribute.clone(), - }); - } - _ => { - panic!("tried to get attr of unknown"); - } - }; -} - -fn compare_struct_trait( - struct_: &ast::TypeUsage, - trait_: &ast::TypeUsage, - struct_name: &ast::Identifier, - trait_name: &ast::Identifier, -) -> Result<()> { - match struct_ { - ast::TypeUsage::Named(named) => match trait_ { - ast::TypeUsage::Named(trait_named) => { - if named.name.name.value == trait_named.name.name.value - || (named.name.name.value == struct_name.name.value && trait_named.name.name.value == trait_name.name.value) - { - return Ok(()); - } - return Err(errors::TypingError::TypeMismatch { - type_one: struct_.clone(), - type_two: trait_.clone(), + _ => { + return Err(errors::TypingError::AttributeOfNonstruct { + identifier: attribute.clone(), }); } - ast::TypeUsage::Function(_) => { - return Err(errors::TypingError::TypeMismatch { - type_one: struct_.clone(), - type_two: trait_.clone(), - }); - } - _ => panic!("Unknown in function definition"), }, - ast::TypeUsage::Function(function) => match trait_ { - ast::TypeUsage::Named(_) => { - return Err(errors::TypingError::TypeMismatch { - type_one: struct_.clone(), - type_two: trait_.clone(), - }); - } - ast::TypeUsage::Function(trait_function) => { - if function.arguments.len() != trait_function.arguments.len() { - return Err(errors::TypingError::TypeMismatch { - type_one: struct_.clone(), - type_two: trait_.clone(), - }); - } - for (i, _) in function.arguments.iter().enumerate() { - compare_struct_trait(&function.arguments[i], &trait_function.arguments[i], struct_name, trait_name)?; - } - compare_struct_trait(&function.return_type, &trait_function.return_type, struct_name, trait_name)?; - return Ok(()); - } - _ => panic!("Unknown in function definition"), - }, - _ => panic!("Unknown in function definition"), } } @@ -300,6 +284,59 @@ impl Context { ctx.current_function_return = Some(function.clone()); return ctx; } + + 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()); + } + // fill out defaults + match &impl_.trait_ { + Some(trait_name) => { + if !traits.contains_key(&trait_name.name.value) { + return Err(errors::TypingError::TypeDoesNotExist { + identifier: trait_name.clone(), + }); + } + for func in traits[&trait_name.name.value].functions.iter() { + match func { + ast::TraitItem::Function(default_function) => { + 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(), + ); + } + } + _ => {} + } + } + } + None => {} + } + let mut result = self.clone(); + let mut env_named = result.environment[&impl_.struct_name.name.value].clone(); + match &mut env_named { + NamedEntity::NamedType(env_type) => { + env_type.impls.push(EnvImpl { + trait_: match &impl_.trait_ { + Some(trait_) => Some(trait_.name.value.to_string()), + None => None, + }, + functions: functions, + }); + result + .environment + .insert(impl_.struct_name.name.value.to_string(), NamedEntity::NamedType(env_type.clone())); + } + NamedEntity::Variable(_) => { + return Err(errors::TypingError::TypeDoesNotExist { + identifier: impl_.struct_name.clone(), + }); + } + } + return Ok(result); + } } fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> { @@ -311,7 +348,7 @@ fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> { }); } match ctx.environment[&named.name.name.value] { - NamedEntity::TypeDeclaration(_) => { + NamedEntity::NamedType(_) => { // is a type } _ => { @@ -464,28 +501,24 @@ impl TypeChecker { pub fn with_module(self: &Self, module: &ast::Module) -> Result<(ast::Module, SubstitutionMap)> { let mut ctx = Context { environment: create_builtins(), - impls: Vec::new(), current_function_return: None, }; + let mut traits = HashMap::new(); + for item in module.items.iter() { match item { ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => { ctx.environment.insert( struct_.name.name.value.to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone())), - ); - } - ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => { - ctx.environment.insert( - alias.name.name.value.to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone())), + NamedEntity::NamedType(EnvType::from_struct(&struct_)), ); } ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => { + traits.insert(trait_.name.name.value.to_string(), trait_.clone()); ctx.environment.insert( trait_.name.name.value.to_string(), - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Trait(trait_.clone())), + NamedEntity::NamedType(EnvType::from_trait(&trait_)), ); } ast::ModuleItem::Function(function) => { @@ -498,8 +531,19 @@ impl TypeChecker { NamedEntity::Variable(ast::TypeUsage::Function(function_type)), ); } + _ => {} + } + } + + for item in module.items.iter() { + match item { ast::ModuleItem::Impl(impl_) => { - ctx.impls.push(impl_.clone()); + if !ctx.environment.contains_key(&impl_.struct_name.name.value) { + return Err(errors::TypingError::IdentifierIsNotType { + identifier: impl_.struct_name.clone(), + }); + } + ctx = ctx.add_impl(&impl_, &traits)?; } _ => {} } @@ -530,11 +574,7 @@ impl TypeChecker { return Ok((result, subst)); } - fn with_function_declaration( - self: &Self, - ctx: &Context, - declaration: &ast::FunctionDeclaration, - ) -> Result { + fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> Result { for arg in declaration.arguments.iter() { type_exists(ctx, &arg.type_)?; } @@ -621,7 +661,7 @@ impl TypeChecker { trait_: &ast::TraitTypeDeclaration, ) -> Result<(ast::TraitTypeDeclaration, SubstitutionMap)> { let mut substitutions = incoming_substitutions.clone(); - let mut result_functions = vec!(); + let mut result_functions = vec![]; for item in &trait_.functions { match item { ast::TraitItem::FunctionDeclaration(declaration) => { @@ -668,79 +708,16 @@ impl TypeChecker { impl_: &ast::Impl, ) -> Result<(ast::Impl, SubstitutionMap)> { let mut substitutions = incoming_substitutions.clone(); - type_exists(ctx, &ast::TypeUsage::new_named(&impl_.struct_name.clone(), &ast::GenericUsage::Unknown))?; + type_exists( + ctx, + &ast::TypeUsage::new_named(&impl_.struct_name.clone(), &ast::GenericUsage::Unknown), + )?; let mut functions = vec![]; for function in impl_.functions.iter() { let (result, function_subs) = self.with_function(&ctx, &substitutions, function)?; substitutions = compose_substitutions(ctx, &substitutions, &function_subs)?; functions.push(result); } - // See if trait actually matches - match &impl_.trait_ { - Some(trait_) => { - // assert trait functions satisfied - if !ctx.environment.contains_key(&trait_.name.value) { - return Err(errors::TypingError::TypeDoesNotExist { - identifier: trait_.clone(), - }); - } - let trait_declaration = match &ctx.environment[&trait_.name.value] { - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Trait(declaration)) => declaration, - _ => { - return Err(errors::TypingError::ImplTraitMustBeTrait { - trait_name: trait_.clone(), - }); - } - }; - for trait_item in trait_declaration.functions.iter() { - match trait_item { - ast::TraitItem::FunctionDeclaration(declaration) => { - let mut found = false; - for impl_function in impl_.functions.iter() { - if impl_function.declaration.name.name.value == declaration.name.name.value { - found = true; - compare_struct_trait(&impl_function.declaration.to_type(), &declaration.to_type(), &impl_.struct_name, &trait_)?; - } - } - if found == false { - return Err(errors::TypingError::MissingTraitFunction { - struct_name: impl_.struct_name.clone(), - function_name: declaration.name.clone(), - }); - } - } - ast::TraitItem::Function(function) => { - // skip found check because it has a default - for impl_function in impl_.functions.iter() { - if impl_function.declaration.name.name.value == function.declaration.name.name.value { - compare_struct_trait(&impl_function.declaration.to_type(), &function.declaration.to_type(), &impl_.struct_name, &trait_)?; - } - } - } - } - } - // assert all functions are in trait - for impl_function in impl_.functions.iter() { - let mut found = false; - for trait_item in trait_declaration.functions.iter() { - let declaration = match trait_item { - ast::TraitItem::Function(function) => &function.declaration, - ast::TraitItem::FunctionDeclaration(declaration) => declaration, - }; - if impl_function.declaration.name.name.value == declaration.name.name.value { - found = true; - break; - } - } - if found == false { - return Err(errors::TypingError::FunctionNotInTrait { - function_name: impl_function.declaration.name.clone(), - }); - } - } - } - None => {} - } return Ok(( ast::Impl { generic: impl_.generic.clone(), @@ -903,7 +880,7 @@ impl TypeChecker { let (source, subst) = self.with_expression(ctx, &substitution, &struct_attr.source)?; let mut subst = subst.clone(); - let field_type = match get_attr(ctx, &source.type_, &struct_attr.attribute)? { + let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_attr.attribute)? { StructAttr::Field(type_) => type_, StructAttr::Method(_) => { return Err(errors::TypingError::CannotAssignToMethod { @@ -1062,40 +1039,47 @@ impl TypeChecker { literal_struct: &ast::LiteralStruct, ) -> Result<(ast::LiteralStruct, SubstitutionMap)> { let mut substitution = substitution.clone(); - let type_declaration = match &ctx.environment[&literal_struct.name.name.value] { - NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => type_declaration, + 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(), + _ => { + return Err(errors::TypingError::NotAStructLiteral { + identifier: literal_struct.name.clone(), + }); + } + }, _ => { return Err(errors::TypingError::NotAStructLiteral { identifier: literal_struct.name.clone(), }); } }; - if type_declaration.fields.len() != literal_struct.fields.len() { + if struct_type.fields.len() != literal_struct.fields.len() { return Err(errors::TypingError::StructLiteralFieldsMismatch { struct_name: literal_struct.name.clone(), - struct_definition_name: type_declaration.name.clone(), }); } let mut fields = vec![]; - for type_field in type_declaration.fields.iter() { + for (type_field_name, type_field_type) in struct_type.fields.iter() { let mut found = false; let mut field_expression: Option = None; + let mut field_name: Option = None; for field in literal_struct.fields.iter() { - if type_field.name.name.value == field.0.name.value { + if type_field_name == &field.0.name.value { found = true; let (result, subst) = self.with_expression(ctx, &substitution, &field.1)?; substitution = compose_substitutions(ctx, &substitution, &subst)?; - substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &type_field.type_, &result.type_)?)?; + substitution = compose_substitutions(ctx, &substitution, &unify(ctx, type_field_type, &result.type_)?)?; field_expression = Some(result); + field_name = Some(field.0.clone()); } } if !found { return Err(errors::TypingError::StructLiteralFieldsMismatch { struct_name: literal_struct.name.clone(), - struct_definition_name: type_field.name.clone(), }); } - fields.push((type_field.name.clone(), field_expression.unwrap())); + fields.push((field_name.unwrap(), field_expression.unwrap())); } Ok(( ast::LiteralStruct { @@ -1164,8 +1148,10 @@ impl TypeChecker { ) -> Result<(ast::VariableUsage, SubstitutionMap)> { let mut substitution = substitution.clone(); match &ctx.environment[&variable_usage.name.name.value] { - NamedEntity::TypeDeclaration(_) => { - panic!("Using types not yet supported"); + NamedEntity::NamedType(_) => { + return Err(errors::TypingError::TypeIsNotAnExpression { + type_name: variable_usage.name.clone(), + }); } NamedEntity::Variable(variable) => { substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &variable)?)?; @@ -1277,7 +1263,7 @@ impl TypeChecker { let (source, subst) = self.with_expression(ctx, &substitution, &struct_getter.source)?; substitution = compose_substitutions(ctx, &substitution, &subst)?; - let field_type = match get_attr(ctx, &source.type_, &struct_getter.attribute)? { + let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_getter.attribute)? { StructAttr::Field(type_) => type_, StructAttr::Method(type_) => type_, }; @@ -1295,14 +1281,24 @@ impl TypeChecker { )) } - fn with_block_expression(self: &Self, ctx: &Context, substitution: &SubstitutionMap, block: &ast::Block) -> Result<(ast::Block, SubstitutionMap)> { + fn with_block_expression( + self: &Self, + ctx: &Context, + substitution: &SubstitutionMap, + block: &ast::Block, + ) -> Result<(ast::Block, SubstitutionMap)> { let mut substitution = substitution.clone(); let (result, subst) = self.with_block(ctx, &substitution, &block)?; substitution = compose_substitutions(ctx, &substitution, &subst)?; Ok((result, substitution)) } - fn with_op(self: &Self, ctx: &Context, substitution: &SubstitutionMap, op: &ast::Operation) -> Result<(ast::Operation, SubstitutionMap)> { + fn with_op( + self: &Self, + ctx: &Context, + substitution: &SubstitutionMap, + op: &ast::Operation, + ) -> Result<(ast::Operation, SubstitutionMap)> { let mut substitution = substitution.clone(); let (expr_left, subst_left) = self.with_expression(ctx, &substitution, &op.left)?; let (expr_right, subst_right) = self.with_expression(ctx, &substitution, &op.right)?;