Skip to content

Commit

Permalink
fix: lookup trait constraints methods in composite types (#5595)
Browse files Browse the repository at this point in the history
# Description

## Problem

Resolves #5186

## Summary

Looking for methods that are bound to trait constraints was already
working for `NamedGeneric`, but when the trait is on a composite type,
like `(T, U)` or even `(T, i32)` methods weren't lookup up in
constraints bound to those types.

We only lookup this in case the regular (`lookup_primitive_method`)
lookup fails to find something.

## Additional Context

None.

## 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.
- [x] I have formatted the changes with [Prettier](https://prettier.io/)
and/or `cargo fmt` on default settings.
  • Loading branch information
asterite authored Jul 23, 2024
1 parent 75bfe13 commit cec6390
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 37 deletions.
80 changes: 43 additions & 37 deletions compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1211,37 +1211,7 @@ impl<'context> Elaborator<'context> {
None
}
Type::NamedGeneric(_, _, _) => {
let func_id = match self.current_item {
Some(DependencyId::Function(id)) => id,
_ => panic!("unexpected method outside a function"),
};
let func_meta = self.interner.function_meta(&func_id);

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,
};
return Some(HirMethodReference::TraitMethodId(
trait_method,
constraint.trait_generics.clone(),
));
}
}
}
}
}

self.push_err(TypeCheckError::UnresolvedMethodCall {
method_name: method_name.to_string(),
object_type: object_type.clone(),
span,
});
None
self.lookup_method_in_trait_constraints(object_type, method_name, span)
}
// Mutable references to another type should resolve to methods of their element type.
// This may be a struct or a primitive type.
Expand All @@ -1264,17 +1234,53 @@ impl<'context> Elaborator<'context> {
other => match self.interner.lookup_primitive_method(&other, method_name) {
Some(method_id) => Some(HirMethodReference::FuncId(method_id)),
None => {
self.push_err(TypeCheckError::UnresolvedMethodCall {
method_name: method_name.to_string(),
object_type: object_type.clone(),
span,
});
None
// It could be that this type is a composite type that is bound to a trait,
// for example `x: (T, U) ... where (T, U): SomeTrait`
// (so this case is a generalization of the NamedGeneric case)
self.lookup_method_in_trait_constraints(object_type, method_name, span)
}
},
}
}

fn lookup_method_in_trait_constraints(
&mut self,
object_type: &Type,
method_name: &str,
span: Span,
) -> Option<HirMethodReference> {
let func_id = match self.current_item {
Some(DependencyId::Function(id)) => id,
_ => panic!("unexpected method outside a function"),
};
let func_meta = self.interner.function_meta(&func_id);

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 };
return Some(HirMethodReference::TraitMethodId(
trait_method,
constraint.trait_generics.clone(),
));
}
}
}
}
}

self.push_err(TypeCheckError::UnresolvedMethodCall {
method_name: method_name.to_string(),
object_type: object_type.clone(),
span,
});

None
}

pub(super) fn type_check_call(
&mut self,
call: &HirCallExpression,
Expand Down
15 changes: 15 additions & 0 deletions compiler/noirc_frontend/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2522,3 +2522,18 @@ fn duplicate_struct_field() {
assert_eq!(first_def.span().start(), 26);
assert_eq!(second_def.span().start(), 42);
}

#[test]
fn trait_constraint_on_tuple_type() {
let src = r#"
trait Foo<A> {
fn foo(self, x: A) -> bool;
}
fn bar<T, U, V>(x: (T, U), y: V) -> bool where (T, U): Foo<V> {
x.foo(y)
}
fn main() {}"#;
assert_no_errors(src);
}

0 comments on commit cec6390

Please sign in to comment.