Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc should first suggest adding a trait bound on generic impl block when inherent method name bound by that trait collides with methods from unintended traits in scope #120568

Open
zjp-CN opened this issue Feb 2, 2024 · 2 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@zjp-CN
Copy link

zjp-CN commented Feb 2, 2024

Code

pub struct Scroll<L>(L);

pub trait Bound {}

impl<L: Bound> Scroll<L> {
    // happens to be the same name with methods in ATraitSomewhere
    pub fn f(&self) -> usize {
        0
    }
}

impl<L> Scroll<L> { // Line12
    pub fn call(&self) {
        self.f(); // meant to call an inherent method
    }
}

// Say an accidental trait is brought into scope via asterisk wildcard
// import syntax or std's prelude or somthing, bad error occurs with
// the following code beacuse it doesn't give the obvious fix by
// patching `L: Bound` on Line12 at all.
#[allow(unused_imports)] use __::ATraitSomewhere;

mod __ {
    pub trait ATraitSomewhere {
        fn f(&self) {}
    }
    impl<T: B> ATraitSomewhere for &T {}
    pub trait B {}
}

Current output

error[E0599]: the method `f` exists for reference `&Scroll<L>`, but its trait bounds were not satisfied
  --> src/lib.rs:14:14
   |
1  | pub struct Scroll<L>(L);
   | -------------------- doesn't satisfy `Scroll<L>: B`
...
14 |         self.f(); // meant to call an inherent method
   |              ^ method cannot be called on `&Scroll<L>` due to unsatisfied trait bounds
   |
note: trait bound `L: Bound` was not satisfied
  --> src/lib.rs:5:9
   |
