Skip to content

Commit 7e2fc9f

Browse files
Mark inactive enum variants along "otherwise" edge as unintitialized
This means all enum variants whose discriminant has a dedicated outgoing edge in the `SwitchInt` terminator.
1 parent 8876ffc commit 7e2fc9f

File tree

1 file changed

+102
-69
lines changed
  • compiler/rustc_mir/src/dataflow/impls

1 file changed

+102
-69
lines changed

compiler/rustc_mir/src/dataflow/impls/mod.rs

Lines changed: 102 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use super::MoveDataParamEnv;
1111

1212
use crate::util::elaborate_drops::DropFlagState;
1313

14-
use super::move_paths::{HasMoveData, InitIndex, InitKind, MoveData, MovePathIndex};
14+
use super::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
1515
use super::{lattice, AnalysisDomain, GenKill, GenKillAnalysis};
1616

1717
use super::drop_flag_effects_for_function_entry;
@@ -362,40 +362,17 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
362362
return;
363363
}
364364

365-
let enum_ = discr.place().and_then(|discr| {
366-
switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr)
367-
});
368-
369-
let (enum_place, enum_def) = match enum_ {
370-
Some(x) => x,
371-
None => return,
372-
};
373-
374-
let mut discriminants = enum_def.discriminants(self.tcx);
375-
edge_effects.apply(|trans, edge| {
376-
let value = match edge.value {
377-
Some(x) => x,
378-
None => return,
379-
};
380-
381-
// MIR building adds discriminants to the `values` array in the same order as they
382-
// are yielded by `AdtDef::discriminants`. We rely on this to match each
383-
// discriminant in `values` to its corresponding variant in linear time.
384-
let (variant, _) = discriminants
385-
.find(|&(_, discr)| discr.val == value)
386-
.expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
387-
388-
// Kill all move paths that correspond to variants we know to be inactive along this
389-
// particular outgoing edge of a `SwitchInt`.
390-
drop_flag_effects::on_all_inactive_variants(
391-
self.tcx,
392-
self.body,
393-
self.move_data(),
394-
enum_place,
395-
variant,
396-
|mpi| trans.kill(mpi),
397-
);
398-
});
365+
// Kill all move paths that correspond to variants we know to be inactive along a
366+
// particular outgoing edge of a `SwitchInt`.
367+
enum_discriminant_switch_inactive_variant_effects(
368+
self.tcx,
369+
self.body,
370+
self.move_data(),
371+
block,
372+
discr,
373+
edge_effects,
374+
|trans, mpi| trans.kill(mpi),
375+
);
399376
}
400377
}
401378

@@ -481,40 +458,17 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
481458
return;
482459
}
483460

484-
let enum_ = discr.place().and_then(|discr| {
485-
switch_on_enum_discriminant(self.tcx, &self.body, &self.body[block], discr)
486-
});
487-
488-
let (enum_place, enum_def) = match enum_ {
489-
Some(x) => x,
490-
None => return,
491-
};
492-
493-
let mut discriminants = enum_def.discriminants(self.tcx);
494-
edge_effects.apply(|trans, edge| {
495-
let value = match edge.value {
496-
Some(x) => x,
497-
None => return,
498-
};
499-
500-
// MIR building adds discriminants to the `values` array in the same order as they
501-
// are yielded by `AdtDef::discriminants`. We rely on this to match each
502-
// discriminant in `values` to its corresponding variant in linear time.
503-
let (variant, _) = discriminants
504-
.find(|&(_, discr)| discr.val == value)
505-
.expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
506-
507-
// Mark all move paths that correspond to variants other than this one as maybe
508-
// uninitialized (in reality, they are *definitely* uninitialized).
509-
drop_flag_effects::on_all_inactive_variants(
510-
self.tcx,
511-
self.body,
512-
self.move_data(),
513-
enum_place,
514-
variant,
515-
|mpi| trans.gen(mpi),
516-
);
517-
});
461+
// Mark all move paths that correspond to variants that are inactive along a given edge as
462+
// maybe uninitialized (in reality, they are *definitely* uninitialized).
463+
enum_discriminant_switch_inactive_variant_effects(
464+
self.tcx,
465+
self.body,
466+
self.move_data(),
467+
block,
468+
discr,
469+
edge_effects,
470+
|trans, mpi| trans.gen(mpi),
471+
);
518472
}
519473
}
520474

