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,17 +29,20 @@ 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 [ ..]
35
- && let StatementKind :: Assign ( box ( l, Rvalue :: Discriminant ( place) ) ) = stmt_before_term. kind
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
36
37
&& l. as_local ( ) == Some ( local)
37
- && let ty = place. ty ( body, tcx) . ty
38
- && ty. is_enum ( )
39
38
{
40
- Some ( ty)
41
- } else {
42
- None
39
+ let ty = place. ty ( body, tcx) . ty ;
40
+ if ty. is_enum ( ) {
41
+ return Some ( ty) ;
42
+ }
43
43
}
44
+
45
+ None
44
46
}
45
47
46
48
fn variant_discriminants < ' tcx > (
@@ -67,28 +69,6 @@ fn variant_discriminants<'tcx>(
67
69
}
68
70
}
69
71
70
- /// Ensures that the `otherwise` branch leads to an unreachable bb, returning `None` if so and a new
71
- /// bb to use as the new target if not.
72
- fn ensure_otherwise_unreachable < ' tcx > (
73
- body : & Body < ' tcx > ,
74
- targets : & SwitchTargets ,
75
- ) -> Option < BasicBlockData < ' tcx > > {
76
- let otherwise = targets. otherwise ( ) ;
77
- let bb = & body. basic_blocks [ otherwise] ;
78
- if bb. terminator ( ) . kind == TerminatorKind :: Unreachable
79
- && bb. statements . iter ( ) . all ( |s| matches ! ( & s. kind, StatementKind :: StorageDead ( _) ) )
80
- {
81
- return None ;
82
- }
83
-
84
- let mut new_block = BasicBlockData :: new ( Some ( Terminator {
85
- source_info : bb. terminator ( ) . source_info ,
86
- kind : TerminatorKind :: Unreachable ,
87
- } ) ) ;
88
- new_block. is_cleanup = bb. is_cleanup ;
89
- Some ( new_block)
90
- }
91
-
92
72
impl < ' tcx > MirPass < ' tcx > for UninhabitedEnumBranching {
93
73
fn is_enabled ( & self , sess : & rustc_session:: Session ) -> bool {
94
74
sess. mir_opt_level ( ) > 0
@@ -97,13 +77,16 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
97
77
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
98
78
trace ! ( "UninhabitedEnumBranching starting for {:?}" , body. source) ;
99
79
100
- 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 ( ) {
101
83
trace ! ( "processing block {:?}" , bb) ;
102
84
103
- let Some ( discriminant_ty) = get_switched_on_type ( & body. basic_blocks [ bb] , tcx, body)
104
- else {
85
+ if bb_data. is_cleanup {
105
86
continue ;
106
- } ;
87
+ }
88
+
89
+ let Some ( discriminant_ty) = get_switched_on_type ( & bb_data, tcx, body) else { continue } ;
107
90
108
91
let layout = tcx. layout_of (
109
92
tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) . and ( discriminant_ty) ,
@@ -117,31 +100,38 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
117
100
118
101
trace ! ( "allowed_variants = {:?}" , allowed_variants) ;
119
102
120
- if let TerminatorKind :: SwitchInt { targets, .. } =
121
- & mut body. basic_blocks_mut ( ) [ bb] . terminator_mut ( ) . kind
122
- {
123
- let mut new_targets = SwitchTargets :: new (
124
- targets. iter ( ) . filter ( |( val, _) | allowed_variants. contains ( val) ) ,
125
- targets. otherwise ( ) ,
126
- ) ;
127
-
128
- if new_targets. iter ( ) . count ( ) == allowed_variants. len ( ) {
129
- if let Some ( updated) = ensure_otherwise_unreachable ( body, & new_targets) {
130
- let new_otherwise = body. basic_blocks_mut ( ) . push ( updated) ;
131
- * new_targets. all_targets_mut ( ) . last_mut ( ) . unwrap ( ) = new_otherwise;
132
- }
133
- }
103
+ let terminator = bb_data. terminator ( ) ;
104
+ let TerminatorKind :: SwitchInt { targets, .. } = & terminator. kind else { bug ! ( ) } ;
134
105
135
- if let TerminatorKind :: SwitchInt { targets , .. } =
136
- & mut body . basic_blocks_mut ( ) [ bb ] . terminator_mut ( ) . kind
137
- {
138
- * 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 ;
139
110
} else {
140
- unreachable ! ( )
111
+ removable_switchs . push ( ( bb , index ) ) ;
141
112
}
142
- } else {
143
- unreachable ! ( )
144
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;
145
135
}
146
136
}
147
137
}
0 commit comments