diff --git a/compiler/noirc_frontend/src/node_interner/methods.rs b/compiler/noirc_frontend/src/node_interner/methods.rs index 3c546daa795..1eff6f6e668 100644 --- a/compiler/noirc_frontend/src/node_interner/methods.rs +++ b/compiler/noirc_frontend/src/node_interner/methods.rs @@ -169,29 +169,15 @@ impl Methods { match function_typ.instantiate(interner).0 { Type::Function(args, _, _, _) => { if check_self_param { - if let Some(object) = args.first() { - if object.try_unify_with_default_bindings(typ).is_ok() { - return true; - } - - // Handle auto-dereferencing `&T` and `&mut T` into `T` - if let Type::Reference(object, _mutable) = object - && object.try_unify_with_default_bindings(typ).is_ok() - { - return true; - } + if let Some(object) = args.first() + && Self::receiver_type_matches(object, typ) + { + return true; } } else { let method_type = func_meta.instantiate(method_type, interner); - if method_type.try_unify_with_default_bindings(typ).is_ok() { - return true; - } - - // Handle auto-dereferencing `&T` and `&mut T` into `T` - if let Type::Reference(method_type, _mutable) = method_type.as_ref() - && method_type.try_unify_with_default_bindings(typ).is_ok() - { + if Self::receiver_type_matches(method_type.as_ref(), typ) { return true; } } @@ -202,4 +188,28 @@ impl Methods { false } + + fn receiver_type_matches(receiver_type: &Type, typ: &Type) -> bool { + let mut receiver_type = receiver_type.clone(); + + loop { + if receiver_type.try_unify_with_default_bindings(typ).is_ok() { + return true; + } + + let followed = receiver_type.follow_bindings(); + if followed != receiver_type { + receiver_type = followed; + continue; + } + + // Handle auto-dereferencing `&T` and `&mut T` into `T`, including aliases to + // references and nested references. + if let Type::Reference(inner, _mutable) = receiver_type { + receiver_type = *inner; + } else { + return false; + } + } + } } diff --git a/compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs b/compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs index 398b5429da1..1f56224293e 100644 --- a/compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs +++ b/compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs @@ -1141,6 +1141,79 @@ fn trait_method_and_struct_method_with_same_name() { assert_no_errors(src); } +#[test] +fn inherent_method_receiver_alias_to_reference_takes_precedence_over_trait_method() { + let src = r#" + struct Foo { x: Field } + type FooRef = &Foo; + + impl Foo { + fn pick(_self: FooRef) -> u32 { 100 } + } + + trait Pickable { + fn pick(self) -> bool; + } + + impl Pickable for Foo { + fn pick(self) -> bool { true } + } + + fn main() { + let foo = Foo { x: 7 }; + let r = &foo; + let _: u32 = r.pick(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn inherent_method_nested_reference_receiver_takes_precedence_over_trait_method() { + let src = r#" + struct Foo { x: Field } + + impl Foo { + fn d_pick(_self: &&Foo) -> u32 { 100 } + } + + trait DoubleRef { + fn d_pick(self) -> bool; + } + + impl DoubleRef for Foo { + fn d_pick(self) -> bool { true } + } + + fn main() { + let foo = Foo { x: 1 }; + let r = &foo; + let rr = &r; + let _: u32 = rr.d_pick(); + } + "#; + assert_no_errors(src); +} + +#[test] +fn inherent_method_generic_alias_reference_receiver_resolves() { + let src = r#" + struct Foo { x: T } + type Ref = &T; + + impl Foo { + fn through_alias(_self: Ref>) -> u32 { 42 } + } + + fn main() { + let foo = Foo { x: 7 }; + let r = &foo; + let _: u32 = r.through_alias(); + } + "#; + assert_no_errors(src); +} + #[test] fn trait_method_and_struct_method_with_same_name_and_turbofish() { let src = r#"