Skip to content

Commit e60c5c1

Browse files
committed
Split out test_remaining_match_pairs_after_or
Only the last candidate can possibly have more match pairs, so this can be separate from the main or-candidate postprocessing loop.
1 parent e091c35 commit e60c5c1

File tree

1 file changed

+58
-43
lines changed
  • compiler/rustc_mir_build/src/build/matches

1 file changed

+58
-43
lines changed

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

Lines changed: 58 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15981598
for subcandidate in candidate.subcandidates.iter_mut() {
15991599
expanded_candidates.push(subcandidate);
16001600
}
1601+
// Note that the subcandidates have been added to `expanded_candidates`,
1602+
// but `candidate` itself has not. If the last candidate has more match pairs,
1603+
// they are handled separately by `test_remaining_match_pairs_after_or`.
16011604
} else {
16021605
// A candidate that doesn't start with an or-pattern has nothing to
16031606
// expand, so it is included in the post-expansion list as-is.
@@ -1613,12 +1616,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16131616
expanded_candidates.as_mut_slice(),
16141617
);
16151618

1616-
// Simplify subcandidates and process any leftover match pairs.
1617-
for candidate in candidates_to_expand {
1619+
// Postprocess subcandidates, and process any leftover match pairs.
1620+
// (Only the last candidate can possibly have more match pairs.)
1621+
debug_assert!({
1622+
let mut all_except_last = candidates_to_expand.iter().rev().skip(1);
1623+
all_except_last.all(|candidate| candidate.match_pairs.is_empty())
1624+
});
1625+
for candidate in candidates_to_expand.iter_mut() {
16181626
if !candidate.subcandidates.is_empty() {
1619-
self.finalize_or_candidate(span, scrutinee_span, candidate);
1627+
self.finalize_or_candidate(candidate);
16201628
}
16211629
}
1630+
if let Some(last_candidate) = candidates_to_expand.last_mut() {
1631+
self.test_remaining_match_pairs_after_or(span, scrutinee_span, last_candidate);
1632+
}
16221633

16231634
remainder_start.and(remaining_candidates)
16241635
}
@@ -1642,8 +1653,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16421653
candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block;
16431654
}
16441655

1645-
/// Simplify subcandidates and process any leftover match pairs. The candidate should have been
1646-
/// expanded with `create_or_subcandidates`.
1656+
/// Simplify subcandidates and remove `is_never` subcandidates.
1657+
/// The candidate should have been expanded with `create_or_subcandidates`.
16471658
///
16481659
/// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
16491660
/// so:
@@ -1695,50 +1706,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16951706
/// |
16961707
/// ...
16971708
/// ```
1698-
fn finalize_or_candidate(
1699-
&mut self,
1700-
span: Span,
1701-
scrutinee_span: Span,
1702-
candidate: &mut Candidate<'_, 'tcx>,
1703-
) {
1709+
fn finalize_or_candidate(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
17041710
if candidate.subcandidates.is_empty() {
17051711
return;
17061712
}
17071713

17081714
self.merge_trivial_subcandidates(candidate);
17091715
self.remove_never_subcandidates(candidate);
1710-
1711-
if !candidate.match_pairs.is_empty() {
1712-
let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span);
1713-
let source_info = self.source_info(or_span);
1714-
// If more match pairs remain, test them after each subcandidate.
1715-
// We could add them to the or-candidates before the call to `test_or_pattern` but this
1716-
// would make it impossible to detect simplifiable or-patterns. That would guarantee
1717-
// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1718-
let mut last_otherwise = None;
1719-
candidate.visit_leaves(|leaf_candidate| {
1720-
last_otherwise = leaf_candidate.otherwise_block;
1721-
});
1722-
let remaining_match_pairs = mem::take(&mut candidate.match_pairs);
1723-
candidate.visit_leaves(|leaf_candidate| {
1724-
assert!(leaf_candidate.match_pairs.is_empty());
1725-
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
1726-
let or_start = leaf_candidate.pre_binding_block.unwrap();
1727-
let otherwise =
1728-
self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]);
1729-
// In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1730-
// R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1731-
// directly to `last_otherwise`. If there is a guard,
1732-
// `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
1733-
// can't skip `Q`.
1734-
let or_otherwise = if leaf_candidate.has_guard {
1735-
leaf_candidate.otherwise_block.unwrap()
1736-
} else {
1737-
last_otherwise.unwrap()
1738-
};
1739-
self.cfg.goto(otherwise, source_info, or_otherwise);
1740-
});
1741-
}
17421716
}
17431717

17441718
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
@@ -1814,6 +1788,47 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18141788
}
18151789
}
18161790

1791+
/// If more match pairs remain, test them after each subcandidate.
1792+
/// We could have added them to the or-candidates during or-pattern expansion, but that
1793+
/// would make it impossible to detect simplifiable or-patterns. That would guarantee
1794+
/// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1795+
fn test_remaining_match_pairs_after_or(
1796+
&mut self,
1797+
span: Span,
1798+
scrutinee_span: Span,
1799+
candidate: &mut Candidate<'_, 'tcx>,
1800+
) {
1801+
if candidate.match_pairs.is_empty() {
1802+
return;
1803+
}
1804+
1805+
let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span);
1806+
let source_info = self.source_info(or_span);
1807+
let mut last_otherwise = None;
1808+
candidate.visit_leaves(|leaf_candidate| {
1809+
last_otherwise = leaf_candidate.otherwise_block;
1810+
});
1811+
let remaining_match_pairs = mem::take(&mut candidate.match_pairs);
1812+
candidate.visit_leaves(|leaf_candidate| {
1813+
assert!(leaf_candidate.match_pairs.is_empty());
1814+
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
1815+
let or_start = leaf_candidate.pre_binding_block.unwrap();
1816+
let otherwise =
1817+
self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]);
1818+
// In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1819+
// R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1820+
// directly to `last_otherwise`. If there is a guard,
1821+
// `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
1822+
// can't skip `Q`.
1823+
let or_otherwise = if leaf_candidate.has_guard {
1824+
leaf_candidate.otherwise_block.unwrap()
1825+
} else {
1826+
last_otherwise.unwrap()
1827+
};
1828+
self.cfg.goto(otherwise, source_info, or_otherwise);
1829+
});
1830+
}
1831+
18171832
/// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at
18181833
/// least one match pair. We currently simply pick the test corresponding to the first match
18191834
/// pair of the first candidate in the list.

0 commit comments

Comments
 (0)