@@ -715,3 +669,82 @@ fn switch_on_enum_discriminant(
715669
_ => None,
716670
}
717671
}
672+
673+
fn enum_discriminant_switch_inactive_variant_effects<D>(
674+
tcx: TyCtxt<'tcx>,
675+
body: &mir::Body<'tcx>,
676+
move_data: &MoveData<'tcx>,
677+
block: mir::BasicBlock,
678+
discr: &mir::Operand<'tcx>,
679+
edge_effects: &mut impl SwitchIntEdgeEffects<D>,
680+
mut on_uninitialized_variant: impl FnMut(&mut D, MovePathIndex),
681+
) {
682+
let enum_ =
683+
discr.place().and_then(|discr| switch_on_enum_discriminant(tcx, body, &body[block], discr));
684+
685+
let (enum_place, enum_def) = match enum_ {
686+
Some(x) => x,
687+
None => return,
688+
};
689+
690+
let enum_mpi = match move_data.rev_lookup.find(enum_place.as_ref()) {
691+
LookupResult::Exact(mpi) => mpi,
692+
LookupResult::Parent(_) => return,
693+
};
694+
695+
let enum_path = &move_data.move_paths[enum_mpi];
696+
let mut discriminants = enum_def.discriminants(tcx);
697+
698+
// `MovePathIndex`s for those variants with their own outgoing edge. These
699+
// get marked as uninitialized along the "otherwise" edge.
700+
let mut variant_mpis_with_edge = vec![];
701+
702+
edge_effects.apply(|trans, edge| {
703+
if let Some(value) = edge.value {
704+
// MIR building adds discriminants to the `values` array in the same order as they
705+
// are yielded by `AdtDef::discriminants`. We rely on this to match each
706+
// discriminant in `values` to its corresponding variant in linear time.
707+
let (active_variant, _) = discriminants
708+
.find(|&(_, discr)| discr.val == value)
709+
.expect("Order of `AdtDef::discriminants` differed from `SwitchInt::values`");
710+
711+
for (variant_mpi, variant_path) in enum_path.children(&move_data.move_paths) {
712+
// Because of the way we build the `MoveData` tree, each child should have exactly one more
713+
// projection than `enum_place`. This additional projection must be a downcast since the
714+
// base is an enum.
715+
let (downcast, base_proj) = variant_path.place.projection.split_last().unwrap();
716+
assert_eq!(enum_place.projection.len(), base_proj.len());
717+
718+
let variant_idx = match *downcast {
719+
mir::ProjectionElem::Downcast(_, idx) => idx,
720+
_ => unreachable!(),
721+
};
722+
723+
if variant_idx == active_variant {
724+
// If this is the move path for the variant that is active along this edge
725+
// of the `SwitchInt` terminator, remember that it is not active as part of
726+
// the "otherwise" edge.
727+
variant_mpis_with_edge.push(variant_mpi);
728+
} else {
729+
// Otherwise, it is the move path for an *inactive* variant. Mark it and
730+
// all of its descendants as uninitialzied.
731+
drop_flag_effects::on_all_children_bits(
732+
tcx,
733+
body,
734+
move_data,
735+
variant_mpi,
736+
|mpi| on_uninitialized_variant(trans, mpi),
737+
);
738+
}
739+
}
740+
} else {
741+
// We're on the otherwise branch. All variants that we visited above are known to be
742+
// uninitialized.
743+
for &variant_mpi in &variant_mpis_with_edge {
744+
drop_flag_effects::on_all_children_bits(tcx, body, move_data, variant_mpi, |mpi| {
745+
on_uninitialized_variant(trans, mpi)
746+
});
747+
}
748+
}
749+
});
750+
}

0 commit comments

Comments
 (0)