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

MIR-borrowck: emit "foo does not live long enough" instead of borrow errors #45989

Merged
merged 5 commits into from
Nov 18, 2017
Merged

MIR-borrowck: emit "foo does not live long enough" instead of borrow errors #45989

merged 5 commits into from
Nov 18, 2017

Conversation

davidtwco
Copy link
Member

@davidtwco davidtwco commented Nov 14, 2017

Fixes #45360. As of writing, contains deduplication of existing errors.

r? @nikomatsakis

@kennytm kennytm added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Nov 14, 2017
@nikomatsakis
Copy link
Contributor

@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.

@davidtwco
Copy link
Member Author

davidtwco commented Nov 14, 2017

@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.

@davidtwco
Copy link
Member Author

@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.

//~^ 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]
Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved this, my bad.

//~| 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved this, my bad.

_ => false,
};

if !has_storage_drop_or_dead_error_reported {
Copy link
Contributor

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.

Copy link
Member Author

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.

Copy link
Contributor

@nikomatsakis nikomatsakis left a 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.


let (sd, rw) = kind;

// Check permissions
self.check_access_permissions(lvalue_span, rw);

let mut has_storage_drop_or_dead_error_reported = false;
Copy link
Contributor

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved this, thanks.

@@ -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 {
Copy link
Contributor

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?

Copy link
Contributor

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved this, thanks.

@nikomatsakis
Copy link
Contributor

So, in terms of how to change the error message. The current error message arises from this call here:

https://github.com/davidtwco/rust/blob/d906a6e94dfaac3a1344db96237d59bb9285bf5b/src/librustc_mir/borrow_check.rs#L637-L638

Note that we pass Write(WriteKind::Move). That seems bogus. Per @arielb1's suggestion, we should rename WriteKind::StorageDead to WriteKind::StorageDeadOrDrop and pass that here, I think. (Note that we sometimes want Move, we'll have to look at the consume_via_drop argument to decide.)

That will then flow down to this case:

https://github.com/davidtwco/rust/blob/d906a6e94dfaac3a1344db96237d59bb9285bf5b/src/librustc_mir/borrow_check.rs#L492-L514

You can see that WriteKind::StorageDead (which we will have renamed) is treated the same as Mutate. We want to give it its own case. Then we want to invoke this helper function:

https://github.com/davidtwco/rust/blob/d906a6e94dfaac3a1344db96237d59bb9285bf5b/src/librustc_mir/util/borrowck_errors.rs#L375-L384

The other cases invoke similar helpers, but they use shallow wrappers like report_move_out_while_borrowed so we probably want to write one of those too.

@davidtwco
Copy link
Member Author

Rebased and producing the correct error message now.

@bors
Copy link
Contributor

bors commented Nov 16, 2017

☔ The latest upstream changes (presumably #45825) made this pull request unmergeable. Please resolve the merge conflicts.

@davidtwco
Copy link
Member Author

davidtwco commented Nov 17, 2017

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 Ast version, only difference is the symbols used and I can't see a way around that. Other than that, I think this is done.

@bors r=nikomatsakis

Edit: I have no idea how bors works apparently.

@bors
Copy link
Contributor

bors commented Nov 17, 2017

@davidtwco: 🔑 Insufficient privileges: Not in reviewers

1 similar comment
@bors
Copy link
Contributor

bors commented Nov 17, 2017

@davidtwco: 🔑 Insufficient privileges: Not in reviewers

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Nov 17, 2017

📋 Looks like this PR is still in progress, ignoring approval

@davidtwco davidtwco changed the title WIP: MIR-borrowck: emit "foo does not live long enough" instead of borrow errors MIR-borrowck: emit "foo does not live long enough" instead of borrow errors Nov 17, 2017
@davidtwco
Copy link
Member Author

Pushed a commit that fixes the error on the Travis build in an unrelated test. Should be good now, apologies.

@nikomatsakis
Copy link
Contributor

@bors r+

@bors
Copy link
Contributor

bors commented Nov 17, 2017

📌 Commit a1d55be has been approved by nikomatsakis

@bors
Copy link
Contributor

bors commented Nov 18, 2017

⌛ Testing commit a1d55be with merge 79a1385...

bors added a commit that referenced this pull request Nov 18, 2017
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
@bors
Copy link
Contributor

bors commented Nov 18, 2017

☀️ Test successful - status-appveyor, status-travis
Approved by: nikomatsakis
Pushing 79a1385 to master...

@bors bors merged commit a1d55be into rust-lang:master Nov 18, 2017
@davidtwco davidtwco deleted the issue-45360 branch December 1, 2017 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants