rust-lang / rust-lang/rust

TAIT defining uses may not be fully general due to additional where-bounds

Open
#154,652 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

F-type_alias_impl_trait needs-triage
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Consider the following two examples:

#![feature(type_alias_impl_trait)]
trait Trait {
    type Assoc;
}
type Tait<T: Trait> = impl Sized;
#[define_opaque(Tait)]
fn foo<T: Trait<Assoc = u32>, U: Trait<Assoc = i32>>() {
    let _: Tait<T> = 1u32;
    let _: Tait<U> = 1i32;
}
error: concrete type differs from previous defining opaque type use
 --> src/lib.rs:9:22
  |
9 |     let _: Tait<U> = 1i32; 
  |                      ^^^^ expected `u32`, got `i32`
  |
note: previous use here
 --> src/lib.rs:8:22
  |
8 |     let _: Tait<T> = 1u32;
  |                      ^^^^

#![feature(type_alias_impl_trait)]
trait Trait {
    type Assoc;
}

trait IsAssoc<T> {}
impl<T: Trait> IsAssoc<T::Assoc> for T {}

struct ReqAssoc<T: Trait + IsAssoc<U>, U = <T as Trait>::Assoc> {
    field: Option<(T, U)>,
}
type Tait<T: Trait> = impl Sized;
#[define_opaque(Tait)]
fn define<T: Trait<Assoc = u32>>() -> Tait<T> {
    ReqAssoc::<T> { field: None }
}
error[E0271]: type mismatch resolving `<T as Trait>::Assoc == u32`
  --> src/lib.rs:15:5
   |
15 |     ReqAssoc::<T> { field: None }
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found associated type
   |
   = note:         expected type `u32`
           found associated type `<T as Trait>::Assoc`
   = help: consider constraining the associated type `<T as Trait>::Assoc` to `u32`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: required by a bound in `ReqAssoc`
  --> src/lib.rs:9:28
   |
 9 | struct ReqAssoc<T: Trait + IsAssoc<U>, U = <T as Trait>::Assoc> {
   |                            ^^^^^^^^^^ required by this bound in `ReqAssoc`

This happens because a fully general use of an opaque type - what we require for a use to be defining - may not actually be fully general if one were to explicitly track dictionaries/where-bounds.

In the first example one could imagine the Tait as an fn tait(T: Type, trait_dict: Trait (t)) -> Type. In this example the defining use Tait<T> is actually tait(T, Trait { assoc: u32 }) = u32. While it takes a variable for T, the trait dictionary actually contains relevant information about the associated type. This means there are now multiple valid function bodies for which the constraint tait(T, Trait { assoc: u32 }) = u32 holds:

  • type Tait<T: Trait> = T::Assoc
  • type Tait<T: Trait> = u32
  • type Tait<T: Trait> = SomeEntirelyDifferentAliasType<T::Assoc>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce both examples in src/lib.rs with the type_alias_impl_trait and define_opaque features enabled. Start by tracing how fully general defining uses and associated-type where-bounds are handled, then compare the observed behavior with the intended opaque-type rules. Done means establishing the correct semantics and implementing or documenting a resolution for these cases.

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.