Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 29 additions & 19 deletions compiler/noirc_frontend/src/node_interner/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand All @@ -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;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@

impl Metadata for Typed<Field> {
comptime fn name() -> str<9> {
"TypedFiel"

Check warning on line 875 in compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs

View workflow job for this annotation

GitHub Actions / Code

Misspelled word (Fiel) Suggestions: (feel*, field*, file*, phial*)
}
}

Expand Down Expand Up @@ -1141,6 +1141,79 @@
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 {

Check warning on line 1154 in compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Pickable)
fn pick(self) -> bool;
}

impl Pickable for Foo {

Check warning on line 1158 in compiler/noirc_frontend/src/tests/traits/trait_method_resolution.rs

View workflow job for this annotation

GitHub Actions / Code

Unknown word (Pickable)
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<T> { x: T }
type Ref<T> = &T;

impl<T> Foo<T> {
fn through_alias(_self: Ref<Foo<T>>) -> 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#"
Expand Down
Loading