diff --git a/src/lower/datatype.rs b/src/lower/datatype.rs index f7f24db..6987ca5 100644 --- a/src/lower/datatype.rs +++ b/src/lower/datatype.rs @@ -24,6 +24,10 @@ pub fn lower_type( match &resolved_type.kind { resolved::TypeKind::Unresolved => panic!("got unresolved type during lower_type!"), resolved::TypeKind::Polymorph(_, _) => todo!("cannot directly lower polymorph"), + resolved::TypeKind::Trait(name, _) => Err(LowerErrorKind::CannotUseTraitDirectly { + name: name.to_string(), + } + .at(resolved_type.source)), resolved::TypeKind::Boolean => Ok(ir::Type::Boolean), resolved::TypeKind::Integer(bits, sign) => Ok(match (bits, sign) { (Bits::Bits8, Sign::Signed) => ir::Type::S8, diff --git a/src/lower/error.rs b/src/lower/error.rs index 2b0e4e4..76e9570 100644 --- a/src/lower/error.rs +++ b/src/lower/error.rs @@ -41,6 +41,9 @@ pub enum LowerErrorKind { }, PolymorphError(PolymorphErrorKind), IncorrectNumberOfTypeArguments, + CannotUseTraitDirectly { + name: String, + }, } impl From for LowerError { @@ -106,6 +109,9 @@ impl Display for LowerErrorKind { write!(f, "Incorrect number of type arguments") } LowerErrorKind::PolymorphError(e) => e.fmt(f), + LowerErrorKind::CannotUseTraitDirectly { name } => { + write!(f, "Cannot use trait '{}' directly", name) + } } } } diff --git a/src/resolve/error.rs b/src/resolve/error.rs index 2639bd6..1e6118a 100644 --- a/src/resolve/error.rs +++ b/src/resolve/error.rs @@ -206,6 +206,9 @@ pub enum ResolveErrorKind { ConstraintsNotSatisfiedForType { name: String, }, + TypeIsNotATrait { + name: String, + }, Other { message: String, }, @@ -537,6 +540,11 @@ impl Display for ResolveErrorKind { ResolveErrorKind::ConstraintsNotSatisfiedForType { name } => { write!(f, "Constraints not satisfied for type '{}'", name)?; } + ResolveErrorKind::TypeIsNotATrait { + name + } => { + write!(f, "Type '{}' is not a trait", name)?; + } ResolveErrorKind::Other { message } => { write!(f, "{}", message)?; } diff --git a/src/resolve/function_head.rs b/src/resolve/function_head.rs index 3387c1a..af5b131 100644 --- a/src/resolve/function_head.rs +++ b/src/resolve/function_head.rs @@ -157,6 +157,7 @@ pub fn collect_constraints(map: &mut HashMap>, ty: & set.insert(constraint.clone()); } } + resolved::TypeKind::Trait(_, _) => (), } } diff --git a/src/resolve/job.rs b/src/resolve/job.rs index 0e08eb9..b81b5b1 100644 --- a/src/resolve/job.rs +++ b/src/resolve/job.rs @@ -1,5 +1,5 @@ use crate::{ - resolved::{self, EnumRef, StructureRef, TypeAliasRef}, + resolved::{self, EnumRef, StructureRef, TraitRef, TypeAliasRef}, workspace::fs::FsNodeId, }; @@ -12,6 +12,7 @@ pub enum FuncJob { pub struct TypeJob { pub physical_file_id: FsNodeId, pub type_aliases: Vec, + pub traits: Vec, pub structures: Vec, pub enums: Vec, } diff --git a/src/resolve/polymorph.rs b/src/resolve/polymorph.rs index b6cff6c..faa3b43 100644 --- a/src/resolve/polymorph.rs +++ b/src/resolve/polymorph.rs @@ -118,6 +118,7 @@ impl PolyRecipe { poly_type.resolved_type.clone() } + resolved::TypeKind::Trait(_, _) => ty.clone(), }) } } @@ -189,7 +190,8 @@ impl PolyCatalog { | resolved::TypeKind::Floating(_) | resolved::TypeKind::Void | resolved::TypeKind::Enum(_, _) - | resolved::TypeKind::TypeAlias(_, _) => { + | resolved::TypeKind::TypeAlias(_, _) + | resolved::TypeKind::Trait(_, _) => { if *pattern_type == *concrete_type { Ok(()) } else { diff --git a/src/resolve/type_ctx/mod.rs b/src/resolve/type_ctx/mod.rs index fc0dcfa..d9734db 100644 --- a/src/resolve/type_ctx/mod.rs +++ b/src/resolve/type_ctx/mod.rs @@ -55,21 +55,49 @@ impl<'a, 'b, 'c> From<&'c ResolveExprCtx<'a, 'b>> for ResolveTypeCtx<'c> { } } -pub fn resolve_constraints(constraints: &[ast::Type]) -> Result, ResolveError> { +pub fn resolve_constraints( + type_ctx: &ResolveTypeCtx, + constraints: &[ast::Type], +) -> Result, ResolveError> { let mut resolved_constraints = vec![]; for constraint in constraints { - if let ast::TypeKind::Named(name, arguments) = &constraint.kind { - resolved_constraints.push(match name.as_plain_str() { - Some("PrimitiveAdd") if arguments.is_empty() => Constraint::PrimitiveAdd, - _ => { - return Err( - ResolveErrorKind::UndeclaredTrait(name.to_string()).at(constraint.source) - ) - } - }); - } + resolved_constraints.push(resolve_constraint(type_ctx, constraint)?); } Ok(resolved_constraints) } + +pub fn resolve_constraint( + type_ctx: &ResolveTypeCtx, + constraint: &ast::Type, +) -> Result { + if let ast::TypeKind::Named(name, arguments) = &constraint.kind { + match name.as_plain_str() { + Some("PrimitiveAdd") if arguments.is_empty() => return Ok(Constraint::PrimitiveAdd), + _ => { + let resolved_type = type_ctx.resolve(constraint).map_err(|err| { + if let ResolveErrorKind::UndeclaredType { name } = err.kind { + ResolveErrorKind::UndeclaredTrait(name).at(err.source) + } else { + err + } + })?; + + let resolved::TypeKind::Trait(_, trait_ref) = &resolved_type.kind else { + return Err(ResolveErrorKind::TypeIsNotATrait { + name: resolved_type.to_string(), + } + .at(resolved_type.source)); + }; + + return Ok(Constraint::Trait(*trait_ref)); + } + } + } + + return Err(ResolveErrorKind::TypeIsNotATrait { + name: constraint.to_string(), + } + .at(constraint.source)); +} diff --git a/src/resolve/type_ctx/resolve_type.rs b/src/resolve/type_ctx/resolve_type.rs index 634fa5f..36fa47f 100644 --- a/src/resolve/type_ctx/resolve_type.rs +++ b/src/resolve/type_ctx/resolve_type.rs @@ -110,7 +110,7 @@ impl<'a> ResolveTypeCtx<'a> { } ast::TypeKind::Polymorph(polymorph, constraints) => Ok(resolved::TypeKind::Polymorph( polymorph.clone(), - resolve_constraints(constraints)?, + resolve_constraints(self, constraints)?, )), } .map(|kind| kind.at(ast_type.source)) diff --git a/src/resolve/type_definition/prepare.rs b/src/resolve/type_definition/prepare.rs index 4af1915..0e634e6 100644 --- a/src/resolve/type_definition/prepare.rs +++ b/src/resolve/type_definition/prepare.rs @@ -5,9 +5,12 @@ use crate::{ ctx::ResolveCtx, error::{ResolveError, ResolveErrorKind}, job::TypeJob, - type_ctx::resolve_constraints, + type_ctx::{resolve_constraints, ResolveTypeCtx}, + }, + resolved::{ + self, CurrentConstraints, EnumRef, HumanName, StructureRef, TraitRef, TypeAliasRef, + TypeDecl, TypeParameters, }, - resolved::{self, EnumRef, HumanName, StructureRef, TypeAliasRef, TypeDecl, TypeParameters}, workspace::fs::FsNodeId, }; use indexmap::IndexMap; @@ -28,15 +31,26 @@ pub fn prepare_type_jobs( let mut job = TypeJob { physical_file_id: *physical_file_id, type_aliases: Vec::with_capacity(file.type_aliases.len()), + traits: Vec::with_capacity(file.traits.len()), structures: Vec::with_capacity(file.structures.len()), enums: Vec::with_capacity(file.enums.len()), }; + for user_trait in file.traits.iter() { + job.traits.push(prepare_trait( + ctx, + resolved_ast, + module_fs_node_id, + user_trait, + )); + } + for structure in file.structures.iter() { job.structures.push(prepare_structure( ctx, resolved_ast, module_fs_node_id, + *physical_file_id, structure, )?); } @@ -69,6 +83,7 @@ fn prepare_structure( ctx: &mut ResolveCtx, resolved_ast: &mut resolved::Ast, module_fs_node_id: FsNodeId, + physical_fs_node_id: FsNodeId, structure: &ast::Structure, ) -> Result { let source = structure.source; @@ -76,7 +91,17 @@ fn prepare_structure( let mut parameters = TypeParameters::default(); for (name, parameter) in structure.parameters.iter() { - let constraints = resolve_constraints(¶meter.constraints)?; + let zero_current_constraints = CurrentConstraints::default(); + let constraints = resolve_constraints( + &ResolveTypeCtx::new( + resolved_ast, + module_fs_node_id, + physical_fs_node_id, + &ctx.types_in_modules, + &zero_current_constraints, + ), + ¶meter.constraints, + )?; if parameters .parameters @@ -155,6 +180,36 @@ fn prepare_enum( enum_ref } +fn prepare_trait( + ctx: &mut ResolveCtx, + resolved_ast: &mut resolved::Ast, + module_fs_node_id: FsNodeId, + definition: &ast::Trait, +) -> TraitRef { + let trait_ref = resolved_ast.traits.insert(resolved::Trait { + methods: vec![], + source: definition.source, + }); + + let kind = resolved::TypeKind::Trait(HumanName(definition.name.to_string()), trait_ref); + let source = definition.source; + let privacy = definition.privacy; + + ctx.types_in_modules + .entry(module_fs_node_id) + .or_default() + .insert( + definition.name.to_string(), + TypeDecl { + kind, + source, + privacy, + }, + ); + + trait_ref +} + fn prepare_type_alias( ctx: &mut ResolveCtx, resolved_ast: &mut resolved::Ast, diff --git a/src/resolve/type_definition/resolve.rs b/src/resolve/type_definition/resolve.rs index 1f49825..ba4733f 100644 --- a/src/resolve/type_definition/resolve.rs +++ b/src/resolve/type_definition/resolve.rs @@ -30,6 +30,10 @@ pub fn resolve_type_jobs( .get_owning_module(job.physical_file_id) .unwrap_or(job.physical_file_id); + for (_trait_ref, _user_trait) in job.traits.iter().zip(file.traits.iter()) { + resolve_trait()?; + } + for (structure_ref, structure) in job.structures.iter().zip(file.structures.iter()) { resolve_structure( ctx, @@ -76,12 +80,22 @@ fn resolve_structure( structure_ref: StructureRef, ) -> Result<(), ResolveError> { for (field_name, field) in structure.fields.iter() { - let mut constraints = HashMap::new(); + let pre_constraints = CurrentConstraints::default(); + let pre_type_ctx = ResolveTypeCtx::new( + &resolved_ast, + module_file_id, + physical_file_id, + &ctx.types_in_modules, + &pre_constraints, + ); + let mut constraints = HashMap::new(); for (name, parameter) in structure.parameters.iter() { constraints.insert( name.into(), - HashSet::from_iter(resolve_constraints(¶meter.constraints)?.drain(..)), + HashSet::from_iter( + resolve_constraints(&pre_type_ctx, ¶meter.constraints)?.drain(..), + ), ); } @@ -165,3 +179,8 @@ fn resolve_type_alias( *resolved_ast.type_aliases.get_mut(type_alias_ref).unwrap() = resolved_type; Ok(()) } + +fn resolve_trait() -> Result<(), ResolveError> { + eprintln!("warning: trait methods are not resolved yet"); + Ok(()) +} diff --git a/src/resolved/datatype/kind/constraint.rs b/src/resolved/datatype/kind/constraint.rs index ebd46a6..5398c87 100644 --- a/src/resolved/datatype/kind/constraint.rs +++ b/src/resolved/datatype/kind/constraint.rs @@ -1,14 +1,17 @@ +use crate::resolved::TraitRef; use std::fmt::Display; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum Constraint { PrimitiveAdd, + Trait(TraitRef), } impl Display for Constraint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Constraint::PrimitiveAdd => write!(f, "PrimitiveAdd"), + Constraint::Trait(_) => write!(f, ""), } } } diff --git a/src/resolved/datatype/kind/mod.rs b/src/resolved/datatype/kind/mod.rs index 9b80215..ed109ab 100644 --- a/src/resolved/datatype/kind/mod.rs +++ b/src/resolved/datatype/kind/mod.rs @@ -6,7 +6,7 @@ mod function_pointer; use super::Type; use crate::{ ast::{fmt_c_integer, CInteger, FloatSize, IntegerBits, IntegerSign}, - resolved::{human_name::HumanName, Ast, EnumRef, StructureRef, TypeAliasRef}, + resolved::{human_name::HumanName, Ast, EnumRef, StructureRef, TraitRef, TypeAliasRef}, source_files::Source, target::Target, }; @@ -39,6 +39,7 @@ pub enum TypeKind { Structure(HumanName, StructureRef, Vec), TypeAlias(HumanName, TypeAliasRef), Polymorph(String, Vec), + Trait(HumanName, TraitRef), } impl TypeKind { @@ -70,6 +71,7 @@ impl TypeKind { .any(|parameter| parameter.kind.contains_polymorph()), TypeKind::TypeAlias(_, _) => false, TypeKind::Polymorph(_, _) => true, + TypeKind::Trait(_, _) => false, } } @@ -98,7 +100,8 @@ impl TypeKind { | TypeKind::FunctionPointer(..) | TypeKind::Enum(_, _) | TypeKind::AnonymousEnum() - | TypeKind::Polymorph(_, _) => None, + | TypeKind::Polymorph(_, _) + | TypeKind::Trait(_, _) => None, } } @@ -190,6 +193,9 @@ impl Display for TypeKind { write!(f, "{:?}", constaint)?; } } + TypeKind::Trait(name, _) => { + write!(f, "{}", name)?; + } } Ok(()) diff --git a/src/resolved/datatype/mod.rs b/src/resolved/datatype/mod.rs index a8ce080..57e21c1 100644 --- a/src/resolved/datatype/mod.rs +++ b/src/resolved/datatype/mod.rs @@ -55,6 +55,7 @@ impl Type { TypeKind::Polymorph(_, constraints) => { constraints.drain(..); } + TypeKind::Trait(_, _) => (), } } } diff --git a/src/resolved/function.rs b/src/resolved/function.rs index 6f75b03..1369a8e 100644 --- a/src/resolved/function.rs +++ b/src/resolved/function.rs @@ -9,7 +9,7 @@ pub struct CurrentConstraints { impl CurrentConstraints { pub fn satisfies(&self, ty: &Type, constraint: &Constraint) -> bool { match constraint { - Constraint::PrimitiveAdd => match &ty.kind { + Constraint::PrimitiveAdd | Constraint::Trait(..) => match &ty.kind { TypeKind::Integer(..) | TypeKind::CInteger(..) | TypeKind::Floating(..) => true, TypeKind::Polymorph(name, constraints) => { constraints.contains(constraint) diff --git a/src/resolved/mod.rs b/src/resolved/mod.rs index 6f406da..f108b0c 100644 --- a/src/resolved/mod.rs +++ b/src/resolved/mod.rs @@ -10,6 +10,7 @@ mod human_name; mod overload; mod stmt; mod structure; +mod trait_constraint; mod type_decl; mod variable_storage; @@ -30,6 +31,7 @@ use slotmap::{new_key_type, SlotMap}; use std::collections::HashMap; pub use stmt::*; pub use structure::*; +pub use trait_constraint::Trait; pub use type_decl::*; pub use variable_storage::*; @@ -39,6 +41,7 @@ new_key_type! { pub struct StructureRef; pub struct EnumRef; pub struct TypeAliasRef; + pub struct TraitRef; } #[derive(Clone, Debug)] @@ -50,6 +53,7 @@ pub struct Ast<'a> { pub globals: SlotMap, pub enums: SlotMap, pub type_aliases: SlotMap, + pub traits: SlotMap, pub workspace: &'a AstWorkspace<'a>, } @@ -65,6 +69,7 @@ impl<'a> Ast<'a> { globals: SlotMap::with_key(), enums: SlotMap::with_key(), type_aliases: SlotMap::with_key(), + traits: SlotMap::with_key(), workspace, } } diff --git a/src/resolved/trait_constraint.rs b/src/resolved/trait_constraint.rs new file mode 100644 index 0000000..c587645 --- /dev/null +++ b/src/resolved/trait_constraint.rs @@ -0,0 +1,16 @@ +use super::{Parameters, Type}; +use crate::source_files::Source; + +#[derive(Clone, Debug)] +pub struct Trait { + pub source: Source, + pub methods: Vec, +} + +#[derive(Clone, Debug)] +pub struct TraitMethod { + pub name: String, + pub parameters: Parameters, + pub return_type: Type, + pub source: Source, +}