Skip to content

Commit 7b764be

Browse files
committed
Expand or-candidates mixed with candidates above
We can't mix them with candidates below them, but we can mix them with candidates above.
1 parent ce374fc commit 7b764be

File tree

2 files changed

+97
-119
lines changed

2 files changed

+97
-119
lines changed

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

+79-97
Original file line numberDiff line numberDiff line change
@@ -1364,61 +1364,105 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13641364
otherwise_block: BasicBlock,
13651365
candidates: &mut [&mut Candidate<'pat, 'tcx>],
13661366
) {
1367-
let expand_or_pats = candidates.iter().any(|candidate| {
1368-
matches!(&*candidate.match_pairs, [MatchPair { test_case: TestCase::Or { .. }, .. }])
1369-
});
1367+
// We process or-patterns here. If any candidate starts with an or-pattern, we have to
1368+
// expand the or-pattern before we can proceed further.
1369+
//
1370+
// We can't expand them freely however. The rule is: if the candidate has an or-pattern as
1371+
// its only remaining match pair, we can expand it freely. If it has other match pairs, we
1372+
// can expand it but we can't process more candidates after it.
1373+
//
1374+
// If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the following,
1375+
// or-pattern simplification (in `merge_trivial_subcandidates`) makes it so the `1` and `2`
1376+
// cases branch to a same block (which then tests `false`). If we took `(2, _)` in the same
1377+
// set of candidates, when we reach the block that tests `false` we don't know whether we
1378+
// came from `1` or `2`, hence we can't know where to branch on failure.
1379+
// ```ignore(illustrative)
1380+
// match (1, true) {
1381+
// (1 | 2, false) => {},
1382+
// (2, _) => {},
1383+
// _ => {}
1384+
// }
1385+
// ```
1386+
//
1387+
// We therefore split the `candidates` slice in two, expand or-patterns in the first half,
1388+
// and process both halves separately.
1389+
let mut expand_until = 0;
1390+
for (i, candidate) in candidates.iter().enumerate() {
1391+
if matches!(
1392+
&*candidate.match_pairs,
1393+
[MatchPair { test_case: TestCase::Or { .. }, .. }, ..]
1394+
) {
1395+
expand_until = i + 1;
1396+
if candidate.match_pairs.len() > 1 {
1397+
break;
1398+
}
1399+
}
1400+
}
1401+
let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
13701402

13711403
ensure_sufficient_stack(|| {
1372-
if expand_or_pats {
1373-
// Split a candidate in which the only match-pair is an or-pattern into multiple
1374-
// candidates. This is so that
1375-
//
1376-
// match x {
1377-
// 0 | 1 => { ... },
1378-
// 2 | 3 => { ... },
1379-
// }
1380-
//
1381-
// only generates a single switch.
1382-
let mut new_candidates = Vec::new();
1383-
for candidate in candidates.iter_mut() {
1384-
if let [MatchPair { test_case: TestCase::Or { .. }, .. }] =
1404+
if candidates_to_expand.is_empty() {
1405+
// No candidates start with an or-pattern, we can continue.
1406+
self.match_expanded_candidates(
1407+
span,
1408+
scrutinee_span,
1409+
start_block,
1410+
otherwise_block,
1411+
remaining_candidates,
1412+
);
1413+
} else {
1414+
// Expand one level of or-patterns for each candidate in `candidates_to_expand`.
1415+
let mut expanded_candidates = Vec::new();
1416+
for candidate in candidates_to_expand.iter_mut() {
1417+
if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] =
13851418
&*candidate.match_pairs
13861419
{
1387-
let match_pair = candidate.match_pairs.pop().unwrap();
1388-
self.create_or_subcandidates(candidate, match_pair);
1420+
let or_match_pair = candidate.match_pairs.remove(0);
1421+
// Expand the or-pattern into subcandidates.
1422+
self.create_or_subcandidates(candidate, or_match_pair);
1423+
// Collect the newly created subcandidates.
13891424
for subcandidate in candidate.subcandidates.iter_mut() {
1390-
new_candidates.push(subcandidate);
1425+
expanded_candidates.push(subcandidate);
13911426
}
13921427
} else {
1393-
new_candidates.push(candidate);
1428+
expanded_candidates.push(candidate);
13941429
}
13951430
}
1431+
1432+
// Process the expanded candidates.
1433+
let remainder_start = self.cfg.start_new_block();
1434+
// There might be new or-patterns obtained from expanding the old ones, so we call
1435+
// `match_candidates` again.
13961436
self.match_candidates(
13971437
span,
13981438
scrutinee_span,
13991439
start_block,
1400-
otherwise_block,
1401-
new_candidates.as_mut_slice(),
1440+
remainder_start,
1441+
expanded_candidates.as_mut_slice(),
14021442
);
14031443

1404-
for candidate in candidates {
1444+
// Simplify subcandidates and process any leftover match pairs.
1445+
for candidate in candidates_to_expand {
14051446
if !candidate.subcandidates.is_empty() {
1406-
self.merge_trivial_subcandidates(candidate);
1447+
self.finalize_or_candidate(span, scrutinee_span, candidate);
14071448
}
14081449
}
1409-
} else {
1410-
self.match_simplified_candidates(
1450+
1451+
// Process the remaining candidates.
1452+
self.match_candidates(
14111453
span,
14121454
scrutinee_span,
1413-
start_block,
1455+
remainder_start,
14141456
otherwise_block,
1415-
candidates,
1457+
remaining_candidates,
14161458
);
14171459
}
14181460
});
14191461
}
14201462

1421-
fn match_simplified_candidates(
1463+
/// Construct the decision tree for `candidates`. Caller must ensure that no candidate in
1464+
/// `candidates` starts with an or-pattern.
1465+
fn match_expanded_candidates(
14221466
&mut self,
14231467
span: Span,
14241468
scrutinee_span: Span,
@@ -1443,7 +1487,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14431487
// The first candidate has satisfied all its match pairs; we link it up and continue
14441488
// with the remaining candidates.
14451489
start_block = self.select_matched_candidate(first, start_block);
1446-
self.match_simplified_candidates(
1490+
self.match_expanded_candidates(
14471491
span,
14481492
scrutinee_span,
14491493
start_block,
@@ -1453,7 +1497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14531497
}
14541498
candidates => {
14551499
// The first candidate has some unsatisfied match pairs; we proceed to do more tests.
1456-
self.test_candidates_with_or(
1500+
self.test_candidates(
14571501
span,
14581502
scrutinee_span,
14591503
candidates,
@@ -1506,8 +1550,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15061550
otherwise_block
15071551
}
15081552

1509-
/// Tests a candidate where there are only or-patterns left to test, or
1510-
/// forwards to [Builder::test_candidates].
1553+
/// Simplify subcandidates and process any leftover match pairs. The candidate should have been
1554+
/// expanded with `create_or_subcandidates`.
15111555
///
15121556
/// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
15131557
/// so:
@@ -1559,45 +1603,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15591603
/// |
15601604
/// ...
15611605
/// ```
1562-
fn test_candidates_with_or(
1563-
&mut self,
1564-
span: Span,
1565-
scrutinee_span: Span,
1566-
candidates: &mut [&mut Candidate<'_, 'tcx>],
1567-
start_block: BasicBlock,
1568-
otherwise_block: BasicBlock,
1569-
) {
1570-
let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
1571-
assert!(first_candidate.subcandidates.is_empty());
1572-
if !matches!(first_candidate.match_pairs[0].test_case, TestCase::Or { .. }) {
1573-
self.test_candidates(span, scrutinee_span, candidates, start_block, otherwise_block);
1574-
return;
1575-
}
1576-
1577-
let first_match_pair = first_candidate.match_pairs.remove(0);
1578-
let remainder_start = self.cfg.start_new_block();
1579-
// Test the alternatives of this or-pattern.
1580-
self.test_or_pattern(
1581-
span,
1582-
scrutinee_span,
1583-
first_candidate,
1584-
start_block,
1585-
remainder_start,
1586-
first_match_pair,
1587-
);
1588-
1589-
// Test the remaining candidates.
1590-
self.match_candidates(
1591-
span,
1592-
scrutinee_span,
1593-
remainder_start,
1594-
otherwise_block,
1595-
remaining_candidates,
1596-
);
1597-
}
1598-
1599-
/// Simplify subcandidates and process any leftover match pairs. The candidate should have been
1600-
/// expanded with `create_or_subcandidates`.
16011606
fn finalize_or_candidate(
16021607
&mut self,
16031608
span: Span,
@@ -1634,40 +1639,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16341639
} else {
16351640
last_otherwise.unwrap()
16361641
};
1637-
self.test_candidates_with_or(
1642+
self.match_candidates(
16381643
span,
16391644
scrutinee_span,
1640-
&mut [leaf_candidate],
16411645
or_start,
16421646
or_otherwise,
1647+
&mut [leaf_candidate],
16431648
);
16441649
});
16451650
}
16461651
}
16471652

1648-
#[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")]
1649-
fn test_or_pattern<'pat>(
1650-
&mut self,
1651-
span: Span,
1652-
scrutinee_span: Span,
1653-
candidate: &mut Candidate<'pat, 'tcx>,
1654-
start_block: BasicBlock,
1655-
otherwise_block: BasicBlock,
1656-
match_pair: MatchPair<'pat, 'tcx>,
1657-
) {
1658-
let or_span = match_pair.pattern.span;
1659-
self.create_or_subcandidates(candidate, match_pair);
1660-
let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect();
1661-
self.match_candidates(
1662-
or_span,
1663-
or_span,
1664-
start_block,
1665-
otherwise_block,
1666-
&mut or_candidate_refs,
1667-
);
1668-
self.finalize_or_candidate(span, scrutinee_span, candidate);
1669-
}
1670-
16711653
/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
16721654
/// subcandidate. Any candidate that has been expanded that way should be passed to
16731655
/// `finalize_or_candidate` after its subcandidates have been processed.

tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir

+18-22
Original file line numberDiff line numberDiff line change
@@ -10,67 +10,63 @@ fn single_switchint() -> () {
1010
StorageLive(_2);
1111
_2 = (const 1_i32, const true);
1212
PlaceMention(_2);
13-
switchInt((_2.0: i32)) -> [1: bb6, 2: bb8, otherwise: bb1];
13+
switchInt((_2.0: i32)) -> [1: bb2, 2: bb4, otherwise: bb1];
1414
}
1515

1616
bb1: {
17-
switchInt((_2.0: i32)) -> [1: bb3, 2: bb3, otherwise: bb2];
17+
switchInt((_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7];
1818
}
1919

2020
bb2: {
21-
switchInt((_2.0: i32)) -> [3: bb5, 4: bb5, otherwise: bb4];
21+
switchInt((_2.1: bool)) -> [0: bb6, otherwise: bb3];
2222
}
2323

2424
bb3: {
25-
falseEdge -> [real: bb12, imaginary: bb2];
25+
falseEdge -> [real: bb9, imaginary: bb4];
2626
}
2727

2828
bb4: {
29-
_1 = const 5_i32;
30-
goto -> bb14;
29+
switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb6];
3130
}
3231

3332
bb5: {
34-
falseEdge -> [real: bb13, imaginary: bb4];
33+
falseEdge -> [real: bb10, imaginary: bb6];
3534
}
3635

3736
bb6: {
38-
switchInt((_2.1: bool)) -> [0: bb1, otherwise: bb7];
37+
falseEdge -> [real: bb11, imaginary: bb1];
3938
}
4039

4140
bb7: {
42-
falseEdge -> [real: bb10, imaginary: bb8];
41+
_1 = const 5_i32;
42+
goto -> bb13;
4343
}
4444

4545
bb8: {
46-
switchInt((_2.1: bool)) -> [0: bb9, otherwise: bb1];
46+
falseEdge -> [real: bb12, imaginary: bb7];
4747
}
4848

4949
bb9: {
50-
falseEdge -> [real: bb11, imaginary: bb1];
51-
}
52-
53-
bb10: {
5450
_1 = const 1_i32;
55-
goto -> bb14;
51+
goto -> bb13;
5652
}
5753

58-
bb11: {
54+
bb10: {
5955
_1 = const 2_i32;
60-
goto -> bb14;
56+
goto -> bb13;
6157
}
6258

63-
bb12: {
59+
bb11: {
6460
_1 = const 3_i32;
65-
goto -> bb14;
61+
goto -> bb13;
6662
}
6763

68-
bb13: {
64+
bb12: {
6965
_1 = const 4_i32;
70-
goto -> bb14;
66+
goto -> bb13;
7167
}
7268

73-
bb14: {
69+
bb13: {
7470
StorageDead(_2);
7571
StorageDead(_1);
7672
_0 = const ();

0 commit comments

Comments
 (0)