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