rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.6k stars 12.48k forks source link

Trait fails to imply trait bounds for super trait associated types #111926

Open Dante-Broggi opened 1 year ago

Dante-Broggi commented 1 year ago

I tried this code:

trait Trait {}

trait Super {
    type Assoc;
}
trait Sub: Super where <Self as Super>::Assoc: Trait {}

fn user<T:Sub>(t:T) {}

I expected to see this happen: It compile

Instead, this happened:

Compiling playground v0.0.1 (/playground)
error[[E0277]](https://doc.rust-lang.org/stable/error_codes/E0277.html): the trait bound `<T as Super>::Assoc: Trait` is not satisfied
 --> src/lib.rs:8:11
  |
8 | fn user<T:Sub>(t:T) {}
  |           ^^^ the trait `Trait` is not implemented for `<T as Super>::Assoc`
  |
note: required by a bound in `Sub`
 --> src/lib.rs:6:48
  |
6 | trait Sub: Super where <Self as Super>::Assoc: Trait {}
  |                                                ^^^^^ required by this bound in `Sub`
help: consider further restricting the associated type
  |
8 | fn user<T:Sub>(t:T) where <T as Super>::Assoc: Trait {}
  |                     ++++++++++++++++++++++++++++++++

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

Meta

Playground (rust 1.69.0 on May 24 2023), and

rustc --version --verbose:

rustc 1.69.0 (84c898d65 2023-04-16) (built from a source tarball)
binary: rustc
commit-hash: 84c898d65adf2f39a5a98507f1fe0ce10a2b8dbc
commit-date: 2023-04-16
host: x86_64-apple-darwin
release: 1.69.0
LLVM version: 15.0.7
compiler-errors commented 1 year ago

The only things implied by a trait bound are other Self: Trait bounds, either in the supertraits (trait A: B + C) or in where clauses (trait A where Self: B).

Jules-Bertholet commented 1 year ago

@rustbot label A-traits

oxalica commented 1 year ago

I tried to workaround by introducing a helper trait with effective bounds on its associated types, but got more strange behaviors:

trait Super {
    type Assoc;
}

trait Helper {
    type AssocHelper: Into<u64>;
}

impl<T> Helper for T
where
    T: Super,
    T::Assoc: Into<u64>,
{
    type AssocHelper = T::Assoc;
}

trait Derived: Super + Helper<AssocHelper = Self::Assoc> {}

fn use_assoc_bounds<T: Derived>(x: T::Assoc) -> u64 {
    // x.into() // not working, somehow.
    helper::<T>(x) // works.
}

fn helper<T: Helper>(x: T::AssocHelper) -> u64 {
    x.into()
}
SOF3 commented 11 months ago

To provide some motivation for code in the pattern above, some example is like this:

pub trait Animal {
    type Mouth: Mouth;
    fn mouth(&self) -> Self::Mouth;
}

pub trait Bird: Animal
where
    Self::Mouth: Beak,
{
}

pub trait Mouth {}
pub trait Beak: Mouth {}

fn feed<T>(animal: T)
where
    T: Bird,
{ todo!() }

Implementations of Bird may return an adapter object that implements Mouth + Beak, which can be consistently retrieved through mouth(). Because of the E0277 limitation, now I have to expose a separate associated function beak(), which rsults in some less consistent API.

tbu- commented 2 months ago

I thought there was some other thread about this, but I couldn't find it. It's not #113195 that I have in mind.