Skip to content

Commit a03fd22

Browse files
committed
More precise false edges
1 parent 8ba04ee commit a03fd22

5 files changed

+65
-25
lines changed

compiler/rustc_mir_build/src/build/matches/mod.rs

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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
/// _ => {},
@@ -246,10 +246,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
246246
/// };
247247
/// ```
248248
///
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).
250254
///
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.
253276
#[instrument(level = "debug", skip(self, arms))]
254277
pub(crate) fn match_expr(
255278
&mut self,
@@ -396,7 +419,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
396419
for candidate in candidates {
397420
candidate.visit_leaves(|leaf_candidate| {
398421
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;
400424
}
401425
previous_candidate = Some(leaf_candidate);
402426
});
@@ -1060,8 +1084,12 @@ struct Candidate<'pat, 'tcx> {
10601084

10611085
/// The block before the `bindings` have been established.
10621086
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>,
10651093
}
10661094

10671095
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
@@ -1083,7 +1111,8 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
10831111
or_span: None,
10841112
otherwise_block: None,
10851113
pre_binding_block: None,
1086-
next_candidate_pre_binding_block: None,
1114+
false_edge_start_block: None,
1115+
next_candidate_start_block: None,
10871116
}
10881117
}
10891118

@@ -1379,6 +1408,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13791408
otherwise_block: BasicBlock,
13801409
candidates: &mut [&mut Candidate<'_, 'tcx>],
13811410
) {
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+
13821417
match candidates {
13831418
[] => {
13841419
// 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> {
15991634
.into_iter()
16001635
.map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
16011636
.collect();
1637+
candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block;
16021638
}
16031639

16041640
/// 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> {
16171653
if can_merge {
16181654
let any_matches = self.cfg.start_new_block();
16191655
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+
}
16201660
for subcandidate in mem::take(&mut candidate.subcandidates) {
16211661
let or_block = subcandidate.pre_binding_block.unwrap();
16221662
self.cfg.goto(or_block, source_info, any_matches);
@@ -2047,12 +2087,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
20472087

20482088
let mut block = candidate.pre_binding_block.unwrap();
20492089

2050-
if candidate.next_candidate_pre_binding_block.is_some() {
2090+
if candidate.next_candidate_start_block.is_some() {
20512091
let fresh_block = self.cfg.start_new_block();
20522092
self.false_edges(
20532093
block,
20542094
fresh_block,
2055-
candidate.next_candidate_pre_binding_block,
2095+
candidate.next_candidate_start_block,
20562096
candidate_source_info,
20572097
);
20582098
block = fresh_block;
@@ -2210,7 +2250,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
22102250
self.false_edges(
22112251
otherwise_post_guard_block,
22122252
otherwise_block,
2213-
candidate.next_candidate_pre_binding_block,
2253+
candidate.next_candidate_start_block,
22142254
source_info,
22152255
);
22162256

tests/mir-opt/building/match_false_edges.main.built.after.mir

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn main() -> () {
4848
}
4949

5050
bb2: {
51-
falseEdge -> [real: bb15, imaginary: bb6];
51+
falseEdge -> [real: bb15, imaginary: bb3];
5252
}
5353

5454
bb3: {

tests/mir-opt/match_arm_scopes.complicated_match.panic-abort.SimplifyCfg-initial.after-ElaborateDrops.after.diff

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@
6060
}
6161

6262
- bb5: {
63-
- falseEdge -> [real: bb13, imaginary: bb3];
63+
- falseEdge -> [real: bb13, imaginary: bb2];
6464
- }
6565
-
6666
- bb6: {
67-
- falseEdge -> [real: bb8, imaginary: bb5];
67+
- falseEdge -> [real: bb8, imaginary: bb1];
6868
- }
6969
-
7070
- bb7: {
@@ -127,7 +127,7 @@
127127
StorageDead(_9);
128128
StorageDead(_8);
129129
StorageDead(_6);
130-
- falseEdge -> [real: bb1, imaginary: bb5];
130+
- falseEdge -> [real: bb1, imaginary: bb1];
131131
+ goto -> bb1;
132132
}
133133

@@ -184,7 +184,7 @@
184184
StorageDead(_12);
185185
StorageDead(_8);
186186
StorageDead(_6);
187-
- falseEdge -> [real: bb2, imaginary: bb3];
187+
- falseEdge -> [real: bb2, imaginary: bb2];
188188
+ goto -> bb2;
189189
}
190190

tests/mir-opt/match_arm_scopes.complicated_match.panic-unwind.SimplifyCfg-initial.after-ElaborateDrops.after.diff

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,11 @@
6060
}
6161

6262
- bb5: {
63-
- falseEdge -> [real: bb13, imaginary: bb3];
63+
- falseEdge -> [real: bb13, imaginary: bb2];
6464
- }
6565
-
6666
- bb6: {
67-
- falseEdge -> [real: bb8, imaginary: bb5];
67+
- falseEdge -> [real: bb8, imaginary: bb1];
6868
- }
6969
-
7070
- bb7: {
@@ -127,7 +127,7 @@
127127
StorageDead(_9);
128128
StorageDead(_8);
129129
StorageDead(_6);
130-
- falseEdge -> [real: bb1, imaginary: bb5];
130+
- falseEdge -> [real: bb1, imaginary: bb1];
131131
+ goto -> bb1;
132132
}
133133

@@ -184,7 +184,7 @@
184184
StorageDead(_12);
185185
StorageDead(_8);
186186
StorageDead(_6);
187-
- falseEdge -> [real: bb2, imaginary: bb3];
187+
- falseEdge -> [real: bb2, imaginary: bb2];
188188
+ goto -> bb2;
189189
}
190190

tests/mir-opt/match_test.main.SimplifyCfg-initial.after.mir

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn main() -> () {
3737
}
3838

3939
bb2: {
40-
falseEdge -> [real: bb9, imaginary: bb4];
40+
falseEdge -> [real: bb9, imaginary: bb3];
4141
}
4242

4343
bb3: {
@@ -46,7 +46,7 @@ fn main() -> () {
4646
}
4747

4848
bb4: {
49-
falseEdge -> [real: bb12, imaginary: bb6];
49+
falseEdge -> [real: bb12, imaginary: bb5];
5050
}
5151

5252
bb5: {
@@ -83,7 +83,7 @@ fn main() -> () {
8383

8484
bb11: {
8585
StorageDead(_9);
86-
falseEdge -> [real: bb1, imaginary: bb4];
86+
falseEdge -> [real: bb1, imaginary: bb3];
8787
}
8888

8989
bb12: {

0 commit comments

Comments
 (0)