-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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: moves of prefixes invalidate uses too #45025
MIR-borrowck: moves of prefixes invalidate uses too #45025
Conversation
r? @eddyb (rust_highfive has picked a reviewer for you, use r? to override) |
let mut line1 = Line::default(); | ||
let _moved = line1.origin; | ||
let _ = line1.origin.x + 1; //[ast]~ ERROR use of collaterally moved value: `line1.origin.x` | ||
//[mir]~^ [E0382] |
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.
(yes, I do see that the error code here don't match. I also know that we're missing the "(Ast)" in the output from the ast-borrowcker when running under -Z borrowck-mir
. I want to fix both of those things, but it need not be in this PR.)
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.
(BTW I think this will be fixed by #45167)
Muchas gracias to @mikhail-m1 for narrowing this down to such a small example! That really helped me quickly identify the cause. |
@bors r+ |
📌 Commit 64cf5a6 has been approved by |
I'm not sure this is correct: // A move of any *supporting prefix* of `lvalue` interferes
// with an attempt to use `lvalue`. This is scenarios 1 and 2
// above; and we are looking only at the supporting prefixes
// because of scenario 5 above. You shouldn't be able to write: let x: &&_;
let y= &**x; @bors r- I think the reason this patch is needed is because the move-path code does not try to handle things behind (non- Of course, I should have added comments there when I first rewrote that code, but I wasn't sure how close MIR borrowck was then. |
r? @arielb1 |
@arielb1 wrote:
Just to be clear: Am I correct that the part of that paragraph that you object to is the "we are looking only at the supporting prefixes because of scenario 5 above", right? I.e., that you think there are reasons that we need to consider even the prefixes that are non-supporting, and that is why you have given your example of let x: &&_;
let y= &**x; Does all that sound correct? (I'm not arguing with the example. I'm just wrapping my head around where that case was meant to fall in my mental model of the analysis.) |
Right |
ab78005
to
10dbf16
Compare
After discussion with @arielb1, I am going to try to handle a generalization of the scenario he presented above. Namely, partial initialization like: let x: S; x.a = 0; use(&x.a); (where in general In parallel with this, @arielb1 is looking into amending the NLL RFC to include this. |
10dbf16
to
54c6ae4
Compare
src/librustc_mir/borrow_check.rs
Outdated
// A move of any shallow suffix of `lvalue` also interferes | ||
// with an attempt to use `lvalue`. This is scenario 3 above. | ||
// | ||
// (Distinct from previous loop because `lvalue` does not |
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: "previous loop" is confusing here. (The code has been internally refactored so that there is no longer a loop immediately present above, because its been factored into a helper method.)
src/librustc_mir/borrow_check.rs
Outdated
} | ||
} | ||
} | ||
|
||
fn move_path_closest_to(&mut self, |
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.
It would be helpful, I think, to explain what this does, and also to cover the cases in which it might return None
. Not having read into this code recently, it's sort of not obvious to me -- for example -- why the code above can do
if let Some(mpi) = ... {
report_error()
}
i.e., why is it that a None
return value means "no error" and not "definite 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.
good point. I will review (and perhaps add an error branch at the call-site when it returns None
)
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.
(one case that kept arising during development, which may or may not be relevant here, are paths that are built off of a static variable; we don't put entries in the MoveData
for those. But I've been musing that it would be better to be more explicit about the scenarios: i.e., if all the cases of None
end up being due to a static variable at the root, we should be able to signal that explicitly in a result here, and then the client will have better info about why its sound to not issue an error.)
src/librustc_mir/borrow_check.rs
Outdated
// find and report move(s) that could cause this to be uninitialized | ||
self.report_use_of_moved(context, desired_action, lvalue_span); | ||
return; // don't bother finding other problems. | ||
} | ||
} |
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.
e.g. here, it seems weird that it's ok to not have a path -- how do we know it's initialized then?
54c6ae4
to
32a381c
Compare
At this point I think the time has come to bite the bullet and generalize the MoveData to track even |
I wrote:
This may have been over-ambitious. In particular, cases like structs with destructors are ones where borrowck wants to map an lvalue like |
src/librustc_mir/borrow_check.rs
Outdated
// This is scenarios 1 and 2 above. | ||
|
||
debug!("check_if_path_is_moved part1 lvalue: {:?}", lvalue); | ||
match self.move_path_closest_to(lvalue, PrefixSet::Shallow) { |
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.
Why is this considering only the shallow set of prefixes? This later does a max_deref_free_locally_rooted_prefix_of
to consider deep prefixes, so I don't see why this is split into 2 parts.
r=me with the shallow prefix thing fixed. |
Fix rust-lang#44833 (a very specific instance of a very broad bug). In `check_if_path_is_moved(L)`, check nearest prefix of L with MovePath, and suffixes of L with MovePaths. Over the course of review, ariel pointed out a number of issues that led to this version of the commit: 1. Looking solely at supporting prefixes does not suffice: it overlooks checking if the path was ever actually initialized in the first place. So you need to be willing to consider non-supporting prefixes. Once you are looking at all prefixes, you *could* just look at the local that forms the base of the projection, but to handle partial initialization (which still needs to be formally specified), this code instead looks at the nearest prefix of L that has an associated MovePath (which, in the limit, will end up being a local). 2. You also need to consider the suffixes of the given Lvalue, due to how dataflow is representing partial moves of individual fields out of struct values. 3. (There was originally a third search, but ariel pointed out that the first and third could be folded into one.) Also includes some drive-by refactorings to simplify some method signatures and prefer `for _ in _` over `loop { }` (at least when it comes semi-naturally).
Expanded to cover partial-initialization ideas.
…+mir borrowcks. (There are other tests that this PR also improves, but were not completely synchronized. I chose to wait until later to pull those into the `revisions: ast mir` testing pattern; later being either when they *are* synchronized, or in some PR where we migrate all borrowck tests, regardless of whether MIR-borrowck is "finished" for them or not.)
f328f93
to
d6caf73
Compare
@bors r=arielb1 |
📌 Commit d6caf73 has been approved by |
…ixes-invalidate-uses-too, r=arielb1 MIR-borrowck: moves of prefixes invalidate uses too I overlooked the fact that when we check if a path is moved, we need to check for interference between the (shallow) prefixes and the use in question. ~~Long term, we may want to revise how this computation is done. For example, it might be better to represent the set of invalidated prefixes in the dataflow computation (the `maybe_uninitialized` dataflow), and thus avoid one of the loops in the code here.~~ * Update: I was wrong in my original recollection of the dataflow code, which actually does the right thing, in terms of precisely tracking substructure initialization and movement. Fix #44833 ---- Update: The initial version of this PR's description (and the code as well) erroneously focused on supporting prefixes. ~~But the two main cases of interest are: 1. the *shallow* prefixes, and 2. the deref-free prefix built off a local (if the lvalue is indeed built off a local)~~ Update 2: The main cases of interest are in fact: 1. the nearest prefix with a MovePath, and 2. the suffixes.
☀️ Test successful - status-appveyor, status-travis |
I overlooked the fact that when we check if a path is moved, we need to check for interference between the (shallow) prefixes and the use in question.
Long term, we may want to revise how this computation is done. For example, it might be better to represent the set of invalidated prefixes in the dataflow computation (themaybe_uninitialized
dataflow), and thus avoid one of the loops in the code here.Fix #44833
Update: The initial version of this PR's description (and the code as well) erroneously focused on supporting prefixes.
But the two main cases of interest are: 1. the shallow prefixes, and 2. the deref-free prefix built off a local (if the lvalue is indeed built off a local)Update 2: The main cases of interest are in fact: 1. the nearest prefix with a MovePath, and 2. the suffixes.