Use create_or_subcandidates for all or-pattern expansions

This commit is contained in:
Nadrieril 2024-03-05 22:10:48 +01:00
parent 23c9f698c0
commit 7410f78e9a
2 changed files with 37 additions and 46 deletions

View File

@ -1272,9 +1272,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) { ) {
let mut split_or_candidate = false; let mut split_or_candidate = false;
for candidate in &mut *candidates { for candidate in &mut *candidates {
if let [MatchPair { test_case: TestCase::Or { pats, .. }, .. }] = if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs {
&*candidate.match_pairs
{
// Split a candidate in which the only match-pair is an or-pattern into multiple // Split a candidate in which the only match-pair is an or-pattern into multiple
// candidates. This is so that // candidates. This is so that
// //
@ -1284,9 +1282,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// } // }
// //
// only generates a single switch. // only generates a single switch.
candidate.subcandidates = self.create_or_subcandidates(pats, candidate.has_guard); let match_pair = candidate.match_pairs.pop().unwrap();
let first_match_pair = candidate.match_pairs.pop().unwrap(); self.create_or_subcandidates(candidate, match_pair);
candidate.or_span = Some(first_match_pair.pattern.span);
split_or_candidate = true; split_or_candidate = true;
} }
} }
@ -1474,14 +1471,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
return; return;
} }
let match_pairs = mem::take(&mut first_candidate.match_pairs); let first_match_pair = first_candidate.match_pairs.remove(0);
let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap(); let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs);
let TestCase::Or { ref pats } = &first_match_pair.test_case else { unreachable!() };
let remainder_start = self.cfg.start_new_block(); let remainder_start = self.cfg.start_new_block();
let or_span = first_match_pair.pattern.span;
// Test the alternatives of this or-pattern. // 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 !remaining_match_pairs.is_empty() {
// If more match pairs remain, test them after each subcandidate. // If more match pairs remain, test them after each subcandidate.
@ -1516,25 +1510,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
); );
} }
#[instrument( #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")]
skip(self, start_block, otherwise_block, or_span, candidate, pats),
level = "debug"
)]
fn test_or_pattern<'pat>( fn test_or_pattern<'pat>(
&mut self, &mut self,
candidate: &mut Candidate<'pat, 'tcx>, candidate: &mut Candidate<'pat, 'tcx>,
start_block: BasicBlock, start_block: BasicBlock,
otherwise_block: BasicBlock, otherwise_block: BasicBlock,
pats: &[FlatPat<'pat, 'tcx>], match_pair: MatchPair<'pat, 'tcx>,
or_span: Span,
) { ) {
debug!("candidate={:#?}\npats={:#?}", candidate, pats); let or_span = match_pair.pattern.span;
let mut or_candidates: Vec<_> = pats self.create_or_subcandidates(candidate, match_pair);
.iter() let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect();
.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();
self.match_candidates( self.match_candidates(
or_span, or_span,
or_span, or_span,
@ -1542,26 +1528,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
otherwise_block, otherwise_block,
&mut or_candidate_refs, &mut or_candidate_refs,
); );
candidate.subcandidates = or_candidates;
candidate.or_span = Some(or_span);
self.merge_trivial_subcandidates(candidate); self.merge_trivial_subcandidates(candidate);
} }
/// Try to merge all of the subcandidates of the given candidate into one. /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
/// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. /// 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>) { fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
if candidate.subcandidates.is_empty() || candidate.has_guard { if candidate.subcandidates.is_empty() || candidate.has_guard {
// FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
return; return;
} }
let mut can_merge = true;
for subcandidate in &mut candidate.subcandidates {
// FIXME(or_patterns; matthewjasper) Try to be more aggressive here. // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
can_merge &= let can_merge = candidate.subcandidates.iter().all(|subcandidate| {
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty(); subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
} });
if can_merge { if can_merge {
let any_matches = self.cfg.start_new_block(); let any_matches = self.cfg.start_new_block();
let or_span = candidate.or_span.take().unwrap(); let or_span = candidate.or_span.take().unwrap();

View File

@ -12,7 +12,7 @@
//! sort of test: for example, testing which variant an enum is, or //! sort of test: for example, testing which variant an enum is, or
//! testing a value against a constant. //! 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 crate::build::Builder;
use std::mem; use std::mem;
@ -66,13 +66,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. })); match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
debug!(simplified = ?match_pairs, "simplify_match_pairs"); debug!(simplified = ?match_pairs, "simplify_match_pairs");
} }
/// Create a new candidate for each pattern in `pats`.
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| Candidate::from_flat_pat(flat_pat, has_guard)).collect()
}
} }