2
2
3
3
use crate :: MirPass ;
4
4
use rustc_data_structures:: fx:: FxHashSet ;
5
+ use rustc_middle:: mir:: patch:: MirPatch ;
5
6
use rustc_middle:: mir:: {
6
- BasicBlockData , Body , Local , Operand , Rvalue , StatementKind , Terminator , TerminatorKind ,
7
+ BasicBlock , BasicBlockData , BasicBlocks , Body , Local , Operand , Rvalue , StatementKind ,
8
+ TerminatorKind ,
7
9
} ;
8
10
use rustc_middle:: ty:: layout:: TyAndLayout ;
9
11
use rustc_middle:: ty:: { Ty , TyCtxt } ;
@@ -77,7 +79,8 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
77
79
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
78
80
trace ! ( "UninhabitedEnumBranching starting for {:?}" , body. source) ;
79
81
80
- let mut removable_switchs = Vec :: new ( ) ;
82
+ let mut unreachable_targets = Vec :: new ( ) ;
83
+ let mut patch = MirPatch :: new ( body) ;
81
84
82
85
for ( bb, bb_data) in body. basic_blocks . iter_enumerated ( ) {
83
86
trace ! ( "processing block {:?}" , bb) ;
@@ -92,46 +95,73 @@ impl<'tcx> MirPass<'tcx> for UninhabitedEnumBranching {
92
95
tcx. param_env_reveal_all_normalized ( body. source . def_id ( ) ) . and ( discriminant_ty) ,
93
96
) ;
94
97
95
- let allowed_variants = if let Ok ( layout) = layout {
98
+ let mut allowed_variants = if let Ok ( layout) = layout {
96
99
variant_discriminants ( & layout, discriminant_ty, tcx)
100
+ } else if let Some ( variant_range) = discriminant_ty. variant_range ( tcx) {
101
+ variant_range
102
+ . map ( |variant| {
103
+ discriminant_ty. discriminant_for_variant ( tcx, variant) . unwrap ( ) . val
104
+ } )
105
+ . collect ( )
97
106
} else {
98
107
continue ;
99
108
} ;
100
109
101
110
trace ! ( "allowed_variants = {:?}" , allowed_variants) ;
102
111
103
- let terminator = bb_data. terminator ( ) ;
104
- let TerminatorKind :: SwitchInt { targets, .. } = & terminator. kind else { bug ! ( ) } ;
112
+ unreachable_targets. clear ( ) ;
113
+ let TerminatorKind :: SwitchInt { targets, discr } = & bb_data. terminator ( ) . kind else {
114
+ bug ! ( )
115
+ } ;
105
116
106
- let mut reachable_count = 0 ;
107
117
for ( index, ( val, _) ) in targets. iter ( ) . enumerate ( ) {
108
- if allowed_variants. contains ( & val) {
109
- reachable_count += 1 ;
110
- } else {
111
- removable_switchs. push ( ( bb, index) ) ;
118
+ if !allowed_variants. remove ( & val) {
119
+ unreachable_targets. push ( index) ;
120
+ }
121
+ }
122
+ let otherwise_is_empty_unreachable =
123
+ body. basic_blocks [ targets. otherwise ( ) ] . is_empty_unreachable ( ) ;
124
+ // After resolving https://github.com/llvm/llvm-project/issues/78578,
125
+ // we can remove the limit on the number of successors.
126
+ fn check_successors ( basic_blocks : & BasicBlocks < ' _ > , bb : BasicBlock ) -> bool {
127
+ let mut successors = basic_blocks[ bb] . terminator ( ) . successors ( ) ;
128
+ let Some ( first_successor) = successors. next ( ) else { return true } ;
129
+ if successors. next ( ) . is_some ( ) {
130
+ return true ;
112
131
}
132
+ if let TerminatorKind :: SwitchInt { .. } =
133
+ & basic_blocks[ first_successor] . terminator ( ) . kind
134
+ {
135
+ return false ;
136
+ } ;
137
+ true
113
138
}
139
+ let otherwise_is_last_variant = !otherwise_is_empty_unreachable
140
+ && allowed_variants. len ( ) == 1
141
+ && check_successors ( & body. basic_blocks , targets. otherwise ( ) ) ;
142
+ let replace_otherwise_to_unreachable = otherwise_is_last_variant
143
+ || !otherwise_is_empty_unreachable && allowed_variants. is_empty ( ) ;
114
144
115
- if reachable_count == allowed_variants . len ( ) {
116
- removable_switchs . push ( ( bb , targets . iter ( ) . count ( ) ) ) ;
145
+ if unreachable_targets . is_empty ( ) && !replace_otherwise_to_unreachable {
146
+ continue ;
117
147
}
118
- }
119
148
120
- if removable_switchs. is_empty ( ) {
121
- return ;
149
+ let unreachable_block = patch. unreachable_no_cleanup_block ( ) ;
150
+ let mut targets = targets. clone ( ) ;
151
+ if replace_otherwise_to_unreachable {
152
+ if otherwise_is_last_variant {
153
+ #[ allow( rustc:: potential_query_instability) ]
154
+ let last_variant = * allowed_variants. iter ( ) . next ( ) . unwrap ( ) ;
155
+ targets. add_target ( last_variant, targets. otherwise ( ) ) ;
156
+ }
157
+ unreachable_targets. push ( targets. iter ( ) . count ( ) ) ;
158
+ }
159
+ for index in unreachable_targets. iter ( ) {
160
+ targets. all_targets_mut ( ) [ * index] = unreachable_block;
161
+ }
162
+ patch. patch_terminator ( bb, TerminatorKind :: SwitchInt { targets, discr : discr. clone ( ) } ) ;
122
163
}
123
164
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;
135
- }
165
+ patch. apply ( body) ;
136
166
}
137
167
}
0 commit comments