@@ -207,9 +207,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
207
207
/// - at any point, any or none of the patterns and guards seen so far may have been tested;
208
208
/// - after the match, any of the patterns may have matched.
209
209
///
210
- /// For example, all of these would fail to error if borrowck could see the real CFG (taken from
211
- /// `tests/ui/nll/match-cfg-fake-edges.rs`):
212
- /// ```rust,ignore(too many errors)
210
+ /// For example, all of these would fail to error if borrowck could see the real CFG (examples
211
+ /// taken from `tests/ui/nll/match-cfg-fake-edges.rs`):
212
+ /// ```rust,ignore(too many errors, this is already in the test suite )
213
213
/// let x = String::new();
214
214
/// let _ = match true {
215
215
/// _ => {},
@@ -246,10 +246,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
246
246
/// };
247
247
/// ```
248
248
///
249
- /// To ensure this, we add false edges:
249
+ /// We add false edges to act as if we were naively matching each arm in order. What we need is
250
+ /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding
251
+ /// block to next candidate D's pre-binding block. For maximum precision (needed for deref
252
+ /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to
253
+ /// avoid loops).
250
254
///
251
- /// * From each pre-binding block to the next pre-binding block.
252
- /// * From each otherwise block to the next pre-binding block.
255
+ /// This turns out to be easy to compute: that block is the `start_block` of the first call to
256
+ /// `match_candidates` where D is the first candidate in the list.
257
+ ///
258
+ /// For example:
259
+ /// ```rust
260
+ /// # let (x, y) = (true, true);
261
+ /// match (x, y) {
262
+ /// (true, true) => 1,
263
+ /// (false, true) => 2,
264
+ /// (true, false) => 3,
265
+ /// _ => 4,
266
+ /// }
267
+ /// # ;
268
+ /// ```
269
+ /// In this example, the pre-binding block of arm 1 has a false edge to the block for result
270
+ /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks
271
+ /// of the next arm.
272
+ ///
273
+ /// On top of this, we also add a false edge from the otherwise_block of each guard to the
274
+ /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which
275
+ /// guards may have run.
253
276
#[ instrument( level = "debug" , skip( self , arms) ) ]
254
277
pub ( crate ) fn match_expr (
255
278
& mut self ,
@@ -396,7 +419,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
396
419
for candidate in candidates {
397
420
candidate. visit_leaves ( |leaf_candidate| {
398
421
if let Some ( ref mut prev) = previous_candidate {
399
- prev. next_candidate_pre_binding_block = leaf_candidate. pre_binding_block ;
422
+ assert ! ( leaf_candidate. false_edge_start_block. is_some( ) ) ;
423
+ prev. next_candidate_start_block = leaf_candidate. false_edge_start_block ;
400
424
}
401
425
previous_candidate = Some ( leaf_candidate) ;
402
426
} ) ;
@@ -1060,8 +1084,12 @@ struct Candidate<'pat, 'tcx> {
1060
1084
1061
1085
/// The block before the `bindings` have been established.
1062
1086
pre_binding_block : Option < BasicBlock > ,
1063
- /// The pre-binding block of the next candidate.
1064
- next_candidate_pre_binding_block : Option < BasicBlock > ,
1087
+
1088
+ /// The earliest block that has only candidates >= this one as descendents. Used for false
1089
+ /// edges, see the doc for [`Builder::match_expr`].
1090
+ false_edge_start_block : Option < BasicBlock > ,
1091
+ /// The `false_edge_start_block` of the next candidate.
1092
+ next_candidate_start_block : Option < BasicBlock > ,
1065
1093
}
1066
1094
1067
1095
impl < ' tcx , ' pat > Candidate < ' pat , ' tcx > {
@@ -1083,7 +1111,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
1083
1111
or_span : None ,
1084
1112
otherwise_block : None ,
1085
1113
pre_binding_block : None ,
1086
- next_candidate_pre_binding_block : None ,
1114
+ false_edge_start_block : None ,
1115
+ next_candidate_start_block : None ,
1087
1116
}
1088
1117
}
1089
1118
@@ -1379,6 +1408,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1379
1408
otherwise_block : BasicBlock ,
1380
1409
candidates : & mut [ & mut Candidate < ' _ , ' tcx > ] ,
1381
1410
) {
1411
+ if let [ first, ..] = candidates {
1412
+ if first. false_edge_start_block . is_none ( ) {
1413
+ first. false_edge_start_block = Some ( start_block) ;
1414
+ }
1415
+ }
1416
+
1382
1417
match candidates {
1383
1418
[ ] => {
1384
1419
// If there are no candidates that still need testing, we're done. Since all matches are
@@ -1599,6 +1634,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1599
1634
. into_iter ( )
1600
1635
. map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, candidate. has_guard ) )
1601
1636
. collect ( ) ;
1637
+ candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
1602
1638
}
1603
1639
1604
1640
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
@@ -1617,6 +1653,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1617
1653
if can_merge {
1618
1654
let any_matches = self . cfg . start_new_block ( ) ;
1619
1655
let source_info = self . source_info ( candidate. or_span . unwrap ( ) ) ;
1656
+ if candidate. false_edge_start_block . is_none ( ) {
1657
+ candidate. false_edge_start_block =
1658
+ candidate. subcandidates [ 0 ] . false_edge_start_block ;
1659
+ }
1620
1660
for subcandidate in mem:: take ( & mut candidate. subcandidates ) {
1621
1661
let or_block = subcandidate. pre_binding_block . unwrap ( ) ;
1622
1662
self . cfg . goto ( or_block, source_info, any_matches) ;
@@ -2047,12 +2087,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2047
2087
2048
2088
let mut block = candidate. pre_binding_block . unwrap ( ) ;
2049
2089
2050
- if candidate. next_candidate_pre_binding_block . is_some ( ) {
2090
+ if candidate. next_candidate_start_block . is_some ( ) {
2051
2091
let fresh_block = self . cfg . start_new_block ( ) ;
2052
2092
self . false_edges (
2053
2093
block,
2054
2094
fresh_block,
2055
- candidate. next_candidate_pre_binding_block ,
2095
+ candidate. next_candidate_start_block ,
2056
2096
candidate_source_info,
2057
2097
) ;
2058
2098
block = fresh_block;
@@ -2210,7 +2250,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2210
2250
self . false_edges (
2211
2251
otherwise_post_guard_block,
2212
2252
otherwise_block,
2213
- candidate. next_candidate_pre_binding_block ,
2253
+ candidate. next_candidate_start_block ,
2214
2254
source_info,
2215
2255
) ;
2216
2256
0 commit comments