@@ -207,9 +207,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
207207 /// - at any point, any or none of the patterns and guards seen so far may have been tested;
208208 /// - after the match, any of the patterns may have matched.
209209 ///
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 )
213213 /// let x = String::new();
214214 /// let _ = match true {
215215 /// _ => {},
@@ -243,10 +243,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
243243 /// };
244244 /// ```
245245 ///
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).
247251 ///
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.
250273 #[ instrument( level = "debug" , skip( self , arms) ) ]
251274 pub ( crate ) fn match_expr (
252275 & mut self ,
@@ -393,7 +416,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
393416 for candidate in candidates {
394417 candidate. visit_leaves ( |leaf_candidate| {
395418 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 ;
397421 }
398422 previous_candidate = Some ( leaf_candidate) ;
399423 } ) ;
@@ -1057,8 +1081,12 @@ struct Candidate<'pat, 'tcx> {
10571081
10581082 /// The block before the `bindings` have been established.
10591083 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 > ,
10621090}
10631091
10641092impl < ' tcx , ' pat > Candidate < ' pat , ' tcx > {
@@ -1080,7 +1108,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
10801108 or_span : None ,
10811109 otherwise_block : None ,
10821110 pre_binding_block : None ,
1083- next_candidate_pre_binding_block : None ,
1111+ false_edge_start_block : None ,
1112+ next_candidate_start_block : None ,
10841113 }
10851114 }
10861115
@@ -1376,6 +1405,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13761405 otherwise_block : BasicBlock ,
13771406 candidates : & mut [ & mut Candidate < ' _ , ' tcx > ] ,
13781407 ) {
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+
13791414 match candidates {
13801415 [ ] => {
13811416 // 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> {
15961631 . into_iter ( )
15971632 . map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, candidate. has_guard ) )
15981633 . collect ( ) ;
1634+ candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
15991635 }
16001636
16011637 /// 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> {
16141650 if can_merge {
16151651 let any_matches = self . cfg . start_new_block ( ) ;
16161652 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+ }
16171657 for subcandidate in mem:: take ( & mut candidate. subcandidates ) {
16181658 let or_block = subcandidate. pre_binding_block . unwrap ( ) ;
16191659 self . cfg . goto ( or_block, source_info, any_matches) ;
@@ -2044,12 +2084,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
20442084
20452085 let mut block = candidate. pre_binding_block . unwrap ( ) ;
20462086
2047- if candidate. next_candidate_pre_binding_block . is_some ( ) {
2087+ if candidate. next_candidate_start_block . is_some ( ) {
20482088 let fresh_block = self . cfg . start_new_block ( ) ;
20492089 self . false_edges (
20502090 block,
20512091 fresh_block,
2052- candidate. next_candidate_pre_binding_block ,
2092+ candidate. next_candidate_start_block ,
20532093 candidate_source_info,
20542094 ) ;
20552095 block = fresh_block;
@@ -2207,7 +2247,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22072247 self . false_edges (
22082248 otherwise_post_guard_block,
22092249 otherwise_block,
2210- candidate. next_candidate_pre_binding_block ,
2250+ candidate. next_candidate_start_block ,
22112251 source_info,
22122252 ) ;
22132253
0 commit comments