Repository navigation
"Unused" associated type bounds in dyn no longer parameterize the type #140645
Description
Code
#![allow(unused_associated_type_bounds)]
pub trait Other {}
pub trait Trait {
type Assoc where Self: Sized;
}
impl Other for dyn Trait {}
impl Other for dyn Trait<Assoc = ()> {}
impl<T> dyn Trait<Assoc = T> {}I expected to see this happen: successful compilation.
Instead, this happened:
error[E0119]: conflicting implementations of trait `Other` for type `(dyn Trait + 'static)`
--> src/lib.rs:9:1
|
8 | impl Other for dyn Trait {}
| ------------------------ first implementation here
9 | impl Other for dyn Trait<Assoc = ()> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `(dyn Trait + 'static)`
error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:10:6
|
10 | impl<T> dyn Trait<Assoc = T> {}
| ^ unconstrained type parameter
Some errors have detailed explanations: E0119, E0207.
For more information about an error, try `rustc --explain E0119`.Version it worked on
It most recently worked on: Rust 1.86.0
Version with regression
Playground: Beta version: 1.87.0-beta.8 (2025-05-03 973ec11)
More notes
CC #125560
Before Beta, dyn Trait and dyn Trait<Assoc = ()> were different types. Now they are the same type.
Here's a more complete example that makes practical (albeit contrived) use of distinct dyn Trait<Assoc = ...> types.
Considering the release notes, #136458 may be the cause (but there's no discussion about this change in particular). Assuming this is the case and the use case that broke is considered unimportant, this aspect of the change could at least be mentioned in the release notes.
Incidentally, this also means dyn Trait<Assoc = &'can_be_non_static str> meets a 'static bound now. Hopefully irrelevant, but it does seem like an exception to outlive bounds being syntactical.
@rustbot modify labels: +regression-from-stable-to-beta, -regression-untriaged
Activity
compiler-errors commentedon May 4, 2025
I don't believe that this behavioral change is worth concern. I'll discuss this with @lcnr tomorrow since we worked together on these dyn issues, b/c we have several options here:
unused_associated_type_boundsup to a future-compat warning.I kinda prefer (0.) or (1.) here, personally. I don't see any problem with writing these bounds, but we could probably be clearer in explaining to users that we now don't consider
dyn Trait<NeedsSized = T>anddyn Trait<NeedsSized = U>as distinct types ifT != U. (2.) would be annoying but doable.Side-note, it's not exactly true to say that
dyn Trait<Assoc = T>now implies thatTsatisfies the item bounds ofAssocdue to this change; we're simply not enforcing the item bounds since we ignore the user-written associated type bound sincedyn Traitis definitely unsized.This is IMO irrelevant because that bound is never relevant for a dyn type. It's as if we were saying "if
dyn Sized: Sized, then require&'a str: 'static". It's got a false premise.compiler-errors commentedon May 4, 2025
The fact that impossible associated type bounds used to constrain type parameters to me is interesting at best, but is technically an observable change. I think this warrants an FCP, and I'll open up one tomorrow when I can write up some coherent justification for why this behavioral change is at best desirable and at worst worth ignoring.
QuineDot commentedon May 5, 2025
Let me call out one particular part of my more complete playground:
Or in other words: "
Box<T>coerces toBox<dyn Use<Assoc = Assoc> + 't>" no longer impliesT: Use<Assoc = Assoc> + 't, because now sometimesBox<dyn Use<Assoc = Assoc> + 't>is synonymous withBox<dyn Use + 't>.With that in mind, here's a version of the playground where code that used to be sound by relying on that implication is no longer sound, but still compiles on Beta. Even ignoring soundness, the code suddenly becomes very misleading IMO. I think assuming the implication holds is reasonable, but on Beta it's no longer always true.
Even if (2) and (3) won't happen, perhaps this is relevant re:
unused_associated_type_boundsbecoming a FCW.If do-nothing bounds are eventually disallowed, the implication that "what you can (syntactically) coerce to reflects the capability of the coercee" will become true again.
Under a mental model of "
dyn Trait<..> + 'ameansErasedType: Trait<..> + 'a", the item bound is a restriction on the erased type, which was notunsized. That take correlates to the now-violated assumption above.Anyway, I don't care strongly enough to argue hard for (2) or (3), and I'm not trying to do that. The lost ability is neat but I also recognize the change is cleaner. The code I wrote using the lost ability were POCs, not load bearing. And I only pointed out the
'staticthing in case there was some soundness consideration I didn't know about.But I do think "do-nothing item bounds" are quite misleading without the invariant, and should be loud about it. I think one can see how
previously_sound_cratecould be convincing in a code review, say.compiler-errors commentedon May 5, 2025
I changed my mind and implemented #140684.
Rollup merge of rust-lang#140684 - compiler-errors:unnecessary-assoc,…
Rollup merge of rust-lang#140684 - compiler-errors:unnecessary-assoc,…
Unrolled build for rust-lang#140684