Skip to content

Tracking issue for where-bounds shadowing trait implementations #152409

Open

Description

@lcnr

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 #152409 (comment). @danielhenrymantilla provided a workaround in #152409 (comment).


In case your issue cannot be fixed using either of these approaches, we would like to hear from you.

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Feb 9, 2026
  2. added
    A-associated-itemsArea: Associated items (types, constants & functions)
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Feb 9, 2026
  3. theemathas commented on Feb 17, 2026

    @theemathas
    Contributor

    #129846 is an instance of this, according to the compiler. I think that issue can be worked around in all cases by adding an associated type bound on the supertrait, but I'm not sure.

  4. theemathas commented on Jun 8, 2026

    @theemathas
    Contributor

    The following is code that runs into this issue and appears to have no workaround. Discovered by @ruriww

    trait Super {
        type Assoc;
        fn make() -> Self::Assoc;
    }
    
    trait Sub: Super {}
    
    struct Thing<T>(T);
    
    impl<T> Super for Thing<T> where Thing<T>: Sub {
        type Assoc = i32;
        fn make() -> Self::Assoc {
            1_i32
        }
    }
    
    impl Sub for Thing<i64> {}
    error[E0308]: mismatched types
      --> src/lib.rs:13:9
       |
    12 |     fn make() -> Self::Assoc {
       |                  ----------- expected `<Thing<T> as Super>::Assoc` because of return type
    13 |         1_i32
       |         ^^^^^ expected associated type, found `i32`
       |
       = note: expected associated type `<Thing<T> as Super>::Assoc`
                             found type `i32`
       = help: consider constraining the associated type `<Thing<T> as Super>::Assoc` to `i32` or calling a method that returns `<Thing<T> as Super>::Assoc`
       = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
       = note: the associated type `<Thing<T> as Super>::Assoc` is defined as `i32` in the implementation, but the where-bound `Thing<T>` shadows this definition
               see issue #152409 <https://github.com/rust-lang/rust/issues/152409> for more information
    
    For more information about this error, try `rustc --explain E0308`.
    error: could not compile `playground` (lib) due to 1 previous error
    

    Changing where Thing<T>: Sub to where Thing<T>: Sub<Assoc = i32> gives the following error instead:

    error[E0275]: overflow evaluating the requirement `Thing<i64>: Super`
      --> src/lib.rs:17:14
       |
    17 | impl Sub for Thing<i64> {}
       |              ^^^^^^^^^^
       |
       = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`playground`)
    note: required by a bound in `Sub`
      --> src/lib.rs:6:12
       |
     6 | trait Sub: Super {}
       |            ^^^^^ required by this bound in `Sub`
    
    For more information about this error, try `rustc --explain E0275`.
    error: could not compile `playground` (lib) due to 1 previous error
    
  5. danielhenrymantilla commented on Jun 9, 2026

    @danielhenrymantilla
    Contributor

    It's not pretty, but there is a workaround for this for someone controlling the trait Super definition:

    1. define a verbatim copy of your Super trait; e.g., under some mod dopple { or whatnot;
    2. write a blanket "forwarding impl" of the genuine Super trait for implementors of dopple::Super.
    3. Replace any concrete impl of the genuine Super trait with a concrete impl of the dopple::Super.

    Demo

    trait Super {
        type Assoc;
        fn make() -> Self::Assoc;
    }
    
    // 👇 ADD THIS:
    mod dopple {
        #[allow(unused)]
        use super::*;
    
        // 1. Verbatim copy of `trait Super`'s definition above.
        pub trait Super {
            type Assoc;
            fn make() -> Self::Assoc;
        }
    
        // 2. Blanket forwarding impl.
        impl<X: ?Sized + Super> super::Super for X {
            type Assoc = <Self as Super>::Assoc;
    
            fn make() -> Self::Assoc {
                <Self as Super>::make()
            }
        }
    }
    
    trait Sub: Super {}
    
    struct Thing<T>(T);
    
    impl Sub for Thing<i64> {}
    
    // 3. instead of `impl`ementing (genuine) `Super`, implement `dopple::Super`:
    impl<T> dopple::Super for Thing<T>
    where
        Thing<T>: Sub,
    {
        type Assoc = i32;
        fn make() -> Self::Assoc {
            42
        }
    }
    
    fn main() {
        dbg!(Thing::<i64>::make());
    }
  6. lcnr commented on Jul 14, 2026

    @lcnr
    Author
  7. lcnr commented on Jul 20, 2026

    @lcnr
    Author
  8. theemathas commented on Aug 1, 2026

    @theemathas
  9. theemathas commented on Aug 1, 2026

    @theemathas
  10. weihanglo commented on Aug 1, 2026

    @weihanglo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-associated-itemsArea: Associated items (types, constants & functions)A-type-systemArea: Type systemC-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCT-typesRelevant to the types team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions