rust
rust copied to clipboard
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
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.
- https://github.com/rust-lang/rust/issues/120186
- https://github.com/rust-lang/rust/issues/108428
- an ongoing PR to solve the two linked issues above: https://github.com/rust-lang/rust/pull/120507
So I think this issue would be a different case.
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`
+ |
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)`