Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f2bd51f
Parse trait inheritance
asterite Oct 8, 2024
be1ae07
Make sure nargo_fmt doesn't break with trait inheritance
asterite Oct 8, 2024
19016be
Resolve trait parent bounds
asterite Oct 8, 2024
1bdbcf4
Embed ResolvedTraitBound in TraitConstraint
asterite Oct 8, 2024
3937bd0
Solve trait bounds and lookup methods in parent types
asterite Oct 8, 2024
95fa2c6
Use filter_map
asterite Oct 9, 2024
3b3565e
Merge branch 'master' into ab/trait-inheritance
asterite Oct 9, 2024
c65f865
Add assumed trait implementations for parent traits
asterite Oct 9, 2024
57a538f
Detect trait cycles
asterite Oct 9, 2024
dc50605
Avoid looping forever in case there are cycles
asterite Oct 9, 2024
4b65f50
Add a failing test for trait parents with generics
asterite Oct 9, 2024
436f0be
Add a failing test for when a trait impl is missing a parent implemen…
asterite Oct 9, 2024
38dd4e7
Merge branch 'master' into ab/trait-inheritance
asterite Oct 9, 2024
c32803e
Make it work with generics
asterite Oct 9, 2024
3f8ae2d
Refactor
asterite Oct 9, 2024
9153f27
Check parent traits are implemented
asterite Oct 10, 2024
810b87f
Add a test program
asterite Oct 10, 2024
6701a42
Merge branch 'master' into ab/trait-inheritance
asterite Oct 10, 2024
3278cc6
Correct way to format trait name with generics
asterite Oct 10, 2024
5d79bd5
Correct parent check regarding generics
asterite Oct 10, 2024
7b34a78
Add one more test
asterite Oct 10, 2024
fdce653
Improve missing trait bound error message
asterite Oct 10, 2024
e7ea16f
Add some docs
asterite Oct 10, 2024
8c5f3f4
Simpler way to construct a Diagnostic
asterite Oct 10, 2024
81b7f3b
Apply suggestions from code review
asterite Oct 10, 2024
f69a6ab
Add supertrait and supertraits to cspell
asterite Oct 10, 2024
216fc2e
Extract `bind_ordered_generics`
asterite Oct 10, 2024
566fce7
Remove more duplicated code
asterite Oct 10, 2024
5ca1dee
Add subtrait to cspell
asterite Oct 10, 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
8 changes: 7 additions & 1 deletion compiler/noirc_frontend/src/ast/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use super::{Documented, GenericTypeArgs, ItemVisibility};
pub struct NoirTrait {
pub name: Ident,
pub generics: UnresolvedGenerics,
pub bounds: Vec<TraitBound>,
pub where_clause: Vec<UnresolvedTraitConstraint>,
pub span: Span,
pub items: Vec<Documented<TraitItem>>,
Expand Down Expand Up @@ -134,7 +135,12 @@ impl Display for NoirTrait {
let generics = vecmap(&self.generics, |generic| generic.to_string());
let generics = if generics.is_empty() { "".into() } else { generics.join(", ") };

writeln!(f, "trait {}{} {{", self.name, generics)?;
write!(f, "trait {}{}", self.name, generics)?;
if !self.bounds.is_empty() {
let bounds = vecmap(&self.bounds, |bound| bound.to_string()).join(" + ");
write!(f, ": {}", bounds)?;
}
writeln!(f, " {{")?;

for item in self.items.iter() {
let item = item.to_string();
Expand Down
10 changes: 6 additions & 4 deletions compiler/noirc_frontend/src/elaborator/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
HirPrefixExpression,
},
stmt::HirStatement,
traits::TraitConstraint,
traits::{ResolvedTraitBound, TraitConstraint},
},
node_interner::{DefinitionKind, ExprId, FuncId, InternedStatementKind, TraitMethodId},
token::Tokens,
Expand Down Expand Up @@ -713,9 +713,11 @@ impl<'context> Elaborator<'context> {
// that implements the trait.
let constraint = TraitConstraint {
typ: operand_type.clone(),
trait_id: trait_id.trait_id,
trait_generics: TraitGenerics::default(),
span,
trait_bound: ResolvedTraitBound {
trait_id: trait_id.trait_id,
trait_generics: TraitGenerics::default(),
span,
},
};
self.push_trait_constraint(constraint, expr_id);
self.type_check_operator_method(expr_id, trait_id, operand_type, span);
Expand Down
29 changes: 14 additions & 15 deletions compiler/noirc_frontend/src/elaborator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{
rc::Rc,
};

use crate::ast::ItemVisibility;
use crate::{ast::ItemVisibility, hir_def::traits::ResolvedTraitBound};
use crate::{
ast::{
BlockExpression, FunctionKind, GenericTypeArgs, Ident, NoirFunction, NoirStruct, Param,
Expand Down Expand Up @@ -433,7 +433,8 @@ impl<'context> Elaborator<'context> {

// Now remove all the `where` clause constraints we added
for constraint in &func_meta.trait_constraints {
self.interner.remove_assumed_trait_implementations_for_trait(constraint.trait_id);
self.interner
.remove_assumed_trait_implementations_for_trait(constraint.trait_bound.trait_id);
}

let func_scope_tree = self.scopes.end_function();
Expand Down Expand Up @@ -479,9 +480,9 @@ impl<'context> Elaborator<'context> {

self.verify_trait_constraint(
&constraint.typ,
constraint.trait_id,
&constraint.trait_generics.ordered,
&constraint.trait_generics.named,
constraint.trait_bound.trait_id,
&constraint.trait_bound.trait_generics.ordered,
&constraint.trait_bound.trait_generics.named,
expr_id,
span,
);
Expand Down Expand Up @@ -510,7 +511,8 @@ impl<'context> Elaborator<'context> {
let generic_type = Type::NamedGeneric(new_generic, Rc::new(name));
let trait_bound = TraitBound { trait_path, trait_id: None, trait_generics };

if let Some(new_constraint) = self.resolve_trait_bound(&trait_bound, generic_type.clone()) {
if let Some(trait_bound) = self.resolve_trait_bound(&trait_bound) {
let new_constraint = TraitConstraint { typ: generic_type.clone(), trait_bound };
trait_constraints.push(new_constraint);
}

Expand Down Expand Up @@ -668,22 +670,19 @@ impl<'context> Elaborator<'context> {
constraint: &UnresolvedTraitConstraint,
) -> Option<TraitConstraint> {
let typ = self.resolve_type(constraint.typ.clone());
self.resolve_trait_bound(&constraint.trait_bound, typ)
let trait_bound = self.resolve_trait_bound(&constraint.trait_bound)?;
Some(TraitConstraint { typ, trait_bound })
}

pub fn resolve_trait_bound(
&mut self,
bound: &TraitBound,
typ: Type,
) -> Option<TraitConstraint> {
pub fn resolve_trait_bound(&mut self, bound: &TraitBound) -> Option<ResolvedTraitBound> {
let the_trait = self.lookup_trait_or_error(bound.trait_path.clone())?;
let trait_id = the_trait.id;
let span = bound.trait_path.span;

let (ordered, named) = self.resolve_type_args(bound.trait_generics.clone(), trait_id, span);

let trait_generics = TraitGenerics { ordered, named };
Some(TraitConstraint { typ, trait_id, trait_generics, span })
Some(ResolvedTraitBound { trait_id, trait_generics, span })
}

/// Extract metadata from a NoirFunction
Expand Down Expand Up @@ -943,8 +942,8 @@ impl<'context> Elaborator<'context> {
fn add_trait_constraints_to_scope(&mut self, func_meta: &FuncMeta) {
for constraint in &func_meta.trait_constraints {
let object = constraint.typ.clone();
let trait_id = constraint.trait_id;
let generics = constraint.trait_generics.clone();
let trait_id = constraint.trait_bound.trait_id;
let generics = constraint.trait_bound.trait_generics.clone();

if !self.interner.add_assumed_trait_implementation(object, trait_id, generics) {
if let Some(the_trait) = self.interner.try_get_trait(trait_id) {
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ impl<'context> Elaborator<'context> {
if let ImplKind::TraitMethod(mut method) = ident.impl_kind {
method.constraint.apply_bindings(&bindings);
if method.assumed {
let trait_generics = method.constraint.trait_generics.clone();
let trait_generics = method.constraint.trait_bound.trait_generics.clone();
let object_type = method.constraint.typ;
let trait_impl = TraitImplKind::Assumed { object_type, trait_generics };
self.interner.select_impl_for_expression(expr_id, trait_impl);
Expand Down Expand Up @@ -738,7 +738,7 @@ impl<'context> Elaborator<'context> {
HirMethodReference::TraitMethodId(method_id, generics) => {
let mut constraint =
self.interner.get_trait(method_id.trait_id).as_constraint(span);
constraint.trait_generics = generics;
constraint.trait_bound.trait_generics = generics;
ImplKind::TraitMethod(TraitMethod { method_id, constraint, assumed: false })
}
};
Expand Down
22 changes: 13 additions & 9 deletions compiler/noirc_frontend/src/elaborator/trait_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,36 +167,40 @@ impl<'context> Elaborator<'context> {
let mut substituted_method_ids = HashSet::default();
for method_constraint in method.trait_constraints.iter() {
let substituted_constraint_type = method_constraint.typ.substitute(&bindings);
let substituted_trait_generics =
method_constraint.trait_generics.map(|generic| generic.substitute(&bindings));
let substituted_trait_generics = method_constraint
.trait_bound
.trait_generics
.map(|generic| generic.substitute(&bindings));

substituted_method_ids.insert((
substituted_constraint_type,
method_constraint.trait_id,
method_constraint.trait_bound.trait_id,
substituted_trait_generics,
));
}

for override_trait_constraint in override_meta.trait_constraints.clone() {
let override_constraint_is_from_impl =
trait_impl_where_clause.iter().any(|impl_constraint| {
impl_constraint.trait_id == override_trait_constraint.trait_id
impl_constraint.trait_bound.trait_id
== override_trait_constraint.trait_bound.trait_id
});
if override_constraint_is_from_impl {
continue;
}

if !substituted_method_ids.contains(&(
override_trait_constraint.typ.clone(),
override_trait_constraint.trait_id,
override_trait_constraint.trait_generics.clone(),
override_trait_constraint.trait_bound.trait_id,
override_trait_constraint.trait_bound.trait_generics.clone(),
)) {
let the_trait = self.interner.get_trait(override_trait_constraint.trait_id);
let the_trait =
self.interner.get_trait(override_trait_constraint.trait_bound.trait_id);
self.push_err(DefCollectorErrorKind::ImplIsStricterThanTrait {
constraint_typ: override_trait_constraint.typ,
constraint_name: the_trait.name.0.contents.clone(),
constraint_generics: override_trait_constraint.trait_generics,
constraint_span: override_trait_constraint.span,
constraint_generics: override_trait_constraint.trait_bound.trait_generics,
constraint_span: override_trait_constraint.trait_bound.span,
trait_method_name: method.name.0.contents.clone(),
trait_method_span: method.location.span,
});
Expand Down
23 changes: 22 additions & 1 deletion compiler/noirc_frontend/src/elaborator/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use crate::{
UnresolvedTraitConstraint, UnresolvedType,
},
hir::{def_collector::dc_crate::UnresolvedTrait, type_check::TypeCheckError},
hir_def::{function::Parameters, traits::TraitFunction},
hir_def::{
function::Parameters,
traits::{ResolvedTraitBound, TraitFunction},
},
node_interner::{FuncId, NodeInterner, ReferenceId, TraitId},
ResolvedGeneric, Type, TypeBindings,
};
Expand All @@ -34,10 +37,13 @@ impl<'context> Elaborator<'context> {
this.generics.push(associated_type.clone());
}

let resolved_trait_bounds = this.resolve_trait_bounds(unresolved_trait);

let methods = this.resolve_trait_methods(*trait_id, unresolved_trait);

this.interner.update_trait(*trait_id, |trait_def| {
trait_def.set_methods(methods);
trait_def.set_trait_bounds(resolved_trait_bounds);
});
});

Expand All @@ -53,6 +59,21 @@ impl<'context> Elaborator<'context> {
self.current_trait = None;
}

fn resolve_trait_bounds(
&mut self,
unresolved_trait: &UnresolvedTrait,
) -> Vec<ResolvedTraitBound> {
let mut resolved_trait_bounds = Vec::new();

for trait_bound in &unresolved_trait.trait_def.bounds {
if let Some(resolved_trait_bound) = self.resolve_trait_bound(trait_bound) {
resolved_trait_bounds.push(resolved_trait_bound);
}
}

resolved_trait_bounds
}

fn resolve_trait_methods(
&mut self,
trait_id: TraitId,
Expand Down
51 changes: 36 additions & 15 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::{
},
function::{FuncMeta, Parameters},
stmt::HirStatement,
traits::{NamedType, TraitConstraint},
traits::{NamedType, ResolvedTraitBound, Trait, TraitConstraint},
},
node_interner::{
DefinitionKind, DependencyId, ExprId, GlobalId, ImplSearchErrorKind, NodeInterner, TraitId,
Expand Down Expand Up @@ -586,7 +586,7 @@ impl<'context> Elaborator<'context> {
continue;
}

let the_trait = self.interner.get_trait(constraint.trait_id);
let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id);
if let Some(method) = the_trait.find_method(path.last_name()) {
return Some(TraitPathResolution {
method: TraitMethod { method_id: method, constraint, assumed: true },
Expand Down Expand Up @@ -1332,14 +1332,23 @@ impl<'context> Elaborator<'context> {

for constraint in &func_meta.trait_constraints {
if *object_type == constraint.typ {
if let Some(the_trait) = self.interner.try_get_trait(constraint.trait_id) {
for (method_index, method) in the_trait.methods.iter().enumerate() {
if method.name.0.contents == method_name {
let trait_method =
TraitMethodId { trait_id: constraint.trait_id, method_index };

let generics = constraint.trait_generics.clone();
return Some(HirMethodReference::TraitMethodId(trait_method, generics));
if let Some(the_trait) =
self.interner.try_get_trait(constraint.trait_bound.trait_id)
{
if let Some(method) =
lookup_method_in_trait(the_trait, method_name, &constraint.trait_bound)
{
return Some(method);
}

// Search in the trait bounds
for trait_bound in &the_trait.trait_bounds {
if let Some(the_trait) = self.interner.try_get_trait(trait_bound.trait_id) {
if let Some(method) =
lookup_method_in_trait(the_trait, method_name, trait_bound)
{
return Some(method);
}
}
}
}
Expand Down Expand Up @@ -1762,10 +1771,12 @@ impl<'context> Elaborator<'context> {
assumed: bool,
bindings: &mut TypeBindings,
) {
let the_trait = self.interner.get_trait(constraint.trait_id);
assert_eq!(the_trait.generics.len(), constraint.trait_generics.ordered.len());
let the_trait = self.interner.get_trait(constraint.trait_bound.trait_id);
assert_eq!(the_trait.generics.len(), constraint.trait_bound.trait_generics.ordered.len());

for (param, arg) in the_trait.generics.iter().zip(&constraint.trait_generics.ordered) {
for (param, arg) in
the_trait.generics.iter().zip(&constraint.trait_bound.trait_generics.ordered)
{
// Avoid binding t = t
if !arg.occurs(param.type_var.id()) {
bindings.insert(
Expand All @@ -1776,9 +1787,9 @@ impl<'context> Elaborator<'context> {
}

let mut associated_types = the_trait.associated_types.clone();
assert_eq!(associated_types.len(), constraint.trait_generics.named.len());
assert_eq!(associated_types.len(), constraint.trait_bound.trait_generics.named.len());

for arg in &constraint.trait_generics.named {
for arg in &constraint.trait_bound.trait_generics.named {
let i = associated_types
.iter()
.position(|typ| *typ.name == arg.name.0.contents)
Expand Down Expand Up @@ -1808,6 +1819,16 @@ impl<'context> Elaborator<'context> {
}
}

fn lookup_method_in_trait(
the_trait: &Trait,
method_name: &str,
trait_bound: &ResolvedTraitBound,
) -> Option<HirMethodReference> {
let trait_method = the_trait.find_method(method_name)?;
let generics = trait_bound.trait_generics.clone();
return Some(HirMethodReference::TraitMethodId(trait_method, generics));
}

pub fn try_eval_array_length_id(
interner: &NodeInterner,
rhs: ExprId,
Expand Down
7 changes: 5 additions & 2 deletions compiler/noirc_frontend/src/hir/comptime/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,11 @@ impl<'token, 'interner> Display for TokenPrinter<'token, 'interner> {
}

fn display_trait_constraint(interner: &NodeInterner, trait_constraint: &TraitConstraint) -> String {
let trait_ = interner.get_trait(trait_constraint.trait_id);
format!("{}: {}{}", trait_constraint.typ, trait_.name, trait_constraint.trait_generics)
let trait_ = interner.get_trait(trait_constraint.trait_bound.trait_id);
format!(
"{}: {}{}",
trait_constraint.typ, trait_.name, trait_constraint.trait_bound.trait_generics
)
}

// Returns a new Expression where all Interned and Resolved expressions have been turned into non-interned ExpressionKind.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ fn quoted_as_trait_constraint(
)?;
let bound = interpreter
.elaborate_in_function(interpreter.current_function, |elaborator| {
elaborator.resolve_trait_bound(&trait_bound, Type::Unit)
elaborator.resolve_trait_bound(&trait_bound)
})
.ok_or(InterpreterError::FailedToResolveTraitBound { trait_bound, location })?;

Expand Down Expand Up @@ -2725,7 +2725,7 @@ fn trait_def_as_trait_constraint(
let trait_id = get_trait_def(argument)?;
let constraint = interner.get_trait(trait_id).as_constraint(location.span);

Ok(Value::TraitConstraint(trait_id, constraint.trait_generics))
Ok(Value::TraitConstraint(trait_id, constraint.trait_bound.trait_generics))
}

/// Creates a value that holds an `Option`.
Expand Down
4 changes: 2 additions & 2 deletions compiler/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,8 +486,8 @@ impl NoMatchingImplFoundError {
let constraints = failing_constraints
.into_iter()
.map(|constraint| {
let r#trait = interner.try_get_trait(constraint.trait_id)?;
let name = format!("{}{}", r#trait.name, constraint.trait_generics);
let r#trait = interner.try_get_trait(constraint.trait_bound.trait_id)?;
let name = format!("{}{}", r#trait.name, constraint.trait_bound.trait_generics);
Some((constraint.typ, name))
})
.collect::<Option<Vec<_>>>()?;
Expand Down
Loading