diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 50b39757fda..0581e7900f8 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -11,7 +11,10 @@ use crate::{ }, hir::{ def_collector::{ - dc_crate::{CompilationError, UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias}, + dc_crate::{ + filter_literal_globals, CompilationError, UnresolvedGlobal, UnresolvedStruct, + UnresolvedTrait, UnresolvedTypeAlias, + }, errors::DuplicateType, }, resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext}, @@ -25,13 +28,14 @@ use crate::{ HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression, HirMethodReference, HirPrefixExpression, }, + stmt::HirLetStatement, traits::TraitConstraint, }, macros_api::{ BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirExpression, HirLiteral, HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression, - MethodCallExpression, NodeInterner, NoirFunction, NoirStruct, PrefixExpression, Statement, - StatementKind, StructId, + MethodCallExpression, NodeInterner, NoirFunction, NoirStruct, Pattern, PrefixExpression, + SecondaryAttribute, Statement, StatementKind, StructId, }, node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, StmtId, TraitId, TypeAliasId}, Shared, StructType, Type, TypeVariable, @@ -207,25 +211,48 @@ impl<'context> Elaborator<'context> { ) -> Vec<(CompilationError, FileId)> { let mut this = Self::new(context, crate_id); - // the resolver filters literal globals first - for global in items.globals {} + // We must first resolve and intern the globals before we can resolve any stmts inside each function. + // Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope + // + // Additionally, we must resolve integer globals before structs since structs may refer to + // the values of integer globals as numeric generics. + let (literal_globals, non_literal_globals) = filter_literal_globals(items.globals); + + for global in literal_globals { + this.elaborate_global(global); + } for (alias_id, alias) in items.type_aliases { this.define_type_alias(alias_id, alias); } this.collect_traits(items.traits); + + // Must resolve structs before we resolve globals. this.collect_struct_definitions(items.types); + // Bind trait impls to their trait. Collect trait functions, that have a + // default implementation, which hasn't been overridden. for trait_impl in &mut items.trait_impls { this.collect_trait_impl(trait_impl); } + // Before we resolve any function symbols we must go through our impls and + // re-collect the methods within into their proper module. This cannot be + // done during def collection since we need to be able to resolve the type of + // the impl since that determines the module we should collect into. + // + // These are resolved after trait impls so that struct methods are chosen + // over trait methods if there are name conflicts. for ((typ, module), impls) in &items.impls { this.collect_impls(typ, *module, impls); } - // resolver resolves non-literal globals here + // We must wait to resolve non-literal globals until after we resolve structs since struct + // globals will need to reference the struct type they're initialized to to ensure they are valid. + for global in non_literal_globals { + this.elaborate_global(global); + } for functions in items.functions { this.elaborate_functions(functions); @@ -1173,4 +1200,33 @@ impl<'context> Elaborator<'context> { (generics, fields) } + + fn elaborate_global(&mut self, global: UnresolvedGlobal) { + self.local_module = global.module_id; + self.file = global.file_id; + + let global_id = global.global_id; + self.current_item = Some(DependencyId::Global(global_id)); + + let definition_kind = DefinitionKind::Global(global_id); + let let_stmt = global.stmt_def; + + if !self.in_contract + && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) + { + let span = let_stmt.pattern.span(); + self.push_err(ResolverError::AbiAttributeOutsideContract { span }); + } + + if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) { + let span = let_stmt.pattern.span(); + self.push_err(ResolverError::MutableGlobal { span }); + } + + let (let_statement, _typ) = self.elaborate_let(let_stmt); + + let statement_id = self.interner.get_global(global_id).let_statement; + self.interner.get_global_definition_mut(global_id).kind = definition_kind; + self.interner.replace_statement(statement_id, let_statement); + } } diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs index d2eaf79b0f0..afec3839599 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs @@ -486,7 +486,7 @@ fn inject_prelude( /// Separate the globals Vec into two. The first element in the tuple will be the /// literal globals, except for arrays, and the second will be all other globals. /// We exclude array literals as they can contain complex types -fn filter_literal_globals( +pub fn filter_literal_globals( globals: Vec, ) -> (Vec, Vec) { globals.into_iter().partition(|global| match &global.stmt_def.expression.kind { diff --git a/compiler/noirc_frontend/src/node_interner.rs b/compiler/noirc_frontend/src/node_interner.rs index 7f1b67abfbd..d4145ef6a1d 100644 --- a/compiler/noirc_frontend/src/node_interner.rs +++ b/compiler/noirc_frontend/src/node_interner.rs @@ -916,6 +916,13 @@ impl NodeInterner { &self.definitions[id.0] } + /// Retrieves the definition where the given id was defined. + /// This will panic if given DefinitionId::dummy_id. Use try_definition for + /// any call with a possibly undefined variable. + pub fn definition_mut(&mut self, id: DefinitionId) -> &mut DefinitionInfo { + &mut self.definitions[id.0] + } + /// Tries to retrieve the given id's definition. /// This function should be used during name resolution or type checking when we cannot be sure /// all variables have corresponding definitions (in case of an error in the user's code). @@ -997,6 +1004,11 @@ impl NodeInterner { self.definition(global.definition_id) } + pub fn get_global_definition_mut(&mut self, global_id: GlobalId) -> &mut DefinitionInfo { + let global = self.get_global(global_id); + self.definition_mut(global.definition_id) + } + pub fn get_all_globals(&self) -> &[GlobalInfo] { &self.globals }