33use crate :: MirPass ;
44use rustc_data_structures:: fx:: FxHashSet ;
55use rustc_middle:: mir:: {
6- BasicBlockData , Body , Local , Operand , Rvalue , StatementKind , SwitchTargets , Terminator ,
7- TerminatorKind ,
6+ BasicBlockData , Body , Local , Operand , Rvalue , StatementKind , Terminator , TerminatorKind ,
87} ;
98use rustc_middle:: ty:: layout:: TyAndLayout ;
109use rustc_middle:: ty:: { Ty , TyCtxt } ;
@@ -30,18 +29,16 @@ fn get_switched_on_type<'tcx>(
3029 let terminator = block_data. terminator ( ) ;
3130
3231 // Only bother checking blocks which terminate by switching on a local.
33- if let Some ( local) = get_discriminant_local ( & terminator. kind ) {
34- let stmt_before_term = ( !block_data. statements . is_empty ( ) )
35- . then ( || & block_data. statements [ block_data. statements . len ( ) - 1 ] . kind ) ;
36-
37- if let Some ( StatementKind :: Assign ( box ( l, Rvalue :: Discriminant ( place) ) ) ) = stmt_before_term
38- {
39- if l. as_local ( ) == Some ( local) {
40- let ty = place. ty ( body, tcx) . ty ;
41- if ty. is_enum ( ) {
42- return Some ( ty) ;
43- }
44- }
32+ let local = get_discriminant_local ( & terminator. kind ) ?;
33+
34+ let stmt_before_term = block_data. statements . last ( ) ?;
35+
36+ if let StatementKind :: Assign ( box ( l, Rvalue :: Discriminant ( place) ) ) = stmt_before_term. kind
37+ && l. as_local ( ) == Some ( local)
38+ {
39+ let ty = place. ty ( body, tcx) . ty ;
40+ if ty. is_enum ( ) {
41+ return Some ( ty) ;
4542 }
4643 }
4744
@@ -72,28 +69,6 @@ fn variant_discriminants<'tcx>(
7269 }
7370}
7471
75- /// Ensures that the `otherwise` branch leads to an unreachable bb, returning `None` if so and a new
76- /// bb to use as the new target if not.
77- fn ensure_otherwise_unreachable < ' tcx > (
78- body : & Body < ' tcx > ,
79- targets : & SwitchTargets ,
80- ) -> Option < BasicBlockData < ' tcx > > {
81- let otherwise = targets. otherwise ( ) ;
82- let bb = & body. basic_blocks [ otherwise] ;
83- if bb. terminator ( ) . kind == TerminatorKind :: Unreachable
84- && bb. statements . iter ( ) . all ( |s| matches ! ( & s. kind, StatementKind :: StorageDead ( _) ) )
85- {
86- return None ;
87- }
88-
89- let mut new_block = BasicBlockData :: new ( Some ( Terminator {
90- source_info : bb. terminator ( ) . source_info ,
91- kind : TerminatorKind :: Unreachable ,
92- } ) ) ;
93- new_block. is_cleanup = bb. is_cleanup ;
94- Some ( new_block)
95- }
96-
9772impl < ' tcx > MirPass < ' tcx > for UninhabitedEnumBranching {
9873 fn is_enabled ( & self , sess : & rustc_session:: Session ) -> bool {
9974 sess. mir_opt_level ( ) > 0
@@ -102,13 +77,16 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
10277 fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
10378 trace ! ( "UninhabitedEnumBranching starting for {:?}" , body. source) ;
10479
105- for bb in body. basic_blocks . indices ( ) {
80+ let mut removable_switchs = Vec :: new ( ) ;
81+
82+ for ( bb, bb_data) in body. basic_blocks . iter_enumerated ( ) {
10683 trace ! ( "processing block {:?}" , bb) ;
10784
108- let Some ( discriminant_ty) = get_switched_on_type ( & body. basic_blocks [ bb] , tcx, body)
109- else {
85+ if bb_data. is_cleanup {
11086 continue ;
111- } ;
87+ }
88+
89+ let Some ( discriminant_ty) = get_switched_on_type ( & bb_data, tcx, body) else { continue } ;
11290
11391 let layout = tcx. layout_of (
11492 tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) . and ( discriminant_ty) ,
@@ -122,31 +100,38 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
122100
123101 trace ! ( "allowed_variants = {:?}" , allowed_variants) ;
124102
125- if let TerminatorKind :: SwitchInt { targets, .. } =
126- & mut body. basic_blocks_mut ( ) [ bb] . terminator_mut ( ) . kind
127- {
128- let mut new_targets = SwitchTargets :: new (
129- targets. iter ( ) . filter ( |( val, _) | allowed_variants. contains ( val) ) ,
130- targets. otherwise ( ) ,
131- ) ;
132-
133- if new_targets. iter ( ) . count ( ) == allowed_variants. len ( ) {
134- if let Some ( updated) = ensure_otherwise_unreachable ( body, & new_targets) {
135- let new_otherwise = body. basic_blocks_mut ( ) . push ( updated) ;
136- * new_targets. all_targets_mut ( ) . last_mut ( ) . unwrap ( ) = new_otherwise;
137- }
138- }
103+ let terminator = bb_data. terminator ( ) ;
104+ let TerminatorKind :: SwitchInt { targets, .. } = & terminator. kind else { bug ! ( ) } ;
139105
140- if let TerminatorKind :: SwitchInt { targets , .. } =
141- & mut body . basic_blocks_mut ( ) [ bb ] . terminator_mut ( ) . kind
142- {
143- * targets = new_targets ;
106+ let mut reachable_count = 0 ;
107+ for ( index , ( val , _ ) ) in targets . iter ( ) . enumerate ( ) {
108+ if allowed_variants . contains ( & val ) {
109+ reachable_count += 1 ;
144110 } else {
145- unreachable ! ( )
111+ removable_switchs . push ( ( bb , index ) ) ;
146112 }
147- } else {
148- unreachable ! ( )
149113 }
114+
115+ if reachable_count == allowed_variants. len ( ) {
116+ removable_switchs. push ( ( bb, targets. iter ( ) . count ( ) ) ) ;
117+ }
118+ }
119+
120+ if removable_switchs. is_empty ( ) {
121+ return ;
122+ }
123+
124+ let new_block = BasicBlockData :: new ( Some ( Terminator {
125+ source_info : body. basic_blocks [ removable_switchs[ 0 ] . 0 ] . terminator ( ) . source_info ,
126+ kind : TerminatorKind :: Unreachable ,
127+ } ) ) ;
128+ let unreachable_block = body. basic_blocks . as_mut ( ) . push ( new_block) ;
129+
130+ for ( bb, index) in removable_switchs {
131+ let bb = & mut body. basic_blocks . as_mut ( ) [ bb] ;
132+ let terminator = bb. terminator_mut ( ) ;
133+ let TerminatorKind :: SwitchInt { targets, .. } = & mut terminator. kind else { bug ! ( ) } ;
134+ targets. all_targets_mut ( ) [ index] = unreachable_block;
150135 }
151136 }
152137}
0 commit comments