Skip to content

Poor error message for closure expression that does not infer required higher-ranked lifetime #96255

@lilyball

Description

@lilyball

If I have a method that takes a generic parameter constrained by a trait, and the trait is implemented for F: for<'a> FnOnce(Foo<'a>) -> Bar<'a>1, then calling the method with a closure expression fails to infer the higher-ranked lifetime and produces a confusing error.

Given the following code: (play.rust-lang.org)

use std::marker::PhantomData;
struct Value<'a> {
    _marker: PhantomData<&'a ()>,
}
impl<'a> Value<'a> {
    fn new() -> Self {
        Self {
            _marker: PhantomData,
        }
    }
}
trait Callable {
    fn call<'a>(self, s: Value<'a>) -> Value<'a>;
}

impl<F> Callable for F
where
    F: for<'a> FnOnce(Value<'a>) -> Value<'a>,
{
    fn call<'a>(self, s: Value<'a>) -> Value<'a> {
        self(s)
    }
}

fn take_callable<C: Callable>(_: C) {}

fn main() {
    take_callable(|v: Value| Value::new());
}

The current output is:

error[E0308]: mismatched types
  --> src/main.rs:28:5
   |
28 |     take_callable(|v: Value| Value::new());
   |     ^^^^^^^^^^^^^ lifetime mismatch
   |
   = note: expected struct `Value<'_>`
              found struct `Value<'a>`
note: the lifetime requirement is introduced here
  --> src/main.rs:25:21
   |
25 | fn take_callable<C: Callable>(_: C) {}
   |                     ^^^^^^^^

For more information about this error, try `rustc --explain E0308`.

This error does not mention higher-ranked lifetimes, does not explain where the 'a came from or the '_ (and if I use a type like &str instead of Value then it doesn't even print the '_ lifetime), flips the "expected" and "found" types, points at the C: Callable bound as the lifetime requirement despite no lifetime being visible on that bound, and does not even highlight the closure expression's span. If I modify the code to return v instead of a Value<'static> then it does add the following note:

note: the anonymous lifetime #1 defined here doesn't meet the lifetime requirements
  --> src/main.rs:28:19
   |
28 |     take_callable(|v: Value| v);
   |                   ^^^^^^^^^^^^

This is a slight improvement but is still confusing.

This is especially confusing as having a method that is bounded by FnOnce directly avoids this error, which I'm told is a special-case in the compiler for inferring higher-ranked lifetimes on closure expressions.

My expectation here is the error should mention higher-ranked lifetimes somehow, preferably both specifying for<'a> Value<'a> and including a note that explains that closure expressions do not infer higher-ranked lifetimes when the generic bound does not specify the higher-ranked lifetime directly.

Footnotes

  1. Foo<'a> and Bar<'a> may also be &'a T.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-closuresArea: Closures (`|…| { … }`)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