Dyn compatibility error when using an associated type as a type parameter in the super-trait listing

0d24f18
Opened by jonysy at 2024-12-21 06:19:01

I tried this code:

trait Super<T> {}

trait Foo: Super<<Self as Foo>::Bar> { type Bar;  }

type BoxFoo = Box<Foo<Bar = u8>>;

The code should compile since Self::Bar is the type parameter, not Self.

Instead, it will fail with the following message:

the trait Foo cannot be made into an object the trait cannot use Self as a type parameter in the supertrait listing

FYI the discussion that sparked this issue:

So this has to do with object safety -- any trait that references Self is not object safe because the true Self type is erased. However, the error doesn't seem appropriate in this case. I think this warrants a bug report at least to fix the error message, if not to relax the object safety check (because there could be something I don't see preventing it still... can't say I fully understand object safety).

Meta

rustc --version --verbose: rustc 1.17.0-nightly (b1e31766d 2017-03-03)

  1. Current output:

    error[E0038]: the trait `Foo` cannot be made into an object
     --> src/lib.rs:5:19
      |
    5 | type BoxFoo = Box<Foo<Bar = u8>>;
      |                   ^^^^^^^^^^^^^ the trait `Foo` cannot be made into an object
      |
      = note: the trait cannot use `Self` as a type parameter in the supertraits or where-clauses
    

    It should at least point at trait Foo's supertraits.

    Esteban Kuber at 2019-10-11 23:13:57

  2. Output in #68377:

    error[E0038]: the trait `Foo` cannot be made into an object
     --> file.rs:7:19
      |
    3 | trait Foo: Super<<Self as Foo>::Bar> {
      |       ---  ------------------------- ...because it cannot use `Self` as a type parameter in the supertraits or `where`-clauses
      |       |
      |       this trait cannot be made into an object...
    ...
    7 | type BoxFoo = Box<dyn Foo<Bar = u8>>;
      |                   ^^^^^^^^^^^^^^^^^ the trait `Foo` cannot be made into an object
    

    Esteban Kuber at 2020-02-02 21:03:13

  3. I believe that we should change the implementation here

    https://github.com/rust-lang/rust/blob/ed9749324aed9e97741abf569a022353e269a9df/compiler/rustc_trait_selection/src/traits/object_safety.rs#L287-L293

    to account for data=TraitPredicate(<Self as Super<<Self as Foo>::Bar>>, polarity:Positive and explicitly allow it, by ignoring when Self is only referenced to access one of its currently defined associated types.

    Further changes would also be needed here

    https://github.com/rust-lang/rust/blob/ed9749324aed9e97741abf569a022353e269a9df/compiler/rustc_hir_analysis/src/astconv/mod.rs#L1519-L1542

    @rust-lang/lang, would this require team sign-off, or is the current behavior "accidental"/"incidental" and a change of behavior requires no "new rationale"?

    I'm also intrigued on whether this test should have been rejected with a more targeted error, instead of E0038: https://github.com/rust-lang/rust/blob/eb4580a570069175e1290b294d91042a09f9fde3/src/test/ui/issues/issue-26056.rs

    Esteban Kuber at 2022-12-13 02:14:47

  4. @estebank

    Self can appear in projections, but only as part of an "identity" trait-ref. i.e., if you hvae trait Foo<A..Z>, then the trait ref has to be <Self as Foo<A..Z>>::Bar. This ensures that we can extract the associated type from the self type (which will be something like dyn Foo<A..Z, Bar=X>.

    I think such a PR does not require an FCP, but it's prob a good idea to nominate for @rust-lang/types approval or review. This feels to me like a 'nitty gritty' detail. I think in general @rust-lang/types is a good choice for that, and types team can escalate to lang if it seems like it requires a policy decision.

    Niko Matsakis at 2022-12-13 11:30:11

  5. This is a disparity between trait generic parameter and associated type. I very much doubt such disparity is intended in this scenario. Though not particularly damaging, it adds restrictions during high-level type designs.

    trait Super<T> {}
    
    trait Foo<Bar>: Super<Bar> {} // Using a generic parameter does not break object safety
    
    type BoxFoo = Box<Foo<u8>>;
    

    ZhennanWu at 2023-07-05 00:27:10

  6. I think such a PR does not require an FCP, but it's prob a good idea to nominate for @rust-lang/types approval or review. This feels to me like a 'nitty gritty' detail. I think in general @rust-lang/types is a good choice for that, and types team can escalate to lang if it seems like it requires a policy decision.

    I quite strongly believe that a fix for this issue should require a types FCP. Changes to the stable behavior of the type system, especially if the changes are hanbdle some nitty gritty detail should go through a types FCP, see https://rust-lang.zulipchat.com/#narrow/stream/144729-t-types/topic/FCPs.20for.20type.20system.20hacks where we've previously discussed this question on zulip. Most unsound issues and weird backcompat hurdles tend to be caused by one (or the interaction of multiple) such nitty gritty detail(s). Even if the FCP only manages to force t-types members to look at the PR in more detail, this is still very clearly worth it to me.

    lcnr at 2023-07-06 10:51:45