Skip to content

Commit 096598d

Browse files
committed
Auto merge of #121172 - Nadrieril:simplify-empty-selection, r=matthewjasper
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 4e65074 + a45d892 commit 096598d

16 files changed

+443
-368
lines changed

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

+62-103
Original file line numberDiff line numberDiff line change
@@ -1235,53 +1235,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12351235
&mut self,
12361236
span: Span,
12371237
scrutinee_span: Span,
1238-
start_block: BasicBlock,
1238+
mut start_block: BasicBlock,
12391239
otherwise_block: BasicBlock,
12401240
candidates: &mut [&mut Candidate<'_, 'tcx>],
12411241
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
12421242
) {
1243-
// The candidates are sorted by priority. Check to see whether the
1244-
// higher priority candidates (and hence at the front of the slice)
1245-
// have satisfied all their match pairs.
1246-
let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
1247-
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
1248-
let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
1249-
1250-
let block = if !matched_candidates.is_empty() {
1251-
let otherwise_block =
1252-
self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
1253-
1254-
if let Some(last_otherwise_block) = otherwise_block {
1255-
last_otherwise_block
1256-
} else {
1257-
// Any remaining candidates are unreachable.
1258-
if unmatched_candidates.is_empty() {
1259-
return;
1260-
}
1261-
self.cfg.start_new_block()
1243+
match candidates {
1244+
[] => {
1245+
// If there are no candidates that still need testing, we're done. Since all matches are
1246+
// exhaustive, execution should never reach this point.
1247+
let source_info = self.source_info(span);
1248+
self.cfg.goto(start_block, source_info, otherwise_block);
1249+
}
1250+
[first, remaining @ ..] if first.match_pairs.is_empty() => {
1251+
// The first candidate has satisfied all its match pairs; we link it up and continue
1252+
// with the remaining candidates.
1253+
start_block = self.select_matched_candidate(first, start_block, fake_borrows);
1254+
self.match_simplified_candidates(
1255+
span,
1256+
scrutinee_span,
1257+
start_block,
1258+
otherwise_block,
1259+
remaining,
1260+
fake_borrows,
1261+
)
1262+
}
1263+
candidates => {
1264+
// The first candidate has some unsatisfied match pairs; we proceed to do more tests.
1265+
self.test_candidates_with_or(
1266+
span,
1267+
scrutinee_span,
1268+
candidates,
1269+
start_block,
1270+
otherwise_block,
1271+
fake_borrows,
1272+
);
12621273
}
1263-
} else {
1264-
start_block
1265-
};
1266-
1267-
// If there are no candidates that still need testing, we're
1268-
// done. Since all matches are exhaustive, execution should
1269-
// never reach this point.
1270-
if unmatched_candidates.is_empty() {
1271-
let source_info = self.source_info(span);
1272-
self.cfg.goto(block, source_info, otherwise_block);
1273-
return;
12741274
}
1275-
1276-
// Test for the remaining candidates.
1277-
self.test_candidates_with_or(
1278-
span,
1279-
scrutinee_span,
1280-
unmatched_candidates,
1281-
block,
1282-
otherwise_block,
1283-
fake_borrows,
1284-
);
12851275
}
12861276

12871277
/// Link up matched candidates.
@@ -1303,47 +1293,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13031293
/// * the [otherwise block] of the third pattern to a block with an
13041294
/// [`Unreachable` terminator](TerminatorKind::Unreachable).
13051295
///
1306-
/// In addition, we add fake edges from the otherwise blocks to the
1296+
/// In addition, we later add fake edges from the otherwise blocks to the
13071297
/// pre-binding block of the next candidate in the original set of
13081298
/// candidates.
13091299
///
13101300
/// [pre-binding block]: Candidate::pre_binding_block
13111301
/// [otherwise block]: Candidate::otherwise_block
1312-
fn select_matched_candidates(
1302+
fn select_matched_candidate(
13131303
&mut self,
1314-
matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
1304+
candidate: &mut Candidate<'_, 'tcx>,
13151305
start_block: BasicBlock,
13161306
fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
1317-
) -> Option<BasicBlock> {
1318-
debug_assert!(
1319-
!matched_candidates.is_empty(),
1320-
"select_matched_candidates called with no candidates",
1321-
);
1322-
debug_assert!(
1323-
matched_candidates.iter().all(|c| c.subcandidates.is_empty()),
1324-
"subcandidates should be empty in select_matched_candidates",
1325-
);
1307+
) -> BasicBlock {
1308+
assert!(candidate.otherwise_block.is_none());
1309+
assert!(candidate.pre_binding_block.is_none());
1310+
assert!(candidate.subcandidates.is_empty());
13261311

1327-
// Insert a borrows of prefixes of places that are bound and are
1328-
// behind a dereference projection.
1329-
//
1330-
// These borrows are taken to avoid situations like the following:
1331-
//
1332-
// match x[10] {
1333-
// _ if { x = &[0]; false } => (),
1334-
// y => (), // Out of bounds array access!
1335-
// }
1336-
//
1337-
// match *x {
1338-
// // y is bound by reference in the guard and then by copy in the
1339-
// // arm, so y is 2 in the arm!
1340-
// y if { y == 1 && (x = &2) == () } => y,
1341-
// _ => 3,
1342-
// }
13431312
if let Some(fake_borrows) = fake_borrows {
1344-
for Binding { source, .. } in
1345-
matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
1346-
{
1313+
// Insert a borrows of prefixes of places that are bound and are
1314+
// behind a dereference projection.
1315+
//
1316+
// These borrows are taken to avoid situations like the following:
1317+
//
1318+
// match x[10] {
1319+
// _ if { x = &[0]; false } => (),
1320+
// y => (), // Out of bounds array access!
1321+
// }
1322+
//
1323+
// match *x {
1324+
// // y is bound by reference in the guard and then by copy in the
1325+
// // arm, so y is 2 in the arm!
1326+
// y if { y == 1 && (x = &2) == () } => y,
1327+
// _ => 3,
1328+
// }
1329+
for Binding { source, .. } in &candidate.bindings {
13471330
if let Some(i) =
13481331
source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref)
13491332
{
@@ -1357,38 +1340,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13571340
}
13581341
}
13591342

1360-
let fully_matched_with_guard = matched_candidates
1361-
.iter()
1362-
.position(|c| !c.has_guard)
1363-
.unwrap_or(matched_candidates.len() - 1);
1364-
1365-
let (reachable_candidates, unreachable_candidates) =
1366-
matched_candidates.split_at_mut(fully_matched_with_guard + 1);
1367-
1368-
let mut next_prebinding = start_block;
1369-
1370-
for candidate in reachable_candidates.iter_mut() {
1371-
assert!(candidate.otherwise_block.is_none());
1372-
assert!(candidate.pre_binding_block.is_none());
1373-
candidate.pre_binding_block = Some(next_prebinding);
1374-
if candidate.has_guard {
1375-
// Create the otherwise block for this candidate, which is the
1376-
// pre-binding block for the next candidate.
1377-
next_prebinding = self.cfg.start_new_block();
1378-
candidate.otherwise_block = Some(next_prebinding);
1379-
}
1380-
}
1381-
1382-
debug!(
1383-
"match_candidates: add pre_binding_blocks for unreachable {:?}",
1384-
unreachable_candidates,
1385-
);
1386-
for candidate in unreachable_candidates {
1387-
assert!(candidate.pre_binding_block.is_none());
1388-
candidate.pre_binding_block = Some(self.cfg.start_new_block());
1343+
candidate.pre_binding_block = Some(start_block);
1344+
let otherwise_block = self.cfg.start_new_block();
1345+
if candidate.has_guard {
1346+
// Create the otherwise block for this candidate, which is the
1347+
// pre-binding block for the next candidate.
1348+
candidate.otherwise_block = Some(otherwise_block);
13891349
}
1390-
1391-
reachable_candidates.last_mut().unwrap().otherwise_block
1350+
otherwise_block
13921351
}
13931352

13941353
/// 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)