5  | impl<L: Bound> Scroll<L> {
   |         ^^^^^  ---------
   |         |
   |         unsatisfied trait bound introduced here
note: trait bound `Scroll<L>: B` was not satisfied
  --> src/lib.rs:29:13
   |
29 |     impl<T: B> ATraitSomewhere for &T {}
   |             ^  ---------------     --
   |             |
   |             unsatisfied trait bound introduced here
note: the trait `B` must be implemented
  --> src/lib.rs:30:5
   |
30 |     pub trait B {}
   |     ^^^^^^^^^^^

Desired output

error[E0599]: the method `f` exists for reference `&Scroll<L>`, but its trait bounds were not satisfied
  --> f100.rs:14:14
   |
1  | struct Scroll<L>(L);
   | ---------------- doesn't satisfy `Scroll<L>: B`
...
14 |         self.f(); // meant to call an inherent method
   |              ^ method cannot be called on `&Scroll<L>` due to unsatisfied trait bounds

   |
note: trait bound `L: Bound` was not satisfied
  --> f100.rs:5:9
   |
5  | impl<L: Bound> Scroll<L> {
   |         ^^^^^  ---------
   |         |
   |         unsatisfied trait bound introduced here
note: the method `f` can be called with trait bound `L: Bound` satisfied
  --> f100.rs:12:6
   |
12 |     impl<L> Scroll<L> { // Line12
   |          ^  ---------
   |          |
   |          = help: consider adding `: Bound`
note: trait bound `Scroll<L>: B` was not satisfied
  --> f100.rs:29:13
   |
29 |     impl<T: B> ATraitSomewhere for &T {}
   |             ^  ---------------     --
   |             |
   |             unsatisfied trait bound introduced here
note: the trait `B` must be implemented
  --> f100.rs:30:5
   |
30 |     pub trait B {}
   |     ^^^^^^^^^^^
   = help: items from traits can only be used if the trait is implemented and in scope
note: `ATraitSomewhere` defines an item `f`, perhaps you need to implement it
  --> f100.rs:26:5
   |
26 |     pub trait ATraitSomewhere {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^

Rationale and extra context

The code closer to what I met is the following as I posted in URLO

struct Scroll<L>(L);
struct Line;

impl<L: AsRef<[Line]>> Scroll<L> {
    fn len(&self) -> usize { self.0.as_ref().len() }
}

impl<L: AsMut<[Line]>> Scroll<L> {
    fn f1(&mut self) { self.len(); }
}
// current error
error[E0599]: the method `len` exists for mutable reference `&mut Scroll<L>`, but its trait bounds were not satisfied
  --> src/lib.rs:9:29
   |
1  | struct Scroll<L>(L);
   | ---------------- doesn't satisfy `Scroll<L>: ExactSizeIterator`
...
9  |     fn f1(&mut self) { self.len(); }
   |                             ^^^ method cannot be called on `&mut Scroll<L>` due to unsatisfied trait bounds
   |
note: trait bound `L: AsRef<[Line]>` was not satisfied
  --> src/lib.rs:4:9
   |
4  | impl<L: AsRef<[Line]>> Scroll<L> {
   |         ^^^^^^^^^^^^^  ---------
   |         |
   |         unsatisfied trait bound introduced here
   = note: the following trait bounds were not satisfied:
           `Scroll<L>: ExactSizeIterator`
           which is required by `&mut Scroll<L>: ExactSizeIterator`
note: the trait `ExactSizeIterator` must be implemented
  --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/exact_size.rs:86:1
   |
86 | pub trait ExactSizeIterator: Iterator {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Rustc doesn't report the obvious fix by add L: AsRef<[Line]> bound on the call site block,
so it'd be great if we have it

note: trait bound `L: AsRef<[Line]>` was not satisfied
  --> src/lib.rs:4:9
   |
4  | impl<L: AsRef<[Line]>> Scroll<L> {
   |         ^^^^^^^^^^^^^  ---------
   |         |
   |         unsatisfied trait bound introduced here
+note: the method `len` can be called with trait bound `L: AsRef<[Line]>` satisfied
+8  | impl<L: AsMut<[Line]>> Scroll<L> {
+   |      ^
+   |      |
+   |      help: consider adding `L: AsRef<[Line]>`
+   |
note: the following trait bounds were not satisfied:
      `Scroll<L>: ExactSizeIterator`
      which is required by `&mut Scroll<L>: ExactSizeIterator`

Other cases

No response

Rust Version

rustc 1.75.0 (82e1608df 2023-12-21)
binary: rustc
commit-hash: 82e1608dfa6e0b5569232559e3d385fea5a93112
commit-date: 2023-12-21
host: x86_64-unknown-linux-gnu
release: 1.75.0
LLVM version: 17.0.6

Anything else?

Some similar issues but the lack of trait bounds happens on functions, not on generic impl blocks.

So I think this issue would be a different case.

@zjp-CN zjp-CN added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 2, 2024
@zjp-CN
Copy link
Author

zjp-CN commented Feb 2, 2024

The method name collision might not cause trait bound error though. Consider the recursive code:

pub struct Scroll<L>(L);

pub trait Bound {}

impl<L: Bound> Scroll<L> {
    // happens to be the same name with methods in ATraitSomewhere
    pub fn f(&self) -> usize {
        0
    }
}

pub trait Trait {
    fn method(&self);
}

impl<L> ATraitSomewhere for Scroll<L> {
    fn f(&self) {
        self.f(); // meant to call an inherent method
    }
}

use __::ATraitSomewhere;

mod __ {
    pub trait ATraitSomewhere {
        fn f(&self) {}
    }
    impl<T: B> ATraitSomewhere for &T {}
    pub trait B {}
}

it emits

warning: function cannot return without recursing
  --> src/lib.rs:17:5
   |
17 |     fn f(&self) {
   |     ^^^^^^^^^^^ cannot return without recursing
18 |         self.f(); // meant to call an inherent method
   |         -------- recursive call site
   |
   = help: a `loop` may express intention better if this is on purpose
   = note: `#[warn(unconditional_recursion)]` on by default

if we replace self.f() with qualified syntax Scroll::<L>::f(self); or <Scroll<L> as ATraitSomewhere>::f(self);, the same warning.

This indicates rustc eagerly treats self.f() as trait method instead of inherent method here? Maybe this will be a different issue too...


Note the inherent vs trait method intentionally returns distinct type here.

When you specify the return type from inherent method (playground), maybe rustc should suggest adding the L: Bound bound the same as this issue suggests?

error[E0308]: mismatched types
  --> src/lib.rs:18:24
   |
18 |         let _: usize = self.f(); // meant to call an inherent method
   |                -----   ^^^^^^^^ expected `usize`, found `()`
   |                |
   |                expected due to this
+note: the method `f` can be called with trait bound `L: Bound` satisfied
+16 | impl<L> ATraitSomewhere for Scroll<L> {
+   |      ^
+   |      |
+   |      help: consider adding `L: Bound`
+   |

@zjp-CN
Copy link
Author

zjp-CN commented Mar 4, 2024

For the unconditional_recursion warning, here's an example that involves auto-(de)ref in method call from this post.

Simplified as follows: playground

trait Trait<V> {
    fn contains(&self, x: &V) -> bool;
}

#[cfg(fail0)] // comment out to see unconditional_recursion warning
impl<V> Trait<V> for Vec<V> {
    fn contains(&self, x: &V) -> bool {
        self.contains(x)
    }
}

#[cfg(fail1)] // comment out to see unconditional_recursion warning
impl<V: PartialEq> Trait<V> for Vec<V> {
    fn contains(&self, x: &V) -> bool {
        self.contains(x)
    }
}

#[cfg(fail2)] // comment out to see unconditional_recursion warning
impl<V: PartialEq> Trait<V> for Vec<V> {
    fn contains(&self, x: &V) -> bool {
        self.contains(x)
    }
}

#[cfg(ok)] // this is the right case
impl<V: PartialEq> Trait<V> for Vec<V> {
    fn contains(&self, x: &V) -> bool {
        // <[V]>::contains(self, x) // ok
        (**self).contains(x) // method call for slice instead of Vec or &Vec receiver
    }
}

So it's good to see the hint as follows

warning: function cannot return without recursing
 --> src/lib.rs:6:5
  |
6 |     fn contains(&self, x: &V) -> bool {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
7 |         self.contains(x)
  |         ---------------- recursive call site
  |
  = help: a `loop` may express intention better if this is on purpose
  = note: `#[warn(unconditional_recursion)]` on by default

+note: the method `contains` can be called with trait bound `V: PartialEq` satisfied on `<[V]>`
+ 5 | impl<V> Trait<T> for Vec<V> {
+   |      ^
+   |      |
+   |      help: consider adding `V: PartialEq`
+   |
+ 7 |      self.contains(x)
+   |      ^
+   |      |
+   |      help: `self.contains(x)` then should be `<[V]>::contains(self, x)`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

1 participant