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

#[feature(nll)] produces unintelligible error message in corner case with invariance #67007

Closed
Aaron1011 opened this issue Dec 4, 2019 · 0 comments · Fixed by #89501
Closed
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. NLL-diagnostics Working towards the "diagnostic parity" goal T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@Aaron1011
Copy link
Member

While working on a diagnostics improvement for the compiler, I came across an essentially unreadable error. The following (minimized) code:

#![feature(nll)] 

// Covariant over 'a, invariant over 'tcx
struct FnCtxt<'a, 'tcx: 'a>(&'a (), *mut &'tcx ());

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    fn use_it(&self, _: &'tcx ()) {}
}

struct Consumer<'tcx>(&'tcx ());

impl<'tcx> Consumer<'tcx> {
    fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
        let other = self.use_fcx(fcx);
        fcx.use_it(other);
    }
    
    fn use_fcx<'a>(&self, _: &FnCtxt<'a, 'tcx>) -> &'a () {
        &()
    }
}

produces this error:

error[E0521]: borrowed data escapes outside of function
  --> src/lib.rs:14:21
   |
13 |     fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
   |                       -----  --- `fcx` is a reference that is only valid in the function body
   |                       |
   |                       `self` is declared here, outside of the function body
14 |         let other = self.use_fcx(fcx);
   |                     ^^^^^^^^^^^^^^^^^ `fcx` escapes the function body here
   |
   = help: consider adding the following bound: `'a: 'tcx`

There are several problems with this error message:

  1. The span is very unhelpful. Commenting out the call to fcx.use_it(other); causes this code to compile (I think due to the fact that the lifetime of other is no longer required to outlive tcx). However, the error message points to a completely different line, which is not really the source of the problem.
  2. The 'escapes outside of function' terminology is very misleading. In general, there's nothing wrong with passing a reference to another function. The problem is that that by passing fcx to use_fcx, we (indirectly) end up constraining a.
  3. The only place where the error message refers to any lifetimes is in the suggestion to add 'a: 'tcx. Without reading the hint, you might not even realize that the named lifetimes are involved in any way.
  4. The error depends on the invariance of the 'tcx lifetime (caused by the raw pointer, which is invariant over the pointee type). I discovered this through blind luck, as I've seen variance-related errors before. If someone has never even heard of variance, it would be virtually impossible for them to discover this - the invariance of a lifetime may be caused by the lifetime being passed to a deeply nested invariant type.

Removing #![feature(nll)] makes the error marginally better:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
  --> src/lib.rs:14:26
   |
14 |         let other = self.use_fcx(fcx);
   |                          ^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the method body at 13:19...
  --> src/lib.rs:13:19
   |
13 |     fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
   |                   ^^
note: ...but the lifetime must also be valid for the lifetime `'tcx` as defined on the impl at 12:6...
  --> src/lib.rs:12:6
   |
12 | impl<'tcx> Consumer<'tcx> {
   |      ^^^^
   = note: ...so that the expression is assignable:
           expected &FnCtxt<'_, '_>
              found &FnCtxt<'a, 'tcx>

Here, we at least know that 'a and 'tcx are involved. However, the span is still bad - there's no indication that the call to fcx.use_it(other) is involved at all. The note 'so that the expression is assignable' also lacks a span entirely, making it difficult to tell what it actually means.

For some reason, removing the 'tcx: 'a requirement from the FnCtxt definition (e.g. changing it to just 'tcx) results in a better span with #![feature(nll)]:

error[E0521]: borrowed data escapes outside of function
  --> src/lib.rs:15:9
   |
13 |     fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
   |                       -----  --- `fcx` is a reference that is only valid in the function body
   |                       |
   |                       `self` is declared here, outside of the function body
14 |         let other = self.use_fcx(fcx);
15 |         fcx.use_it(other);
   |         ^^^^^^^^^^^^^^^^^ `fcx` escapes the function body here
   |
   = help: consider adding the following bound: `'a: 'tcx`

error: aborting due to previous error

but the error is unchanged when #![feature(nll)] is not enabled.

cc #62953

@Aaron1011 Aaron1011 changed the title #[feature(nll)] produces unintelligble error message in corner case with invariance #[feature(nll)] produces unintelligible error message in corner case with invariance Dec 4, 2019
@Centril Centril added A-diagnostics Area: Messages for errors, warnings, and lints A-NLL Area: Non-lexical lifetimes (NLL) NLL-diagnostics Working towards the "diagnostic parity" goal T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. D-confusing Diagnostics: Confusing error or lint that should be reworked. labels Dec 4, 2019
@JohnTitor JohnTitor added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Dec 8, 2019
Manishearth added a commit to Manishearth/rust that referenced this issue Oct 6, 2021
…davidtwco

Note specific regions involved in 'borrowed data escapes' error

Fixes rust-lang#67007

Currently, a 'borrowed data escapes' error does not mention
the specific lifetime involved (except indirectly through a suggestion
about adding a lifetime bound). We now explain the specific lifetime
relationship that failed to hold, which improves otherwise vague
error messages.
Manishearth added a commit to Manishearth/rust that referenced this issue Oct 6, 2021
…davidtwco

Note specific regions involved in 'borrowed data escapes' error

Fixes rust-lang#67007

Currently, a 'borrowed data escapes' error does not mention
the specific lifetime involved (except indirectly through a suggestion
about adding a lifetime bound). We now explain the specific lifetime
relationship that failed to hold, which improves otherwise vague
error messages.
@bors bors closed this as completed in 3c974ad Oct 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-NLL Area: Non-lexical lifetimes (NLL) C-enhancement Category: An issue proposing an enhancement or a PR with one. D-confusing Diagnostics: Confusing error or lint that should be reworked. NLL-diagnostics Working towards the "diagnostic parity" goal T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants