diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index d9104ebb5ae..367c391b45a 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -258,10 +258,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// }; /// ``` /// - /// To ensure this, we add false edges: + /// We add false edges to act as if we were naively matching each arm in order. What we need is + /// a (fake) path from each candidate to the next, specifically from candidate C's pre-binding + /// block to next candidate D's pre-binding block. For maximum precision (needed for deref + /// patterns), we choose the earliest node on D's success path that doesn't also lead to C (to + /// avoid loops). /// - /// * From each pre-binding block to the next pre-binding block. - /// * From each otherwise block to the next pre-binding block. + /// This turns out to be easy to compute: that block is the `start_block` of the first call to + /// `match_candidates` where D is the first candidate in the list. + /// + /// For example: + /// ```rust + /// # let (x, y) = (true, true); + /// match (x, y) { + /// (true, true) => 1, + /// (false, true) => 2, + /// (true, false) => 3, + /// _ => 4, + /// } + /// # ; + /// ``` + /// In this example, the pre-binding block of arm 1 has a false edge to the block for result + /// `false` of the first test on `x`. The other arms have false edges to the pre-binding blocks + /// of the next arm. + /// + /// On top of this, we also add a false edge from the otherwise_block of each guard to the + /// aforementioned start block of the next candidate, to ensure borrock doesn't rely on which + /// guards may have run. #[instrument(level = "debug", skip(self, arms))] pub(crate) fn match_expr( &mut self, @@ -407,7 +430,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for candidate in candidates { candidate.visit_leaves(|leaf_candidate| { if let Some(ref mut prev) = previous_candidate { - prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; + assert!(leaf_candidate.false_edge_start_block.is_some()); + prev.next_candidate_start_block = leaf_candidate.false_edge_start_block; } previous_candidate = Some(leaf_candidate); }); @@ -1052,8 +1076,12 @@ struct Candidate<'pat, 'tcx> { /// The block before the `bindings` have been established. pre_binding_block: Option, - /// The pre-binding block of the next candidate. - next_candidate_pre_binding_block: Option, + + /// The earliest block that has only candidates >= this one as descendents. Used for false + /// edges, see the doc for [`Builder::match_expr`]. + false_edge_start_block: Option, + /// The `false_edge_start_block` of the next candidate. + next_candidate_start_block: Option, } impl<'tcx, 'pat> Candidate<'pat, 'tcx> { @@ -1075,7 +1103,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> { or_span: None, otherwise_block: None, pre_binding_block: None, - next_candidate_pre_binding_block: None, + false_edge_start_block: None, + next_candidate_start_block: None, } } @@ -1367,6 +1396,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: BasicBlock, candidates: &mut [&mut Candidate<'_, 'tcx>], ) { + if let [first, ..] = candidates { + if first.false_edge_start_block.is_none() { + first.false_edge_start_block = Some(start_block); + } + } + match candidates { [] => { // 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> { .into_iter() .map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard)) .collect(); + candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; } /// 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> { let any_matches = self.cfg.start_new_block(); let or_span = candidate.or_span.take().unwrap(); let source_info = self.source_info(or_span); + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = + candidate.subcandidates[0].false_edge_start_block; + } for subcandidate in mem::take(&mut candidate.subcandidates) { let or_block = subcandidate.pre_binding_block.unwrap(); self.cfg.goto(or_block, source_info, any_matches); @@ -2021,12 +2061,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut block = candidate.pre_binding_block.unwrap(); - if candidate.next_candidate_pre_binding_block.is_some() { + if candidate.next_candidate_start_block.is_some() { let fresh_block = self.cfg.start_new_block(); self.false_edges( block, fresh_block, - candidate.next_candidate_pre_binding_block, + candidate.next_candidate_start_block, candidate_source_info, ); block = fresh_block; @@ -2174,7 +2214,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.false_edges( otherwise_post_guard_block, otherwise_block, - candidate.next_candidate_pre_binding_block, + candidate.next_candidate_start_block, source_info, ); diff --git a/tests/mir-opt/building/match/match_false_edges.main.built.after.mir b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir index b71b2412cdf..dfa31cfff6b 100644 --- a/tests/mir-opt/building/match/match_false_edges.main.built.after.mir +++ b/tests/mir-opt/building/match/match_false_edges.main.built.after.mir @@ -48,7 +48,7 @@ fn main() -> () { } bb2: { - falseEdge -> [real: bb15, imaginary: bb6]; + falseEdge -> [real: bb15, imaginary: bb3]; } bb3: { diff --git a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir index e95a97b5b87..c3497c6989d 100644 --- a/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.constant_eq.SimplifyCfg-initial.after.mir @@ -40,7 +40,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { } bb4: { - falseEdge -> [real: bb12, imaginary: bb9]; + falseEdge -> [real: bb12, imaginary: bb7]; } bb5: { @@ -48,7 +48,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { } bb6: { - falseEdge -> [real: bb16, imaginary: bb3]; + falseEdge -> [real: bb16, imaginary: bb1]; } bb7: { @@ -60,7 +60,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { } bb9: { - falseEdge -> [real: bb15, imaginary: bb6]; + falseEdge -> [real: bb15, imaginary: bb5]; } bb10: { @@ -89,7 +89,7 @@ fn constant_eq(_1: &str, _2: bool) -> u32 { bb14: { StorageDead(_10); - falseEdge -> [real: bb5, imaginary: bb9]; + falseEdge -> [real: bb5, imaginary: bb7]; } bb15: { diff --git a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir index 80d3c2e5c23..4a1e4fb9ec5 100644 --- a/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir +++ b/tests/mir-opt/building/match/sort_candidates.disjoint_ranges.SimplifyCfg-initial.after.mir @@ -23,7 +23,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 { } bb2: { - falseEdge -> [real: bb9, imaginary: bb4]; + falseEdge -> [real: bb9, imaginary: bb3]; } bb3: { @@ -32,7 +32,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 { } bb4: { - falseEdge -> [real: bb12, imaginary: bb6]; + falseEdge -> [real: bb12, imaginary: bb5]; } bb5: { @@ -69,7 +69,7 @@ fn disjoint_ranges(_1: i32, _2: bool) -> u32 { bb11: { StorageDead(_8); - falseEdge -> [real: bb1, imaginary: bb4]; + falseEdge -> [real: bb1, imaginary: bb3]; } bb12: { diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 307f7105dd2..ba333ba1a58 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -60,11 +60,11 @@ } - bb5: { -- falseEdge -> [real: bb13, imaginary: bb3]; +- falseEdge -> [real: bb13, imaginary: bb2]; - } - - bb6: { -- falseEdge -> [real: bb8, imaginary: bb5]; +- falseEdge -> [real: bb8, imaginary: bb1]; - } - - bb7: { @@ -127,7 +127,7 @@ StorageDead(_9); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb1, imaginary: bb5]; +- falseEdge -> [real: bb1, imaginary: bb1]; + goto -> bb1; } @@ -184,7 +184,7 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb2, imaginary: bb3]; +- falseEdge -> [real: bb2, imaginary: bb2]; + goto -> bb2; } diff --git a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff index 307f7105dd2..ba333ba1a58 100644 --- a/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff +++ b/tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff @@ -60,11 +60,11 @@ } - bb5: { -- falseEdge -> [real: bb13, imaginary: bb3]; +- falseEdge -> [real: bb13, imaginary: bb2]; - } - - bb6: { -- falseEdge -> [real: bb8, imaginary: bb5]; +- falseEdge -> [real: bb8, imaginary: bb1]; - } - - bb7: { @@ -127,7 +127,7 @@ StorageDead(_9); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb1, imaginary: bb5]; +- falseEdge -> [real: bb1, imaginary: bb1]; + goto -> bb1; } @@ -184,7 +184,7 @@ StorageDead(_12); StorageDead(_8); StorageDead(_6); -- falseEdge -> [real: bb2, imaginary: bb3]; +- falseEdge -> [real: bb2, imaginary: bb2]; + goto -> bb2; }