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): Add types and traits to the elaborator #5066

Merged
merged 30 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 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
464d58c
Format
jfecher May 21, 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
87 changes: 80 additions & 7 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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::{
Expand Down Expand Up @@ -68,6 +71,7 @@ mod expressions;
mod patterns;
mod scope;
mod statements;
mod traits;
mod types;

use fm::FileId;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<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)
}
}
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);
jfecher marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}
33 changes: 31 additions & 2 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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,
};

Expand Down Expand Up @@ -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));
}
}
}
Loading