Skip to content

Commit

Permalink
chore(experimental): Elaborate globals (#5069)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
jfecher authored May 22, 2024
1 parent 904f5eb commit 20c4b81
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 7 deletions.
68 changes: 62 additions & 6 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/hir/def_collector/dc_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<UnresolvedGlobal>,
) -> (Vec<UnresolvedGlobal>, Vec<UnresolvedGlobal>) {
globals.into_iter().partition(|global| match &global.stmt_def.expression.kind {
Expand Down
12 changes: 12 additions & 0 deletions compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit 20c4b81

Please sign in to comment.