1
Fork 0

Handle inactive enum variants in MaybeUninitializedPlaces

This commit is contained in:
Dylan MacKenzie 2020-06-29 17:20:41 -07:00
parent 0ca7f74dbd
commit ca8678b23e
3 changed files with 89 additions and 23 deletions

View file

@ -1,6 +1,7 @@
use crate::util::elaborate_drops::DropFlagState; use crate::util::elaborate_drops::DropFlagState;
use rustc_middle::mir::{self, Body, Location}; use rustc_middle::mir::{self, Body, Location};
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_target::abi::VariantIdx;
use super::indexes::MovePathIndex; use super::indexes::MovePathIndex;
use super::move_paths::{InitKind, LookupResult, MoveData}; use super::move_paths::{InitKind, LookupResult, MoveData};
@ -228,3 +229,42 @@ pub(crate) fn for_location_inits<'tcx, F>(
} }
} }
} }
/// 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 enum_mpi = match move_data.rev_lookup.find(enum_place.as_ref()) {
LookupResult::Exact(mpi) => mpi,
LookupResult::Parent(_) => 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 variant_idx = match *downcast {
mir::ProjectionElem::Downcast(_, idx) => idx,
_ => unreachable!(),
};
if variant_idx != active_variant {
on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
handle_inactive_variant(mpi)
});
}
}
}

View file

@ -12,7 +12,7 @@ use super::MoveDataParamEnv;
use crate::util::elaborate_drops::DropFlagState; use crate::util::elaborate_drops::DropFlagState;
use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex}; use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis}; use super::{AnalysisDomain, BottomValue, GenKill, GenKillAnalysis};
use super::drop_flag_effects_for_function_entry; use super::drop_flag_effects_for_function_entry;
@ -124,11 +124,23 @@ pub struct MaybeUninitializedPlaces<'a, 'tcx> {
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
body: &'a Body<'tcx>, body: &'a Body<'tcx>,
mdpe: &'a MoveDataParamEnv<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>,
mark_inactive_variants_as_uninit: bool,
} }
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> { impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self { pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
MaybeUninitializedPlaces { tcx, body, mdpe } MaybeUninitializedPlaces { tcx, body, mdpe, mark_inactive_variants_as_uninit: false }
}
/// Causes inactive enum variants to be marked as "maybe uninitialized" after a switch on an
/// enum discriminant.
///
/// This is correct in a vacuum but is not the default because it causes problems in the borrow
/// checker, where this information gets propagated along `FakeEdge`s.
pub fn mark_inactive_variants_as_uninit(mut self) -> Self {
self.mark_inactive_variants_as_uninit = true;
self
} }
} }
@ -350,27 +362,16 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
_adt: &ty::AdtDef, _adt: &ty::AdtDef,
variant: VariantIdx, variant: VariantIdx,
) { ) {
let enum_mpi = match self.move_data().rev_lookup.find(enum_place.as_ref()) { // Kill all move paths that correspond to variants we know to be inactive along this
LookupResult::Exact(mpi) => mpi, // particular outgoing edge of a `SwitchInt`.
LookupResult::Parent(_) => return, drop_flag_effects::on_all_inactive_variants(
}; self.tcx,
self.body,
// Kill all move paths that correspond to variants other than this one self.move_data(),
let move_paths = &self.move_data().move_paths; enum_place,
let enum_path = &move_paths[enum_mpi]; variant,
for (mpi, variant_path) in enum_path.children(move_paths) { |mpi| trans.kill(mpi),
trans.kill(mpi); );
match variant_path.place.projection.last().unwrap() {
mir::ProjectionElem::Downcast(_, idx) if *idx == variant => continue,
_ => drop_flag_effects::on_all_children_bits(
self.tcx,
self.body,
self.move_data(),
mpi,
|mpi| trans.kill(mpi),
),
}
}
} }
} }
@ -443,6 +444,30 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
}, },
); );
} }
fn discriminant_switch_effect(
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
enum_place: mir::Place<'tcx>,
_adt: &ty::AdtDef,
variant: VariantIdx,
) {
if !self.mark_inactive_variants_as_uninit {
return;
}
// Mark all move paths that correspond to variants other than this one as maybe
// uninitialized (in reality, they are *definitely* uninitialized).
drop_flag_effects::on_all_inactive_variants(
self.tcx,
self.body,
self.move_data(),
enum_place,
variant,
|mpi| trans.gen(mpi),
);
}
} }
impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> { impl<'a, 'tcx> AnalysisDomain<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {

View file

@ -48,6 +48,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
.into_results_cursor(body); .into_results_cursor(body);
let uninits = MaybeUninitializedPlaces::new(tcx, body, &env) let uninits = MaybeUninitializedPlaces::new(tcx, body, &env)
.mark_inactive_variants_as_uninit()
.into_engine(tcx, body, def_id) .into_engine(tcx, body, def_id)
.dead_unwinds(&dead_unwinds) .dead_unwinds(&dead_unwinds)
.iterate_to_fixpoint() .iterate_to_fixpoint()