Make simplify_candidate more general

Because we will soon need to apply it to match pairs that aren't
directly in a candidate.
This commit is contained in:
Nadrieril 2024-01-21 21:38:24 +01:00
parent bafad5a737
commit e86c82296f
2 changed files with 56 additions and 48 deletions

View File

@ -1168,7 +1168,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// be a switch or pattern comparison.
let mut split_or_candidate = false;
for candidate in &mut *candidates {
self.simplify_candidate(candidate);
self.simplify_match_pairs(
&mut candidate.match_pairs,
&mut candidate.bindings,
&mut candidate.ascriptions,
);
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] =
&*candidate.match_pairs
{
@ -1181,7 +1185,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// }
//
// only generates a single switch.
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
candidate.subcandidates =
self.create_or_subcandidates(place, pats, candidate.has_guard);
candidate.match_pairs.pop();
split_or_candidate = true;
}

View File

@ -6,7 +6,7 @@
//! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
//! - `place @ x` can be simplified to `[]` by binding `x` to `place`
//!
//! The `simplify_candidate` routine just repeatedly applies these
//! The `simplify_match_pairs` routine just repeatedly applies these
//! sort of simplifications until there is nothing left to
//! simplify. Match pairs cannot be simplified if they require some
//! sort of test: for example, testing which variant an enum is, or
@ -22,10 +22,15 @@ use rustc_middle::ty;
use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Simplify a candidate so that all match pairs require a test.
#[instrument(skip(self, candidate), level = "debug")]
pub(super) fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
debug!("{candidate:#?}");
/// Simplify a list of match pairs so they all require a test. Stores relevant bindings and
/// ascriptions in the provided `Vec`s.
#[instrument(skip(self), level = "debug")]
pub(super) fn simplify_match_pairs<'pat>(
&mut self,
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
candidate_bindings: &mut Vec<Binding<'tcx>>,
candidate_ascriptions: &mut Vec<Ascription<'tcx>>,
) {
// In order to please the borrow checker, in a pattern like `x @ pat` we must lower the
// bindings in `pat` before `x`. E.g. (#69971):
//
@ -53,25 +58,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// bindings in iter 2: [6, 7]
//
// final bindings: [6, 7, 4, 5, 1, 2, 3]
let mut accumulated_bindings = mem::take(&mut candidate.bindings);
let mut accumulated_bindings = mem::take(candidate_bindings);
// Repeatedly simplify match pairs until fixed point is reached
loop {
let mut changed = false;
for match_pair in mem::take(&mut candidate.match_pairs) {
match self.simplify_match_pair(match_pair, candidate) {
for match_pair in mem::take(match_pairs) {
match self.simplify_match_pair(
match_pair,
candidate_bindings,
candidate_ascriptions,
match_pairs,
) {
Ok(()) => {
changed = true;
}
Err(match_pair) => {
candidate.match_pairs.push(match_pair);
match_pairs.push(match_pair);
}
}
}
// This does: accumulated_bindings = candidate.bindings.take() ++ accumulated_bindings
candidate.bindings.extend_from_slice(&accumulated_bindings);
mem::swap(&mut candidate.bindings, &mut accumulated_bindings);
candidate.bindings.clear();
candidate_bindings.extend_from_slice(&accumulated_bindings);
mem::swap(candidate_bindings, &mut accumulated_bindings);
candidate_bindings.clear();
if !changed {
// If we were not able to simplify anymore, done.
@ -79,14 +89,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
// Store computed bindings back in `candidate`.
mem::swap(&mut candidate.bindings, &mut accumulated_bindings);
// Store computed bindings back in `candidate_bindings`.
mem::swap(candidate_bindings, &mut accumulated_bindings);
// Move or-patterns to the end, because they can result in us
// creating additional candidates, so we want to test them as
// late as possible.
candidate.match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
debug!(simplified = ?candidate, "simplify_candidate");
match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
debug!(simplified = ?match_pairs, "simplify_match_pairs");
}
/// Given `candidate` that has a single or-pattern for its match-pairs,
@ -94,19 +104,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// `pats`.
pub(super) fn create_or_subcandidates<'pat>(
&mut self,
candidate: &Candidate<'pat, 'tcx>,
place: &PlaceBuilder<'tcx>,
pats: &'pat [Box<Pat<'tcx>>],
has_guard: bool,
) -> Vec<Candidate<'pat, 'tcx>> {
pats.iter()
.map(|box pat| {
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self);
self.simplify_candidate(&mut candidate);
let mut candidate = Candidate::new(place.clone(), pat, has_guard, self);
self.simplify_match_pairs(
&mut candidate.match_pairs,
&mut candidate.bindings,
&mut candidate.ascriptions,
);
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] =
&*candidate.match_pairs
{
candidate.subcandidates = self.create_or_subcandidates(&candidate, place, pats);
candidate.subcandidates =
self.create_or_subcandidates(place, pats, candidate.has_guard);
candidate.match_pairs.pop();
}
candidate
@ -122,7 +137,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn simplify_match_pair<'pat>(
&mut self,
match_pair: MatchPair<'pat, 'tcx>,
candidate: &mut Candidate<'pat, 'tcx>,
bindings: &mut Vec<Binding<'tcx>>,
ascriptions: &mut Vec<Ascription<'tcx>>,
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
) -> Result<(), MatchPair<'pat, 'tcx>> {
match match_pair.pattern.kind {
PatKind::AscribeUserType {
@ -131,14 +148,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} => {
// Apply the type ascription to the value at `match_pair.place`
if let Some(source) = match_pair.place.try_to_place(self) {
candidate.ascriptions.push(Ascription {
ascriptions.push(Ascription {
annotation: annotation.clone(),
source,
variance,
});
}
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
Ok(())
}
@ -158,7 +175,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
is_primary: _,
} => {
if let Some(source) = match_pair.place.try_to_place(self) {
candidate.bindings.push(Binding {
bindings.push(Binding {
span: match_pair.pattern.span,
source,
var_id: var,
@ -168,7 +185,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
if let Some(subpattern) = subpattern.as_ref() {
// this is the `x @ P` case; have to keep matching against `P` now
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
}
Ok(())
@ -211,13 +228,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span,
user_ty: Box::new(user_ty),
};
candidate.ascriptions.push(Ascription {
ascriptions.push(Ascription {
annotation,
source,
variance: ty::Contravariant,
});
}
candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
Ok(())
}
@ -233,13 +250,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
// irrefutable
self.prefix_slice_suffix(
&mut candidate.match_pairs,
&match_pair.place,
prefix,
slice,
suffix,
);
self.prefix_slice_suffix(match_pairs, &match_pair.place, prefix, slice, suffix);
Ok(())
} else {
Err(match_pair)
@ -260,9 +271,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|| !adt_def.is_variant_list_non_exhaustive());
if irrefutable {
let place_builder = match_pair.place.downcast(adt_def, variant_index);
candidate
.match_pairs
.extend(self.field_match_pairs(place_builder, subpatterns));
match_pairs.extend(self.field_match_pairs(place_builder, subpatterns));
Ok(())
} else {
Err(match_pair)
@ -270,25 +279,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
PatKind::Array { ref prefix, ref slice, ref suffix } => {
self.prefix_slice_suffix(
&mut candidate.match_pairs,
&match_pair.place,
prefix,
slice,
suffix,
);
self.prefix_slice_suffix(match_pairs, &match_pair.place, prefix, slice, suffix);
Ok(())
}
PatKind::Leaf { ref subpatterns } => {
// tuple struct, match subpats (if any)
candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
Ok(())
}
PatKind::Deref { ref subpattern } => {
let place_builder = match_pair.place.deref();
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self));
match_pairs.push(MatchPair::new(place_builder, subpattern, self));
Ok(())
}