Skip to content

Commit 1d1bfb7

Browse files
committed
Auto merge of #121172 - Nadrieril:simplify-empty-selection, r=<try>
match lowering: simplify empty candidate selection In match lowering, `match_simplified_candidates` is tasked with removing candidates that are fully matched and linking them up properly. The code that does that was needlessly complicated; this PR simplifies it. The overall change isn't big but I split it up into tiny commits to convince myself that I was correctly preserving behavior. The test changes are all due to the first commit. Let me know if you'd prefer me to split up the PR to make reviewing easier. r? `@matthewjasper`
2 parents 0f806a9 + 4a9cff7 commit 1d1bfb7

16 files changed

+443
-365
lines changed

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

+62-100
Original file line numberDiff line numberDiff line change
@@ -1207,53 +1207,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12071207
&mut self,
12081208
span: Span,
12091209
scrutinee_span: Span,
1210-
start_block: BasicBlock,
1210+
mut start_block: BasicBlock,
12111211
otherwise_block: BasicBlock,
12121212
candidates: &mut [&mut Candidate<'_, 'tcx>],
12131213
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
12141214
) {
1215-
// The candidates are sorted by priority. Check to see whether the
1216-
// higher priority candidates (and hence at the front of the slice)
1217-
// have satisfied all their match pairs.
1218-
let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
1219-
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
1220-
let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
1221-
1222-
let block = if !matched_candidates.is_empty() {
1223-
let otherwise_block =
1224-
self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
1225-
1226-
if let Some(last_otherwise_block) = otherwise_block {
1227-
last_otherwise_block
1228-
} else {
1229-
// Any remaining candidates are unreachable.
1230-
if unmatched_candidates.is_empty() {
1231-
return;
1232-
}
1233-
self.cfg.start_new_block()
1215+
match candidates {
1216+
[] => {
1217+
// If there are no candidates that still need testing, we're done. Since all matches are
1218+
// exhaustive, execution should never reach this point.
1219+
let source_info = self.source_info(span);
1220+
self.cfg.goto(start_block, source_info, otherwise_block);
1221+
}
1222+
[first, remaining @ ..] if first.match_pairs.is_empty() => {
1223+
// The first candidate has satisfied all its match pairs; we link it up and continue
1224+
// with the remaining candidates.
1225+
start_block = self.select_matched_candidate(first, start_block, fake_borrows);
1226+
self.match_simplified_candidates(
1227+
span,
1228+
scrutinee_span,
1229+
start_block,
1230+
otherwise_block,
1231+
remaining,
1232+
fake_borrows,
1233+
)
1234+
}
1235+
candidates => {
1236+
// The first candidate has some unsatisfied match pairs; we proceed to do more tests.
1237+
self.test_candidates_with_or(
1238+
span,
1239+
scrutinee_span,
1240+
candidates,
1241+
start_block,
1242+
otherwise_block,
1243+
fake_borrows,
1244+
);
12341245
}
1235-
} else {
1236-
start_block
1237-
};
1238-
1239-
// If there are no candidates that still need testing, we're
1240-
// done. Since all matches are exhaustive, execution should
1241-
// never reach this point.
1242-
if unmatched_candidates.is_empty() {
1243-
let source_info = self.source_info(span);
1244-
self.cfg.goto(block, source_info, otherwise_block);
1245-
return;
12461246
}
1247-
1248-
// Test for the remaining candidates.
1249-
self.test_candidates_with_or(
1250-
span,
1251-
scrutinee_span,
1252-
unmatched_candidates,
1253-
block,
1254-
otherwise_block,
1255-
fake_borrows,
1256-
);
12571247
}
12581248

12591249
/// Link up matched candidates.
@@ -1275,47 +1265,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12751265
/// * the [otherwise block] of the third pattern to a block with an
12761266
/// [`Unreachable` terminator](TerminatorKind::Unreachable).
12771267
///
1278-
/// In addition, we add fake edges from the otherwise blocks to the
1268+
/// In addition, we later add fake edges from the otherwise blocks to the
12791269
/// pre-binding block of the next candidate in the original set of
12801270
/// candidates.
12811271
///
12821272
/// [pre-binding block]: Candidate::pre_binding_block
12831273
/// [otherwise block]: Candidate::otherwise_block
1284-
fn select_matched_candidates(
1274+
fn select_matched_candidate(
12851275
&mut self,
1286-
matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
1276+
candidate: &mut Candidate<'_, 'tcx>,
12871277
start_block: BasicBlock,
12881278
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
1289-
) -> Option<BasicBlock> {
1290-
debug_assert!(
1291-
!matched_candidates.is_empty(),
1292-
"select_matched_candidates called with no candidates",
1293-
);
1279+
) -> BasicBlock {
1280+
assert!(candidate.otherwise_block.is_none());
1281+
assert!(candidate.pre_binding_block.is_none());
12941282
debug_assert!(
1295-
matched_candidates.iter().all(|c| c.subcandidates.is_empty()),
1283+
candidate.subcandidates.is_empty(),
12961284
"subcandidates should be empty in select_matched_candidates",
12971285
);
12981286

1299-
// Insert a borrows of prefixes of places that are bound and are
1300-
// behind a dereference projection.
1301-
//
1302-
// These borrows are taken to avoid situations like the following:
1303-
//
1304-
// match x[10] {
1305-
// _ if { x = &[0]; false } => (),
1306-
// y => (), // Out of bounds array access!
1307-
// }
1308-
//
1309-
// match *x {
1310-
// // y is bound by reference in the guard and then by copy in the
1311-
// // arm, so y is 2 in the arm!
1312-
// y if { y == 1 && (x = &2) == () } => y,
1313-
// _ => 3,
1314-
// }
13151287
if let Some(fake_borrows) = fake_borrows {
1316-
for Binding { source, .. } in
1317-
matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
1318-
{
1288+
// Insert a borrows of prefixes of places that are bound and are
1289+
// behind a dereference projection.
1290+
//
1291+
// These borrows are taken to avoid situations like the following:
1292+
//
1293+
// match x[10] {
1294+
// _ if { x = &[0]; false } => (),
1295+
// y => (), // Out of bounds array access!
1296+
// }
1297+
//
1298+
// match *x {
1299+
// // y is bound by reference in the guard and then by copy in the
1300+
// // arm, so y is 2 in the arm!
1301+
// y if { y == 1 && (x = &2) == () } => y,
1302+
// _ => 3,
1303+
// }
1304+
for Binding { source, .. } in &candidate.bindings {
13191305
if let Some(i) =
13201306
source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref)
13211307
{
@@ -1329,38 +1315,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13291315
}
13301316
}
13311317

1332-
let fully_matched_with_guard = matched_candidates
1333-
.iter()
1334-
.position(|c| !c.has_guard)
1335-
.unwrap_or(matched_candidates.len() - 1);
1336-
1337-
let (reachable_candidates, unreachable_candidates) =
1338-
matched_candidates.split_at_mut(fully_matched_with_guard + 1);
1339-
1340-
let mut next_prebinding = start_block;
1341-
1342-
for candidate in reachable_candidates.iter_mut() {
1343-
assert!(candidate.otherwise_block.is_none());
1344-
assert!(candidate.pre_binding_block.is_none());
1345-
candidate.pre_binding_block = Some(next_prebinding);
1346-
if candidate.has_guard {
1347-
// Create the otherwise block for this candidate, which is the
1348-
// pre-binding block for the next candidate.
1349-
next_prebinding = self.cfg.start_new_block();
1350-
candidate.otherwise_block = Some(next_prebinding);
1351-
}
1352-
}
1353-
1354-
debug!(
1355-
"match_candidates: add pre_binding_blocks for unreachable {:?}",
1356-
unreachable_candidates,
1357-
);
1358-
for candidate in unreachable_candidates {
1359-
assert!(candidate.pre_binding_block.is_none());
1360-
candidate.pre_binding_block = Some(self.cfg.start_new_block());
1318+
candidate.pre_binding_block = Some(start_block);
1319+
let otherwise_block = self.cfg.start_new_block();
1320+
if candidate.has_guard {
1321+
// Create the otherwise block for this candidate, which is the
1322+
// pre-binding block for the next candidate.
1323+
candidate.otherwise_block = Some(otherwise_block);
13611324
}
1362-
1363-
reachable_candidates.last_mut().unwrap().otherwise_block
1325+
otherwise_block
13641326
}
13651327

13661328
/// Tests a candidate where there are only or-patterns left to test, or

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

+14-6
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ fn main() -> () {
2727
StorageLive(_5);
2828
PlaceMention(_1);
2929
_6 = discriminant(_1);
30-
switchInt(move _6) -> [1: bb5, otherwise: bb4];
30+
switchInt(move _6) -> [1: bb6, otherwise: bb4];
3131
}
3232

3333
bb1: {
3434
StorageLive(_3);
3535
StorageLive(_4);
36-
_4 = begin_panic::<&str>(const "explicit panic") -> bb8;
36+
_4 = begin_panic::<&str>(const "explicit panic") -> bb10;
3737
}
3838

3939
bb2: {
@@ -48,27 +48,35 @@ fn main() -> () {
4848
}
4949

5050
bb4: {
51-
goto -> bb7;
51+
goto -> bb9;
5252
}
5353

5454
bb5: {
55-
falseEdge -> [real: bb6, imaginary: bb4];
55+
goto -> bb3;
5656
}
5757

5858
bb6: {
59+
falseEdge -> [real: bb8, imaginary: bb4];
60+
}
61+
62+
bb7: {
63+
goto -> bb4;
64+
}
65+
66+
bb8: {
5967
_5 = ((_1 as Some).0: u8);
6068
_0 = const ();
6169
StorageDead(_5);
6270
StorageDead(_1);
6371
return;
6472
}
6573

66-
bb7: {
74+
bb9: {
6775
StorageDead(_5);
6876
goto -> bb1;
6977
}
7078

71-
bb8 (cleanup): {
79+
bb10 (cleanup): {
7280
resume;
7381
}
7482
}

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

+21-13
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ fn main() -> () {
1717
}
1818

1919
bb1: {
20-
falseUnwind -> [real: bb2, unwind: bb12];
20+
falseUnwind -> [real: bb2, unwind: bb14];
2121
}
2222

2323
bb2: {
2424
StorageLive(_2);
2525
StorageLive(_3);
2626
_3 = const true;
2727
PlaceMention(_3);
28-
switchInt(_3) -> [0: bb4, otherwise: bb5];
28+
switchInt(_3) -> [0: bb4, otherwise: bb6];
2929
}
3030

3131
bb3: {
@@ -34,51 +34,59 @@ fn main() -> () {
3434
}
3535

3636
bb4: {
37-
falseEdge -> [real: bb6, imaginary: bb5];
37+
falseEdge -> [real: bb8, imaginary: bb6];
3838
}
3939

4040
bb5: {
41-
_0 = const ();
42-
goto -> bb11;
41+
goto -> bb3;
4342
}
4443

4544
bb6: {
46-
_2 = const 4_i32;
47-
goto -> bb9;
45+
_0 = const ();
46+
goto -> bb13;
4847
}
4948

5049
bb7: {
51-
unreachable;
50+
goto -> bb3;
5251
}
5352

5453
bb8: {
55-
goto -> bb9;
54+
_2 = const 4_i32;
55+
goto -> bb11;
5656
}
5757

5858
bb9: {
59+
unreachable;
60+
}
61+
62+
bb10: {
63+
goto -> bb11;
64+
}
65+
66+
bb11: {
5967
FakeRead(ForLet(None), _2);
6068
StorageDead(_3);
6169
StorageLive(_5);
6270
StorageLive(_6);
6371
_6 = &_2;
64-
_5 = std::mem::drop::<&i32>(move _6) -> [return: bb10, unwind: bb12];
72+
_5 = std::mem::drop::<&i32>(move _6) -> [return: bb12, unwind: bb14];
6573
}
6674

67-
bb10: {
75+
bb12: {
6876
StorageDead(_6);
6977
StorageDead(_5);
7078
_1 = const ();
7179
StorageDead(_2);
7280
goto -> bb1;
7381
}
7482

75-
bb11: {
83+
bb13: {
7684
StorageDead(_3);
7785
StorageDead(_2);
7886
return;
7987
}
8088

81-
bb12 (cleanup): {
89+
bb14 (cleanup): {
8290
resume;
8391
}
8492
}

0 commit comments

Comments
 (0)