1 //! A pass that eliminates branches on uninhabited enum variants.
3 use crate::transform
::{MirPass, MirSource}
;
5 BasicBlock
, BasicBlockData
, Body
, BodyAndCache
, Local
, Operand
, Rvalue
, StatementKind
,
8 use rustc
::ty
::layout
::{Abi, TyLayout, Variants}
;
9 use rustc
::ty
::{Ty, TyCtxt}
;
11 pub struct UninhabitedEnumBranching
;
13 fn get_discriminant_local(terminator
: &TerminatorKind
<'_
>) -> Option
<Local
> {
14 if let TerminatorKind
::SwitchInt { discr: Operand::Move(p), .. }
= terminator
{
21 /// If the basic block terminates by switching on a discriminant, this returns the `Ty` the
22 /// discriminant is read from. Otherwise, returns None.
23 fn get_switched_on_type
<'tcx
>(
24 block_data
: &BasicBlockData
<'tcx
>,
26 ) -> Option
<Ty
<'tcx
>> {
27 let terminator
= block_data
.terminator();
29 // Only bother checking blocks which terminate by switching on a local.
30 if let Some(local
) = get_discriminant_local(&terminator
.kind
) {
31 let stmt_before_term
= (block_data
.statements
.len() > 0)
32 .then(|| &block_data
.statements
[block_data
.statements
.len() - 1].kind
);
34 if let Some(StatementKind
::Assign(box (l
, Rvalue
::Discriminant(place
)))) = stmt_before_term
36 if l
.as_local() == Some(local
) {
37 if let Some(r_local
) = place
.as_local() {
38 let ty
= body
.local_decls
[r_local
].ty
;
51 fn variant_discriminants
<'tcx
>(
52 layout
: &TyLayout
<'tcx
>,
56 match &layout
.details
.variants
{
57 Variants
::Single { index }
=> vec
![index
.as_u32() as u128
],
58 Variants
::Multiple { variants, .. }
=> variants
60 .filter_map(|(idx
, layout
)| {
61 (layout
.abi
!= Abi
::Uninhabited
)
62 .then(|| ty
.discriminant_for_variant(tcx
, idx
).unwrap().val
)
68 impl<'tcx
> MirPass
<'tcx
> for UninhabitedEnumBranching
{
69 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, source
: MirSource
<'tcx
>, body
: &mut BodyAndCache
<'tcx
>) {
70 if source
.promoted
.is_some() {
74 trace
!("UninhabitedEnumBranching starting for {:?}", source
);
76 let basic_block_count
= body
.basic_blocks().len();
78 for bb
in 0..basic_block_count
{
79 let bb
= BasicBlock
::from_usize(bb
);
80 trace
!("processing block {:?}", bb
);
83 if let Some(ty
) = get_switched_on_type(&body
.basic_blocks()[bb
], body
) {
89 let layout
= tcx
.layout_of(tcx
.param_env(source
.def_id()).and(discriminant_ty
));
91 let allowed_variants
= if let Ok(layout
) = layout
{
92 variant_discriminants(&layout
, discriminant_ty
, tcx
)
97 trace
!("allowed_variants = {:?}", allowed_variants
);
99 if let TerminatorKind
::SwitchInt { values, targets, .. }
=
100 &mut body
.basic_blocks_mut()[bb
].terminator_mut().kind
103 let zipped
= vals
.iter().zip(targets
.into_iter());
105 let mut matched_values
= Vec
::with_capacity(allowed_variants
.len());
106 let mut matched_targets
= Vec
::with_capacity(allowed_variants
.len() + 1);
108 for (val
, target
) in zipped
{
109 if allowed_variants
.contains(val
) {
110 matched_values
.push(*val
);
111 matched_targets
.push(*target
);
113 trace
!("eliminating {:?} -> {:?}", val
, target
);
117 // handle the "otherwise" branch
118 matched_targets
.push(targets
.pop().unwrap());
120 *values
= matched_values
.into();
121 *targets
= matched_targets
;