1
Fork 0

Populate pattern bindings/ascriptions while building MatchPairTree

This commit is contained in:
Zalathar 2025-03-05 23:25:59 +11:00
parent 281455add7
commit ef44273838
3 changed files with 100 additions and 46 deletions

View file

@ -6,7 +6,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use crate::builder::Builder;
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
use crate::builder::matches::{FlatPat, MatchPairTree, TestCase};
use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestCase};
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
@ -17,12 +17,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn field_match_pairs(
&mut self,
match_pairs: &mut Vec<MatchPairTree<'tcx>>,
extra_data: &mut PatternExtraData<'tcx>,
place: PlaceBuilder<'tcx>,
subpatterns: &[FieldPat<'tcx>],
) {
for fieldpat in subpatterns {
let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs);
MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data);
}
}
@ -33,6 +34,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn prefix_slice_suffix(
&mut self,
match_pairs: &mut Vec<MatchPairTree<'tcx>>,
extra_data: &mut PatternExtraData<'tcx>,
place: &PlaceBuilder<'tcx>,
prefix: &[Pat<'tcx>],
opt_slice: &Option<Box<Pat<'tcx>>>,
@ -57,7 +59,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let elem =
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
}
if let Some(subslice_pat) = opt_slice {
@ -67,7 +69,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
to: if exact_size { min_length - suffix_len } else { suffix_len },
from_end: !exact_size,
});
MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs);
MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
}
for (idx, subpattern) in suffix.iter().rev().enumerate() {
@ -78,7 +80,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
from_end: !exact_size,
};
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
}
}
}
@ -86,11 +88,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
impl<'tcx> MatchPairTree<'tcx> {
/// Recursively builds a match pair tree for the given pattern and its
/// subpatterns.
pub(in crate::builder) fn for_pattern(
pub(super) fn for_pattern(
mut place_builder: PlaceBuilder<'tcx>,
pattern: &Pat<'tcx>,
cx: &mut Builder<'_, 'tcx>,
match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
) {
// Force the place type to the pattern's type.
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
@ -113,7 +116,7 @@ impl<'tcx> MatchPairTree<'tcx> {
}
let place = place_builder.try_to_place(cx);
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
let default_irrefutable = || TestCase::Irrefutable {};
let mut subpairs = Vec::new();
let test_case = match pattern.kind {
PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
@ -137,39 +140,77 @@ impl<'tcx> MatchPairTree<'tcx> {
ref subpattern,
..
} => {
// Apply the type ascription to the value at `match_pair.place`
let ascription = place.map(|source| super::Ascription {
annotation: annotation.clone(),
source,
variance,
});
MatchPairTree::for_pattern(
place_builder,
subpattern,
cx,
&mut subpairs,
extra_data,
);
MatchPairTree::for_pattern(place_builder, subpattern, cx, &mut subpairs);
TestCase::Irrefutable { ascription, binding: None }
// Apply the type ascription to the value at `match_pair.place`
if let Some(source) = place {
let annotation = annotation.clone();
extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
}
default_irrefutable()
}
PatKind::Binding { mode, var, ref subpattern, .. } => {
let binding = place.map(|source| super::Binding {
span: pattern.span,
source,
var_id: var,
binding_mode: mode,
});
// In order to please the borrow checker, when lowering a pattern
// like `x @ subpat` we must establish any bindings in `subpat`
// before establishing the binding for `x`.
//
// For example (from #69971):
//
// ```ignore (illustrative)
// struct NonCopyStruct {
// copy_field: u32,
// }
//
// fn foo1(x: NonCopyStruct) {
// let y @ NonCopyStruct { copy_field: z } = x;
// // the above should turn into
// let z = x.copy_field;
// let y = x;
// }
// ```
// First, recurse into the subpattern, if any.
if let Some(subpattern) = subpattern.as_ref() {
// this is the `x @ P` case; have to keep matching against `P` now
MatchPairTree::for_pattern(place_builder, subpattern, cx, &mut subpairs);
MatchPairTree::for_pattern(
place_builder,
subpattern,
cx,
&mut subpairs,
extra_data,
);
}
TestCase::Irrefutable { ascription: None, binding }
// Then push this binding, after any bindings in the subpattern.
if let Some(source) = place {
extra_data.bindings.push(super::Binding {
span: pattern.span,
source,
var_id: var,
binding_mode: mode,
});
}
default_irrefutable()
}
PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => {
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs);
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
default_irrefutable()
}
PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => {
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
// Apply a type ascription for the inline constant to the value at `match_pair.place`
let ascription = place.map(|source| {
if let Some(source) = place {
let span = pattern.span;
let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
let args = ty::InlineConstArgs::new(
@ -188,19 +229,33 @@ impl<'tcx> MatchPairTree<'tcx> {
span,
user_ty: Box::new(user_ty),
};
super::Ascription { annotation, source, variance: ty::Contravariant }
});
let variance = ty::Contravariant;
extra_data.ascriptions.push(super::Ascription { annotation, source, variance });
}
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs);
TestCase::Irrefutable { ascription, binding: None }
default_irrefutable()
}
PatKind::Array { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
cx.prefix_slice_suffix(
&mut subpairs,
extra_data,
&place_builder,
prefix,
slice,
suffix,
);
default_irrefutable()
}
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
cx.prefix_slice_suffix(
&mut subpairs,
extra_data,
&place_builder,
prefix,
slice,
suffix,
);
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
default_irrefutable()
@ -214,7 +269,7 @@ impl<'tcx> MatchPairTree<'tcx> {
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
cx.field_match_pairs(&mut subpairs, downcast_place, subpatterns);
cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index
@ -232,12 +287,18 @@ impl<'tcx> MatchPairTree<'tcx> {
}
PatKind::Leaf { ref subpatterns } => {
cx.field_match_pairs(&mut subpairs, place_builder, subpatterns);
cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
default_irrefutable()
}
PatKind::Deref { ref subpattern } => {
MatchPairTree::for_pattern(place_builder.deref(), subpattern, cx, &mut subpairs);
MatchPairTree::for_pattern(
place_builder.deref(),
subpattern,
cx,
&mut subpairs,
extra_data,
);
default_irrefutable()
}
@ -253,6 +314,7 @@ impl<'tcx> MatchPairTree<'tcx> {
subpattern,
cx,
&mut subpairs,
extra_data,
);
TestCase::Deref { temp, mutability }
}

View file

@ -1008,17 +1008,15 @@ impl<'tcx> FlatPat<'tcx> {
/// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest
/// for the given pattern.
fn new(place: PlaceBuilder<'tcx>, pattern: &Pat<'tcx>, cx: &mut Builder<'_, 'tcx>) -> Self {
// First, recursively build a tree of match pairs for the given pattern.
// Recursively build a tree of match pairs for the given pattern.
let mut match_pairs = vec![];
MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs);
let mut extra_data = PatternExtraData {
span: pattern.span,
bindings: Vec::new(),
ascriptions: Vec::new(),
is_never: pattern.is_never_pattern(),
};
// Recursively remove irrefutable match pairs, while recording their
// bindings/ascriptions, and sort or-patterns after other match pairs.
MatchPairTree::for_pattern(place, pattern, cx, &mut match_pairs, &mut extra_data);
cx.simplify_match_pairs(&mut match_pairs, &mut extra_data);
Self { match_pairs, extra_data }
@ -1238,7 +1236,7 @@ struct Ascription<'tcx> {
/// - See [`Builder::expand_and_match_or_candidates`].
#[derive(Debug, Clone)]
enum TestCase<'tcx> {
Irrefutable { binding: Option<Binding<'tcx>>, ascription: Option<Ascription<'tcx>> },
Irrefutable {},
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
Constant { value: mir::Const<'tcx> },
Range(Arc<PatRange<'tcx>>),

View file

@ -47,13 +47,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// match lowering forces us to lower bindings inside or-patterns last.
for mut match_pair in mem::take(match_pairs) {
self.simplify_match_pairs(&mut match_pair.subpairs, extra_data);
if let TestCase::Irrefutable { binding, ascription } = match_pair.test_case {
if let Some(binding) = binding {
extra_data.bindings.push(binding);
}
if let Some(ascription) = ascription {
extra_data.ascriptions.push(ascription);
}
if let TestCase::Irrefutable {} = match_pair.test_case {
// Simplifiable pattern; we replace it with its already simplified subpairs.
match_pairs.append(&mut match_pair.subpairs);
} else {