Improve documentation for drop elaboration
This commit is contained in:
parent
ed084b0b83
commit
1e4b663005
@ -277,7 +277,18 @@ fn param_env(&self) -> ty::ParamEnv<'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<Operand<'tcx>> {
|
||||
|
@ -12,10 +12,15 @@
|
||||
|
||||
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 @@ pub fn value(self) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 @@ fn map<F>(self, f: F) -> Self
|
||||
}
|
||||
|
||||
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<Operand<'tcx>>;
|
||||
|
||||
/// 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<Self::Path>;
|
||||
|
||||
/// 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<T>`, where the contained `T` can be moved out of the box.
|
||||
fn deref_subpath(&self, path: Self::Path) -> Option<Self::Path>;
|
||||
|
||||
/// 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<Self::Path>;
|
||||
|
||||
/// 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<Self::Path>;
|
||||
}
|
||||
|
||||
@ -106,6 +168,14 @@ struct DropCtxt<'l, 'b, 'tcx, D>
|
||||
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 @@ fn open_drop_for_box(&mut self, adt: &'tcx ty::AdtDef, substs: SubstsRef<'tcx>)
|
||||
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 @@ fn complete_drop(&mut self, succ: BasicBlock, unwind: Unwind) -> BasicBlock {
|
||||
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 @@ fn drop_flag_reset_block(
|
||||
|
||||
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 @@ fn box_free_block(
|
||||
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 @@ fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock {
|
||||
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,
|
||||
|
Loading…
Reference in New Issue
Block a user