rust-lang / rust-lang/rust

E0195 diagnostic should take into account GATs, etc

Open
#135,350 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-diagnostics T-compiler
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code
pub trait Trait {
    type Gat<'a>;
    fn example(self, _: Self::Gat<'_>) -> Self::Gat<'_>;
}

impl Trait for () {
    type Gat<'a> = ();
    fn example(self, _: Self::Gat<'_>) {}
}
Current output
error[E0195]: lifetime parameters or bounds on method `example` do not match the trait declaration
 --> <source>:8:15
  |
3 |     fn example(self, _: Self::Gat<'_>) -> Self::Gat<'_>;
  |               - lifetimes in impl do not match this method in trait
...
8 |     fn example(self, _: Self::Gat<'_>) {}
  |               ^ lifetimes do not match method in trait
Desired output
error[E0195]: lifetime parameters or bounds on method `example` do not match the trait declaration
 --> <source>:8:15
  |
3 |     fn example(self, _: Self::Gat<'_>) -> Self::Gat<'_>;
  |                                   --                --
  |         lifetimes in impl do not match this method in trait
...
8 |     fn example(self, _: Gat<'_>) {}
  |                             --
  |         lifetimes do not match method in trait
  |

note: The lifetime in the trait does not constrain the lifetime parameter,
      but the lifetime in the implementation signature is constrained

hint: Make the lifetime in the implementation unconstrained by mentioning
      the lifetime in an explicit bound:

    fn example<'a:'a>(self, _: Self::Gat<'a>) {}
              +++++++                    ~~
Rationale and extra context

Context: #109476 and #87803. Those are filed as bugs, so I'm filing this diagnostic issue separately. It's plausible this will become moot if those issues are resolved, but it's not certain that will happen (or that it will happen soon).

When I encountered this, it took me awhile to figure out that E0195 was really about late-vs-early lifetimes parameters. Only when I figured that out did the error make sense. Late-vs-early lifetime parameters are niche knowledge, so I'm not entirely sure how to phrase the error message; it needs to probably spell out a workaround, as it's not obvious.

The current phrasing of E0195 makes sense when lifetime parameters are early bound due to appearing in explicit bounds. However, lifetime parameters can also become early bound implicitly/invisibly, such as in the example. There are similar cases (see #87803) when the return type is an RPIT/RPITIT (-> impl Trait) -- in which case the lifetime is early bound due to appearing in the implicit use<..> bounds.

Also in those cases, exactly matching the signature from the trait is not always an option or desirable. For example, when using refinement and/or precise capturing -- i.e. intentionally removing a lifetime from the return type in order to provide more functionality than the trait requires.

So for example while -> Self::Gat<'_> would be a usable suggestion for the code at the top of this issue, it is not a usable suggestion here:

pub trait Trait {
    type Gat<'a>;
    fn example(&self, _: Self::Gat<'_>) -> impl Sized;
}

impl Trait for () {
    type Gat<'a> = &'a str;
    #[allow(refining_impl_trait)]
    fn example(&self, _: Self::Gat<'_>) -> i32 { 0 }
}
Other cases

Being explicit about lifetimes doesn't improve things; the span highlighted has an "Expected X found X" flavor.

3 |     fn example<'a, 'b>(&'a self, _: Self::Gat<'b>) -> Self::Gat<'b>;
  |               -------- lifetimes in impl do not match this method in trait
...
8 |     fn example<'a, 'b>(&'a self, _: Self::Gat<'b>) {}
  |               ^^^^^^^^ lifetimes do not match method in trait
Rust Version
Rust Playground
Build using the Stable version: 1.84.0
Anything else?

No response

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

Start by reproducing the E0195 example in Rust Playground on stable 1.84.0 and compare the current and desired diagnostics, including the RPIT/RPITIT and explicit-lifetime cases. Trace the compiler's E0195 diagnostic path; done means the spans explain the lifetime mismatch and offer a valid workaround without assuming exact signature matching.

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
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.