Rollup merge of #107271 - Zeegomo:drop-rmw, r=oli-obk
Treat Drop as a rmw operation Previously, a Drop terminator was considered a move in MIR. This commit changes the behavior to only treat Drop as a mutable access to the dropped place. In order for this change to be correct, we need to guarantee that 1. A dropped value won't be used again 2. Places that appear in a drop won't be used again before a subsequent initialization. We can ensure this to be correct at MIR construction because Drop will only be emitted when a variable goes out of scope, thus having: * (1) as there is no way of reaching the old value. drop-elaboration will also remove any uninitialized drop. * (2) as the place can't be named following the end of the scope. However, the initialization status, previously tracked by moves, should also be tied to the execution of a Drop, hence the additional logic in the dataflow analyses. From discussion in [this thread](https://rust-lang.zulipchat.com/#narrow/stream/233931-t-compiler.2Fmajor-changes/topic/.60DROP.60.20to.20.60DROP_IF.60.20compiler-team.23558), originating from https://github.com/rust-lang/compiler-team/issues/558. See also https://github.com/rust-lang/rust/pull/104488#discussion_r1085556010
This commit is contained in:
commit
05748c66a0
4 changed files with 46 additions and 9 deletions
|
@ -18,6 +18,35 @@ use rustc_span::Span;
|
|||
use rustc_target::abi::VariantIdx;
|
||||
use std::fmt;
|
||||
|
||||
/// During MIR building, Drop and DropAndReplace terminators are inserted in every place where a drop may occur.
|
||||
/// However, in this phase, the presence of these terminators does not guarantee that a destructor will run,
|
||||
/// as the target of the drop may be uninitialized.
|
||||
/// In general, the compiler cannot determine at compile time whether a destructor will run or not.
|
||||
///
|
||||
/// At a high level, this pass refines Drop and DropAndReplace to only run the destructor if the
|
||||
/// target is initialized. The way this is achievied is by inserting drop flags for every variable
|
||||
/// that may be dropped, and then using those flags to determine whether a destructor should run.
|
||||
/// This pass also removes DropAndReplace, replacing it with a Drop paired with an assign statement.
|
||||
/// Once this is complete, Drop terminators in the MIR correspond to a call to the "drop glue" or
|
||||
/// "drop shim" for the type of the dropped place.
|
||||
///
|
||||
/// This pass relies on dropped places having an associated move path, which is then used to determine
|
||||
/// the initialization status of the place and its descendants.
|
||||
/// It's worth noting that a MIR containing a Drop without an associated move path is probably ill formed,
|
||||
/// as it would allow running a destructor on a place behind a reference:
|
||||
///
|
||||
/// ```text
|
||||
// fn drop_term<T>(t: &mut T) {
|
||||
// mir!(
|
||||
// {
|
||||
// Drop(*t, exit)
|
||||
// }
|
||||
// exit = {
|
||||
// Return()
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
/// ```
|
||||
pub struct ElaborateDrops;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for ElaborateDrops {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue