mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-04 05:57:36 +00:00
Factor out the special handling of or-patterns
This commit is contained in:
parent
5bf50e66f9
commit
bff4d213fa
@ -1427,108 +1427,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
/// Note how we test `x` twice. This is the tradeoff of backtracking automata: we prefer smaller
|
/// Note how we test `x` twice. This is the tradeoff of backtracking automata: we prefer smaller
|
||||||
/// code size at the expense of non-optimal code paths.
|
/// code size at the expense of non-optimal code paths.
|
||||||
#[instrument(skip(self), level = "debug")]
|
#[instrument(skip(self), level = "debug")]
|
||||||
fn match_candidates<'pat>(
|
fn match_candidates(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
scrutinee_span: Span,
|
scrutinee_span: Span,
|
||||||
start_block: BasicBlock,
|
start_block: BasicBlock,
|
||||||
otherwise_block: BasicBlock,
|
otherwise_block: BasicBlock,
|
||||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
||||||
) {
|
) {
|
||||||
// We process or-patterns here. If any candidate starts with an or-pattern, we have to
|
// If any candidate starts with an or-pattern, we have to expand the or-pattern before we
|
||||||
// expand the or-pattern before we can proceed further.
|
// can proceed further.
|
||||||
//
|
let expand_ors = candidates.iter().any(|candidate| {
|
||||||
// We can't expand them freely however. The rule is: if the candidate has an or-pattern as
|
matches!(
|
||||||
// its only remaining match pair, we can expand it freely. If it has other match pairs, we
|
|
||||||
// can expand it but we can't process more candidates after it.
|
|
||||||
//
|
|
||||||
// If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the following,
|
|
||||||
// or-pattern simplification (in `merge_trivial_subcandidates`) makes it so the `1` and `2`
|
|
||||||
// cases branch to a same block (which then tests `false`). If we took `(2, _)` in the same
|
|
||||||
// set of candidates, when we reach the block that tests `false` we don't know whether we
|
|
||||||
// came from `1` or `2`, hence we can't know where to branch on failure.
|
|
||||||
// ```ignore(illustrative)
|
|
||||||
// match (1, true) {
|
|
||||||
// (1 | 2, false) => {},
|
|
||||||
// (2, _) => {},
|
|
||||||
// _ => {}
|
|
||||||
// }
|
|
||||||
// ```
|
|
||||||
//
|
|
||||||
// We therefore split the `candidates` slice in two, expand or-patterns in the first half,
|
|
||||||
// and process both halves separately.
|
|
||||||
let mut expand_until = 0;
|
|
||||||
for (i, candidate) in candidates.iter().enumerate() {
|
|
||||||
if matches!(
|
|
||||||
&*candidate.match_pairs,
|
&*candidate.match_pairs,
|
||||||
[MatchPair { test_case: TestCase::Or { .. }, .. }, ..]
|
[MatchPair { test_case: TestCase::Or { .. }, .. }, ..]
|
||||||
) {
|
)
|
||||||
expand_until = i + 1;
|
});
|
||||||
if candidate.match_pairs.len() > 1 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expand_until != 0 {
|
|
||||||
expand_until = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
|
|
||||||
|
|
||||||
ensure_sufficient_stack(|| {
|
ensure_sufficient_stack(|| {
|
||||||
if candidates_to_expand.is_empty() {
|
if !expand_ors {
|
||||||
// No candidates start with an or-pattern, we can continue.
|
// No candidates start with an or-pattern, we can continue.
|
||||||
self.match_expanded_candidates(
|
self.match_expanded_candidates(
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
start_block,
|
start_block,
|
||||||
otherwise_block,
|
otherwise_block,
|
||||||
remaining_candidates,
|
candidates,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// Expand one level of or-patterns for each candidate in `candidates_to_expand`.
|
self.expand_and_match_or_candidates(
|
||||||
let mut expanded_candidates = Vec::new();
|
|
||||||
for candidate in candidates_to_expand.iter_mut() {
|
|
||||||
if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] =
|
|
||||||
&*candidate.match_pairs
|
|
||||||
{
|
|
||||||
let or_match_pair = candidate.match_pairs.remove(0);
|
|
||||||
// Expand the or-pattern into subcandidates.
|
|
||||||
self.create_or_subcandidates(candidate, or_match_pair);
|
|
||||||
// Collect the newly created subcandidates.
|
|
||||||
for subcandidate in candidate.subcandidates.iter_mut() {
|
|
||||||
expanded_candidates.push(subcandidate);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
expanded_candidates.push(candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the expanded candidates.
|
|
||||||
let remainder_start = self.cfg.start_new_block();
|
|
||||||
// There might be new or-patterns obtained from expanding the old ones, so we call
|
|
||||||
// `match_candidates` again.
|
|
||||||
self.match_candidates(
|
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
start_block,
|
start_block,
|
||||||
remainder_start,
|
|
||||||
expanded_candidates.as_mut_slice(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Simplify subcandidates and process any leftover match pairs.
|
|
||||||
for candidate in candidates_to_expand {
|
|
||||||
if !candidate.subcandidates.is_empty() {
|
|
||||||
self.finalize_or_candidate(span, scrutinee_span, candidate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the remaining candidates.
|
|
||||||
self.match_candidates(
|
|
||||||
span,
|
|
||||||
scrutinee_span,
|
|
||||||
remainder_start,
|
|
||||||
otherwise_block,
|
otherwise_block,
|
||||||
remaining_candidates,
|
candidates,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1624,6 +1555,98 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
otherwise_block
|
otherwise_block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes a list of candidates such that some of the candidates' first match pairs are
|
||||||
|
/// or-patterns, expands as many or-patterns as possible, and processes the resulting
|
||||||
|
/// candidates.
|
||||||
|
fn expand_and_match_or_candidates(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
scrutinee_span: Span,
|
||||||
|
start_block: BasicBlock,
|
||||||
|
otherwise_block: BasicBlock,
|
||||||
|
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
||||||
|
) {
|
||||||
|
// We can't expand or-patterns freely. The rule is: if the candidate has an
|
||||||
|
// or-pattern as its only remaining match pair, we can expand it freely. If it has
|
||||||
|
// other match pairs, we can expand it but we can't process more candidates after
|
||||||
|
// it.
|
||||||
|
//
|
||||||
|
// If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the
|
||||||
|
// following, or-pattern simplification (in `merge_trivial_subcandidates`) makes it
|
||||||
|
// so the `1` and `2` cases branch to a same block (which then tests `false`). If we
|
||||||
|
// took `(2, _)` in the same set of candidates, when we reach the block that tests
|
||||||
|
// `false` we don't know whether we came from `1` or `2`, hence we can't know where
|
||||||
|
// to branch on failure.
|
||||||
|
//
|
||||||
|
// ```ignore(illustrative)
|
||||||
|
// match (1, true) {
|
||||||
|
// (1 | 2, false) => {},
|
||||||
|
// (2, _) => {},
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// We therefore split the `candidates` slice in two, expand or-patterns in the first half,
|
||||||
|
// and process the rest separately.
|
||||||
|
let mut expand_until = 0;
|
||||||
|
for (i, candidate) in candidates.iter().enumerate() {
|
||||||
|
expand_until = i + 1;
|
||||||
|
if candidate.match_pairs.len() > 1
|
||||||
|
&& matches!(&candidate.match_pairs[0].test_case, TestCase::Or { .. })
|
||||||
|
{
|
||||||
|
// The candidate has an or-pattern as well as more match pairs: we must
|
||||||
|
// split the candidates list here.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
|
||||||
|
|
||||||
|
// Expand one level of or-patterns for each candidate in `candidates_to_expand`.
|
||||||
|
let mut expanded_candidates = Vec::new();
|
||||||
|
for candidate in candidates_to_expand.iter_mut() {
|
||||||
|
if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] = &*candidate.match_pairs
|
||||||
|
{
|
||||||
|
let or_match_pair = candidate.match_pairs.remove(0);
|
||||||
|
// Expand the or-pattern into subcandidates.
|
||||||
|
self.create_or_subcandidates(candidate, or_match_pair);
|
||||||
|
// Collect the newly created subcandidates.
|
||||||
|
for subcandidate in candidate.subcandidates.iter_mut() {
|
||||||
|
expanded_candidates.push(subcandidate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expanded_candidates.push(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the expanded candidates.
|
||||||
|
let remainder_start = self.cfg.start_new_block();
|
||||||
|
// There might be new or-patterns obtained from expanding the old ones, so we call
|
||||||
|
// `match_candidates` again.
|
||||||
|
self.match_candidates(
|
||||||
|
span,
|
||||||
|
scrutinee_span,
|
||||||
|
start_block,
|
||||||
|
remainder_start,
|
||||||
|
expanded_candidates.as_mut_slice(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Simplify subcandidates and process any leftover match pairs.
|
||||||
|
for candidate in candidates_to_expand {
|
||||||
|
if !candidate.subcandidates.is_empty() {
|
||||||
|
self.finalize_or_candidate(span, scrutinee_span, candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the remaining candidates.
|
||||||
|
self.match_candidates(
|
||||||
|
span,
|
||||||
|
scrutinee_span,
|
||||||
|
remainder_start,
|
||||||
|
otherwise_block,
|
||||||
|
remaining_candidates,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
|
/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
|
||||||
/// subcandidate. Any candidate that has been expanded that way should be passed to
|
/// subcandidate. Any candidate that has been expanded that way should be passed to
|
||||||
/// `finalize_or_candidate` after its subcandidates have been processed.
|
/// `finalize_or_candidate` after its subcandidates have been processed.
|
||||||
|
Loading…
Reference in New Issue
Block a user