-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
[NLL] Avoid borrowed value must be valid for lifetime '_#2r..." in errors #48592
Conversation
r? @pnkfelix (rust_highfive has picked a reviewer for you, use r? to override) |
ac09a40
to
dd187ca
Compare
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
#![feature(nll)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For now, can you add // compile-flags: -Znll-dump-cause
?
&v | ||
//~^ ERROR `v` does not live long enough [E0597] | ||
}); | ||
println!("{:?}", x); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current output for this test:
lunch-box. rustc +nightly borrowed-local-error.rs -Znll-dump-cause
error[E0597]: `v` does not live long enough
--> borrowed-local-error.rs:20:9
|
18 | let x = gimme({
| _____________-
19 | | let v = (22,);
20 | | &v
| | ^^ borrowed value does not live long enough
21 | | //~^ ERROR `v` does not live long enough [E0597]
22 | | });
| |_____-- borrow later used here
| |
| borrowed value only lives until here
|
= note: borrowed value must be valid for lifetime '_#2r...
error: aborting due to previous error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this output is pretty terrible, but mostly because of the multi-line highlight and the extra text about must be valid for lifetime '_#2r
.
However, the "borrowed value only lives until here" and "borrow later used here" are also not especially helpful.
@estebank -- how do you normally deal with those btw? I remember it's a bit of a case-by-case basis; I think in this case the span comes from the MIR. Are there some helper functions we can invoke to try and reduce the span to a single line?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I think it should look like:
error[E0597]: `v` does not live long enough
--> borrowed-local-error.rs:20:9
|
18 | let x = gimme({
| ----- borrow later used here, during the call
19 | let v = (22,);
20 | &v
| ^^ borrowed value does not live long enough
21 | });
| - borrowed value only lives until here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that to make this happen, we need to:
(a) identify that the later use is a call
and 'shorten' the highlight to just the function name and/or method name -- @estebank may be able to help with that point
(b) modify the text, since it is due to a function call, to say "during the call"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also:
borrowed value only lives until here
that is ungreat. I think it should be more of action, like "v
is dropped here". So let me take another crack at this. I also think the "sequence" here is perhaps a bit confusing, so i'm going to try adding numbers:
error[E0597]: `v` does not live long enough
--> borrowed-local-error.rs:20:9
|
18 | let x = gimme({
| ----- (3) But the reference is later used here, during the call
19 | let v = (22,);
20 | &v
| ^^ (1) First, `v` is borrowed here, creating a reference
21 | });
| - (2) Then, `v` is dropped here, at the end of the block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all of this is feasible: for example, when the message is about something going out of scope, and that thing is a local variable (v
) rather than a temporary, we can say "at the end of the block", since that is always when local variables go out of scope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that the order things happen is meaningful to providing a good explanation, we should either use span notes:
error[E0597]: `v` does not live long enough
--> borrowed-local-error.rs:20:9
|
20 | &v
| ^^ borrowed here, creating a reference
21 | });
| - `v` is dropped here, at the end of the block
|
note: ...but the reference must live long enough to be used here, during the call
|
18 | let x = gimme({
| -----
or change the text to be "orderless":
error[E0597]: `v` does not live long enough
--> borrowed-local-error.rs:20:9
|
18 | let x = gimme({
| ----- `v` must live long enough to be used in this call, but it doesn't
19 | let v = (22,);
20 | &v
| ^^ borrowed here, creating a reference
21 | });
| - `v` is dropped here, at the end of the block
|
As for wether there's a method to extract the function call, there isn't one now and in all errors where we point at the function call itself we've kept the span around since the AST. I'd have to dig in the current code to see if we do have it available already, but I don't think it should be that hard to find/add.
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
current output for this test:
lunch-box. rustc +nightly borrowed-temporary-error.rs -Znll-dump-cause
error[E0597]: borrowed value does not live long enough
--> borrowed-temporary-error.rs:20:10
|
20 | &(v,)
| ^^^^ temporary value does not live long enough
21 | //~^ ERROR borrowed value does not live long enough [E0597]
22 | });
| - temporary value only lives until here
23 | println!("{:?}", x);
| - borrow later used here
|
= note: borrowed value must be valid for lifetime '_#2r...
error: aborting due to previous error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems pretty good to me, modulo the borrowed value must be valid for lifetime '_#2r...
, which I know you already removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should only be showing the borrowed value must be valid for lifetime '_#2r
for anon lifetimes that cover an entire block and named lifetimes. That shouldn't be that hard to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand. I don't think there is any circumstances in which we should show the text '_#2r
. I guess you are saying that a sentence of this kind only makes sense for either an entire block or named lifetimes?
In that case, I both agree and disagree: it probably makes sense for named lifetimes, but I don't think it makes sense for blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mistyped and you interpreted me correctly. Meant to write borrowed value must be valid for {}
for the current cases (entire block or named).
For named we currently only point at the signature of the scope that defines it, while for anonymous we point at the entire block on its own span note. We could change this to point at the end where it must live, but that doesn't seem to me entirely different to what we do now, and would lose some context... Of course, this only applies to LL.
I think the best bet is still to continue handling specific cases with more targeted suggestions/explanations, as gaurikholkar was doing. These can get increasingly niche, but those are the cases where rustc
holding your hand is most impactful and can be a great teachable opportunity.
fn foo<'a>(x: &'a (u32,)) -> &'a u32 { | ||
let v = 22; | ||
gimme(&(v,)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current output for this test:
lunch-box. rustc +nightly borrowed-universal-error.rs -Znll-dump-cause
error[E0597]: borrowed value does not live long enough
--> borrowed-universal-error.rs:18:12
|
18 | gimme(&(v,))
| ^^^^ temporary value does not live long enough
19 | }
| - temporary value only lives until here
|
= note: borrowed value must be valid for lifetime '_#3r...
= note: because of an outlives relation created at `bb0[11]`
because of an outlives relation created at `bb0[12]`
because of an outlives relation created at `bb0[12]`
because of an outlives relation created at `bb0[12]`
because of an outlives relation created at `bb2[0]`
because of an outlives relation created at `bb2[1]`
because of an outlives relation created at `bb2[1]`
because of an outlives relation created at `bb0[0]`
because `'_#1r` is universally quantified
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is clearly missing some mention of 'a
. Compare to the AST checker:
lunch-box. rustc +nightly borrowed-universal-error.rs -Znll-dump-cause
error[E0597]: borrowed value does not live long enough
--> borrowed-universal-error.rs:17:12
|
17 | gimme(&(v,))
| ^^^^ temporary value does not live long enough
18 | }
| - temporary value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 15:1...
--> borrowed-universal-error.rs:15:1
|
15 | fn foo<'a>(x: &'a (u32,)) -> &'a u32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If we invoke note_and_explain_region
from explain_why_borrow_contains_point
in the case of a LiveUniversal
root cause, then we would get output similar to the AST-checker.
let v = 22; | ||
let r = gimme(&(v,)); | ||
r | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pushed one more example. Current output:
> rustc +nightly borrowed-universal-error-2.rs -Znll-dump-cause
error[E0597]: borrowed value does not live long enough
--> borrowed-universal-error-2.rs:18:20
|
18 | let r = gimme(&(v,));
| ^^^^ - temporary value only lives until here
| |
| temporary value does not live long enough
19 | r
| - borrow later used here
|
= note: borrowed value must be valid for lifetime '_#3r...
error: aborting due to previous error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops, this example is not that interesting I guess. Well, it is interesting, but not the reason I initially thought.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it would be an example where we are returning something that has to have the lifetime 'a
, like the borrow-universal-error.rs
, but in fact we get the error earlier on, because of the temporary we've created. Let me revise it a bit.
fn foo<'a>(x: &'a (u32,)) -> &'a u32 { | ||
let v = 22; | ||
let r = gimme(&(v,)); | ||
r | ||
&v |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Output now:
lunch-box. rustc +nightly borrowed-universal-error-2.rs -Znll-dump-cause
error[E0597]: `v` does not live long enough
--> borrowed-universal-error-2.rs:16:5
|
16 | &v
| ^^ borrowed value does not live long enough
17 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for lifetime '_#3r...
= note: because of an outlives relation created at `bb0[4]`
because of an outlives relation created at `bb0[5]`
because of an outlives relation created at `bb0[5]`
because of an outlives relation created at `bb0[0]`
because `'_#1r` is universally quantified
error: aborting due to previous error
Still ungreat.
fde93fe
to
2b95e34
Compare
3100389
to
71dc3ff
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good @spastorino
Some(hir_map::NodeItem(it)) => item_scope_tag(&it), | ||
Some(hir_map::NodeTraitItem(it)) => trait_item_scope_tag(&it), | ||
Some(hir_map::NodeImplItem(it)) => impl_item_scope_tag(&it), | ||
Some(hir_map::NodeItem(it)) => TyCtxt::item_scope_tag(&it), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: in cases like these, you can write Self::
, which I personally prefer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
71dc3ff
to
99c42dc
Compare
@bors r+ |
📌 Commit 99c42dc has been approved by |
[NLL] Avoid borrowed value must be valid for lifetime '_#2r..." in errors Closes #48428 - [x] If NLL is enabled, [do not invoke `note_and_explain_region`](#48428 (comment)) - [x] Modify `-Zdump-nll-cause` to not print [the overwhelming debug output here](https://github.com/rust-lang/rust/blob/master/src/librustc_mir/borrow_check/nll/region_infer/mod.rs#L1288-L1299). This way we should I believe at least get nice-ish output for [our original example](#48428 (comment)). - [x] Extend `explain_why_borrow_contains_point` to also work for "universal lifetimes" like the `'a` in [the example at the end of this comment](#48428 (comment)). - [ ] Figure out how to enable causal information all the time (but that is #46590).
☀️ Test successful - status-appveyor, status-travis |
Closes #48428
note_and_explain_region
-Zdump-nll-cause
to not print the overwhelming debug output here. This way we should I believe at least get nice-ish output for our original example.explain_why_borrow_contains_point
to also work for "universal lifetimes" like the'a
in the example at the end of this comment.