Adding HRTB ops::Rem bound breaks code that uses owned bound

The following code compiles fine until you'll uncomment the for<'a> Rem<&'a Self, Output = Self> bound.

use std::ops::Rem;

trait Trait: 
    Sized +
    Rem<Output = Self> +
    //for<'a> Rem<&'a Self, Output = Self> 
{}

trait A {
    type Inner: Trait;
}

fn test<T: A>() -> T::Inner {
    let (x, y): (T::Inner, T::Inner) = panic!();
    x % y
}

With the hrtb bound compilation fails with the following error:

error[E0308]: mismatched types
  --> src/lib.rs:16:9
   |
16 |     x % y
   |         ^ expected `&<T as A>::Inner`, found associated type
   |
   = note:    expected reference `&<T as A>::Inner`
           found associated type `<T as A>::Inner`
help: consider constraining the associated type `<T as A>::Inner` to `&<T as A>::Inner`
   |
13 | fn test<T: A<Inner = &<T as A>::Inner>>() -> T::Inner {
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider borrowing here
   |
16 |     x % &y
   |         ^^

Which is really weird because

  1. it worked before
  2. error suggests using T: A<Inner = &<T as A>::Inner> which is recursion
  3. T::Inner should be both Rem<&'_ Self> and Rem<Self> so (x: T::Inner) % (y: T::Inner) should compile

The workaround is to use either function that the same type for rhs:

fn rem_own<T: Rem>(x: T, y: T) -> T::Output {
    x % y
}

Or to use explicit <T::Inner as Rem<T::Inner>>::rem(x, y) instead of x % y.

To me, this feels like a compiler bug, but maybe I'm missing something?

Playground: [link]

I've managed to reduce your issue to:

  • not involve HRTB,

  • not involve sigil magic desugaring.

use ::core::ops::Rem;

trait Trait<'lt>
    : 'lt
    + Sized
    + Rem<Self, Output = Self>
    + Rem<&'lt Self, Output = Self> 
{}

trait A<'lt> {
    type Inner : Trait<'lt>;
}

fn test<'lt, T : A<'lt>> (
    (x, y): (T::Inner, T::Inner),
) -> T::Inner
{
    <T::Inner as Rem<_ /* Rhs? */>>::rem(x, y)
}

fails too.


My guess is that we can see there is a type to be inferred when looking for the right implementation of Rem for T::Inner, and instead of it inferring the Rhs type from the actual param, the compiler seems to try and first infer the Rhs from the available impls (first semi-suprising thing).

Now, the very surprising thing here, is that despite there being two perfectly good candidates for Rhs (in this case, T::Inner and &'lt T::inner), Rust decides that Rhs must be the latter, hence erroring when seeing the actual type of y.


That, plus the absurd error message suggestion (T and &T cannot possibly be the same type!) does seem to indicate that it is an error of the compiler. Feel free to submit an issue.


EDIT: Further reduction of the problematic code:

trait Op<Arg> { fn op (_: Arg); }

trait Trait<'lt> : 'lt + Op<Self> + Op<&'lt Self> {}

trait WithAssoc<'lt> { type Assoc : Trait<'lt>; }

fn test<'lt, T : WithAssoc<'lt>> (it: T::Assoc)
{
    <T::Assoc as Op<_>>::op(it)
}
3 Likes

Opened an issue: #77159

1 Like