From 1e4b66300551149fd49ce499aaae5c231cefde7f Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Sat, 16 May 2020 22:10:05 +0200 Subject: [PATCH] Improve documentation for drop elaboration --- src/librustc_mir/shim.rs | 13 +++- src/librustc_mir/util/elaborate_drops.rs | 95 ++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index d025468d28bf3..e3982c654d5fa 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -277,7 +277,18 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle { - if let DropFlagMode::Shallow = mode { DropStyle::Static } else { DropStyle::Open } + match mode { + DropFlagMode::Shallow => { + // Drops for the contained fields are "shallow" and "static" - they will simply call + // the field's own drop glue. + DropStyle::Static + } + DropFlagMode::Deep => { + // The top-level drop is "deep" and "open" - it will be elaborated to a drop ladder + // dropping each field contained in the value. + DropStyle::Open + } + } } fn get_drop_flag(&mut self, _path: Self::Path) -> Option> { diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index af7c88b178d32..ed999c6871bb6 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -12,10 +12,15 @@ use std::fmt; use std::convert::TryInto; +/// The value of an inserted drop flag. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum DropFlagState { - Present, // i.e., initialized - Absent, // i.e., deinitialized or "moved" + /// The tracked value is initialized and needs to be dropped when leaving its scope. + Present, + + /// The tracked value is uninitialized or was moved out of and does not need to be dropped when + /// leaving its scope. + Absent, } impl DropFlagState { @@ -27,23 +32,42 @@ impl DropFlagState { } } +/// Describes how/if a value should be dropped. #[derive(Debug)] pub enum DropStyle { + /// The value is already dead at the drop location, no drop will be executed. Dead, + + /// The value is known to always be initialized at the drop location, drop will always be + /// executed. Static, + + /// Whether the value needs to be dropped depends on its drop flag. Conditional, + + /// An "open" drop is one where only the fields of a value are dropped. + /// + /// For example, this happens when moving out of a struct field: The rest of the struct will be + /// dropped in such an "open" drop. It is also used to generate drop glue for the individual + /// components of a value, for example for dropping array elements. Open, } +/// Which drop flags to affect/check with an operation. #[derive(Debug)] pub enum DropFlagMode { + /// Only affect the top-level drop flag, not that of any contained fields. Shallow, + /// Affect all nested drop flags in addition to the top-level one. Deep, } +/// Describes if unwinding is necessary and where to unwind to if a panic occurs. #[derive(Copy, Clone, Debug)] pub enum Unwind { + /// Unwind to this block. To(BasicBlock), + /// Already in an unwind path, any panic will cause an abort. InCleanup, } @@ -74,20 +98,58 @@ impl Unwind { } pub trait DropElaborator<'a, 'tcx>: fmt::Debug { + /// The type representing paths that can be moved out of. + /// + /// Users can move out of individual fields of a struct, such as `a.b.c`. This type is used to + /// represent such move paths. Sometimes tracking individual move paths is not necessary, in + /// which case this may be set to (for example) `()`. type Path: Copy + fmt::Debug; + // Accessors + fn patch(&mut self) -> &mut MirPatch<'tcx>; fn body(&self) -> &'a Body<'tcx>; fn tcx(&self) -> TyCtxt<'tcx>; fn param_env(&self) -> ty::ParamEnv<'tcx>; + // Drop logic + + /// Returns how `path` should be dropped, given `mode`. fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; + + /// Returns the drop flag of `path` as a MIR `Operand` (or `None` if `path` has no drop flag). fn get_drop_flag(&mut self, path: Self::Path) -> Option>; + + /// Modifies the MIR patch so that the drop flag of `path` (if any) is cleared at `location`. + /// + /// If `mode` is deep, drop flags of all child paths should also be cleared by inserting + /// additional statements. fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + // Subpaths + + /// Returns the subpath of a field of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `field` will not get a dedicated drop flag. fn field_subpath(&self, path: Self::Path, field: Field) -> Option; + + /// Returns the subpath of a dereference of `path` (or `None` if there is no dedicated subpath). + /// + /// If this returns `None`, `*path` will not get a dedicated drop flag. + /// + /// This is only relevant for `Box`, where the contained `T` can be moved out of the box. fn deref_subpath(&self, path: Self::Path) -> Option; + + /// Returns the subpath of downcasting `path` to one of its variants. + /// + /// If this returns `None`, the downcast of `path` will not get a dedicated drop flag. fn downcast_subpath(&self, path: Self::Path, variant: VariantIdx) -> Option; + + /// Returns the subpath of indexing a fixed-size array `path`. + /// + /// If this returns `None`, elements of `path` will not get a dedicated drop flag. + /// + /// This is only relevant for array patterns, which can move out of individual array elements. fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option; } @@ -106,6 +168,14 @@ where unwind: Unwind, } +/// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. +/// +/// The passed `elaborator` is used to determine what should happen at the drop terminator. It +/// decides whether the drop can be statically determined or whether it needs a dynamic drop flag, +/// and whether the drop is "open", ie. should be expanded to drop all subfields of the dropped +/// value. +/// +/// When this returns, the MIR patch in the `elaborator` contains the necessary changes. pub fn elaborate_drop<'b, 'tcx, D>( elaborator: &mut D, source_info: SourceInfo, @@ -346,9 +416,7 @@ where let interior = self.tcx().mk_place_deref(self.place); let interior_path = self.elaborator.deref_subpath(self.path); - let succ = self.succ; // FIXME(#43234) - let unwind = self.unwind; - let succ = self.box_free_block(adt, substs, succ, unwind); + let succ = self.box_free_block(adt, substs, self.succ, self.unwind); let unwind_succ = self.unwind.map(|unwind| self.box_free_block(adt, substs, unwind, Unwind::InCleanup)); @@ -829,6 +897,8 @@ where self.drop_flag_test_block(drop_block, succ, unwind) } + /// Creates a block that resets the drop flag. If `mode` is deep, all children drop flags will + /// also be cleared. fn drop_flag_reset_block( &mut self, mode: DropFlagMode, @@ -850,13 +920,15 @@ where fn elaborated_drop_block(&mut self) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); - let unwind = self.unwind; // FIXME(#43234) - let succ = self.succ; - let blk = self.drop_block(succ, unwind); + let blk = self.drop_block(self.succ, self.unwind); self.elaborate_drop(blk); blk } + /// Creates a block that frees the backing memory of a `Box` if its drop is required (either + /// statically or by checking its drop flag). + /// + /// The contained value will not be dropped. fn box_free_block( &mut self, adt: &'tcx ty::AdtDef, @@ -868,6 +940,8 @@ where self.drop_flag_test_block(block, target, unwind) } + /// Creates a block that frees the backing memory of a `Box` (without dropping the contained + /// value). fn unelaborated_free_block( &mut self, adt: &'tcx ty::AdtDef, @@ -914,6 +988,11 @@ where self.new_block(unwind, block) } + /// Returns the block to jump to in order to test the drop flag and execute the drop. + /// + /// Depending on the required `DropStyle`, this might be a generated block with an `if` + /// terminator (for dynamic/open drops), or it might be `on_set` or `on_unset` itself, in case + /// the drop can be statically determined. fn drop_flag_test_block( &mut self, on_set: BasicBlock,