blob: 82c769eb3d5845680e572fc7fc610dc42fae5a9c [file] [log] [blame]
use std::sync::Arc;
use rustc_hir::ByRef;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
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, PatternExtraData, TestCase};
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// 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>],
) {
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
/// array pattern or slice pattern, and adds those trees to `match_pairs`.
///
/// Used internally by [`MatchPairTree::for_pattern`].
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>>>,
suffix: &[Pat<'tcx>],
) {
let tcx = self.tcx;
let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
match place_resolved.ty(&self.local_decls, tcx).ty.kind() {
ty::Array(_, length) => (
length
.try_to_target_usize(tcx)
.expect("expected len of array pat to be definite"),
true,
),
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
}
} else {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};
for (idx, subpattern) in prefix.iter().enumerate() {
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, extra_data)
}
if let Some(subslice_pat) = opt_slice {
let suffix_len = suffix.len() as u64;
let subslice = place.clone_project(PlaceElem::Subslice {
from: prefix.len() as u64,
to: if exact_size { min_length - suffix_len } else { suffix_len },
from_end: !exact_size,
});
MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
}
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, match_pairs, extra_data)
}
}
}
impl<'tcx> MatchPairTree<'tcx> {
/// Recursively builds a match pair tree for the given pattern and its
/// subpatterns.
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?
if let Some(resolved) = place_builder.resolve_upvar(cx) {
place_builder = resolved;
}
if !cx.tcx.next_trait_solver_globally() {
// Only add the OpaqueCast projection if the given place is an opaque type and the
// expected type from the pattern is not.
let may_need_cast = match place_builder.base() {
PlaceBase::Local(local) => {
let ty =
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
.ty;
ty != pattern.ty && ty.has_opaque_types()
}
_ => true,
};
if may_need_cast {
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
}
}
let place = place_builder.try_to_place(cx);
let mut subpairs = Vec::new();
let test_case = match pattern.kind {
PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
PatKind::Or { ref pats } => {
let pats: Box<[FlatPat<'tcx>]> =
pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
if !pats[0].extra_data.bindings.is_empty() {
// Hold a place for any bindings established in (possibly-nested) or-patterns.
// By only holding a place when bindings are present, we skip over any
// or-patterns that will be simplified by `merge_trivial_subcandidates`. In
// other words, we can assume this expands into subcandidates.
// FIXME(@dianne): this needs updating/removing if we always merge or-patterns
extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
}
Some(TestCase::Or { pats })
}
PatKind::Range(ref range) => {
if range.is_full_range(cx.tcx) == Some(true) {
None
} else {
Some(TestCase::Range(Arc::clone(range)))
}
}
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`
if let Some(source) = place {
let annotation = annotation.clone();
extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
}
None
}
PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
// 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,
extra_data,
);
}
// Then push this binding, after any bindings in the subpattern.
if let Some(source) = place {
extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
span: pattern.span,
source,
var_id: var,
binding_mode: mode,
is_shorthand,
}));
}
None
}
PatKind::ExpandedConstant { subpattern: ref pattern, .. } => {
MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
None
}
PatKind::Array { ref prefix, ref slice, ref suffix } => {
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,
extra_data,
&place_builder,
prefix,
slice,
suffix,
);
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
None
} else {
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)`
cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index
|| !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
cx.tcx,
cx.infcx.typing_env(cx.param_env),
cx.def_id.into(),
)
}) && !adt_def.variant_list_has_applicable_non_exhaustive();
if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
}
PatKind::Leaf { ref subpatterns } => {
cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
None
}
PatKind::Deref { ref subpattern }
| PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) {
// Only deref patterns on boxes can be lowered using a built-in deref.
debug_assert!(pattern.ty.is_box());
}
MatchPairTree::for_pattern(
place_builder.deref(),
subpattern,
cx,
&mut subpairs,
extra_data,
);
None
}
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => {
// Create a new temporary for each deref pattern.
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
let temp = cx.temp(
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
pattern.span,
);
MatchPairTree::for_pattern(
PlaceBuilder::from(temp).deref(),
subpattern,
cx,
&mut subpairs,
extra_data,
);
Some(TestCase::Deref { temp, mutability })
}
PatKind::Never => Some(TestCase::Never),
};
if let Some(test_case) = test_case {
// This pattern is refutable, so push a new match-pair node.
match_pairs.push(MatchPairTree {
place,
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);
}
}
}