Auto merge of #122046 - Nadrieril:integrate-or-pats2, r=matthewjasper
match lowering: handle or-patterns one layer at a time `create_or_subcandidates` and `merge_trivial_subcandidates` both call themselves recursively to handle nested or-patterns, which is hard to follow. In this PR I avoid the need for that; we now process a single "layer" of or-patterns at a time. By calling back into `match_candidates`, we only need to expand one layer at a time. Conversely, since we always try to simplify a layer that we just expanded (thanks to https://github.com/rust-lang/rust/pull/123067), we only have to merge one layer at a time. r? `@matthewjasper`
This commit is contained in:
commit
6bb6b816bf
@ -1270,9 +1270,7 @@ fn match_candidates<'pat>(
|
||||
) {
|
||||
let mut split_or_candidate = false;
|
||||
for candidate in &mut *candidates {
|
||||
if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] =
|
||||
&*candidate.match_pairs
|
||||
{
|
||||
if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs {
|
||||
// Split a candidate in which the only match-pair is an or-pattern into multiple
|
||||
// candidates. This is so that
|
||||
//
|
||||
@ -1282,9 +1280,8 @@ fn match_candidates<'pat>(
|
||||
// }
|
||||
//
|
||||
// only generates a single switch.
|
||||
candidate.subcandidates = self.create_or_subcandidates(pats, candidate.has_guard);
|
||||
let first_match_pair = candidate.match_pairs.pop().unwrap();
|
||||
candidate.or_span = Some(first_match_pair.pattern.span);
|
||||
let match_pair = candidate.match_pairs.pop().unwrap();
|
||||
self.create_or_subcandidates(candidate, match_pair);
|
||||
split_or_candidate = true;
|
||||
}
|
||||
}
|
||||
@ -1297,7 +1294,7 @@ fn match_candidates<'pat>(
|
||||
for candidate in candidates.iter_mut() {
|
||||
candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
|
||||
}
|
||||
self.match_simplified_candidates(
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
start_block,
|
||||
@ -1472,14 +1469,11 @@ fn test_candidates_with_or(
|
||||
return;
|
||||
}
|
||||
|
||||
let match_pairs = mem::take(&mut first_candidate.match_pairs);
|
||||
let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap();
|
||||
let TestCase::Or { ref pats } = &first_match_pair.test_case else { unreachable!() };
|
||||
|
||||
let first_match_pair = first_candidate.match_pairs.remove(0);
|
||||
let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs);
|
||||
let remainder_start = self.cfg.start_new_block();
|
||||
let or_span = first_match_pair.pattern.span;
|
||||
// Test the alternatives of this or-pattern.
|
||||
self.test_or_pattern(first_candidate, start_block, remainder_start, pats, or_span);
|
||||
self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair);
|
||||
|
||||
if !remaining_match_pairs.is_empty() {
|
||||
// If more match pairs remain, test them after each subcandidate.
|
||||
@ -1514,25 +1508,17 @@ fn test_candidates_with_or(
|
||||
);
|
||||
}
|
||||
|
||||
#[instrument(
|
||||
skip(self, start_block, otherwise_block, or_span, candidate, pats),
|
||||
level = "debug"
|
||||
)]
|
||||
#[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")]
|
||||
fn test_or_pattern<'pat>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
start_block: BasicBlock,
|
||||
otherwise_block: BasicBlock,
|
||||
pats: &[FlatPat<'pat, 'tcx>],
|
||||
or_span: Span,
|
||||
match_pair: MatchPair<'pat, 'tcx>,
|
||||
) {
|
||||
debug!("candidate={:#?}\npats={:#?}", candidate, pats);
|
||||
let mut or_candidates: Vec<_> = pats
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
|
||||
.collect();
|
||||
let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
|
||||
let or_span = match_pair.pattern.span;
|
||||
self.create_or_subcandidates(candidate, match_pair);
|
||||
let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect();
|
||||
self.match_candidates(
|
||||
or_span,
|
||||
or_span,
|
||||
@ -1540,30 +1526,40 @@ fn test_or_pattern<'pat>(
|
||||
otherwise_block,
|
||||
&mut or_candidate_refs,
|
||||
);
|
||||
candidate.subcandidates = or_candidates;
|
||||
candidate.or_span = Some(or_span);
|
||||
self.merge_trivial_subcandidates(candidate);
|
||||
}
|
||||
|
||||
/// Try to merge all of the subcandidates of the given candidate into one.
|
||||
/// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`.
|
||||
/// 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
|
||||
/// `merge_trivial_subcandidates` after its subcandidates have been processed.
|
||||
fn create_or_subcandidates<'pat>(
|
||||
&mut self,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
match_pair: MatchPair<'pat, 'tcx>,
|
||||
) {
|
||||
let TestCase::Or { pats } = match_pair.test_case else { bug!() };
|
||||
debug!("expanding or-pattern: candidate={:#?}\npats={:#?}", candidate, pats);
|
||||
candidate.or_span = Some(match_pair.pattern.span);
|
||||
candidate.subcandidates = pats
|
||||
.into_vec()
|
||||
.into_iter()
|
||||
.map(|flat_pat| Candidate::from_flat_pat(flat_pat, candidate.has_guard))
|
||||
.collect();
|
||||
}
|
||||
|
||||
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
|
||||
/// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have
|
||||
/// been expanded with `create_or_subcandidates`.
|
||||
fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
|
||||
if candidate.subcandidates.is_empty() || candidate.has_guard {
|
||||
// FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
|
||||
return;
|
||||
}
|
||||
|
||||
let mut can_merge = true;
|
||||
|
||||
// Not `Iterator::all` because we don't want to short-circuit.
|
||||
for subcandidate in &mut candidate.subcandidates {
|
||||
self.merge_trivial_subcandidates(subcandidate);
|
||||
|
||||
// FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
|
||||
can_merge &=
|
||||
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty();
|
||||
}
|
||||
|
||||
// FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
|
||||
let can_merge = candidate.subcandidates.iter().all(|subcandidate| {
|
||||
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
|
||||
});
|
||||
if can_merge {
|
||||
let any_matches = self.cfg.start_new_block();
|
||||
let or_span = candidate.or_span.take().unwrap();
|
||||
|
@ -12,7 +12,7 @@
|
||||
//! sort of test: for example, testing which variant an enum is, or
|
||||
//! testing a value against a constant.
|
||||
|
||||
use crate::build::matches::{Candidate, FlatPat, MatchPair, PatternExtraData, TestCase};
|
||||
use crate::build::matches::{MatchPair, PatternExtraData, TestCase};
|
||||
use crate::build::Builder;
|
||||
|
||||
use std::mem;
|
||||
@ -66,27 +66,4 @@ pub(super) fn simplify_match_pairs<'pat>(
|
||||
match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
|
||||
debug!(simplified = ?match_pairs, "simplify_match_pairs");
|
||||
}
|
||||
|
||||
/// Create a new candidate for each pattern in `pats`, and recursively simplify tje
|
||||
/// single-or-pattern case.
|
||||
pub(super) fn create_or_subcandidates<'pat>(
|
||||
&mut self,
|
||||
pats: &[FlatPat<'pat, 'tcx>],
|
||||
has_guard: bool,
|
||||
) -> Vec<Candidate<'pat, 'tcx>> {
|
||||
pats.iter()
|
||||
.cloned()
|
||||
.map(|flat_pat| {
|
||||
let mut candidate = Candidate::from_flat_pat(flat_pat, has_guard);
|
||||
if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] =
|
||||
&*candidate.match_pairs
|
||||
{
|
||||
candidate.subcandidates = self.create_or_subcandidates(pats, has_guard);
|
||||
let first_match_pair = candidate.match_pairs.pop().unwrap();
|
||||
candidate.or_span = Some(first_match_pair.pattern.span);
|
||||
}
|
||||
candidate
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user