Skip to content

Inferred struct lifetime bounds result in bad diagnostics around HRTBs #87241

Open
@mcy

Description

@mcy

Given the following code: https://godbolt.org/z/he33YcxPM

struct C<'a, 'b>(&'a &'b ());

fn foo<'a>(f: impl FnOnce(&mut C<'_, 'a>)) {}
fn bar<'a>(f: impl FnOnce(&mut C<'_, 'a>)) {
    foo(|x: &mut C<'_, 'a>| f(x))
}

The current output is the default borrow check diagnostic.

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in generic type due to conflicting requirements
 --> <source>:5:29
  |
5 |     foo(|x: &mut C<'_, 'a>| f(x))
  |                             ^^^^
  |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the function body at 4:8...
 --> <source>:4:8
  |
4 | fn bar<'a>(f: impl FnOnce(&mut C<'_, 'a>)) {
  |        ^^
note: ...so that the declared lifetime parameter bounds are satisfied
 --> <source>:5:29
  |
5 |     foo(|x: &mut C<'_, 'a>| f(x))
  |                             ^^^^
note: but, the lifetime must be valid for the anonymous lifetime #2 defined on the body at 5:9...
 --> <source>:5:9
  |
5 |     foo(|x: &mut C<'_, 'a>| f(x))
  |         ^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the expression is assignable
 --> <source>:5:31
  |
5 |     foo(|x: &mut C<'_, 'a>| f(x))
  |                               ^
  = note: expected `&mut C<'_, 'a>`
             found `&mut C<'_, '_>`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
Compiler returned: 1

This code can be made to compile (ht @Manishearth) by observing that the compiler infers 'b: 'a for C<'a, 'b>:

struct C<'a, 'b>(&'a &'b ());

fn foo<'b, 'a: 'b>(f: impl FnOnce(&mut C<'b, 'a>)) {}
fn bar<'b, 'a: 'b>(f: impl FnOnce(&mut C<'b, 'a>)) {
    foo(|x: &mut C<'b, 'a>| f(x))
}

It would be useful for rustc to suggest this fix, but it looks a bit hard to find. Either way, this one puzzled me for several hours until I poked Manish.

It is unclear if this is a bug, but it seems incorrect for rustc to accept the original code for borrow check at all, since x types at for<'1, '2> C<'1, '2> in the closure argument, not for<'1> C<'1, 'a>, as the user has written. In particular, the following compiles, but should probably be rejected:

struct C<'a, 'b>(&'a &'b ());

fn foo<'a>(f: impl FnOnce(&mut C<'_, 'a>)) {}
fn bar<'a>(f: impl FnOnce(&mut C<'_, 'a>)) {
    foo(|x: &mut C<'_, 'a>| {})
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-borrow-checkerArea: The borrow checkerA-closuresArea: Closures (`|…| { … }`)A-diagnosticsArea: Messages for errors, warnings, and lintsA-higher-rankedArea: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs)A-lifetimesArea: Lifetimes / regionsD-confusingDiagnostics: Confusing error or lint that should be reworked.D-newcomer-roadblockDiagnostics: Confusing error or lint; hard to understand for new users.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