blob: d0d49c13f133dc9074e2e45606fa2c25744f5b2d [file] [log] [blame]
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
use crate::build::Builder;
use rustc_data_structures::fx::FxIndexSet;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::mir::*;
use rustc_middle::thir::{self, *};
use rustc_middle::ty;
use rustc_middle::ty::TypeVisitableExt;
impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn field_match_pairs<'pat>(
&mut self,
place: PlaceBuilder<'tcx>,
subpatterns: &'pat [FieldPat<'tcx>],
) -> Vec<MatchPair<'pat, 'tcx>> {
subpatterns
.iter()
.map(|fieldpat| {
let place =
place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
MatchPair::new(place, &fieldpat.pattern, self)
})
.collect()
}
pub(crate) fn prefix_slice_suffix<'pat>(
&mut self,
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
place: &PlaceBuilder<'tcx>,
prefix: &'pat [Box<Pat<'tcx>>],
opt_slice: &'pat Option<Box<Pat<'tcx>>>,
suffix: &'pat [Box<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.eval_target_usize(tcx, self.param_env), true),
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
}
} else {
((prefix.len() + suffix.len()).try_into().unwrap(), false)
};
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
let elem =
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
MatchPair::new(place.clone_project(elem), subpattern, self)
}));
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,
});
match_pairs.push(MatchPair::new(subslice, subslice_pat, self));
}
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
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);
MatchPair::new(place, subpattern, self)
}));
}
/// Creates a false edge to `imaginary_target` and a real edge to
/// real_target. If `imaginary_target` is none, or is the same as the real
/// target, a Goto is generated instead to simplify the generated MIR.
pub(crate) fn false_edges(
&mut self,
from_block: BasicBlock,
real_target: BasicBlock,
imaginary_target: Option<BasicBlock>,
source_info: SourceInfo,
) {
match imaginary_target {
Some(target) if target != real_target => {
self.cfg.terminate(
from_block,
source_info,
TerminatorKind::FalseEdge { real_target, imaginary_target: target },
);
}
_ => self.cfg.goto(from_block, source_info, real_target),
}
}
}
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
pub(in crate::build) fn new(
mut place: PlaceBuilder<'tcx>,
pattern: &'pat Pat<'tcx>,
cx: &mut Builder<'_, 'tcx>,
) -> MatchPair<'pat, 'tcx> {
// 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.resolve_upvar(cx) {
place = resolved;
}
// 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.base() {
PlaceBase::Local(local) => {
let ty = Place::ty_from(local, place.projection(), &cx.local_decls, cx.tcx).ty;
ty != pattern.ty && ty.has_opaque_types()
}
_ => true,
};
if may_need_cast {
place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
}
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
let mut subpairs = Vec::new();
let test_case = match pattern.kind {
PatKind::Never | PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
PatKind::Or { ref pats } => TestCase::Or {
pats: pats.iter().map(|pat| FlatPat::new(place.clone(), pat, cx)).collect(),
},
PatKind::Range(ref range) => {
if range.is_full_range(cx.tcx) == Some(true) {
default_irrefutable()
} else {
TestCase::Range(range)
}
}
PatKind::Constant { value } => TestCase::Constant { value },
PatKind::AscribeUserType {
ascription: thir::Ascription { ref annotation, variance },
ref subpattern,
..
} => {
// Apply the type ascription to the value at `match_pair.place`
let ascription = place.try_to_place(cx).map(|source| super::Ascription {
annotation: annotation.clone(),
source,
variance,
});
subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
TestCase::Irrefutable { ascription, binding: None }
}
PatKind::Binding {
name: _,
mutability: _,
mode,
var,
ty: _,
ref subpattern,
is_primary: _,
} => {
let binding = place.try_to_place(cx).map(|source| super::Binding {
span: pattern.span,
source,
var_id: var,
binding_mode: mode,
});
if let Some(subpattern) = subpattern.as_ref() {
// this is the `x @ P` case; have to keep matching against `P` now
subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
}
TestCase::Irrefutable { ascription: None, binding }
}
PatKind::InlineConstant { subpattern: ref pattern, def, .. } => {
// Apply a type ascription for the inline constant to the value at `match_pair.place`
let ascription = place.try_to_place(cx).map(|source| {
let span = pattern.span;
let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
let args = ty::InlineConstArgs::new(
cx.tcx,
ty::InlineConstArgsParts {
parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
ty: cx.infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span,
}),
},
)
.args;
let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
def.to_def_id(),
ty::UserArgs { args, user_self_ty: None },
));
let annotation = ty::CanonicalUserTypeAnnotation {
inferred_ty: pattern.ty,
span,
user_ty: Box::new(user_ty),
};
super::Ascription { annotation, source, variance: ty::Contravariant }
});
subpairs.push(MatchPair::new(place.clone(), pattern, cx));
TestCase::Irrefutable { ascription, binding: None }
}
PatKind::Array { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix);
default_irrefutable()
}
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix);
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
default_irrefutable()
} else {
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.clone().downcast(adt_def, variant_index); // `(x as Variant)`
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
i == variant_index || {
(cx.tcx.features().exhaustive_patterns
|| cx.tcx.features().min_exhaustive_patterns)
&& !v
.inhabited_predicate(cx.tcx, adt_def)
.instantiate(cx.tcx, args)
.apply_ignore_module(cx.tcx, 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 }
}
}
PatKind::Leaf { ref subpatterns } => {
subpairs = cx.field_match_pairs(place.clone(), subpatterns);
default_irrefutable()
}
PatKind::Deref { ref subpattern } => {
let place_builder = place.clone().deref();
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
default_irrefutable()
}
};
MatchPair { place, test_case, subpairs, pattern }
}
}
pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
cx: &'a mut Builder<'b, 'tcx>,
fake_borrows: FxIndexSet<Place<'tcx>>,
}
impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
pub(super) fn collect_fake_borrows(
cx: &'a mut Builder<'b, 'tcx>,
candidates: &[&mut Candidate<'_, 'tcx>],
) -> FxIndexSet<Place<'tcx>> {
let mut collector = Self { cx, fake_borrows: FxIndexSet::default() };
for candidate in candidates.iter() {
collector.visit_candidate(candidate);
}
collector.fake_borrows
}
fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
for binding in &candidate.extra_data.bindings {
self.visit_binding(binding);
}
for match_pair in &candidate.match_pairs {
self.visit_match_pair(match_pair);
}
}
fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) {
for binding in &flat_pat.extra_data.bindings {
self.visit_binding(binding);
}
for match_pair in &flat_pat.match_pairs {
self.visit_match_pair(match_pair);
}
}
fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) {
if let TestCase::Or { pats, .. } = &match_pair.test_case {
for flat_pat in pats.iter() {
self.visit_flat_pat(flat_pat)
}
} else {
// Insert a Shallow borrow of any place that is switched on.
if let Some(resolved_place) = match_pair.place.try_to_place(self.cx) {
self.fake_borrows.insert(resolved_place);
}
for subpair in &match_pair.subpairs {
self.visit_match_pair(subpair);
}
}
}
fn visit_binding(&mut self, Binding { source, .. }: &Binding<'tcx>) {
// Insert a borrows of prefixes of places that are bound and are
// behind a dereference projection.
//
// These borrows are taken to avoid situations like the following:
//
// match x[10] {
// _ if { x = &[0]; false } => (),
// y => (), // Out of bounds array access!
// }
//
// match *x {
// // y is bound by reference in the guard and then by copy in the
// // arm, so y is 2 in the arm!
// y if { y == 1 && (x = &2) == () } => y,
// _ => 3,
// }
if let Some(i) = source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref) {
let proj_base = &source.projection[..i];
self.fake_borrows.insert(Place {
local: source.local,
projection: self.cx.tcx.mk_place_elems(proj_base),
});
}
}
}