Tracking issue for where-bounds shadowing trait implementations
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
This is a tracking issues for errors caused by where-bounds shadowing trait implementations.
trait Trait {
type Assoc;
}
impl<T> Trait for T {
type Assoc = i32;
}
fn where_bound_shadows_impl<T: Trait>(x: T::Assoc) -> i32 {
x
}
This snippet results in the following error:
error[E0308]: mismatched types
--> src/lib.rs:9:5
|
8 | fn where_bound_shadows_impl<T: Trait>(x: T::Assoc) -> i32 {
| --- expected `i32` because of return type
9 | x
| ^ expected `i32`, found associated type
|
= note: expected type `i32`
found associated type `<T as Trait>::Assoc`
= note: the associated type `<T as Trait>::Assoc` is defined as `i32` in the implementation, but the where-bound `T` shadows this definition
see issue #152409 <https://github.com/rust-lang/rust/issues/152409> for more information
help: consider constraining the associated type `<T as Trait>::Assoc` to `i32`
|
8 | fn where_bound_shadows_impl<T: Trait<Assoc = i32>>(x: T::Assoc) -> i32 {
| +++++++++++++
This subtle issue is caused by a limitation of the current implementation of the type system and we intend to fix at least some of these errors longterm.
Background
An associated type like <T as Trait>::Assoc can be replaced with the type specified in the relevant trait implementation.
trait UsingImpl {
type Assoc;
}
impl<T> UsingImpl for T {
type Assoc = i32;
}
fn foo<T>(x: <T as UsingImpl>::Assoc) -> i32 {
// We can replace the associated type with `i32`
// by looking at the impl above.
x
}
If the corresponding trait-bound is satisfied due to a where-bound which does not specify the associated type, then we keep the associated type as is and it will only be equal to itself.
trait UsingWhereClause {
type Assoc;
}
fn does_not_specify_assoc<T: UsingWhereClause>(x: T::Assoc) -> i32 {
// We do not know the underlying type of `T::Assoc`, so
// it is only equal to itself. The following line errors
x // ERROR: mismatched types
}
fn specifies_assoc<T: UsingWhereClause<Assoc = i32>(x: T::Assoc) -> i32 {
// We can replace the associated type with `i32`
// as the where-bound specifies its underlying type.
x
}
In case the corresponding trait-bound is satisfied due to both an impl and a where-clause, we ignore the underlying type provided by the impl and only consider ones specified by the where-clause. In case the where-clause does not specify the associated type, the associated type is only equal to itself, even though the impl specifies its underlying type.
trait Trait {
type Assoc;
}
impl<T> Trait for T {
type Assoc = i32;
}
fn does_not_specify_assoc<T: UsingWhereClause>(x: T::Assoc) -> i32 {
// We do not specify the associated type in the where-clause.
// Even though there is an impl which specifies the underlying type
// of `T::Assoc`, the associated type is only equal to itself, causing
// an error.
x // ERROR: mismatched types
}
While there are subtle cases where this behavior is necessary and desirable, it is largely due to technical limitation of the current implementation. For an in-depth summary, see https://rustc-dev-guide.rust-lang.org/solve/candidate-preference.html#trait-where-bounds-shadow-impls. We intend to reduce the impact of this issue longterm.
How to deal with this issue
The issue is often caused by unnecessary where-bounds. In the initial example, the where-bound T: Trait is not necessary as this trait-bound can already be satisfied by using a trait implementation. The original example compiles after removing that where-bound.
It isn't always possible to remove where-bounds however. This issue can also happen due to super traits of a required where-bound.
trait Super {
type Assoc;
}
impl<T> Super for T {
type Assoc = u32!
}
trait Trait: Super {}
fn requires_trait<T: Trait>() {}
fn my_function<T: Trait>(x: T::Assoc) -> u32 {
requires_trait::<T>()
x // ERROR: mismatched types
}
While T: Super is known due the implementation, we do need the T: Trait where-bound and can't just remove it.
What we can do instead is to add a redundant where-bound explicitly specifying the associated type:
// or alternatively the more explicit `T: Trait + Super<Assoc = u32>`.
fn my_function<T: Trait<Assoc = u32>>(x: T::Assoc) -> u32 {
requires_trait::<T>()
x // ERROR: mismatched types
}
One example which we is a lot more challenging to work around is a blanket impl of a super trait only if a subtrait is implemented, see https://github.com/rust-lang/rust/issues/152409#issuecomment-4646353809. @danielhenrymantilla provided a workaround in https://github.com/rust-lang/rust/issues/152409#issuecomment-4659615416.
In case your issue cannot be fixed using either of these approaches, we would like to hear from you.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the minimal Rust examples in the issue and read the referenced rustc-dev-guide section on trait where-bounds shadowing impls. Trace how the current trait-solver behavior produces these diagnostics and define a focused case to address; done requires a scoped compiler change that handles the selected case without regressing the necessary shadowing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100