Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(experimental): Elaborate globals #5069

Merged
merged 36 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d0afc12
Add elaborator
jfecher May 2, 2024
1c433ec
Merge branch 'master' into jf/elaborator
jfecher May 3, 2024
225303f
More expressions
jfecher May 6, 2024
29e67a1
Finish each expression kind
jfecher May 7, 2024
0177c12
Merge branch 'master' into jf/elaborator
jfecher May 7, 2024
6b30028
Add compiler flag
jfecher May 8, 2024
7550fb8
Move expressions into their own file
jfecher May 8, 2024
39d23dc
Merge branch 'jf/elaborator' into jf/integrate-elaborator
jfecher May 8, 2024
2e77d87
Connect elaborator
jfecher May 8, 2024
e542d32
Code review
jfecher May 8, 2024
fd287df
Finish removing Scope
jfecher May 8, 2024
73dc473
Merge branch 'jf/elaborator' into jf/integrate-elaborator
jfecher May 8, 2024
9614b87
Finish functions
jfecher May 8, 2024
b44c98a
Add should_panic
jfecher May 8, 2024
9579646
clippy
jfecher May 8, 2024
e4d5f61
Fix test
jfecher May 8, 2024
556e6ef
Fix test
jfecher May 9, 2024
7487b03
Elaborate impls
jfecher May 9, 2024
82d5871
Fix yet another missed line
jfecher May 9, 2024
a3872e0
Merge branch 'jf/integrate-elaborator' into jf/elaborator-impls
jfecher May 9, 2024
3fb05dc
Merge branch 'master' into jf/integrate-elaborator
jfecher May 9, 2024
4fa4677
Merge branch 'jf/integrate-elaborator' into jf/elaborator-impls
jfecher May 9, 2024
e9e69f4
Resolve weird git merge
jfecher May 9, 2024
d7d91ae
Wrong arg order
jfecher May 9, 2024
e9afa1a
Merge branch 'jf/integrate-elaborator' into jf/elaborator-impls
jfecher May 9, 2024
13564f8
Merge branch 'master' into jf/elaborator-impls
jfecher May 9, 2024
75c6451
Code review
jfecher May 10, 2024
bbecf41
Start work on traits + types (+ globals)?
jfecher May 10, 2024
0029558
Merge branch 'master' into jf/elaborator-types
jfecher May 21, 2024
7a37bf3
Add globals
jfecher May 21, 2024
464d58c
Format
jfecher May 21, 2024
ba893ae
Merge branch 'jf/elaborator-types' into jf/elaborator-globals
jfecher May 21, 2024
397fe78
Copy over comments from dc_crate
jfecher May 22, 2024
afcf26e
Remove unneeded comment
jfecher May 22, 2024
1f3e79b
Merge branch 'master' into jf/elaborator-globals
jfecher May 22, 2024
8522936
Re-add accidentally removed collect_traits call
jfecher May 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 122 additions & 10 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
UnresolvedTraitConstraint, UnresolvedTypeExpression,
},
hir::{
def_collector::{dc_crate::CompilationError, errors::DuplicateType},
def_collector::{
dc_crate::{
filter_literal_globals, CompilationError, UnresolvedGlobal, UnresolvedStruct,
UnresolvedTrait, UnresolvedTypeAlias,
},
errors::DuplicateType,
},
resolution::{errors::ResolverError, path_resolver::PathResolver, resolver::LambdaContext},
scope::ScopeForest as GenericScopeForest,
type_check::TypeCheckError,
Expand All @@ -22,15 +28,16 @@
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, PrefixExpression, Statement,
StatementKind, StructId,
MethodCallExpression, NodeInterner, NoirFunction, NoirStruct, Pattern, PrefixExpression,
SecondaryAttribute, 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::{
Expand Down Expand Up @@ -68,6 +75,7 @@
mod patterns;
mod scope;
mod statements;
mod traits;
mod types;

use fm::FileId;
Expand Down Expand Up @@ -203,14 +211,19 @@
) -> Vec<(CompilationError, FileId)> {
let mut this = Self::new(context, crate_id);

// the resolver filters literal globals first
for global in items.globals {}
let (literal_globals, other_globals) = filter_literal_globals(items.globals);

for alias in items.type_aliases {}
// the resolver filters literal globals first
for global in literal_globals {
this.elaborate_global(global);
}

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);
Expand All @@ -220,7 +233,9 @@
this.collect_impls(typ, *module, impls);
}

// resolver resolves non-literal globals here
jfecher marked this conversation as resolved.
Show resolved Hide resolved
for global in other_globals {
this.elaborate_global(global);
}

for functions in items.functions {
this.elaborate_functions(functions);
Expand Down Expand Up @@ -306,7 +321,7 @@
// when multiple impls are available. Instead we default first to choose the Field or u64 impl.
for typ in &self.type_variables {
if let Type::TypeVariable(variable, kind) = typ.follow_bindings() {
let msg = "TypeChecker should only track defaultable type vars";

Check warning on line 324 in compiler/noirc_frontend/src/elaborator/mod.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (defaultable)
variable.bind(kind.default_type().expect(msg));
}
}
Expand Down Expand Up @@ -1100,4 +1115,101 @@
});
}
}

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<StructId, UnresolvedStruct>) {
// 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::<Vec<_>>();

// 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)
}

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);
}
}
196 changes: 196 additions & 0 deletions compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
@@ -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<TraitId, UnresolvedTrait>) {
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<TraitType> {
// TODO
vec![]
}

fn resolve_trait_constants(
&mut self,
_unresolved_trait: &UnresolvedTrait,
) -> Vec<TraitConstant> {
// TODO
vec![]
}

fn resolve_trait_methods(
&mut self,
trait_id: TraitId,
unresolved_trait: &UnresolvedTrait,
trait_generics: &Generics,
) -> Vec<TraitFunction> {
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);
}
}
Loading
Loading