Early/late bound lifetime coercion produces a confusing diagnostic
Open
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
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):
- Introduce the concept of early/late bounds so that the problem becomes searchable.
- 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. - Suggest using a closure to convert from early bounds to late bounds. (I'm not sure if this always works?)
- Don't reuse
'aand'bin the diagnostic. Using the existing lifetime names instead of "shadowing" the definitions would be nice. This introduced more confusion since I commonly used'aand'bwhen 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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