@@ -258,10 +258,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
258
258
/// };
259
259
/// ```
260
260
///
261
- /// To ensure this, we add false edges:
261
+ /// We add false edges to act as if we were naively matching each arm in order. What we need is
262
+ /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding
263
+ /// block to next candidate D's pre-binding block. For maximum precision (needed for deref
264
+ /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to
265
+ /// avoid loops).
262
266
///
263
- /// * From each pre-binding block to the next pre-binding block.
264
- /// * From each otherwise block to the next pre-binding block.
267
+ /// This turns out to be easy to compute: that block is the `start_block` of the first call to
268
+ /// `match_candidates` where D is the first candidate in the list.
269
+ ///
270
+ /// For example:
271
+ /// ```rust
272
+ /// # let (x, y) = (true, true);
273
+ /// match (x, y) {
274
+ /// (true, true) => 1,
275
+ /// (false, true) => 2,
276
+ /// (true, false) => 3,
277
+ /// _ => 4,
278
+ /// }
279
+ /// # ;
280
+ /// ```
281
+ /// In this example, the pre-binding block of arm 1 has a false edge to the block for result
282
+ /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks
283
+ /// of the next arm.
284
+ ///
285
+ /// On top of this, we also add a false edge from the otherwise_block of each guard to the
286
+ /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which
287
+ /// guards may have run.
265
288
#[ instrument( level = "debug" , skip( self , arms) ) ]
266
289
pub ( crate ) fn match_expr (
267
290
& mut self ,
@@ -407,7 +430,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
407
430
for candidate in candidates {
408
431
candidate. visit_leaves ( |leaf_candidate| {
409
432
if let Some ( ref mut prev) = previous_candidate {
410
- prev. next_candidate_pre_binding_block = leaf_candidate. pre_binding_block ;
433
+ assert ! ( leaf_candidate. false_edge_start_block. is_some( ) ) ;
434
+ prev. next_candidate_start_block = leaf_candidate. false_edge_start_block ;
411
435
}
412
436
previous_candidate = Some ( leaf_candidate) ;
413
437
} ) ;
@@ -1052,8 +1076,12 @@ struct Candidate<'pat, 'tcx> {
1052
1076
1053
1077
/// The block before the `bindings` have been established.
1054
1078
pre_binding_block : Option < BasicBlock > ,
1055
- /// The pre-binding block of the next candidate.
1056
- next_candidate_pre_binding_block : Option < BasicBlock > ,
1079
+
1080
+ /// The earliest block that has only candidates >= this one as descendents. Used for false
1081
+ /// edges, see the doc for [`Builder::match_expr`].
1082
+ false_edge_start_block : Option < BasicBlock > ,
1083
+ /// The `false_edge_start_block` of the next candidate.
1084
+ next_candidate_start_block : Option < BasicBlock > ,
1057
1085
}
1058
1086
1059
1087
impl < ' tcx , ' pat > Candidate < ' pat , ' tcx > {
@@ -1075,7 +1103,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
1075
1103
or_span : None ,
1076
1104
otherwise_block : None ,
1077
1105
pre_binding_block : None ,
1078
- next_candidate_pre_binding_block : None ,
1106
+ false_edge_start_block : None ,
1107
+ next_candidate_start_block : None ,
1079
1108
}
1080
1109
}
1081
1110
@@ -1367,6 +1396,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1367
1396
otherwise_block : BasicBlock ,
1368
1397
candidates : & mut [ & mut Candidate < ' _ , ' tcx > ] ,
1369
1398
) {
1399
+ if let [ first, ..] = candidates {
1400
+ if first. false_edge_start_block . is_none ( ) {
1401
+ first. false_edge_start_block = Some ( start_block) ;
1402
+ }
1403
+ }
1404
+
1370
1405
match candidates {
1371
1406
[ ] => {
1372
1407
// If there are no candidates that still need testing, we're done. Since all matches are
@@ -1587,6 +1622,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1587
1622
. into_iter ( )
1588
1623
. map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, candidate. has_guard ) )
1589
1624
. collect ( ) ;
1625
+ candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
1590
1626
}
1591
1627
1592
1628
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
@@ -1606,6 +1642,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1606
1642
let any_matches = self . cfg . start_new_block ( ) ;
1607
1643
let or_span = candidate. or_span . take ( ) . unwrap ( ) ;
1608
1644
let source_info = self . source_info ( or_span) ;
1645
+ if candidate. false_edge_start_block . is_none ( ) {
1646
+ candidate. false_edge_start_block =
1647
+ candidate. subcandidates [ 0 ] . false_edge_start_block ;
1648
+ }
1609
1649
for subcandidate in mem:: take ( & mut candidate. subcandidates ) {
1610
1650
let or_block = subcandidate. pre_binding_block . unwrap ( ) ;
1611
1651
self . cfg . goto ( or_block, source_info, any_matches) ;
@@ -2021,12 +2061,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2021
2061
2022
2062
let mut block = candidate. pre_binding_block . unwrap ( ) ;
2023
2063
2024
- if candidate. next_candidate_pre_binding_block . is_some ( ) {
2064
+ if candidate. next_candidate_start_block . is_some ( ) {
2025
2065
let fresh_block = self . cfg . start_new_block ( ) ;
2026
2066
self . false_edges (
2027
2067
block,
2028
2068
fresh_block,
2029
- candidate. next_candidate_pre_binding_block ,
2069
+ candidate. next_candidate_start_block ,
2030
2070
candidate_source_info,
2031
2071
) ;
2032
2072
block = fresh_block;
@@ -2174,7 +2214,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2174
2214
self . false_edges (
2175
2215
otherwise_post_guard_block,
2176
2216
otherwise_block,
2177
- candidate. next_candidate_pre_binding_block ,
2217
+ candidate. next_candidate_start_block ,
2178
2218
source_info,
2179
2219
) ;
2180
2220
0 commit comments