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> { 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>, place: &PlaceBuilder<'tcx>, prefix: &'pat [Box>], opt_slice: &'pat Option>>, suffix: &'pat [Box>], ) { 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, 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_builder: 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_builder.resolve_upvar(cx) { place_builder = 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_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 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_builder.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.map(|source| super::Ascription { annotation: annotation.clone(), source, variance, }); subpairs.push(MatchPair::new(place_builder, subpattern, cx)); TestCase::Irrefutable { ascription, binding: None } } PatKind::Binding { name: _, mutability: _, mode, var, ty: _, ref subpattern, is_primary: _, } => { let binding = place.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_builder, 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.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_builder, pattern, cx)); TestCase::Irrefutable { ascription, binding: None } } PatKind::Array { ref prefix, ref slice, ref suffix } => { cx.prefix_slice_suffix(&mut subpairs, &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); 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_builder.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_builder, subpatterns); default_irrefutable() } PatKind::Deref { ref subpattern } => { subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx)); default_irrefutable() } PatKind::DerefPattern { .. } => { // FIXME(deref_patterns) // Treat it like a wildcard for now. default_irrefutable() } }; MatchPair { place, test_case, subpairs, pattern } } } pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> { cx: &'a mut Builder<'b, 'tcx>, fake_borrows: FxIndexSet>, } 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> { 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(place) = match_pair.place { self.fake_borrows.insert(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), }); } } }