-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Add help on reinitialization between move and access #85686
Conversation
r? @estebank (rust-highfive has picked a reviewer for you, use r? to override) |
// Check if we can reach these reinits from a move location. | ||
let mut maybe_reinitialized = false; | ||
let mut visited = FxHashSet::default(); | ||
let mut stack = reinits; |
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.
Any reason for the rename of reinits
?
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.
At first we just collect the reinits we found and then use those to initialize the stack for the search. I thought this would be less confusing. Alternatively we could rename reinits to something like stack_reinits and remove the rename.
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.
The rename makes sense to me.
@@ -1478,6 +1487,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | |||
})); | |||
|
|||
let mut visited = FxHashSet::default(); | |||
let mut reinits = vec![]; | |||
let mut move_locations = vec![]; |
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 should likely be an FxHashSet
instead of a Vec
.
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 agree.
LL | while let Some(foo) = val { | ||
| ^^^ value moved here, in previous iteration of loop | ||
| | ||
= help: make sure that it is reinitialized on all paths |
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'm not entirely happy with the help
wording because it feels like it might lead to confusion. It's likely better than we have already, but I'd feel better with a more newcomer friendly wording and potentially some spans pointing at the move locations when stating this.
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.
How about something like:
46 | while let Some(_foo) = val { //~ ERROR use of moved value
| ^^^^
| |
| the value might not be reinitialized at this point
| value moved here, in previous iteration of loop
We could also print the spans of the reinits that might get skipped, though I think this would probably clutter the output.
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.
We could also print the spans of the reinits that might get skipped, though I think this would probably clutter the output.
Could we see what that would look like in the tests we have available? Something we do in other cases is provide a full print out if few things are involved, and hide almost everything when they become too verbose. We could do something like that, but I'd like to see what it'd look like first.
I actually think this is an improvement:
If we have multiple ones we could do something like:
I capped the number of printed reinitializations at 3. What do you think? |
7fdcb77
to
ab1a719
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.
I love it and am ok to merge as is, but there's a small tweak I'd like to address as well to avoid accidentally misleading when there are more than 3 reinits.
if let UseSpans::PatUse(span) = move_spans { | ||
err.span_suggestion_verbose( | ||
span.shrink_to_lo(), | ||
&format!( | ||
"borrow this field in the pattern to avoid moving {}", | ||
self.describe_place(moved_place.as_ref()) | ||
.map(|n| format!("`{}`", n)) | ||
.unwrap_or_else(|| "the value".to_string()) | ||
), | ||
"ref ".to_string(), | ||
Applicability::MachineApplicable, | ||
); | ||
in_pattern = true; | ||
if maybe_reinitialized_locations.is_empty() { |
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.
You can merge these two lines with something like the following to avoid one level of indentation:
if let (UseSpans::PatUse(span), []) = (move_spans, &maybe_reinitialized_locations[..]) {
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.
Done
// Check if we can reach these reinits from a move location. | ||
let reinits_reachable = reinits | ||
.into_iter() | ||
.filter(|reinit| { | ||
let mut visited = FxHashSet::default(); | ||
let mut stack = vec![*reinit]; | ||
while let Some(location) = stack.pop() { | ||
if !visited.insert(location) { | ||
continue; | ||
} | ||
if move_locations.contains(&location) { | ||
return true; | ||
} | ||
stack.extend(predecessor_locations(self.body, location)); | ||
} | ||
false | ||
}) | ||
.collect::<Vec<Location>>(); |
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.
Does this happen unconditionally, even in the happy path? We might want to do a perf run just to be sure we don't regress things too much.
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.
The only place where get_moved_indexes is called is in report_use_of_moved_or_uninitialized. I am pretty sure that this function is only called if we know for sure that an error occurred. This shouldn't run in normal error free compilation.
let reinit_spans = maybe_reinitialized_locations | ||
.iter() | ||
.take(3) | ||
.map(|loc| { | ||
self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc) | ||
.args_or_use() | ||
}) | ||
.collect::<Vec<Span>>(); | ||
if reinit_spans.len() == 1 { | ||
err.span_label(reinit_spans[0], "this reinitialization might get skipped"); | ||
} else if reinit_spans.len() > 1 { | ||
err.span_note( | ||
MultiSpan::from_spans(reinit_spans), | ||
"these reinitializations might get skipped", | ||
); | ||
} |
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 love the new output, but I would like it if for the case where there are more than 3 we provided something like
note: these 3 reinitializations and N others might get skipped
--> $DIR/issue-83760.rs:30:9
|
LL | foo = Some(Struct);
| ^^^^^^^^^^^^^^^^^^
LL | } else if true {
LL | foo = Some(Struct);
| ^^^^^^^^^^^^^^^^^^
LL | } else if true {
LL | foo = Some(Struct);
| ^^^^^^^^^^^^^^^^^^
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 idea.
ab1a719
to
76f3613
Compare
☔ The latest upstream changes (presumably #86901) made this pull request unmergeable. Please resolve the merge conflicts. |
76f3613
to
34ff259
Compare
@bors r+ |
📌 Commit 34ff259 has been approved by |
☀️ Test successful - checks-actions |
Fixes #83760