From cec63902c52710ba7df433eda310296f7b7652d2 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Tue, 23 Jul 2024 17:18:16 -0300 Subject: [PATCH] fix: lookup trait constraints methods in composite types (#5595) # 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. --- .../noirc_frontend/src/elaborator/types.rs | 80 ++++++++++--------- compiler/noirc_frontend/src/tests.rs | 15 ++++ 2 files changed, 58 insertions(+), 37 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index d5dbb170843..f884d76dbf7 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -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. @@ -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 { + 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, diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index c29d9b03c7a..80ec22139dd 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -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 { + fn foo(self, x: A) -> bool; + } + + fn bar(x: (T, U), y: V) -> bool where (T, U): Foo { + x.foo(y) + } + + fn main() {}"#; + assert_no_errors(src); +}