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
, Local
, Operand
, Rvalue
, StatementKind
, TerminatorKind
,
7 use rustc_middle
::ty
::layout
::TyAndLayout
;
8 use rustc_middle
::ty
::{Ty, TyCtxt}
;
9 use rustc_target
::abi
::{Abi, Variants}
;
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
.is_empty())
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
: &TyAndLayout
<'tcx
>,
56 match &layout
.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 Body
<'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
102 // take otherwise out early
103 let otherwise
= targets
.pop().unwrap();
104 assert_eq
!(targets
.len(), values
.len());
107 let keep
= allowed_variants
.contains(&values
[i
]);
111 targets
.push(otherwise
);
113 values
.to_mut().retain(|var
| allowed_variants
.contains(var
));