Skip to content

Confusing diagnostic when forgetting to capture lifetime in a APIT #124479

@WaffleLapkin

Description

@WaffleLapkin

Code

trait A<'a> {
    type Assoc;
}

trait B {}

fn f(_: impl for<'a> A<'a, Assoc = impl B>) {}

impl<'a> A<'a> for () {
    type Assoc = &'a u8;
}

impl B for &u8 {}

fn main() {
    f(());
}

Current output

error[E0308]: mismatched types
  --> ./t.rs:16:5
   |
16 |     f(());
   |     ^^^^^ one type is more general than the other
   |
   = note: expected reference `&'a _`
              found reference `&_`
note: the lifetime requirement is introduced here
  --> ./t.rs:7:28
   |
7  | fn f(_: impl for<'a> A<'a, Assoc = impl B>) {}
   |                            ^^^^^^^^^^^^^^

Desired output

error[E0308]: mismatched types
  --> ./t.rs:16:5
   |
16 |     f(());
   |     ^^^^^ one type is more general than the other
   |
   = note: expected reference `for<'a> &'a _`
              found reference `&'0 _` for some specific lifetime `'0`
   = note: `impl B` can't name `'a`
note: the lifetime requirement is introduced here
  --> ./t.rs:7:28
   |
7  | fn f(_: impl for<'a> A<'a, Assoc = impl B>) {}
   |                            ^^^^^^^^^^^^^^

Rationale and extra context

It is not very clear from the error why impl B introduces the lifetime requirement or why we "found" a &_ (this leaks generalizer impl kinda). Ideally we would explain the actual cause of the error (impl B introduces a new generic parameter outside of the scope of for<'a>).

I don't like having for<'a> &'a _ in the desired error either, because it implies that it's a type that exists, but I couldn't come up with a better explanation for the fact that 'a is from the for<'a>...

Maybe we could even suggest impl for<'a> A<'a, Assoc: B>) but @BoxyUwU says it's probably hard. We could just lint on impl Trait<Assoc = impl Other> as a style thing suggesting Assoc: Other since it allows more things.

Other cases

If you add `+ 'a` the diagnostic is a bit better:


trait A<'a> {
    type Assoc;
}

trait B {}

fn f(_: impl for<'a> A<'a, Assoc = impl B + 'a>) {}

impl<'a> A<'a> for () {
    type Assoc = &'a u8;
}

impl B for &u8 {}

fn main() {
    f(());
}
error: `impl Trait` can only mention lifetimes from an fn or impl
 --> ./t.rs:7:45
  |
7 | fn f(_: impl for<'a> A<'a, Assoc = impl B + 'a>) {}
  |                  -- lifetime declared here  ^^

error[E0308]: mismatched types
  --> ./t.rs:16:5
   |
16 |     f(());
   |     ^^^^^ one type is more general than the other
   |
   = note: expected reference `&'a _`
              found reference `&_`
note: the lifetime requirement is introduced here
  --> ./t.rs:7:28
   |
7  | fn f(_: impl for<'a> A<'a, Assoc = impl B + 'a>) {}
   |                            ^^^^^^^^^^^^^^^^^^^

Rust Version

rustc 1.79.0-nightly (ef8b9dcf2 2024-04-24)
binary: rustc
commit-hash: ef8b9dcf23700f2e2265317611460d3a65c19eff
commit-date: 2024-04-24
host: x86_64-unknown-linux-gnu
release: 1.79.0-nightly
LLVM version: 18.1.4

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-lifetimesArea: Lifetimes / regionsD-terseDiagnostics: An error or lint that doesn't give enough information about the problem at hand.T-compilerRelevant to the compiler 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