diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 0f9d22ca9b5..50b39757fda 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -10,7 +10,10 @@ use crate::{ UnresolvedTraitConstraint, UnresolvedTypeExpression, }, hir::{ - def_collector::{dc_crate::CompilationError, errors::DuplicateType}, + def_collector::{ + dc_crate::{CompilationError, UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}, + errors::DuplicateType, + }, resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext}, scope::ScopeForest as GenericScopeForest, type_check::TypeCheckError, @@ -27,10 +30,10 @@ use crate::{ macros_api::{ BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirExpression, HirLiteral, HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, - MethodCallExpression, NodeInterner, NoirFunction, PrefixExpression, Statement, + MethodCallExpression, NodeInterner, NoirFunction, NoirStruct, PrefixExpression, Statement, StatementKind, StructId, }, - node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, StmtId, TraitId}, + node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, StmtId, TraitId, TypeAliasId}, Shared, StructType, Type, TypeVariable, }; use crate::{ @@ -68,6 +71,7 @@ mod expressions; mod patterns; mod scope; mod statements; +mod traits; mod types; use fm::FileId; @@ -206,11 +210,12 @@ impl<'context> Elaborator<'context> { // the resolver filters literal globals first for global in items.globals {} - for alias in items.type_aliases {} - - for trait_ in items.traits {} + for (alias_id, alias) in items.type_aliases { + this.define_type_alias(alias_id, alias); + } - for struct_ in items.types {} + this.collect_traits(items.traits); + this.collect_struct_definitions(items.types); for trait_impl in &mut items.trait_impls { this.collect_trait_impl(trait_impl); @@ -1100,4 +1105,72 @@ impl<'context> Elaborator<'context> { }); } } + + fn define_type_alias(&mut self, alias_id: TypeAliasId, alias: UnresolvedTypeAlias) { + self.file = alias.file_id; + self.local_module = alias.module_id; + + let generics = self.add_generics(&alias.type_alias_def.generics); + self.resolve_local_globals(); + self.current_item = Some(DependencyId::Alias(alias_id)); + let typ = self.resolve_type(alias.type_alias_def.typ); + self.interner.set_type_alias(alias_id, typ, generics); + } + + fn collect_struct_definitions(&mut self, structs: BTreeMap) { + // This is necessary to avoid cloning the entire struct map + // when adding checks after each struct field is resolved. + let struct_ids = structs.keys().copied().collect::>(); + + // Resolve each field in each struct. + // Each struct should already be present in the NodeInterner after def collection. + for (type_id, typ) in structs { + self.file = typ.file_id; + self.local_module = typ.module_id; + let (generics, fields) = self.resolve_struct_fields(typ.struct_def, type_id); + + self.interner.update_struct(type_id, |struct_def| { + struct_def.set_fields(fields); + struct_def.generics = generics; + }); + } + + // Check whether the struct fields have nested slices + // We need to check after all structs are resolved to + // make sure every struct's fields is accurately set. + for id in struct_ids { + let struct_type = self.interner.get_struct(id); + // Only handle structs without generics as any generics args will be checked + // after monomorphization when performing SSA codegen + if struct_type.borrow().generics.is_empty() { + let fields = struct_type.borrow().get_fields(&[]); + for (_, field_type) in fields.iter() { + if field_type.is_nested_slice() { + let location = struct_type.borrow().location; + self.file = location.file; + self.push_err(ResolverError::NestedSlices { span: location.span }); + } + } + } + } + } + + pub fn resolve_struct_fields( + &mut self, + unresolved: NoirStruct, + struct_id: StructId, + ) -> (Generics, Vec<(Ident, Type)>) { + let generics = self.add_generics(&unresolved.generics); + + // Check whether the struct definition has globals in the local module and add them to the scope + self.resolve_local_globals(); + + self.current_item = Some(DependencyId::Struct(struct_id)); + + self.resolving_ids.insert(struct_id); + let fields = vecmap(unresolved.fields, |(ident, typ)| (ident, self.resolve_type(typ))); + self.resolving_ids.remove(&struct_id); + + (generics, fields) + } } diff --git a/compiler/noirc_frontend/src/elaborator/traits.rs b/compiler/noirc_frontend/src/elaborator/traits.rs new file mode 100644 index 00000000000..e7018d900d8 --- /dev/null +++ b/compiler/noirc_frontend/src/elaborator/traits.rs @@ -0,0 +1,196 @@ +use std::collections::BTreeMap; + +use iter_extended::vecmap; +use noirc_errors::Location; + +use crate::{ + ast::{FunctionKind, TraitItem, UnresolvedGenerics, UnresolvedTraitConstraint}, + hir::{ + def_collector::dc_crate::UnresolvedTrait, def_map::ModuleId, + resolution::path_resolver::StandardPathResolver, + }, + hir_def::{ + function::{FuncMeta, HirFunction}, + traits::{TraitConstant, TraitFunction, TraitType}, + }, + macros_api::{ + BlockExpression, FunctionDefinition, FunctionReturnType, Ident, ItemVisibility, + NoirFunction, Param, Pattern, UnresolvedType, Visibility, + }, + node_interner::{FuncId, TraitId}, + token::Attributes, + Generics, Type, TypeVariable, TypeVariableKind, +}; + +use super::Elaborator; + +impl<'context> Elaborator<'context> { + pub fn collect_traits(&mut self, traits: BTreeMap) { + for (trait_id, unresolved_trait) in &traits { + self.interner.push_empty_trait(*trait_id, unresolved_trait); + } + + for (trait_id, unresolved_trait) in traits { + let generics = vecmap(&unresolved_trait.trait_def.generics, |_| { + TypeVariable::unbound(self.interner.next_type_variable_id()) + }); + + // Resolve order + // 1. Trait Types ( Trait constants can have a trait type, therefore types before constants) + let _ = self.resolve_trait_types(&unresolved_trait); + // 2. Trait Constants ( Trait's methods can use trait types & constants, therefore they should be after) + let _ = self.resolve_trait_constants(&unresolved_trait); + // 3. Trait Methods + let methods = self.resolve_trait_methods(trait_id, &unresolved_trait, &generics); + + self.interner.update_trait(trait_id, |trait_def| { + trait_def.set_methods(methods); + trait_def.generics = generics; + }); + + // This check needs to be after the trait's methods are set since + // the interner may set `interner.ordering_type` based on the result type + // of the Cmp trait, if this is it. + if self.crate_id.is_stdlib() { + self.interner.try_add_operator_trait(trait_id); + } + } + } + + fn resolve_trait_types(&mut self, _unresolved_trait: &UnresolvedTrait) -> Vec { + // TODO + vec![] + } + + fn resolve_trait_constants( + &mut self, + _unresolved_trait: &UnresolvedTrait, + ) -> Vec { + // TODO + vec![] + } + + fn resolve_trait_methods( + &mut self, + trait_id: TraitId, + unresolved_trait: &UnresolvedTrait, + trait_generics: &Generics, + ) -> Vec { + self.local_module = unresolved_trait.module_id; + self.file = self.def_maps[&self.crate_id].file_id(unresolved_trait.module_id); + + let mut functions = vec![]; + + for item in &unresolved_trait.trait_def.items { + if let TraitItem::Function { + name, + generics, + parameters, + return_type, + where_clause, + body: _, + } = item + { + let old_generic_count = self.generics.len(); + + let the_trait = self.interner.get_trait(trait_id); + let self_typevar = the_trait.self_type_typevar.clone(); + let self_type = Type::TypeVariable(self_typevar.clone(), TypeVariableKind::Normal); + let name_span = the_trait.name.span(); + + self.add_generics(generics); + self.add_existing_generics(&unresolved_trait.trait_def.generics, trait_generics); + self.add_existing_generic("Self", name_span, self_typevar); + self.self_type = Some(self_type.clone()); + + let func_id = unresolved_trait.method_ids[&name.0.contents]; + self.resolve_trait_function( + name, + generics, + parameters, + return_type, + where_clause, + func_id, + ); + + let arguments = vecmap(parameters, |param| self.resolve_type(param.1.clone())); + let return_type = self.resolve_type(return_type.get_type().into_owned()); + + let generics = vecmap(&self.generics, |(_, type_var, _)| type_var.clone()); + + let default_impl_list: Vec<_> = unresolved_trait + .fns_with_default_impl + .functions + .iter() + .filter(|(_, _, q)| q.name() == name.0.contents) + .collect(); + + let default_impl = if default_impl_list.len() == 1 { + Some(Box::new(default_impl_list[0].2.clone())) + } else { + None + }; + + let no_environment = Box::new(Type::Unit); + let function_type = + Type::Function(arguments, Box::new(return_type), no_environment); + + functions.push(TraitFunction { + name: name.clone(), + typ: Type::Forall(generics, Box::new(function_type)), + location: Location::new(name.span(), unresolved_trait.file_id), + default_impl, + default_impl_module_id: unresolved_trait.module_id, + }); + + self.generics.truncate(old_generic_count); + } + } + functions + } + + pub fn resolve_trait_function( + &mut self, + name: &Ident, + generics: &UnresolvedGenerics, + parameters: &[(Ident, UnresolvedType)], + return_type: &FunctionReturnType, + where_clause: &[UnresolvedTraitConstraint], + func_id: FuncId, + ) { + let old_generic_count = self.generics.len(); + self.scopes.start_function(); + + // Check whether the function has globals in the local module and add them to the scope + self.resolve_local_globals(); + + self.trait_bounds = where_clause.to_vec(); + + let kind = FunctionKind::Normal; + let def = FunctionDefinition { + name: name.clone(), + attributes: Attributes::empty(), + is_unconstrained: false, + is_comptime: false, + visibility: ItemVisibility::Public, // Trait functions are always public + generics: generics.clone(), + parameters: vecmap(parameters, |(name, typ)| Param { + visibility: Visibility::Private, + pattern: Pattern::Identifier(name.clone()), + typ: typ.clone(), + span: name.span(), + }), + body: BlockExpression { statements: Vec::new() }, + span: name.span(), + where_clause: where_clause.to_vec(), + return_type: return_type.clone(), + return_visibility: Visibility::Private, + }; + + self.elaborate_function(NoirFunction { kind, def }, func_id); + let _ = self.scopes.end_function(); + // Don't check the scope tree for unused variables, they can't be used in a declaration anyway. + self.trait_bounds.clear(); + self.generics.truncate(old_generic_count); + } +} diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 4c8364b6dda..b0d29cffde7 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -4,7 +4,10 @@ use iter_extended::vecmap; use noirc_errors::{Location, Span}; use crate::{ - ast::{BinaryOpKind, IntegerBitSize, UnresolvedTraitConstraint, UnresolvedTypeExpression}, + ast::{ + BinaryOpKind, IntegerBitSize, NoirTypeAlias, UnresolvedGenerics, UnresolvedTraitConstraint, + UnresolvedTypeExpression, + }, hir::{ def_map::ModuleDefId, resolution::{ @@ -26,7 +29,10 @@ use crate::{ HirExpression, HirLiteral, HirStatement, Path, PathKind, SecondaryAttribute, Signedness, UnaryOp, UnresolvedType, UnresolvedTypeData, }, - node_interner::{DefinitionKind, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId}, + node_interner::{ + DefinitionKind, DependencyId, ExprId, GlobalId, TraitId, TraitImplKind, TraitMethodId, + TypeAliasId, + }, Generics, Shared, StructType, Type, TypeAlias, TypeBinding, TypeVariable, TypeVariableKind, }; @@ -1435,4 +1441,27 @@ impl<'context> Elaborator<'context> { } } } + + pub fn add_existing_generics(&mut self, names: &UnresolvedGenerics, generics: &Generics) { + assert_eq!(names.len(), generics.len()); + + for (name, typevar) in names.iter().zip(generics) { + self.add_existing_generic(&name.0.contents, name.0.span(), typevar.clone()); + } + } + + pub fn add_existing_generic(&mut self, name: &str, span: Span, typevar: TypeVariable) { + // Check for name collisions of this generic + let rc_name = Rc::new(name.to_owned()); + + if let Some((_, _, first_span)) = self.find_generic(&rc_name) { + self.push_err(ResolverError::DuplicateDefinition { + name: name.to_owned(), + first_span: *first_span, + second_span: span, + }); + } else { + self.generics.push((rc_name, typevar, span)); + } + } }