@@ -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
/// _ => {},
@@ -243,10 +243,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
243
243
/// };
244
244
/// ```
245
245
///
246
- /// To ensure this, we add false edges:
246
+ /// We add false edges to act as if we were naively matching each arm in order. What we need is
247
+ /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding
248
+ /// block to next candidate D's pre-binding block. For maximum precision (needed for deref
249
+ /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to
250
+ /// avoid loops).
247
251
///
248
- /// * From each pre-binding block to the next pre-binding block.
249
- /// * From each otherwise block to the next pre-binding block.
252
+ /// This turns out to be easy to compute: that block is the `start_block` of the first call to
253
+ /// `match_candidates` where D is the first candidate in the list.
254
+ ///
255
+ /// For example:
256
+ /// ```rust
257
+ /// # let (x, y) = (true, true);
258
+ /// match (x, y) {
259
+ /// (true, true) => 1,
260
+ /// (false, true) => 2,
261
+ /// (true, false) => 3,
262
+ /// _ => 4,
263
+ /// }
264
+ /// # ;
265
+ /// ```
266
+ /// In this example, the pre-binding block of arm 1 has a false edge to the block for result
267
+ /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks
268
+ /// of the next arm.
269
+ ///
270
+ /// On top of this, we also add a false edge from the otherwise_block of each guard to the
271
+ /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which
272
+ /// guards may have run.
250
273
#[ instrument( level = "debug" , skip( self , arms) ) ]
251
274
pub ( crate ) fn match_expr (
252
275
& mut self ,
@@ -393,7 +416,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
393
416
for candidate in candidates {
394
417
candidate. visit_leaves ( |leaf_candidate| {
395
418
if let Some ( ref mut prev) = previous_candidate {
396
- prev. next_candidate_pre_binding_block = leaf_candidate. pre_binding_block ;
419
+ assert ! ( leaf_candidate. false_edge_start_block. is_some( ) ) ;
420
+ prev. next_candidate_start_block = leaf_candidate. false_edge_start_block ;
397
421
}
398
422
previous_candidate = Some ( leaf_candidate) ;
399
423
} ) ;
@@ -1057,8 +1081,12 @@ struct Candidate<'pat, 'tcx> {
1057
1081
1058
1082
/// The block before the `bindings` have been established.
1059
1083
pre_binding_block : Option < BasicBlock > ,
1060
- /// The pre-binding block of the next candidate.
1061
- next_candidate_pre_binding_block : Option < BasicBlock > ,
1084
+
1085
+ /// The earliest block that has only candidates >= this one as descendents. Used for false
1086
+ /// edges, see the doc for [`Builder::match_expr`].
1087
+ false_edge_start_block : Option < BasicBlock > ,
1088
+ /// The `false_edge_start_block` of the next candidate.
1089
+ next_candidate_start_block : Option < BasicBlock > ,
1062
1090
}
1063
1091
1064
1092
impl < ' tcx , ' pat > Candidate < ' pat , ' tcx > {
@@ -1080,7 +1108,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
1080
1108
or_span : None ,
1081
1109
otherwise_block : None ,
1082
1110
pre_binding_block : None ,
1083
- next_candidate_pre_binding_block : None ,
1111
+ false_edge_start_block : None ,
1112
+ next_candidate_start_block : None ,
1084
1113
}
1085
1114
}
1086
1115
@@ -1376,6 +1405,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1376
1405
otherwise_block : BasicBlock ,
1377
1406
candidates : & mut [ & mut Candidate < ' _ , ' tcx > ] ,
1378
1407
) {
1408
+ if let [ first, ..] = candidates {
1409
+ if first. false_edge_start_block . is_none ( ) {
1410
+ first. false_edge_start_block = Some ( start_block) ;
1411
+ }
1412
+ }
1413
+
1379
1414
match candidates {
1380
1415
[ ] => {
1381
1416
// If there are no candidates that still need testing, we're done. Since all matches are
@@ -1596,6 +1631,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1596
1631
. into_iter ( )
1597
1632
. map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, candidate. has_guard ) )
1598
1633
. collect ( ) ;
1634
+ candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
1599
1635
}
1600
1636
1601
1637
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
@@ -1614,6 +1650,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1614
1650
if can_merge {
1615
1651
let any_matches = self . cfg . start_new_block ( ) ;
1616
1652
let source_info = self . source_info ( candidate. or_span . unwrap ( ) ) ;
1653
+ if candidate. false_edge_start_block . is_none ( ) {
1654
+ candidate. false_edge_start_block =
1655
+ candidate. subcandidates [ 0 ] . false_edge_start_block ;
1656
+ }
1617
1657
for subcandidate in mem:: take ( & mut candidate. subcandidates ) {
1618
1658
let or_block = subcandidate. pre_binding_block . unwrap ( ) ;
1619
1659
self . cfg . goto ( or_block, source_info, any_matches) ;
@@ -2044,12 +2084,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2044
2084
2045
2085
let mut block = candidate. pre_binding_block . unwrap ( ) ;
2046
2086
2047
- if candidate. next_candidate_pre_binding_block . is_some ( ) {
2087
+ if candidate. next_candidate_start_block . is_some ( ) {
2048
2088
let fresh_block = self . cfg . start_new_block ( ) ;
2049
2089
self . false_edges (
2050
2090
block,
2051
2091
fresh_block,
2052
- candidate. next_candidate_pre_binding_block ,
2092
+ candidate. next_candidate_start_block ,
2053
2093
candidate_source_info,
2054
2094
) ;
2055
2095
block = fresh_block;
@@ -2207,7 +2247,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2207
2247
self . false_edges (
2208
2248
otherwise_post_guard_block,
2209
2249
otherwise_block,
2210
- candidate. next_candidate_pre_binding_block ,
2250
+ candidate. next_candidate_start_block ,
2211
2251
source_info,
2212
2252
) ;
2213
2253
0 commit comments