|
| 1 | +use rustc_middle::mir::*; |
| 2 | +use rustc_middle::thir::{self, *}; |
| 3 | +use rustc_middle::ty::{self, Ty, TypeVisitableExt}; |
| 4 | + |
| 5 | +use crate::build::expr::as_place::{PlaceBase, PlaceBuilder}; |
| 6 | +use crate::build::matches::{FlatPat, MatchPair, TestCase}; |
| 7 | +use crate::build::Builder; |
| 8 | + |
| 9 | +impl<'a, 'tcx> Builder<'a, 'tcx> { |
| 10 | + /// Builds and returns [`MatchPair`] trees, one for each pattern in |
| 11 | + /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or |
| 12 | + /// [`PatKind::Leaf`]. |
| 13 | + /// |
| 14 | + /// Used internally by [`MatchPair::new`]. |
| 15 | + fn field_match_pairs<'pat>( |
| 16 | + &mut self, |
| 17 | + place: PlaceBuilder<'tcx>, |
| 18 | + subpatterns: &'pat [FieldPat<'tcx>], |
| 19 | + ) -> Vec<MatchPair<'pat, 'tcx>> { |
| 20 | + subpatterns |
| 21 | + .iter() |
| 22 | + .map(|fieldpat| { |
| 23 | + let place = |
| 24 | + place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty)); |
| 25 | + MatchPair::new(place, &fieldpat.pattern, self) |
| 26 | + }) |
| 27 | + .collect() |
| 28 | + } |
| 29 | + |
| 30 | + /// Builds [`MatchPair`] trees for the prefix/middle/suffix parts of an |
| 31 | + /// array pattern or slice pattern, and adds those trees to `match_pairs`. |
| 32 | + /// |
| 33 | + /// Used internally by [`MatchPair::new`]. |
| 34 | + fn prefix_slice_suffix<'pat>( |
| 35 | + &mut self, |
| 36 | + match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>, |
| 37 | + place: &PlaceBuilder<'tcx>, |
| 38 | + prefix: &'pat [Box<Pat<'tcx>>], |
| 39 | + opt_slice: &'pat Option<Box<Pat<'tcx>>>, |
| 40 | + suffix: &'pat [Box<Pat<'tcx>>], |
| 41 | + ) { |
| 42 | + let tcx = self.tcx; |
| 43 | + let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) { |
| 44 | + match place_resolved.ty(&self.local_decls, tcx).ty.kind() { |
| 45 | + ty::Array(_, length) => (length.eval_target_usize(tcx, self.param_env), true), |
| 46 | + _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false), |
| 47 | + } |
| 48 | + } else { |
| 49 | + ((prefix.len() + suffix.len()).try_into().unwrap(), false) |
| 50 | + }; |
| 51 | + |
| 52 | + match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| { |
| 53 | + let elem = |
| 54 | + ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false }; |
| 55 | + MatchPair::new(place.clone_project(elem), subpattern, self) |
| 56 | + })); |
| 57 | + |
| 58 | + if let Some(subslice_pat) = opt_slice { |
| 59 | + let suffix_len = suffix.len() as u64; |
| 60 | + let subslice = place.clone_project(PlaceElem::Subslice { |
| 61 | + from: prefix.len() as u64, |
| 62 | + to: if exact_size { min_length - suffix_len } else { suffix_len }, |
| 63 | + from_end: !exact_size, |
| 64 | + }); |
| 65 | + match_pairs.push(MatchPair::new(subslice, subslice_pat, self)); |
| 66 | + } |
| 67 | + |
| 68 | + match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| { |
| 69 | + let end_offset = (idx + 1) as u64; |
| 70 | + let elem = ProjectionElem::ConstantIndex { |
| 71 | + offset: if exact_size { min_length - end_offset } else { end_offset }, |
| 72 | + min_length, |
| 73 | + from_end: !exact_size, |
| 74 | + }; |
| 75 | + let place = place.clone_project(elem); |
| 76 | + MatchPair::new(place, subpattern, self) |
| 77 | + })); |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +impl<'pat, 'tcx> MatchPair<'pat, 'tcx> { |
| 82 | + /// Recursively builds a `MatchPair` tree for the given pattern and its |
| 83 | + /// subpatterns. |
| 84 | + pub(in crate::build) fn new( |
| 85 | + mut place_builder: PlaceBuilder<'tcx>, |
| 86 | + pattern: &'pat Pat<'tcx>, |
| 87 | + cx: &mut Builder<'_, 'tcx>, |
| 88 | + ) -> MatchPair<'pat, 'tcx> { |
| 89 | + // Force the place type to the pattern's type. |
| 90 | + // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? |
| 91 | + if let Some(resolved) = place_builder.resolve_upvar(cx) { |
| 92 | + place_builder = resolved; |
| 93 | + } |
| 94 | + |
| 95 | + // Only add the OpaqueCast projection if the given place is an opaque type and the |
| 96 | + // expected type from the pattern is not. |
| 97 | + let may_need_cast = match place_builder.base() { |
| 98 | + PlaceBase::Local(local) => { |
| 99 | + let ty = |
| 100 | + Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty; |
| 101 | + ty != pattern.ty && ty.has_opaque_types() |
| 102 | + } |
| 103 | + _ => true, |
| 104 | + }; |
| 105 | + if may_need_cast { |
| 106 | + place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); |
| 107 | + } |
| 108 | + |
| 109 | + let place = place_builder.try_to_place(cx); |
| 110 | + let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None }; |
| 111 | + let mut subpairs = Vec::new(); |
| 112 | + let test_case = match pattern.kind { |
| 113 | + PatKind::Wild | PatKind::Error(_) => default_irrefutable(), |
| 114 | + |
| 115 | + PatKind::Or { ref pats } => TestCase::Or { |
| 116 | + pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(), |
| 117 | + }, |
| 118 | + |
| 119 | + PatKind::Range(ref range) => { |
| 120 | + if range.is_full_range(cx.tcx) == Some(true) { |
| 121 | + default_irrefutable() |
| 122 | + } else { |
| 123 | + TestCase::Range(range) |
| 124 | + } |
| 125 | + } |
| 126 | + |
| 127 | + PatKind::Constant { value } => TestCase::Constant { value }, |
| 128 | + |
| 129 | + PatKind::AscribeUserType { |
| 130 | + ascription: thir::Ascription { ref annotation, variance }, |
| 131 | + ref subpattern, |
| 132 | + .. |
| 133 | + } => { |
| 134 | + // Apply the type ascription to the value at `match_pair.place` |
| 135 | + let ascription = place.map(|source| super::Ascription { |
| 136 | + annotation: annotation.clone(), |
| 137 | + source, |
| 138 | + variance, |
| 139 | + }); |
| 140 | + |
| 141 | + subpairs.push(MatchPair::new(place_builder, subpattern, cx)); |
| 142 | + TestCase::Irrefutable { ascription, binding: None } |
| 143 | + } |
| 144 | + |
| 145 | + PatKind::Binding { mode, var, ref subpattern, .. } => { |
| 146 | + let binding = place.map(|source| super::Binding { |
| 147 | + span: pattern.span, |
| 148 | + source, |
| 149 | + var_id: var, |
| 150 | + binding_mode: mode, |
| 151 | + }); |
| 152 | + |
| 153 | + if let Some(subpattern) = subpattern.as_ref() { |
| 154 | + // this is the `x @ P` case; have to keep matching against `P` now |
| 155 | + subpairs.push(MatchPair::new(place_builder, subpattern, cx)); |
| 156 | + } |
| 157 | + TestCase::Irrefutable { ascription: None, binding } |
| 158 | + } |
| 159 | + |
| 160 | + PatKind::InlineConstant { subpattern: ref pattern, def, .. } => { |
| 161 | + // Apply a type ascription for the inline constant to the value at `match_pair.place` |
| 162 | + let ascription = place.map(|source| { |
| 163 | + let span = pattern.span; |
| 164 | + let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id()); |
| 165 | + let args = ty::InlineConstArgs::new( |
| 166 | + cx.tcx, |
| 167 | + ty::InlineConstArgsParts { |
| 168 | + parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id), |
| 169 | + ty: cx.infcx.next_ty_var(span), |
| 170 | + }, |
| 171 | + ) |
| 172 | + .args; |
| 173 | + let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf( |
| 174 | + def.to_def_id(), |
| 175 | + ty::UserArgs { args, user_self_ty: None }, |
| 176 | + )); |
| 177 | + let annotation = ty::CanonicalUserTypeAnnotation { |
| 178 | + inferred_ty: pattern.ty, |
| 179 | + span, |
| 180 | + user_ty: Box::new(user_ty), |
| 181 | + }; |
| 182 | + super::Ascription { annotation, source, variance: ty::Contravariant } |
| 183 | + }); |
| 184 | + |
| 185 | + subpairs.push(MatchPair::new(place_builder, pattern, cx)); |
| 186 | + TestCase::Irrefutable { ascription, binding: None } |
| 187 | + } |
| 188 | + |
| 189 | + PatKind::Array { ref prefix, ref slice, ref suffix } => { |
| 190 | + cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); |
| 191 | + default_irrefutable() |
| 192 | + } |
| 193 | + PatKind::Slice { ref prefix, ref slice, ref suffix } => { |
| 194 | + cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix); |
| 195 | + |
| 196 | + if prefix.is_empty() && slice.is_some() && suffix.is_empty() { |
| 197 | + default_irrefutable() |
| 198 | + } else { |
| 199 | + TestCase::Slice { |
| 200 | + len: prefix.len() + suffix.len(), |
| 201 | + variable_length: slice.is_some(), |
| 202 | + } |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => { |
| 207 | + let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` |
| 208 | + subpairs = cx.field_match_pairs(downcast_place, subpatterns); |
| 209 | + |
| 210 | + let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| { |
| 211 | + i == variant_index || { |
| 212 | + (cx.tcx.features().exhaustive_patterns |
| 213 | + || cx.tcx.features().min_exhaustive_patterns) |
| 214 | + && !v |
| 215 | + .inhabited_predicate(cx.tcx, adt_def) |
| 216 | + .instantiate(cx.tcx, args) |
| 217 | + .apply_ignore_module(cx.tcx, cx.param_env) |
| 218 | + } |
| 219 | + }) && (adt_def.did().is_local() |
| 220 | + || !adt_def.is_variant_list_non_exhaustive()); |
| 221 | + if irrefutable { |
| 222 | + default_irrefutable() |
| 223 | + } else { |
| 224 | + TestCase::Variant { adt_def, variant_index } |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + PatKind::Leaf { ref subpatterns } => { |
| 229 | + subpairs = cx.field_match_pairs(place_builder, subpatterns); |
| 230 | + default_irrefutable() |
| 231 | + } |
| 232 | + |
| 233 | + PatKind::Deref { ref subpattern } => { |
| 234 | + subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx)); |
| 235 | + default_irrefutable() |
| 236 | + } |
| 237 | + |
| 238 | + PatKind::DerefPattern { ref subpattern, mutability } => { |
| 239 | + // Create a new temporary for each deref pattern. |
| 240 | + // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? |
| 241 | + let temp = cx.temp( |
| 242 | + Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), |
| 243 | + pattern.span, |
| 244 | + ); |
| 245 | + subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx)); |
| 246 | + TestCase::Deref { temp, mutability } |
| 247 | + } |
| 248 | + |
| 249 | + PatKind::Never => TestCase::Never, |
| 250 | + }; |
| 251 | + |
| 252 | + MatchPair { place, test_case, subpairs, pattern } |
| 253 | + } |
| 254 | +} |
0 commit comments