@@ -4,7 +4,7 @@ use crate::{
4
4
} ;
5
5
use rustc_middle:: mir:: * ;
6
6
use rustc_middle:: ty:: { Ty , TyCtxt } ;
7
- use std:: fmt:: Debug ;
7
+ use std:: { borrow :: Cow , fmt:: Debug } ;
8
8
9
9
use super :: simplify:: simplify_cfg;
10
10
@@ -98,22 +98,67 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
98
98
StatementKind :: Assign ( box ( Place :: from ( not_equal_temp) , not_equal_rvalue) ) ,
99
99
) ;
100
100
101
- // switch on the NotEqual. If true, then jump to the `otherwise` case.
102
- // If false, then jump to a basic block that then jumps to the correct disciminant case
101
+ // Switch on the NotEqual. If true, then jump to the `otherwise` case,
102
+ // since we know the discriminant values are not the same.
103
103
let true_case = opt_to_apply. infos [ 0 ] . first_switch_info . otherwise_bb ;
104
- let targets_second_switch =
105
- & opt_to_apply. infos [ 0 ] . second_switch_info . targets_with_values ;
106
- assert_eq ! (
107
- 1 ,
108
- targets_second_switch. len( ) ,
109
- "We should only have one target besides the otherwise"
110
- ) ;
111
104
112
- // Since we know that the two discriminant values are equal,
113
- // we can jump directly to the target in the second switch
114
- let false_case = targets_second_switch[ 0 ] . 0 ;
105
+ // In the false case we know that the two discriminant values are equal,
106
+ // however we still need to account for the following scenario:
107
+ // ```rust
108
+ // match (x, y) {
109
+ // (Some(_), Some(_)) => 0,
110
+ // _ => 2
111
+ // }
112
+ // ```
113
+ //
114
+ // Here, the two match arms have the same discriminant values, but
115
+ // we need to make sure that we did not reach a `(None, None)` pattern.
116
+ // We therefore construct a new basic block that can disambiguate where to go.
117
+
118
+ let mut targets_and_values_to_jump_to = vec ! [ ] ;
119
+ for info in opt_to_apply. infos . iter ( ) {
120
+ for ( _, value) in info. first_switch_info . targets_with_values . iter ( ) {
121
+ // Find corresponding value in second switch info -
122
+ // this is where we want to jump to
123
+ if let Some ( ( target_to_jump_to, _) ) = info
124
+ . second_switch_info
125
+ . targets_with_values
126
+ . iter ( )
127
+ . find ( |( _, second_value) | value == second_value)
128
+ {
129
+ targets_and_values_to_jump_to. push ( ( target_to_jump_to, value) ) ;
130
+ }
131
+ }
132
+ }
133
+
134
+ let ( mut targets_to_jump_to, values_to_jump_to) : ( Vec < _ > , Vec < _ > ) =
135
+ targets_and_values_to_jump_to. iter ( ) . cloned ( ) . unzip ( ) ;
136
+
137
+ // add otherwise case in the end
138
+ targets_to_jump_to. push ( opt_to_apply. infos [ 0 ] . first_switch_info . otherwise_bb ) ;
139
+
140
+ // new block that jumps to the correct discriminant case. This block is switched to if the discriminants are equal
141
+ let mut new_switch_data = BasicBlockData :: new ( Some ( Terminator {
142
+ source_info : opt_to_apply. infos [ 0 ] . second_switch_info . discr_source_info ,
143
+ kind : TerminatorKind :: SwitchInt {
144
+ // the first and second discriminants are equal, so just pick one
145
+ discr : Operand :: Copy ( first_descriminant_place) ,
146
+ switch_ty : discr_type,
147
+ values : Cow :: from ( values_to_jump_to) ,
148
+ targets : targets_to_jump_to,
149
+ } ,
150
+ } ) ) ;
151
+
152
+ let basic_block_first_switch = opt_to_apply. basic_block_first_switch ;
153
+
154
+ // Inherit the is_cleanup from the bb we are jumping from, which is where the first switch is
155
+ new_switch_data. is_cleanup = body. basic_blocks ( ) [ basic_block_first_switch] . is_cleanup ;
156
+
157
+ let new_switch_bb = patch. new_block ( new_switch_data) ;
158
+ let false_case = new_switch_bb;
159
+
115
160
patch. patch_terminator (
116
- opt_to_apply . basic_block_first_switch ,
161
+ basic_block_first_switch,
117
162
TerminatorKind :: if_ (
118
163
tcx,
119
164
Operand :: Move ( Place :: from ( not_equal_temp) ) ,
0 commit comments