rust-lang / rust

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

Lint `type_alias_bounds` fires unnecessarily for trait bounds that define short-hand projections on the RHS #125709

Open danielhenrymantilla opened 1 month ago

danielhenrymantilla commented 1 month ago

Code

trait Trait { type Assoc; }

type AssocOf<T : ?Sized + Trait> = T::Assoc;

Current output

warning: bounds on generic parameters are not enforced in type aliases
 --> src/lib.rs:3:18
  |
3 | type AssocOf<T : ?Sized + Trait> = T::Assoc;
  |                  ^^^^^^   ^^^^^
  |
help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases
 --> src/lib.rs:3:36
  |
3 | type AssocOf<T : ?Sized + Trait> = T::Assoc;
  |                                    ^^^^^^^^
  = note: `#[warn(type_alias_bounds)]` on by default
help: the bound will not be checked when the type alias is used, and should be removed
  |
3 - type AssocOf<T : ?Sized + Trait> = T::Assoc;
3 + type AssocOf<T > = T::Assoc;
  |

Desired output

No warning whatsoever

Rationale and extra context

Whilst it is true that left-hand-side bounds on generic parameters are not enforced in type aliases, { performing a Trait associated type "lookup" on / resolving the Trait associated type of } a given type T does effectively constrain T to be : ?Sized + Trait.

This means that for that specific snippet, the pattern is fine, both presently, and in the future, should these bounds end up enforced.

Whilst it may look like an oddly over-specific example, it is actually a common pattern to define a type alias as a shortcut for a trait associated type lookup.

Granted, the bounds could be skipped, like so:

- type AssocOf<T : ?Sized + Trait> = T::Assoc;
+ type AssocOf<T> = <T as Trait>::Assoc;

but this significantly hinders the quality of the generated documentation (now people need to look at the implementation/"value" of the type alias to try and figure out what the bounds on it are).

Other cases

Bonus points if when : Sized is a supertrait of T, the ?Sized + ends up not being required to prevent the lint from firing.

fmease commented 1 month ago

NB By the way, lazy_type_alias implements the desired semantics. See its tracking issue: #112792.

CC #21903.

danielhenrymantilla commented 1 month ago

Good to know there is progress on the "make type aliases work as expected" front 🙂.

But to avoid confusion, I do want to clarify that this is not what this issue is about, this one is a diagnostics issue, w.r.t. how type aliases currently work. That is, when using <T as Trait>::Assoc in the rhs of a type alias, this already, currently, enforces that T : ?Sized + Trait be true (and when Trait : Sized, we end up with T : Trait). Given that, we should be allowed to add such bounds without having Rust warn us about our bounds being ignored, because given the rhs, they cannot be.

fmease commented 1 month ago

Right, that makes sense (I just had to do some advertising :P).

Yeah, the lint isn't super smart atm. This isn't the only case where bounds on type parameters of (eager) type aliases actually have a user-visible effect. Outlives-bounds for example may affect trait object lifetime defaults (we have an A-lint issue for that somewhere).

danielhenrymantilla commented 1 month ago

Thanks for improving my poor issue title 😄 @fmease 🙏