-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Desugaring of drop and replace at MIR build #107844
Conversation
r? @lcnr (rustbot has picked a reviewer for you, use r? to override) |
Some changes occurred to MIR optimizations cc @rust-lang/wg-mir-opt |
b31b11b
to
d809b3f
Compare
r? compiler |
compiler/rustc_borrowck/src/lib.rs
Outdated
Some(kind), | ||
), | ||
WriteKind::StorageDeadOrDrop => { | ||
if let Some(DesugaringKind::Replace) = place_span.1.desugaring_kind() { |
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 may deserve a helper function. The fact that checking desugaring_kind()
is enough comes from the fact that DesugaringKind::Replace
can only be created after the other (HIR-based) variants.
|
||
// create the new block for the assignment in the case of unwinding | ||
let assign_unwind = self.cfg.start_new_cleanup_block(); | ||
self.cfg.push_assign(assign_unwind, source_info, place, value.clone()); |
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.
Having the assignment even in the unwind case is a bit surprising. This is the current behaviour, so ok. Should eventually be removed in the future?
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.
As long as drop can unwind, it is necessary to avoid leaving partially destroyed places behind. See #30380 for an example.
@@ -8,6 +8,7 @@ fn a() { | |||
//~^ NOTE `vec[_]` is borrowed here | |||
vec[0] = Box::new(4); //~ ERROR cannot assign | |||
//~^ NOTE `vec[_]` is assigned to here | |||
//~| NOTE in this expansion of desugaring of drop and replace |
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.
Should DesugaringKind::Replace
be filtered out from diagnostic notes?
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's not shown in human readable output but only in json, like other desugarings (for
, ?
, ...). I think it makes sense they all have the same behavior
@@ -40,7 +40,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | |||
// Generate better code for things that don't need to be | |||
// dropped. | |||
if lhs.ty.needs_drop(this.tcx, this.param_env) { | |||
let rhs = unpack!(block = this.as_local_operand(block, rhs)); | |||
let rhs = unpack!(block = this.as_local_rvalue(block, rhs)); |
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's a little surprising to me that this wasn't .as_local_rvalue
already. Were we just less precise than we could be before?
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.
Oh no, I guess this change is because build_drop_and_replace
was changed to take an rvalue.
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.
Could you make the ElaborateDrops
tests output ElaborateDrops.diff
instead of ElaborateDrops.after.mir
? It will be easier to check the output.
// A drop and replace behind a pointer/array/whatever. | ||
// The borrow checker requires that these locations are initialized before the assignment, | ||
// so we just leave an unconditional drop. | ||
assert!(!data.is_cleanup); |
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 sure I have a clear idea of MIR looks like in that case. Do you have an example?
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.
fn replace(c: &mut String) {
*c = "new".to_string();
}
fn main() {
let mut c = "old".to_string();
replace(&mut c);
}
the body of replace
gets translated to something like:
bb1: {
_2 = <something that builds the new string>;
drop((*_1)) -> [return: bb3, unwind: bb2];
}
bb2 (cleanup): {
(*_1) = move _2;
resume;
}
bb3: {
(*_1) = move _2;
return;
}
Under normal conditions, dropping something behind a pointer would be bad (drop((*_1))
), but in a drop and replace it's fine because it's always initialized before any other use.
assert!(!data.is_cleanup)
is there because a drop and replace is never emitted in the unwind path so it shouldn't be possible to hit this branch.
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.
In that case, should we assert that we will assign to *_1
starting both target and unwind bbs? Just to future-proof against changes to MIR building.
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.
That's what the check for DesugaringKind::Replace
is for.
Alternatively we could check for assignments, but it could be that some unrelated code is inserted before those that does not access that variable and is therefore valid but breaks the check.
For this reason I was kind of considering DesugaringKind::Replace
as a guarantee that there will be an assignment later to the same place that is compliant with the semantics of a replace, without having to know exactly how the desugaring is done.
However, I don't see any reason why we should insert stuff between the drop and the assignment for now, so if you think it's safer that way it's quite easy to do. I 'm just worrying it might be a bit sensitive to reorderings in the MIR
0224acf
to
e9acc52
Compare
Once #106430 is merged I'll rebase this PR on top of that to make reviewing easier |
☔ The latest upstream changes (presumably #106430) made this pull request unmergeable. Please resolve the merge conflicts. |
e9acc52
to
d8d8287
Compare
@rustbot label -S-waiting-on-author +S-waiting-on-review |
I've looked at the failure and it seems like the code for generating coverage spans for closure signatures is a bit fragile.
Changing it to a stable sort fixes this issue and also add coverage info in other closure sigs where it was missing before (presumably because of the same problem). Since it looks like we are fine with some closures sigs not having coverage info and changing to a stable sort could have perf implications, I would go ahead with this for now (changed the expected output) and deal with this problem in a separate PR |
@bors r+ |
@bors rollup=never let's get this out of the queue |
☀️ Test successful - checks-actions |
Finished benchmarking commit (14c54b6): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesThis benchmark run did not return any relevant results for this metric. |
Desugaring DropAndReplace at MIR build (rust-lang#107844) fixed issue 70919. Add regressions tests, borrowed from rust-lang#102078, to ensure we check for this in the future. Co-authored-by: Aaron Hill <aa1ronham@gmail.com>
Add regression tests for issue 70919 Desugaring DropAndReplace at MIR build (rust-lang#107844) fixed rust-lang#70919. Add regressions tests, borrowed from rust-lang#102078, to ensure we check for this in the future. cc `@Aaron1011`
Add regression tests for issue 70919 Desugaring DropAndReplace at MIR build (rust-lang#107844) fixed rust-lang#70919. Add regressions tests, borrowed from rust-lang#102078, to ensure we check for this in the future. cc ``@Aaron1011``
…asko Remove DropAndReplace terminator rust-lang#107844 made DropAndReplace unused, let's remove it completely from the codebase.
…asko Remove DropAndReplace terminator rust-lang#107844 made DropAndReplace unused, let's remove it completely from the codebase.
…asko Remove DropAndReplace terminator rust-lang#107844 made DropAndReplace unused, let's remove it completely from the codebase.
This PR made the following code compile:
I didn't see an FCP, so I'm noting. |
…asko Remove DropAndReplace terminator rust-lang#107844 made DropAndReplace unused, let's remove it completely from the codebase.
…asko Remove DropAndReplace terminator rust-lang#107844 made DropAndReplace unused, let's remove it completely from the codebase.
We previously had to use an atomic to work around a Rust compiler bug. However, now that rust-lang/rust#107844 is fixed, this can just be `FnMut`.
This commit desugars the drop and replace deriving from an
assignment at MIR build, avoiding the construction of the
DropAndReplace
terminator (which will be removed in a following PR).In order to retain the same error messages for replaces a new
DesugaringKind::Replace
variant is introduced.The changes in the borrowck are also useful for future work in moving drop elaboration
before borrowck, as no
DropAndReplace
would be present there anymore.Notes on test diffs:
tests/ui/borrowck/issue-58776-borrowck-scans-children
: the assignment deriving from the desugaring kills the borrow.tests/ui/async-await/async-fn-size-uninit-locals.rs
,tests/mir-opt/issue_41110.test.ElaborateDrops.after.mir
,tests/mir-opt/issue_41888.main.ElaborateDrops.after.mir
: drop elaboration generates (or reads from) a useless drop flag due to an issue with the dataflow analysis. Will be fixed independently by Remove dead unwinds before drop elaboration #106430.See #104488 for more context