3
3
use crate :: MirPass ;
4
4
use rustc_data_structures:: fx:: FxHashSet ;
5
5
use rustc_middle:: mir:: {
6
- BasicBlockData , Body , Local , Operand , Rvalue , StatementKind , SwitchTargets , Terminator ,
7
- TerminatorKind ,
6
+ BasicBlockData , Body , Local , Operand , Rvalue , StatementKind , Terminator , TerminatorKind ,
8
7
} ;
9
8
use rustc_middle:: ty:: layout:: TyAndLayout ;
10
9
use rustc_middle:: ty:: { Ty , TyCtxt } ;
@@ -30,18 +29,16 @@ fn get_switched_on_type<'tcx>(
30
29
let terminator = block_data. terminator ( ) ;
31
30
32
31
// 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) ;
45
42
}
46
43
}
47
44
@@ -72,28 +69,6 @@ fn variant_discriminants<'tcx>(
72
69
}
73
70
}
74
71
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
-
97
72
impl < ' tcx > MirPass < ' tcx > for UninhabitedEnumBranching {
98
73
fn is_enabled ( & self , sess : & rustc_session:: Session ) -> bool {
99
74
sess. mir_opt_level ( ) > 0
@@ -102,13 +77,16 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
102
77
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
103
78
trace ! ( "UninhabitedEnumBranching starting for {:?}" , body. source) ;
104
79
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 ( ) {
106
83
trace ! ( "processing block {:?}" , bb) ;
107
84
108
- let Some ( discriminant_ty) = get_switched_on_type ( & body. basic_blocks [ bb] , tcx, body)
109
- else {
85
+ if bb_data. is_cleanup {
110
86
continue ;
111
- } ;
87
+ }
88
+
89
+ let Some ( discriminant_ty) = get_switched_on_type ( & bb_data, tcx, body) else { continue } ;
112
90
113
91
let layout = tcx. layout_of (
114
92
tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) . and ( discriminant_ty) ,
@@ -122,31 +100,38 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
122
100
123
101
trace ! ( "allowed_variants = {:?}" , allowed_variants) ;
124
102
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 ! ( ) } ;
139
105
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 ;
144
110
} else {
145
- unreachable ! ( )
111
+ removable_switchs . push ( ( bb , index ) ) ;
146
112
}
147
- } else {
148
- unreachable ! ( )
149
113
}
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;
150
135
}
151
136
}
152
137
}
0 commit comments