rust-lang / rust-lang/rust

Early/late bound lifetime coercion produces a confusing diagnostic

Open
#140,896 0 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

https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=965a19cf1d3e124734e44af61178208f

struct A<'a>(&'a ());

impl<'a> A<'a> {
    fn associated(a: &'a (), b: &()) {}
}

fn main() {
    let works_not: for<'a> fn(&'a (), &()) = A::associated; // Error
    let works_not: for<'a, 'b> fn(&'a (), &'b ()) = A::<'_>::associated; // Error
    let works: for<'a> fn(&'a (), &()) = |a, b| A::associated(a, b); // Ok
    let works: for<'a, 'b> fn(&'a (), &'b ()) = |a, b| A::associated(a, b); // Ok
}
Current output
error[E0308]: mismatched types
 --> src\main.rs:8:46
  |
8 |     let works_not: for<'a> fn(&'a (), &()) = A::associated; // Error
  |                    -----------------------   ^^^^^^^^^^^^^ one type is more general than the other
  |                    |
  |                    expected due to this
  |
  = note: expected fn pointer `for<'a, 'b> fn(&'a (), &'b ())`
                found fn item `for<'a> fn(&(), &'a ()) {A::<'_>::associated}`

error[E0308]: mismatched types
 --> src\main.rs:9:53
  |
9 |     let works_not: for<'a, 'b> fn(&'a (), &'b ()) = A::<'_>::associated; // Error
  |                    ------------------------------   ^^^^^^^^^^^^^^^^^^^ one type is more general than the other
  |                    |
  |                    expected due to this
  |
  = note: expected fn pointer `for<'a, 'b> fn(&'a (), &'b ())`
                found fn item `for<'a> fn(&(), &'a ()) {A::<'_>::associated}`
Desired output
error[E0308]: mismatched types
  --> src/main.rs:8:46
   |
10 |     let no_works: for<'a> fn(&'a (), &()) = A::associated; // Error
   |                   -----------------------   ^^^^^^^^^^^^^ one type is more general than the other
   |                   |
   |                   expected due to this
   |
   = note: expected fn pointer `for<'a, 'b> fn(&'a (), &'b ())`
                  found fn item `for<'b> fn(&'1 (), &'b ()) {A::<'1>::associated}`
help: you can convert a early bound lifetime to a late bound using a closure
  --> src/main.rs:8:46
   |
10 |     let no_works: for<'a> fn(&'a (), &()) = |a, b| A::associated(a, b); // Error
   |                                             ++++++              ++++++
Rationale and extra context

Proposed changes (ranked by importance):

  1. Introduce the concept of early/late bounds so that the problem becomes searchable.
  2. Explicitly name the early bound lifetimes ('1) since I didn't really parse the {A::<'_>::associated} part of the fn type as linked/important. I thought that I might just need to add/remove lifetimes.
  3. Suggest using a closure to convert from early bounds to late bounds. (I'm not sure if this always works?)
  4. Don't reuse 'a and 'b in the diagnostic. Using the existing lifetime names instead of "shadowing" the definitions would be nice. This introduced more confusion since I commonly used 'a and 'b when writing lifetimes and it wasn't clear to me that these were different.

In my mind this is is an ideal output but some of these might not be feasible for one reason or another.

Other cases

Rust Version
$ cargo +stable -V -v
cargo 1.86.0 (adf9b6ad1 2025-02-28)
release: 1.86.0
commit-hash: adf9b6ad14cfa10ff680d5806741a144f7163698
commit-date: 2025-02-28
host: x86_64-pc-windows-msvc
libgit2: 1.9.0 (sys:0.20.0 vendored)
libcurl: 8.12.0-DEV (sys:0.4.79+curl-8.12.0 vendored ssl:Schannel)
os: Windows 10.0.26100 (Windows 11 Professional) [64-bit]
Anything else?

You can see my initial confusion here: https://github.com/rust-lang/rust/issues/140663.

I would be interested in trying to implement some of these changes.

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 the two failing examples from the linked Rust Playground and compare the current and desired diagnostics. Trace the compiler's lifetime diagnostic handling; done means the output explains early and late bound lifetimes, identifies the early-bound lifetime, and suggests a closure where applicable.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.