Skip to content

Commit 20c4b81

Browse files
author
jfecher
authored
chore(experimental): Elaborate globals (#5069)
# Description ## Problem\* Working towards #4992 ## Summary\* Adds support for elaborating globals. This was the last major item for resolution + type checking in the elaborator. ## Additional Context Next up in the elaborator will be integrating the scanning pass/interpreter and hooking it up to our integration tests. ## Documentation\* Check one: - [x] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist\* - [x] I have tested the changes locally. - [ ] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent 904f5eb commit 20c4b81

3 files changed

Lines changed: 75 additions & 7 deletions

File tree

compiler/noirc_frontend/src/elaborator/mod.rs

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ use crate::{
1111
},
1212
hir::{
1313
def_collector::{
14-
dc_crate::{CompilationError, UnresolvedStruct, UnresolvedTrait, UnresolvedTypeAlias},
14+
dc_crate::{
15+
filter_literal_globals, CompilationError, UnresolvedGlobal, UnresolvedStruct,
16+
UnresolvedTrait, UnresolvedTypeAlias,
17+
},
1518
errors::DuplicateType,
1619
},
1720
resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext},
@@ -25,13 +28,14 @@ use crate::{
2528
HirInfixExpression, HirLambda, HirMemberAccess, HirMethodCallExpression,
2629
HirMethodReference, HirPrefixExpression,
2730
},
31+
stmt::HirLetStatement,
2832
traits::TraitConstraint,
2933
},
3034
macros_api::{
3135
BlockExpression, CallExpression, CastExpression, Expression, ExpressionKind, HirExpression,
3236
HirLiteral, HirStatement, Ident, IndexExpression, Literal, MemberAccessExpression,
33-
MethodCallExpression, NodeInterner, NoirFunction, NoirStruct, PrefixExpression, Statement,
34-
StatementKind, StructId,
37+
MethodCallExpression, NodeInterner, NoirFunction, NoirStruct, Pattern, PrefixExpression,
38+
SecondaryAttribute, Statement, StatementKind, StructId,
3539
},
3640
node_interner::{DefinitionKind, DependencyId, ExprId, FuncId, StmtId, TraitId, TypeAliasId},
3741
Shared, StructType, Type, TypeVariable,
@@ -207,25 +211,48 @@ impl<'context> Elaborator<'context> {
207211
) -> Vec<(CompilationError, FileId)> {
208212
let mut this = Self::new(context, crate_id);
209213

210-
// the resolver filters literal globals first
211-
for global in items.globals {}
214+
// We must first resolve and intern the globals before we can resolve any stmts inside each function.
215+
// Each function uses its own resolver with a newly created ScopeForest, and must be resolved again to be within a function's scope
216+
//
217+
// Additionally, we must resolve integer globals before structs since structs may refer to
218+
// the values of integer globals as numeric generics.
219+
let (literal_globals, non_literal_globals) = filter_literal_globals(items.globals);
220+
221+
for global in literal_globals {
222+
this.elaborate_global(global);
223+
}
212224

213225
for (alias_id, alias) in items.type_aliases {
214226
this.define_type_alias(alias_id, alias);
215227
}
216228

217229
this.collect_traits(items.traits);
230+
231+
// Must resolve structs before we resolve globals.
218232
this.collect_struct_definitions(items.types);
219233

234+
// Bind trait impls to their trait. Collect trait functions, that have a
235+
// default implementation, which hasn't been overridden.
220236
for trait_impl in &mut items.trait_impls {
221237
this.collect_trait_impl(trait_impl);
222238
}
223239

240+
// Before we resolve any function symbols we must go through our impls and
241+
// re-collect the methods within into their proper module. This cannot be
242+
// done during def collection since we need to be able to resolve the type of
243+
// the impl since that determines the module we should collect into.
244+
//
245+
// These are resolved after trait impls so that struct methods are chosen
246+
// over trait methods if there are name conflicts.
224247
for ((typ, module), impls) in &items.impls {
225248
this.collect_impls(typ, *module, impls);
226249
}
227250

228-
// resolver resolves non-literal globals here
251+
// We must wait to resolve non-literal globals until after we resolve structs since struct
252+
// globals will need to reference the struct type they're initialized to to ensure they are valid.
253+
for global in non_literal_globals {
254+
this.elaborate_global(global);
255+
}
229256

230257
for functions in items.functions {
231258
this.elaborate_functions(functions);
@@ -1173,4 +1200,33 @@ impl<'context> Elaborator<'context> {
11731200

11741201
(generics, fields)
11751202
}
1203+
1204+
fn elaborate_global(&mut self, global: UnresolvedGlobal) {
1205+
self.local_module = global.module_id;
1206+
self.file = global.file_id;
1207+
1208+
let global_id = global.global_id;
1209+
self.current_item = Some(DependencyId::Global(global_id));
1210+
1211+
let definition_kind = DefinitionKind::Global(global_id);
1212+
let let_stmt = global.stmt_def;
1213+
1214+
if !self.in_contract
1215+
&& let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_)))
1216+
{
1217+
let span = let_stmt.pattern.span();
1218+
self.push_err(ResolverError::AbiAttributeOutsideContract { span });
1219+
}
1220+
1221+
if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) {
1222+
let span = let_stmt.pattern.span();
1223+
self.push_err(ResolverError::MutableGlobal { span });
1224+
}
1225+
1226+
let (let_statement, _typ) = self.elaborate_let(let_stmt);
1227+
1228+
let statement_id = self.interner.get_global(global_id).let_statement;
1229+
self.interner.get_global_definition_mut(global_id).kind = definition_kind;
1230+
self.interner.replace_statement(statement_id, let_statement);
1231+
}
11761232
}

compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ fn inject_prelude(
486486
/// Separate the globals Vec into two. The first element in the tuple will be the
487487
/// literal globals, except for arrays, and the second will be all other globals.
488488
/// We exclude array literals as they can contain complex types
489-
fn filter_literal_globals(
489+
pub fn filter_literal_globals(
490490
globals: Vec<UnresolvedGlobal>,
491491
) -> (Vec<UnresolvedGlobal>, Vec<UnresolvedGlobal>) {
492492
globals.into_iter().partition(|global| match &global.stmt_def.expression.kind {

compiler/noirc_frontend/src/node_interner.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,13 @@ impl NodeInterner {
916916
&self.definitions[id.0]
917917
}
918918

919+
/// Retrieves the definition where the given id was defined.
920+
/// This will panic if given DefinitionId::dummy_id. Use try_definition for
921+
/// any call with a possibly undefined variable.
922+
pub fn definition_mut(&mut self, id: DefinitionId) -> &mut DefinitionInfo {
923+
&mut self.definitions[id.0]
924+
}
925+
919926
/// Tries to retrieve the given id's definition.
920927
/// This function should be used during name resolution or type checking when we cannot be sure
921928
/// all variables have corresponding definitions (in case of an error in the user's code).
@@ -997,6 +1004,11 @@ impl NodeInterner {
9971004
self.definition(global.definition_id)
9981005
}
9991006

1007+
pub fn get_global_definition_mut(&mut self, global_id: GlobalId) -> &mut DefinitionInfo {
1008+
let global = self.get_global(global_id);
1009+
self.definition_mut(global.definition_id)
1010+
}
1011+
10001012
pub fn get_all_globals(&self) -> &[GlobalInfo] {
10011013
&self.globals
10021014
}

0 commit comments

Comments
 (0)