summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_mir_dataflow/src/drop_flag_effects.rs')
-rw-r--r--compiler/rustc_mir_dataflow/src/drop_flag_effects.rs268
1 files changed, 268 insertions, 0 deletions
diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs
new file mode 100644
index 000000000..f102872cd
--- /dev/null
+++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs
@@ -0,0 +1,268 @@
+use crate::elaborate_drops::DropFlagState;
+use rustc_middle::mir::{self, Body, Location};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_target::abi::VariantIdx;
+
+use super::indexes::MovePathIndex;
+use super::move_paths::{InitKind, LookupResult, MoveData};
+use super::MoveDataParamEnv;
+
+pub fn move_path_children_matching<'tcx, F>(
+ move_data: &MoveData<'tcx>,
+ path: MovePathIndex,
+ mut cond: F,
+) -> Option<MovePathIndex>
+where
+ F: FnMut(mir::PlaceElem<'tcx>) -> bool,
+{
+ let mut next_child = move_data.move_paths[path].first_child;
+ while let Some(child_index) = next_child {
+ let move_path_children = &move_data.move_paths[child_index];
+ if let Some(&elem) = move_path_children.place.projection.last() {
+ if cond(elem) {
+ return Some(child_index);
+ }
+ }
+ next_child = move_path_children.next_sibling;
+ }
+
+ None
+}
+
+/// When enumerating the child fragments of a path, don't recurse into
+/// paths (1.) past arrays, slices, and pointers, nor (2.) into a type
+/// that implements `Drop`.
+///
+/// Places behind references or arrays are not tracked by elaboration
+/// and are always assumed to be initialized when accessible. As
+/// references and indexes can be reseated, trying to track them can
+/// only lead to trouble.
+///
+/// Places behind ADT's with a Drop impl are not tracked by
+/// elaboration since they can never have a drop-flag state that
+/// differs from that of the parent with the Drop impl.
+///
+/// In both cases, the contents can only be accessed if and only if
+/// their parents are initialized. This implies for example that there
+/// is no need to maintain separate drop flags to track such state.
+//
+// FIXME: we have to do something for moving slice patterns.
+fn place_contents_drop_state_cannot_differ<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ place: mir::Place<'tcx>,
+) -> bool {
+ let ty = place.ty(body, tcx).ty;
+ match ty.kind() {
+ ty::Array(..) => {
+ debug!(
+ "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} => false",
+ place, ty
+ );
+ false
+ }
+ ty::Slice(..) | ty::Ref(..) | ty::RawPtr(..) => {
+ debug!(
+ "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} refd => true",
+ place, ty
+ );
+ true
+ }
+ ty::Adt(def, _) if (def.has_dtor(tcx) && !def.is_box()) || def.is_union() => {
+ debug!(
+ "place_contents_drop_state_cannot_differ place: {:?} ty: {:?} Drop => true",
+ place, ty
+ );
+ true
+ }
+ _ => false,
+ }
+}
+
+pub fn on_lookup_result_bits<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ move_data: &MoveData<'tcx>,
+ lookup_result: LookupResult,
+ each_child: F,
+) where
+ F: FnMut(MovePathIndex),
+{
+ match lookup_result {
+ LookupResult::Parent(..) => {
+ // access to untracked value - do not touch children
+ }
+ LookupResult::Exact(e) => on_all_children_bits(tcx, body, move_data, e, each_child),
+ }
+}
+
+pub fn on_all_children_bits<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ move_data: &MoveData<'tcx>,
+ move_path_index: MovePathIndex,
+ mut each_child: F,
+) where
+ F: FnMut(MovePathIndex),
+{
+ fn is_terminal_path<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ move_data: &MoveData<'tcx>,
+ path: MovePathIndex,
+ ) -> bool {
+ place_contents_drop_state_cannot_differ(tcx, body, move_data.move_paths[path].place)
+ }
+
+ fn on_all_children_bits<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ move_data: &MoveData<'tcx>,
+ move_path_index: MovePathIndex,
+ each_child: &mut F,
+ ) where
+ F: FnMut(MovePathIndex),
+ {
+ each_child(move_path_index);
+
+ if is_terminal_path(tcx, body, move_data, move_path_index) {
+ return;
+ }
+
+ let mut next_child_index = move_data.move_paths[move_path_index].first_child;
+ while let Some(child_index) = next_child_index {
+ on_all_children_bits(tcx, body, move_data, child_index, each_child);
+ next_child_index = move_data.move_paths[child_index].next_sibling;
+ }
+ }
+ on_all_children_bits(tcx, body, move_data, move_path_index, &mut each_child);
+}
+
+pub fn on_all_drop_children_bits<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ ctxt: &MoveDataParamEnv<'tcx>,
+ path: MovePathIndex,
+ mut each_child: F,
+) where
+ F: FnMut(MovePathIndex),
+{
+ on_all_children_bits(tcx, body, &ctxt.move_data, path, |child| {
+ let place = &ctxt.move_data.move_paths[path].place;
+ let ty = place.ty(body, tcx).ty;
+ debug!("on_all_drop_children_bits({:?}, {:?} : {:?})", path, place, ty);
+
+ let erased_ty = tcx.erase_regions(ty);
+ if erased_ty.needs_drop(tcx, ctxt.param_env) {
+ each_child(child);
+ } else {
+ debug!("on_all_drop_children_bits - skipping")
+ }
+ })
+}
+
+pub fn drop_flag_effects_for_function_entry<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ ctxt: &MoveDataParamEnv<'tcx>,
+ mut callback: F,
+) where
+ F: FnMut(MovePathIndex, DropFlagState),
+{
+ let move_data = &ctxt.move_data;
+ for arg in body.args_iter() {
+ let place = mir::Place::from(arg);
+ let lookup_result = move_data.rev_lookup.find(place.as_ref());
+ on_lookup_result_bits(tcx, body, move_data, lookup_result, |mpi| {
+ callback(mpi, DropFlagState::Present)
+ });
+ }
+}
+
+pub fn drop_flag_effects_for_location<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ ctxt: &MoveDataParamEnv<'tcx>,
+ loc: Location,
+ mut callback: F,
+) where
+ F: FnMut(MovePathIndex, DropFlagState),
+{
+ let move_data = &ctxt.move_data;
+ debug!("drop_flag_effects_for_location({:?})", loc);
+
+ // first, move out of the RHS
+ for mi in &move_data.loc_map[loc] {
+ let path = mi.move_path_index(move_data);
+ debug!("moving out of path {:?}", move_data.move_paths[path]);
+
+ on_all_children_bits(tcx, body, move_data, path, |mpi| callback(mpi, DropFlagState::Absent))
+ }
+
+ debug!("drop_flag_effects: assignment for location({:?})", loc);
+
+ for_location_inits(tcx, body, move_data, loc, |mpi| callback(mpi, DropFlagState::Present));
+}
+
+pub fn for_location_inits<'tcx, F>(
+ tcx: TyCtxt<'tcx>,
+ body: &Body<'tcx>,
+ move_data: &MoveData<'tcx>,
+ loc: Location,
+ mut callback: F,
+) where
+ F: FnMut(MovePathIndex),
+{
+ for ii in &move_data.init_loc_map[loc] {
+ let init = move_data.inits[*ii];
+ match init.kind {
+ InitKind::Deep => {
+ let path = init.path;
+
+ on_all_children_bits(tcx, body, move_data, path, &mut callback)
+ }
+ InitKind::Shallow => {
+ let mpi = init.path;
+ callback(mpi);
+ }
+ InitKind::NonPanicPathOnly => (),
+ }
+ }
+}
+
+/// Calls `handle_inactive_variant` for each descendant move path of `enum_place` that contains a
+/// `Downcast` to a variant besides the `active_variant`.
+///
+/// NOTE: If there are no move paths corresponding to an inactive variant,
+/// `handle_inactive_variant` will not be called for that variant.
+pub(crate) fn on_all_inactive_variants<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mir::Body<'tcx>,
+ move_data: &MoveData<'tcx>,
+ enum_place: mir::Place<'tcx>,
+ active_variant: VariantIdx,
+ mut handle_inactive_variant: impl FnMut(MovePathIndex),
+) {
+ let LookupResult::Exact(enum_mpi) = move_data.rev_lookup.find(enum_place.as_ref()) else {
+ return;
+ };
+
+ let enum_path = &move_data.move_paths[enum_mpi];
+ for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
+ // Because of the way we build the `MoveData` tree, each child should have exactly one more
+ // projection than `enum_place`. This additional projection must be a downcast since the
+ // base is an enum.
+ let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
+ assert_eq!(enum_place.projection.len(), base_proj.len());
+
+ let mir::ProjectionElem::Downcast(_, variant_idx) = *downcast else {
+ unreachable!();
+ };
+
+ if variant_idx != active_variant {
+ on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
+ handle_inactive_variant(mpi)
+ });
+ }
+ }
+}