Inconsistent Warning of Unconditional Looping

Rust Playground (rust-lang.org)

It seems the warning is inconsistent in that it does not warn against my own type in the same mod but against the built-in Vec.

Yeah, the diagnostic is very not friendly for method with same name.

It's basically what I wrote in 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 · Issue #120568 · rust-lang/rust · GitHub

// you want this
impl<V> T<V> for Vec<V> {
    fn contains(&self, x: &V) -> bool {
        <[V]>::contains(self, x)
    }
}

error[E0277]: can't compare `V` with `V`
    --> src/lib.rs:7:9
     |
7    |         <[V]>::contains(self, x)
     |         ^^^^^^^^^^^^^^^ no implementation for `V == V`
     |
note: required by a bound in `core::slice::<impl [T]>::contains`
    --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/slice/mod.rs:2569:12
     |
2567 |     pub fn contains(&self, x: &T) -> bool
     |            -------- required by a bound in this associated function
2568 |     where
2569 |         T: PartialEq,
     |            ^^^^^^^^^ required by this bound in `core::slice::<impl [T]>::contains`
help: consider restricting type parameter `V`
     |
5    | impl<V: std::cmp::PartialEq> T<V> for Vec<V> {
     |       +++++++++++++++++++++

Then the compiler gives the real useful solution of the lacking trait bound.

impl<V: PartialEq> T<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
    }
}

Note: even though you add the PartialEq bound, same loop warning for (*self).contains(x) and self.contains(x) if you don't use (**self).contains(x) or fully-qualified syntax as I wrote.

The warning is correct. In the Vec<T> case the inherent contains method requires T: PartialEq which is not satisfied in your implementation, so the only valid contains to call is the one on the T<V> implementation, leading to infinite recursion. This is not the case for A<V> were the inherent method has no bounds and hence is callable from the T<V> implementation.

3 Likes

It may turn out the problem is not diagnostic but the actual rustc method resolution. When I try to use the method, it causes the stack to overflow. It could be because contains is not a method of Vec but a method of []. I hypothesize that the method of trait impl of that very type is prioritized against deref coersion (not sure this?).

Do we have systematic documentation of how the method resolution works?

It's Method call expressions - The Rust Reference .

No. Inherent methods are first looked up.

Share the reproducible code. It doesn't happen.

P.S. method call will resolve to a method on a specific type (with a potential trait impl), which means method .contain(...) is not endlessly looked up to cause the stack overflow.

1 Like

When I try to use the method, it causes the stack to overflow.

Sorry for the ambiguity. By The method I mean my original code that causes a warning. The warning is correct because by the resolution order, T::contains is more prioritized than methods found in deref chains.

If I don't misunderstand, the resolution order is ordered by deref order, reference qualifier (Self > &Self > &mut Self) and then (impl Self > impl Trait for Self). When the resolution is still ambiguous at the end, a compiler error is reported.

<[]>::contains is after T::contains because it has a less prioritized deref order, therefore the compiler warning is correct in this case.

Rust Playground (rust-lang.org)

You didn't make it wrong :smiley:

It's recursive to call self.contains() even with the bound, because it's actually :

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


impl<V> T<V> for Vec<V> where V: PartialEq {
    fn contains_alias(&self, x: &V) -> bool {
        self.contains_alias(x) // equivalently `<Vec<T> as T<V>>::contains_alias(self, x)`
    }
}

dbg!(v.contains_alias(&1usize));

That's what I inform you of the correct thing in the first reply to explicitly tell the compiler we want the inherent method contain on slice!

2 Likes

What I mean by saying the diagnostic is not good here is the compiler can emit the correct unconditional_recursion (or correct trait bounds were not satisfied error in linked issue) and with a better hint solution.

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]>`
+ 6 | impl<V> T<T> for Vec<V> {
+   |      ^
+   |      |
+   |      help: consider adding `V: PartialEq`
+   |
+ 7 |      self.contains(x)
+   |      ^
+   |      |
+   |      help: `self.contains(x)` then should be `<[V]>::contains(self, x)`
1 Like