From bff4d213fac5ba32b51d320800700124205f6565 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 30 Jun 2024 12:18:31 +0200 Subject: [PATCH] Factor out the special handling of or-patterns --- .../rustc_mir_build/src/build/matches/mod.rs | 185 ++++++++++-------- 1 file changed, 104 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index cc46a5603c3d5..47f4fd0d07b59 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -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 /// code size at the expense of non-optimal code paths. #[instrument(skip(self), level = "debug")] - fn match_candidates<'pat>( + fn match_candidates( &mut self, span: Span, scrutinee_span: Span, start_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 - // expand the or-pattern before we can proceed further. - // - // We can't expand them freely however. 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 both halves separately. - let mut expand_until = 0; - for (i, candidate) in candidates.iter().enumerate() { - if matches!( + // If any candidate starts with an or-pattern, we have to expand the or-pattern before we + // can proceed further. + let expand_ors = candidates.iter().any(|candidate| { + matches!( &*candidate.match_pairs, [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(|| { - if candidates_to_expand.is_empty() { + if !expand_ors { // No candidates start with an or-pattern, we can continue. self.match_expanded_candidates( span, scrutinee_span, start_block, otherwise_block, - remaining_candidates, + candidates, ); } else { - // 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( + self.expand_and_match_or_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, + candidates, ); } }); @@ -1624,6 +1555,98 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 /// subcandidate. Any candidate that has been expanded that way should be passed to /// `finalize_or_candidate` after its subcandidates have been processed.