@@ -258,10 +258,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
258258 /// };
259259 /// ```
260260 ///
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).
262266 ///
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.
265288 #[ instrument( level = "debug" , skip( self , arms) ) ]
266289 pub ( crate ) fn match_expr (
267290 & mut self ,
@@ -407,7 +430,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
407430 for candidate in candidates {
408431 candidate. visit_leaves ( |leaf_candidate| {
409432 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 ;
411435 }
412436 previous_candidate = Some ( leaf_candidate) ;
413437 } ) ;
@@ -1052,8 +1076,12 @@ struct Candidate<'pat, 'tcx> {
10521076
10531077 /// The block before the `bindings` have been established.
10541078 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 > ,
10571085}
10581086
10591087impl < ' tcx , ' pat > Candidate < ' pat , ' tcx > {
@@ -1075,7 +1103,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
10751103 or_span : None ,
10761104 otherwise_block : None ,
10771105 pre_binding_block : None ,
1078- next_candidate_pre_binding_block : None ,
1106+ false_edge_start_block : None ,
1107+ next_candidate_start_block : None ,
10791108 }
10801109 }
10811110
@@ -1367,6 +1396,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13671396 otherwise_block : BasicBlock ,
13681397 candidates : & mut [ & mut Candidate < ' _ , ' tcx > ] ,
13691398 ) {
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+
13701405 match candidates {
13711406 [ ] => {
13721407 // 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> {
15871622 . into_iter ( )
15881623 . map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, candidate. has_guard ) )
15891624 . collect ( ) ;
1625+ candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
15901626 }
15911627
15921628 /// 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> {
16061642 let any_matches = self . cfg . start_new_block ( ) ;
16071643 let or_span = candidate. or_span . take ( ) . unwrap ( ) ;
16081644 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+ }
16091649 for subcandidate in mem:: take ( & mut candidate. subcandidates ) {
16101650 let or_block = subcandidate. pre_binding_block . unwrap ( ) ;
16111651 self . cfg . goto ( or_block, source_info, any_matches) ;
@@ -2021,12 +2061,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
20212061
20222062 let mut block = candidate. pre_binding_block . unwrap ( ) ;
20232063
2024- if candidate. next_candidate_pre_binding_block . is_some ( ) {
2064+ if candidate. next_candidate_start_block . is_some ( ) {
20252065 let fresh_block = self . cfg . start_new_block ( ) ;
20262066 self . false_edges (
20272067 block,
20282068 fresh_block,
2029- candidate. next_candidate_pre_binding_block ,
2069+ candidate. next_candidate_start_block ,
20302070 candidate_source_info,
20312071 ) ;
20322072 block = fresh_block;
@@ -2174,7 +2214,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
21742214 self . false_edges (
21752215 otherwise_post_guard_block,
21762216 otherwise_block,
2177- candidate. next_candidate_pre_binding_block ,
2217+ candidate. next_candidate_start_block ,
21782218 source_info,
21792219 ) ;
21802220
0 commit comments