1 //! A pass that eliminates branches on uninhabited enum variants.
3 use crate::transform
::{MirPass, MirSource}
;
4 use rustc_middle
::mir
::{
5 BasicBlock
, BasicBlockData
, Body
, BodyAndCache
, Local
, Operand
, Rvalue
, StatementKind
,
8 use rustc_middle
::ty
::layout
::TyAndLayout
;
9 use rustc_middle
::ty
::{Ty, TyCtxt}
;
10 use rustc_target
::abi
::{Abi, Variants}
;
12 pub struct UninhabitedEnumBranching
;
14 fn get_discriminant_local(terminator
: &TerminatorKind
<'_
>) -> Option
<Local
> {
15 if let TerminatorKind
::SwitchInt { discr: Operand::Move(p), .. }
= terminator
{
22 /// If the basic block terminates by switching on a discriminant, this returns the `Ty` the
23 /// discriminant is read from. Otherwise, returns None.
24 fn get_switched_on_type
<'tcx
>(
25 block_data
: &BasicBlockData
<'tcx
>,
27 ) -> Option
<Ty
<'tcx
>> {
28 let terminator
= block_data
.terminator();
30 // Only bother checking blocks which terminate by switching on a local.
31 if let Some(local
) = get_discriminant_local(&terminator
.kind
) {
32 let stmt_before_term
= (!block_data
.statements
.is_empty())
33 .then(|| &block_data
.statements
[block_data
.statements
.len() - 1].kind
);
35 if let Some(StatementKind
::Assign(box (l
, Rvalue
::Discriminant(place
)))) = stmt_before_term
37 if l
.as_local() == Some(local
) {
38 if let Some(r_local
) = place
.as_local() {
39 let ty
= body
.local_decls
[r_local
].ty
;
52 fn variant_discriminants
<'tcx
>(
53 layout
: &TyAndLayout
<'tcx
>,
57 match &layout
.variants
{
58 Variants
::Single { index }
=> vec
![index
.as_u32() as u128
],
59 Variants
::Multiple { variants, .. }
=> variants
61 .filter_map(|(idx
, layout
)| {
62 (layout
.abi
!= Abi
::Uninhabited
)
63 .then(|| ty
.discriminant_for_variant(tcx
, idx
).unwrap().val
)
69 impl<'tcx
> MirPass
<'tcx
> for UninhabitedEnumBranching
{
70 fn run_pass(&self, tcx
: TyCtxt
<'tcx
>, source
: MirSource
<'tcx
>, body
: &mut BodyAndCache
<'tcx
>) {
71 if source
.promoted
.is_some() {
75 trace
!("UninhabitedEnumBranching starting for {:?}", source
);
77 let basic_block_count
= body
.basic_blocks().len();
79 for bb
in 0..basic_block_count
{
80 let bb
= BasicBlock
::from_usize(bb
);
81 trace
!("processing block {:?}", bb
);
84 if let Some(ty
) = get_switched_on_type(&body
.basic_blocks()[bb
], body
) {
90 let layout
= tcx
.layout_of(tcx
.param_env(source
.def_id()).and(discriminant_ty
));
92 let allowed_variants
= if let Ok(layout
) = layout
{
93 variant_discriminants(&layout
, discriminant_ty
, tcx
)
98 trace
!("allowed_variants = {:?}", allowed_variants
);
100 if let TerminatorKind
::SwitchInt { values, targets, .. }
=
101 &mut body
.basic_blocks_mut()[bb
].terminator_mut().kind
104 let zipped
= vals
.iter().zip(targets
.iter());
106 let mut matched_values
= Vec
::with_capacity(allowed_variants
.len());
107 let mut matched_targets
= Vec
::with_capacity(allowed_variants
.len() + 1);
109 for (val
, target
) in zipped
{
110 if allowed_variants
.contains(val
) {
111 matched_values
.push(*val
);
112 matched_targets
.push(*target
);
114 trace
!("eliminating {:?} -> {:?}", val
, target
);
118 // handle the "otherwise" branch
119 matched_targets
.push(targets
.pop().unwrap());
121 *values
= matched_values
.into();
122 *targets
= matched_targets
;