Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mir_build: Integrate "simplification" steps into match-pair-tree creation #137875

Merged
merged 6 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 141 additions & 76 deletions compiler/rustc_mir_build/src/builder/matches/match_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,25 @@ 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 returns [`MatchPairTree`] subtrees, one for each pattern in
/// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
/// `subpatterns`, representing the fields of a [`PatKind::Variant`] or
/// [`PatKind::Leaf`].
///
/// Used internally by [`MatchPairTree::for_pattern`].
fn field_match_pairs(
&mut self,
match_pairs: &mut Vec<MatchPairTree<'tcx>>,
extra_data: &mut PatternExtraData<'tcx>,
place: PlaceBuilder<'tcx>,
subpatterns: &[FieldPat<'tcx>],
) -> Vec<MatchPairTree<'tcx>> {
subpatterns
.iter()
.map(|fieldpat| {
let place =
place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
MatchPairTree::for_pattern(place, &fieldpat.pattern, self)
})
.collect()
) {
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, extra_data);
}
}

/// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an
Expand All @@ -36,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>>>,
Expand All @@ -56,11 +55,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};

match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
for (idx, subpattern) in prefix.iter().enumerate() {
let elem =
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self)
}));
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
}

if let Some(subslice_pat) = opt_slice {
let suffix_len = suffix.len() as u64;
Expand All @@ -69,30 +69,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
to: if exact_size { min_length - suffix_len } else { suffix_len },
from_end: !exact_size,
});
match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self));
MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
}

match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
for (idx, subpattern) in suffix.iter().rev().enumerate() {
let end_offset = (idx + 1) as u64;
let elem = ProjectionElem::ConstantIndex {
offset: if exact_size { min_length - end_offset } else { end_offset },
min_length,
from_end: !exact_size,
};
let place = place.clone_project(elem);
MatchPairTree::for_pattern(place, subpattern, self)
}));
MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
}
}
}

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>,
) -> MatchPairTree<'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?
if let Some(resolved) = place_builder.resolve_upvar(cx) {
Expand All @@ -113,64 +115,102 @@ impl<'tcx> MatchPairTree<'tcx> {
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
}

// Place can be none if the pattern refers to a non-captured place in a closure.
let place = place_builder.try_to_place(cx);
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
let mut subpairs = Vec::new();
let test_case = match pattern.kind {
PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
PatKind::Wild | PatKind::Error(_) => None,

PatKind::Or { ref pats } => TestCase::Or {
PatKind::Or { ref pats } => Some(TestCase::Or {
pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
},
}),

PatKind::Range(ref range) => {
if range.is_full_range(cx.tcx) == Some(true) {
default_irrefutable()
None
} else {
TestCase::Range(Arc::clone(range))
Some(TestCase::Range(Arc::clone(range)))
}
}

PatKind::Constant { value } => TestCase::Constant { value },
PatKind::Constant { value } => Some(TestCase::Constant { value }),

PatKind::AscribeUserType {
ascription: Ascription { ref annotation, variance },
ref subpattern,
..
} => {
MatchPairTree::for_pattern(
place_builder,
subpattern,
cx,
&mut subpairs,
extra_data,
);

// Apply the type ascription to the value at `match_pair.place`
let ascription = place.map(|source| super::Ascription {
annotation: annotation.clone(),
source,
variance,
});

subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx));
TestCase::Irrefutable { ascription, binding: None }
if let Some(source) = place {
let annotation = annotation.clone();
extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
}

None
}

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
subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx));
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,
});
}

None
}

PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => {
subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx));
default_irrefutable()
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
None
}
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(
Expand All @@ -189,33 +229,47 @@ 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 });
}

subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx));
TestCase::Irrefutable { ascription, binding: None }
None
}

PatKind::Array { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
default_irrefutable()
cx.prefix_slice_suffix(
&mut subpairs,
extra_data,
&place_builder,
prefix,
slice,
suffix,
);
None
}
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()
None
} else {
TestCase::Slice {
Some(TestCase::Slice {
len: prefix.len() + suffix.len(),
variable_length: slice.is_some(),
}
})
}
}

PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
subpairs = cx.field_match_pairs(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
Expand All @@ -225,21 +279,23 @@ impl<'tcx> MatchPairTree<'tcx> {
.apply_ignore_module(cx.tcx, cx.infcx.typing_env(cx.param_env))
}) && (adt_def.did().is_local()
|| !adt_def.is_variant_list_non_exhaustive());
if irrefutable {
default_irrefutable()
} else {
TestCase::Variant { adt_def, variant_index }
}
if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
}

PatKind::Leaf { ref subpatterns } => {
subpairs = cx.field_match_pairs(place_builder, subpatterns);
default_irrefutable()
cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
None
}

PatKind::Deref { ref subpattern } => {
subpairs.push(MatchPairTree::for_pattern(place_builder.deref(), subpattern, cx));
default_irrefutable()
MatchPairTree::for_pattern(
place_builder.deref(),
subpattern,
cx,
&mut subpairs,
extra_data,
);
None
}

PatKind::DerefPattern { ref subpattern, mutability } => {
Expand All @@ -249,23 +305,32 @@ impl<'tcx> MatchPairTree<'tcx> {
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
pattern.span,
);
subpairs.push(MatchPairTree::for_pattern(
MatchPairTree::for_pattern(
PlaceBuilder::from(temp).deref(),
subpattern,
cx,
));
TestCase::Deref { temp, mutability }
&mut subpairs,
extra_data,
);
Some(TestCase::Deref { temp, mutability })
}

PatKind::Never => TestCase::Never,
PatKind::Never => Some(TestCase::Never),
};

MatchPairTree {
place,
test_case,
subpairs,
pattern_ty: pattern.ty,
pattern_span: pattern.span,
if let Some(test_case) = test_case {
// This pattern is refutable, so push a new match-pair node.
match_pairs.push(MatchPairTree {
place: place.expect("refutable patterns should always have a place to inspect"),
test_case,
subpairs,
pattern_ty: pattern.ty,
pattern_span: pattern.span,
})
} else {
// This pattern is irrefutable, so it doesn't need its own match-pair node.
// Just push its refutable subpatterns instead, if any.
match_pairs.extend(subpairs);
}
}
}
Loading
Loading