-
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
MIR-borrowck: emit "foo
does not live long enough" instead of borrow errors
#45989
Conversation
@davidtwco Awesome! I didn't get a chance to look closely, but I wanted to point you at this gist: https://gist.github.com/nikomatsakis/a0d5dee0bd3c4d4f0442914a9abdf74c It describes how to update the existing AST borrow check tests so that we can simultaneously test: (a) the current behavior on stable and (b) the behavior with MIR-borrowck enabled and compare the two. It'd be helpful if you update a test or two to show how the current PR behaves. |
@nikomatsakis That's great. I tested it locally running the same example from the issue - I'll update some tests tomorrow based on that gist. |
@nikomatsakis I updated the test given as an example in the issue to reflect the expected result once this PR is fully implemented. For now it demonstrates the deduplication. This is the updated test. I'd be happy to update any other tests that this PR would fix. |
src/test/compile-fail/issue-36082.rs
Outdated
//~^ ERROR borrowed value does not live long enough | ||
let val: &_ = x.borrow().0; //[ast]~ ERROR borrowed value does not live long enough [E0597] | ||
//[mir]~^ ERROR borrowed value does not live long enough (Ast) [E0597] | ||
//[mir]~^ ERROR borrowed value does not live long enough (Mir) [E0597] |
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 needs to be //[mir]~|
, I believe.
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.
Resolved this, my bad.
src/test/compile-fail/issue-36082.rs
Outdated
//~| temporary value dropped here while still borrowed | ||
//~| temporary value created here | ||
//~| consider using a `let` binding to increase its lifetime | ||
println!("{}", val); | ||
} | ||
//~^ temporary value needs to live until here | ||
//[ast]~^ temporary value needs to live until here | ||
//[mir]~^ temporary value needs to live 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.
Same 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.
Resolved this, my bad.
src/librustc_mir/borrow_check.rs
Outdated
_ => false, | ||
}; | ||
|
||
if !has_storage_drop_or_dead_error_reported { |
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 @arielb1 pointed out that there is a risk here. The calls to access_lvalue
are not guaranteed to report an error -- and, in fact, DROP will report errors in a broader set of conditions than StorageDead
(I think strictly broader). So it would be better to populate the map (and hence skip the latter check) only if an error was actually reported.
What @arielb1 suggested was issuing precisely the same error message in both cases, and letting rustc deduplicate (which it will do). That's an option, though I don't think it's the best path, since I think that the error messages should perhaps be different between the two cases.
Alternatively, we could make access_lvalue
return a boolean or something, indicating whether an error was reported, and then do:
let has_storage_drop_or_dead_error_reported = self.storage_drop_or_dead_error_reported.contains_key(...);
let error_reported;
if has_storage_drop_or_dead_error_reported { error_reported = false; } else {
if moves_by_default { error_reported = self.access_lvalue(..); } else { ... }
}
if error_reported { self.storage_drop_or_dead_error_reported.insert(local); }
It would also be good to comment about this. =)
It's hard to come up with a test case that would necesarily show off this problem though -- specifically, Drop
will always come before StorageDead
, and hence I imagine we'll visit it first, so the bug won't show itself. But I wouldn't want to rely on that.
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've modified what was there based on this, should be fine now.
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.
Looks great! Left a few nits.
src/librustc_mir/borrow_check.rs
Outdated
|
||
let (sd, rw) = kind; | ||
|
||
// Check permissions | ||
self.check_access_permissions(lvalue_span, rw); | ||
|
||
let mut has_storage_drop_or_dead_error_reported = false; |
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: we should probably just call this error_reported
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.
Resolved this, thanks.
src/librustc_mir/borrow_check.rs
Outdated
@@ -444,13 +455,14 @@ impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> | |||
context: Context, | |||
lvalue_span: (&Lvalue<'tcx>, Span), | |||
kind: (ShallowOrDeep, ReadOrWrite), | |||
flow_state: &InProgress<'b, 'gcx, 'tcx>) { | |||
flow_state: &InProgress<'b, 'gcx, 'tcx>) -> bool { |
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.
Can you add a comment to the function indicating what the return value means?
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 see there is no existing comment, either! How about something like this:
Checks an access to the given lvalue to see if it is allowed. Examines the set of borrows that are in scope, as well as which paths have been initialized, to ensure that (a) the lvalue is initialized and (b) it is not borrowed in some way that would prevent this access.
Returns true if an error is reported, false otherwise.
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.
Resolved this, thanks.
So, in terms of how to change the error message. The current error message arises from this call here: Note that we pass That will then flow down to this case: You can see that The other cases invoke similar helpers, but they use shallow wrappers like |
Rebased and producing the correct error message now. |
☔ The latest upstream changes (presumably #45825) made this pull request unmergeable. Please resolve the merge conflicts. |
Rebased on top of the conflicting changes and to squash my own commits. Error message now looks more or less what we're seeing with the @bors r=nikomatsakis Edit: I have no idea how bors works apparently. |
@davidtwco: 🔑 Insufficient privileges: Not in reviewers |
1 similar comment
@davidtwco: 🔑 Insufficient privileges: Not in reviewers |
@bors r+ |
📋 Looks like this PR is still in progress, ignoring approval |
foo
does not live long enough" instead of borrow errors foo
does not live long enough" instead of borrow errors
…s from the same line in other tests.
Pushed a commit that fixes the error on the Travis build in an unrelated test. Should be good now, apologies. |
@bors r+ |
📌 Commit a1d55be has been approved by |
MIR-borrowck: emit "`foo` does not live long enough" instead of borrow errors Fixes #45360. As of writing, contains deduplication of existing errors. r? @nikomatsakis
☀️ Test successful - status-appveyor, status-travis |
Fixes #45360. As of writing, contains deduplication of existing errors.
r? @nikomatsakis