Skip to content

When a variable is incorrectly used in a closure, error points to the source of the borrow, not the variable #81877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kornelski opened this issue Feb 8, 2021 · 0 comments
Labels
A-borrow-checker Area: The borrow checker A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions C-enhancement Category: An issue proposing an enhancement or a PR with one. D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@kornelski
Copy link
Contributor

kornelski commented Feb 8, 2021

Unhelpful diagnostic. It's technically correct, but points to a variable that isn't the direct cause of the problem, obscuring the actual issue.

playground

fn indirect(source: &str) {
    let actual_variable = &source[..];
    
    std::thread::spawn(move || {
        drop(actual_variable);
    });
}

error[E0621]: explicit lifetime required in the type of source
fn indirect(source: &str) {
| ---- help: add explicit lifetime 'static to the type of source: `&'static

The problem here (apart from #69350) is that the source argument is not used directly in the closure. Searching closure's code for the source variable won't find it. let source = source.to_owned() will not help. In non-trivial functions this diagnostic is very puzzling, because it appears to complain about an unused/unrelated variable.

Fixing this problem requires user to manually find and trace all places where the source arg has been borrowed, which could end up being in the closure. This may be difficult when borrowing is obscured by function calls or match ergonomics.

The error should point to actual_variable. Instead of tracing the lifetime all the way to its origin, it would be more helpful to trace it to the binding that ended up captured by the closure:

fn indirect(source: &str) {
    let actual_variable = &source[..];
        ^^^^^^^^^^^^^^^
...
    std::thread::spawn(move || {
        drop(actual_variable);
             ^^^^^^^^^^^^^^^

Another case:

fn indirect(source: i32) {
    let foo = obscure(&source);
    let actual_variable = foo;
    
    std::thread::spawn(move || {
        drop(actual_variable);
    });
}

fn obscure(x: &i32) -> &i32 { x }
2 |     let foo = obscure(&source);
  |               --------^^^^^^^-
  |               |       |
  |               |       borrowed value does not live long enough
  |               argument requires that `source` is borrowed for `'static`

complains about the source and the call to obscure, which is wrong on both fronts, because the problem is about actual_variable and the call to spawn.

@jonas-schievink jonas-schievink added A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. labels Feb 8, 2021
@estebank estebank added A-borrow-checker Area: The borrow checker A-closures Area: Closures (`|…| { … }`) A-lifetimes Area: Lifetimes / regions D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-borrow-checker Area: The borrow checker A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions C-enhancement Category: An issue proposing an enhancement or a PR with one. D-papercut Diagnostics: An error or lint that needs small tweaks. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants