Skip to content
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

Higher-ranked closure error messages should avoid "anonymous lifetime #2 defined on the body" #45983

Closed
arielb1 opened this issue Nov 14, 2017 · 10 comments · Fixed by #47144
Closed
Labels
A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. WG-diagnostics Working group: Diagnostics

Comments

@arielb1
Copy link
Contributor

arielb1 commented Nov 14, 2017

When a higher-ranked closure is misused, as in:

fn give_any<F: for<'r> FnOnce(&'r ())>(f: F) {
    f(&());
}

fn main() {
    let x = None;
    give_any(|y| x = Some(y));
}

The compiler gives an obscure error message:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 7:14...
 --> src/main.rs:7:14
  |
7 |     give_any(|y| x = Some(y));
  |              ^^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected &(), found &())
 --> src/main.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
note: but, the lifetime must be valid for the expression at 7:14...
 --> src/main.rs:7:14
  |
7 |     give_any(|y| x = Some(y));
  |              ^^^^^^^^^^^^^^^
note: ...so type `[closure@src/main.rs:7:14: 7:29 x:&mut std::option::Option<&()>]` of expression is valid during the expression
 --> src/main.rs:7:14
  |
7 |     give_any(|y| x = Some(y));
  |              ^^^^^^^^^^^^^^^

error: aborting due to previous error

The problem isn't that complicated: give_any was declared as taking F: for<'r> FnOnce(&'r ()), so it can pass a y can be of type &'α () for any lifetime , which might not live long enough to be assigned to x.

However, the error message talks about an "anonymous lifetime #2 defined on the body", which is not something someone can understand without knowing the compiler. It should at least mention the problem argument.

cc @cengizio @estebank

@arielb1 arielb1 added A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints WG-diagnostics Working group: Diagnostics E-needs-mentor labels Nov 14, 2017
@arielb1
Copy link
Contributor Author

arielb1 commented Nov 14, 2017

cc @nikomatsakis

@cengiz-io
Copy link
Contributor

I'd like to work on this if noone is already interested.

@nikomatsakis

@TimNN TimNN added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Nov 14, 2017
@nikomatsakis
Copy link
Contributor

So this specific case:

  • If we see one bound that is a free region scoped to a closure expression;
  • and the other bound is outside the closure expression,

probably we should say something like

error[E0495]: borrowed data cannot be used outside closure
 --> src/main.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^ data from the parameter `y` is only usable inside the closure
  |

It should be pretty easy to use the routines that @gaurikholkar added to identify which parameter the bound region comes from, at least much of the time (i.e., y). We probably won't always be able to say for sure, but that's ok, we can fallback to "data is only usable inside the closure" or something.

Not perfect. Thoughts on how to improve? I want to say "cannot escape the closure" but that sounds like jargon.

Maybe "data is only borrowed during the closure body"?

@estebank
Copy link
Contributor

I wonder if it is worth it to also point at the closure as well:

error[E0495]: borrowed data cannot be used outside closure
 --> src/main.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |              -------------^-
  |              |            |
  |              |            data from the parameter `y` is only usable inside its closure
  |              ...this closure

@nikomatsakis
Copy link
Contributor

@estebank do we have some way to just highlight the |y| part?

@estebank
Copy link
Contributor

@nikomatsakis I believe there was a recent change that either added or merely used that, so it is possible.

@nikomatsakis
Copy link
Contributor

@estebank if so, I'd be in favor. Otherwise, we're just setting ourselves up for the "overlapping spans" problem.

@cengiz-io
Copy link
Contributor

@estebank @nikomatsakis I was pretty much away from keyboard for the last couple days. Finally I'm getting into the issue

@estebank
Copy link
Contributor

I believe #45927 might be the PR that I'd seen pointing at the closure's arg block.

@estebank
Copy link
Contributor

estebank commented Jan 2, 2018

The current output is still sub-par, but it is slightly clearer on what is going on:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> src/main.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 7:14...
 --> src/main.rs:7:14
  |
7 |     give_any(|y| x = Some(y));
  |              ^^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected &(), found &())
 --> src/main.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
note: but, the lifetime must be valid for the block suffix following statement 0 at 6:5...
 --> src/main.rs:6:5
  |
6 | /     let x = None;
7 | |     give_any(|y| x = Some(y));
8 | | }
  | |_^
note: ...so that variable is valid at time of its declaration
 --> src/main.rs:6:9
  |
6 |     let x = None;
  |         ^

I now think that the output should point at the closure, the outside binding being affected and its declaration:

error[E0495]: borrowed data cannot be used outside closure
 --> src/main.rs:7:27
  |
6 |     let x = None;
  |         - binding declared outside of the closure
7 |     give_any(|y| x = Some(y));
  |              --- -        ^ data from the parameter `y` is only usable inside its closure
  |              |   |
  |              |   you're assigning to this binding declared outside of the closure
  |              the closure you can't escape

bors added a commit that referenced this issue Jan 22, 2018
Custom error when moving arg outside of its closure

When given the following code:

```rust
fn give_any<F: for<'r> FnOnce(&'r ())>(f: F) {
    f(&());
}

fn main() {
    let mut x = None;
    give_any(|y| x = Some(y));
}
```

provide a custom error:

```
error: borrowed data cannot be moved outside of its closure
 --> file.rs:7:27
  |
6 |     let mut x = None;
  |         ----- borrowed data cannot be moved into here...
7 |     give_any(|y| x = Some(y));
  |              ---          ^ cannot be moved outside of its closure
  |              |
  |              ...because it cannot outlive this closure
```

instead of the generic lifetime error:

```
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
 --> file.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
  |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 7:14...
 --> file.rs:7:14
  |
7 |     give_any(|y| x = Some(y));
  |              ^^^^^^^^^^^^^^^
note: ...so that expression is assignable (expected &(), found &())
 --> file.rs:7:27
  |
7 |     give_any(|y| x = Some(y));
  |                           ^
note: but, the lifetime must be valid for the block suffix following statement 0 at 6:5...
 --> file.rs:6:5
  |
6 | /     let mut x = None;
7 | |     give_any(|y| x = Some(y));
8 | | }
  | |_^
note: ...so that variable is valid at time of its declaration
 --> file.rs:6:9
  |
6 |     let mut x = None;
  |         ^^^^^
```

Fix #45983.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. WG-diagnostics Working group: Diagnostics
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants