Factor out the special handling of or-patterns · rust-lang/rust@bff4d21 (original) (raw)
`@@ -1427,108 +1427,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
`
1427
1427
`` /// Note how we test x twice. This is the tradeoff of backtracking automata: we prefer smaller
``
1428
1428
`/// code size at the expense of non-optimal code paths.
`
1429
1429
`#[instrument(skip(self), level = "debug")]
`
1430
``
`-
fn match_candidates<'pat>(
`
``
1430
`+
fn match_candidates(
`
1431
1431
`&mut self,
`
1432
1432
`span: Span,
`
1433
1433
`scrutinee_span: Span,
`
1434
1434
`start_block: BasicBlock,
`
1435
1435
`otherwise_block: BasicBlock,
`
1436
``
`-
candidates: &mut [&mut Candidate<'pat, 'tcx>],
`
``
1436
`+
candidates: &mut [&mut Candidate<'_, 'tcx>],
`
1437
1437
`) {
`
1438
``
`-
// We process or-patterns here. If any candidate starts with an or-pattern, we have to
`
1439
``
`-
// expand the or-pattern before we can proceed further.
`
1440
``
`-
//
`
1441
``
`-
// We can't expand them freely however. The rule is: if the candidate has an or-pattern as
`
1442
``
`-
// its only remaining match pair, we can expand it freely. If it has other match pairs, we
`
1443
``
`-
// can expand it but we can't process more candidates after it.
`
1444
``
`-
//
`
1445
``
`` -
// If we didn't stop, the otherwise cases could get mixed up. E.g. in the following,
``
1446
``
`` -
// or-pattern simplification (in merge_trivial_subcandidates) makes it so the 1 and 2
``
1447
``
`` -
// cases branch to a same block (which then tests false). If we took (2, _) in the same
``
1448
``
`` -
// set of candidates, when we reach the block that tests false we don't know whether we
``
1449
``
`` -
// came from 1 or 2, hence we can't know where to branch on failure.
``
1450
``
// ```ignore(illustrative)
1451
``
`-
// match (1, true) {
`
1452
``
`-
// (1 | 2, false) => {},
`
1453
``
`-
// (2, _) => {},
`
1454
``
`-
// _ => {}
`
1455
``
`-
// }
`
1456
``
// ```
1457
``
`-
//
`
1458
``
`` -
// We therefore split the candidates slice in two, expand or-patterns in the first half,
``
1459
``
`-
// and process both halves separately.
`
1460
``
`-
let mut expand_until = 0;
`
1461
``
`-
for (i, candidate) in candidates.iter().enumerate() {
`
1462
``
`-
if matches!(
`
``
1438
`+
// If any candidate starts with an or-pattern, we have to expand the or-pattern before we
`
``
1439
`+
// can proceed further.
`
``
1440
`+
let expand_ors = candidates.iter().any(|candidate| {
`
``
1441
`+
matches!(
`
1463
1442
`&*candidate.match_pairs,
`
1464
1443
`[MatchPair { test_case: TestCase::Or { .. }, .. }, ..]
`
1465
``
`-
) {
`
1466
``
`-
expand_until = i + 1;
`
1467
``
`-
if candidate.match_pairs.len() > 1 {
`
1468
``
`-
break;
`
1469
``
`-
}
`
1470
``
`-
}
`
1471
``
`-
if expand_until != 0 {
`
1472
``
`-
expand_until = i + 1;
`
1473
``
`-
}
`
1474
``
`-
}
`
1475
``
`-
let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
`
1476
``
-
``
1444
`+
)
`
``
1445
`+
});
`
1477
1446
`ensure_sufficient_stack(|| {
`
1478
``
`-
if candidates_to_expand.is_empty() {
`
``
1447
`+
if !expand_ors {
`
1479
1448
`// No candidates start with an or-pattern, we can continue.
`
1480
1449
`self.match_expanded_candidates(
`
1481
1450
` span,
`
1482
1451
` scrutinee_span,
`
1483
1452
` start_block,
`
1484
1453
` otherwise_block,
`
1485
``
`-
remaining_candidates,
`
``
1454
`+
candidates,
`
1486
1455
`);
`
1487
1456
`} else {
`
1488
``
`` -
// Expand one level of or-patterns for each candidate in candidates_to_expand.
``
1489
``
`-
let mut expanded_candidates = Vec::new();
`
1490
``
`-
for candidate in candidates_to_expand.iter_mut() {
`
1491
``
`-
if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] =
`
1492
``
`-
&*candidate.match_pairs
`
1493
``
`-
{
`
1494
``
`-
let or_match_pair = candidate.match_pairs.remove(0);
`
1495
``
`-
// Expand the or-pattern into subcandidates.
`
1496
``
`-
self.create_or_subcandidates(candidate, or_match_pair);
`
1497
``
`-
// Collect the newly created subcandidates.
`
1498
``
`-
for subcandidate in candidate.subcandidates.iter_mut() {
`
1499
``
`-
expanded_candidates.push(subcandidate);
`
1500
``
`-
}
`
1501
``
`-
} else {
`
1502
``
`-
expanded_candidates.push(candidate);
`
1503
``
`-
}
`
1504
``
`-
}
`
1505
``
-
1506
``
`-
// Process the expanded candidates.
`
1507
``
`-
let remainder_start = self.cfg.start_new_block();
`
1508
``
`-
// There might be new or-patterns obtained from expanding the old ones, so we call
`
1509
``
`` -
// match_candidates again.
``
1510
``
`-
self.match_candidates(
`
``
1457
`+
self.expand_and_match_or_candidates(
`
1511
1458
` span,
`
1512
1459
` scrutinee_span,
`
1513
1460
` start_block,
`
1514
``
`-
remainder_start,
`
1515
``
`-
expanded_candidates.as_mut_slice(),
`
1516
``
`-
);
`
1517
``
-
1518
``
`-
// Simplify subcandidates and process any leftover match pairs.
`
1519
``
`-
for candidate in candidates_to_expand {
`
1520
``
`-
if !candidate.subcandidates.is_empty() {
`
1521
``
`-
self.finalize_or_candidate(span, scrutinee_span, candidate);
`
1522
``
`-
}
`
1523
``
`-
}
`
1524
``
-
1525
``
`-
// Process the remaining candidates.
`
1526
``
`-
self.match_candidates(
`
1527
``
`-
span,
`
1528
``
`-
scrutinee_span,
`
1529
``
`-
remainder_start,
`
1530
1461
` otherwise_block,
`
1531
``
`-
remaining_candidates,
`
``
1462
`+
candidates,
`
1532
1463
`);
`
1533
1464
`}
`
1534
1465
`});
`
`@@ -1624,6 +1555,98 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
`
1624
1555
` otherwise_block
`
1625
1556
`}
`
1626
1557
``
``
1558
`+
/// Takes a list of candidates such that some of the candidates' first match pairs are
`
``
1559
`+
/// or-patterns, expands as many or-patterns as possible, and processes the resulting
`
``
1560
`+
/// candidates.
`
``
1561
`+
fn expand_and_match_or_candidates(
`
``
1562
`+
&mut self,
`
``
1563
`+
span: Span,
`
``
1564
`+
scrutinee_span: Span,
`
``
1565
`+
start_block: BasicBlock,
`
``
1566
`+
otherwise_block: BasicBlock,
`
``
1567
`+
candidates: &mut [&mut Candidate<'_, 'tcx>],
`
``
1568
`+
) {
`
``
1569
`+
// We can't expand or-patterns freely. The rule is: if the candidate has an
`
``
1570
`+
// or-pattern as its only remaining match pair, we can expand it freely. If it has
`
``
1571
`+
// other match pairs, we can expand it but we can't process more candidates after
`
``
1572
`+
// it.
`
``
1573
`+
//
`
``
1574
`` +
// If we didn't stop, the otherwise cases could get mixed up. E.g. in the
``
``
1575
`` +
// following, or-pattern simplification (in merge_trivial_subcandidates) makes it
``
``
1576
`` +
// so the 1 and 2 cases branch to a same block (which then tests false). If we
``
``
1577
`` +
// took (2, _) in the same set of candidates, when we reach the block that tests
``
``
1578
`` +
// false we don't know whether we came from 1 or 2, hence we can't know where
``
``
1579
`+
// to branch on failure.
`
``
1580
`+
//
`
``
1581
// ```ignore(illustrative)
``
1582
`+
// match (1, true) {
`
``
1583
`+
// (1 | 2, false) => {},
`
``
1584
`+
// (2, _) => {},
`
``
1585
`+
// _ => {}
`
``
1586
`+
// }
`
``
1587
// ```
``
1588
`+
//
`
``
1589
`` +
// We therefore split the candidates slice in two, expand or-patterns in the first half,
``
``
1590
`+
// and process the rest separately.
`
``
1591
`+
let mut expand_until = 0;
`
``
1592
`+
for (i, candidate) in candidates.iter().enumerate() {
`
``
1593
`+
expand_until = i + 1;
`
``
1594
`+
if candidate.match_pairs.len() > 1
`
``
1595
`+
&& matches!(&candidate.match_pairs[0].test_case, TestCase::Or { .. })
`
``
1596
`+
{
`
``
1597
`+
// The candidate has an or-pattern as well as more match pairs: we must
`
``
1598
`+
// split the candidates list here.
`
``
1599
`+
break;
`
``
1600
`+
}
`
``
1601
`+
}
`
``
1602
`+
let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
`
``
1603
+
``
1604
`` +
// Expand one level of or-patterns for each candidate in candidates_to_expand.
``
``
1605
`+
let mut expanded_candidates = Vec::new();
`
``
1606
`+
for candidate in candidates_to_expand.iter_mut() {
`
``
1607
`+
if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] = &*candidate.match_pairs
`
``
1608
`+
{
`
``
1609
`+
let or_match_pair = candidate.match_pairs.remove(0);
`
``
1610
`+
// Expand the or-pattern into subcandidates.
`
``
1611
`+
self.create_or_subcandidates(candidate, or_match_pair);
`
``
1612
`+
// Collect the newly created subcandidates.
`
``
1613
`+
for subcandidate in candidate.subcandidates.iter_mut() {
`
``
1614
`+
expanded_candidates.push(subcandidate);
`
``
1615
`+
}
`
``
1616
`+
} else {
`
``
1617
`+
expanded_candidates.push(candidate);
`
``
1618
`+
}
`
``
1619
`+
}
`
``
1620
+
``
1621
`+
// Process the expanded candidates.
`
``
1622
`+
let remainder_start = self.cfg.start_new_block();
`
``
1623
`+
// There might be new or-patterns obtained from expanding the old ones, so we call
`
``
1624
`` +
// match_candidates again.
``
``
1625
`+
self.match_candidates(
`
``
1626
`+
span,
`
``
1627
`+
scrutinee_span,
`
``
1628
`+
start_block,
`
``
1629
`+
remainder_start,
`
``
1630
`+
expanded_candidates.as_mut_slice(),
`
``
1631
`+
);
`
``
1632
+
``
1633
`+
// Simplify subcandidates and process any leftover match pairs.
`
``
1634
`+
for candidate in candidates_to_expand {
`
``
1635
`+
if !candidate.subcandidates.is_empty() {
`
``
1636
`+
self.finalize_or_candidate(span, scrutinee_span, candidate);
`
``
1637
`+
}
`
``
1638
`+
}
`
``
1639
+
``
1640
`+
// Process the remaining candidates.
`
``
1641
`+
self.match_candidates(
`
``
1642
`+
span,
`
``
1643
`+
scrutinee_span,
`
``
1644
`+
remainder_start,
`
``
1645
`+
otherwise_block,
`
``
1646
`+
remaining_candidates,
`
``
1647
`+
);
`
``
1648
`+
}
`
``
1649
+
1627
1650
`/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
`
1628
1651
`/// subcandidate. Any candidate that has been expanded that way should be passed to
`
1629
1652
`` /// finalize_or_candidate after its subcandidates have been processed.
``