rustc_mir_build/src/builder/matches/match_pair.rs
Line | Count | Source |
1 | | use std::sync::Arc; |
2 | | |
3 | | use rustc_abi::FieldIdx; |
4 | | use rustc_middle::mir::*; |
5 | | use rustc_middle::span_bug; |
6 | | use rustc_middle::thir::*; |
7 | | use rustc_middle::ty::{self, Ty, TypeVisitableExt}; |
8 | | |
9 | | use crate::builder::Builder; |
10 | | use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder}; |
11 | | use crate::builder::matches::{ |
12 | | FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase, |
13 | | }; |
14 | | |
15 | | /// For an array or slice pattern's subpatterns (prefix/slice/suffix), returns a list |
16 | | /// of those subpatterns, each paired with a suitably-projected [`PlaceBuilder`]. |
17 | 0 | fn prefix_slice_suffix<'a, 'tcx>( |
18 | 0 | place: &PlaceBuilder<'tcx>, |
19 | 0 | array_len: Option<u64>, // Some for array patterns; None for slice patterns |
20 | 0 | prefix: &'a [Pat<'tcx>], |
21 | 0 | opt_slice: &'a Option<Box<Pat<'tcx>>>, |
22 | 0 | suffix: &'a [Pat<'tcx>], |
23 | 0 | ) -> Vec<(PlaceBuilder<'tcx>, &'a Pat<'tcx>)> { |
24 | 0 | let prefix_len = u64::try_from(prefix.len()).unwrap(); |
25 | 0 | let suffix_len = u64::try_from(suffix.len()).unwrap(); |
26 | | |
27 | 0 | let mut output_pairs = |
28 | 0 | Vec::with_capacity(prefix.len() + usize::from(opt_slice.is_some()) + suffix.len()); |
29 | | |
30 | | // For slice patterns with a `..` followed by 0 or more suffix subpatterns, |
31 | | // the actual slice index of those subpatterns isn't statically known, so |
32 | | // we have to index them relative to the end of the slice. |
33 | | // |
34 | | // For array patterns, all subpatterns are indexed relative to the start. |
35 | 0 | let (min_length, is_array) = match array_len { |
36 | 0 | Some(len) => (len, true), |
37 | 0 | None => (prefix_len + suffix_len, false), |
38 | | }; |
39 | | |
40 | 0 | for (offset, prefix_subpat) in (0u64..).zip(prefix) { |
41 | 0 | let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false }; |
42 | 0 | let subplace = place.clone_project(elem); |
43 | 0 | output_pairs.push((subplace, prefix_subpat)); |
44 | 0 | } |
45 | | |
46 | 0 | if let Some(slice_subpat) = opt_slice { |
47 | 0 | let elem = PlaceElem::Subslice { |
48 | 0 | from: prefix_len, |
49 | 0 | to: if is_array { min_length - suffix_len } else { suffix_len }, |
50 | 0 | from_end: !is_array, |
51 | | }; |
52 | 0 | let subplace = place.clone_project(elem); |
53 | 0 | output_pairs.push((subplace, slice_subpat)); |
54 | 0 | } |
55 | | |
56 | 0 | for (offset_from_end, suffix_subpat) in (1u64..).zip(suffix.iter().rev()) { |
57 | 0 | let elem = ProjectionElem::ConstantIndex { |
58 | 0 | offset: if is_array { min_length - offset_from_end } else { offset_from_end }, |
59 | 0 | min_length, |
60 | 0 | from_end: !is_array, |
61 | | }; |
62 | 0 | let subplace = place.clone_project(elem); |
63 | 0 | output_pairs.push((subplace, suffix_subpat)); |
64 | | } |
65 | | |
66 | 0 | output_pairs |
67 | 0 | } |
68 | | |
69 | | impl<'tcx> MatchPairTree<'tcx> { |
70 | | /// Recursively builds a match pair tree for the given pattern and its |
71 | | /// subpatterns. |
72 | 101k | pub(super) fn for_pattern( |
73 | 101k | mut place_builder: PlaceBuilder<'tcx>, |
74 | 101k | pattern: &Pat<'tcx>, |
75 | 101k | cx: &mut Builder<'_, 'tcx>, |
76 | 101k | match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector |
77 | 101k | extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here |
78 | 101k | ) { |
79 | | // Force the place type to the pattern's type. |
80 | | // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks? |
81 | 101k | if let Some(resolved) = place_builder.resolve_upvar(cx) { |
82 | 0 | place_builder = resolved; |
83 | 101k | } |
84 | | |
85 | 101k | if !cx.tcx.next_trait_solver_globally() { |
86 | | // Only add the OpaqueCast projection if the given place is an opaque type and the |
87 | | // expected type from the pattern is not. |
88 | 101k | let may_need_cast = match place_builder.base() { |
89 | 101k | PlaceBase::Local(local) => { |
90 | 101k | let ty = |
91 | 101k | Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx) |
92 | 101k | .ty; |
93 | 101k | ty != pattern.ty && ty.has_opaque_types() |
94 | | } |
95 | 2 | _ => true, |
96 | | }; |
97 | 101k | if may_need_cast { |
98 | 2 | place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty)); |
99 | 101k | } |
100 | 0 | } |
101 | | |
102 | 101k | let place = place_builder.try_to_place(cx); |
103 | | |
104 | | // Apply any type ascriptions to the value at `match_pair.place`. |
105 | 101k | if let Some(place) = place |
106 | 101k | && let Some(extra) = &pattern.extra |
107 | | { |
108 | 5.21k | for &Ascription { ref annotation, variance } in &extra.ascriptions { |
109 | 5.17k | extra_data.ascriptions.push(super::Ascription { |
110 | 5.17k | source: place, |
111 | 5.17k | annotation: annotation.clone(), |
112 | 5.17k | variance, |
113 | 5.17k | }); |
114 | 5.17k | } |
115 | 96.2k | } |
116 | | |
117 | 101k | let mut subpairs = Vec::new(); |
118 | 101k | let testable_case = match pattern.kind { |
119 | 58.8k | PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None, |
120 | | |
121 | 72 | PatKind::Or { ref pats } => { |
122 | 72 | let pats: Box<[FlatPat<'tcx>]> = |
123 | 144 | pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(); |
124 | 72 | if !pats[0].extra_data.bindings.is_empty() { |
125 | 6 | // Hold a place for any bindings established in (possibly-nested) or-patterns. |
126 | 6 | // By only holding a place when bindings are present, we skip over any |
127 | 6 | // or-patterns that will be simplified by `merge_trivial_subcandidates`. In |
128 | 6 | // other words, we can assume this expands into subcandidates. |
129 | 6 | // FIXME(@dianne): this needs updating/removing if we always merge or-patterns |
130 | 6 | extra_data.bindings.push(super::SubpatternBindings::FromOrPattern); |
131 | 66 | } |
132 | 72 | Some(TestableCase::Or { pats }) |
133 | | } |
134 | | |
135 | 20 | PatKind::Range(ref range) => { |
136 | 20 | assert_eq!(pattern.ty, range.ty); |
137 | 20 | if range.is_full_range(cx.tcx) == Some(true) { |
138 | 0 | None |
139 | | } else { |
140 | 20 | Some(TestableCase::Range(Arc::clone(range))) |
141 | | } |
142 | | } |
143 | | |
144 | 35 | PatKind::Constant { value } => { |
145 | 35 | assert_eq!(pattern.ty, value.ty); |
146 | | |
147 | | // Classify the constant-pattern into further kinds, to |
148 | | // reduce the number of ad-hoc type tests needed later on. |
149 | 35 | let pat_ty = pattern.ty; |
150 | 35 | let const_kind = if pat_ty.is_bool() { |
151 | 12 | PatConstKind::Bool |
152 | 23 | } else if pat_ty.is_integral() || pat_ty.is_char() { |
153 | 23 | PatConstKind::IntOrChar |
154 | 0 | } else if pat_ty.is_floating_point() { |
155 | 0 | PatConstKind::Float |
156 | 0 | } else if pat_ty.is_str() { |
157 | 0 | PatConstKind::String |
158 | | } else { |
159 | | // FIXME(Zalathar): This still covers several different |
160 | | // categories (e.g. raw pointer, pattern-type) |
161 | | // which could be split out into their own kinds. |
162 | 0 | PatConstKind::Other |
163 | | }; |
164 | 35 | Some(TestableCase::Constant { value, kind: const_kind }) |
165 | | } |
166 | | |
167 | 11.0k | PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => { |
168 | | // In order to please the borrow checker, when lowering a pattern |
169 | | // like `x @ subpat` we must establish any bindings in `subpat` |
170 | | // before establishing the binding for `x`. |
171 | | // |
172 | | // For example (from #69971): |
173 | | // |
174 | | // ```ignore (illustrative) |
175 | | // struct NonCopyStruct { |
176 | | // copy_field: u32, |
177 | | // } |
178 | | // |
179 | | // fn foo1(x: NonCopyStruct) { |
180 | | // let y @ NonCopyStruct { copy_field: z } = x; |
181 | | // // the above should turn into |
182 | | // let z = x.copy_field; |
183 | | // let y = x; |
184 | | // } |
185 | | // ``` |
186 | | |
187 | | // First, recurse into the subpattern, if any. |
188 | 11.0k | if let Some(subpattern) = subpattern.as_ref() { |
189 | 38 | // this is the `x @ P` case; have to keep matching against `P` now |
190 | 38 | MatchPairTree::for_pattern( |
191 | 38 | place_builder, |
192 | 38 | subpattern, |
193 | 38 | cx, |
194 | 38 | &mut subpairs, |
195 | 38 | extra_data, |
196 | 38 | ); |
197 | 11.0k | } |
198 | | |
199 | | // Then push this binding, after any bindings in the subpattern. |
200 | 11.0k | if let Some(source) = place { |
201 | 11.0k | extra_data.bindings.push(super::SubpatternBindings::One(super::Binding { |
202 | 11.0k | span: pattern.span, |
203 | 11.0k | source, |
204 | 11.0k | var_id: var, |
205 | 11.0k | binding_mode: mode, |
206 | 11.0k | is_shorthand, |
207 | 11.0k | })); |
208 | 11.0k | } |
209 | | |
210 | 11.0k | None |
211 | | } |
212 | | |
213 | 0 | PatKind::Array { ref prefix, ref slice, ref suffix } => { |
214 | | // Determine the statically-known length of the array type being matched. |
215 | | // This should always succeed for legal programs, but could fail for |
216 | | // erroneous programs (e.g. the type is `[u8; const { panic!() }]`), |
217 | | // so take care not to ICE if this fails. |
218 | 0 | let array_len = match pattern.ty.kind() { |
219 | 0 | ty::Array(_, len) => len.try_to_target_usize(cx.tcx), |
220 | 0 | _ => None, |
221 | | }; |
222 | 0 | if let Some(array_len) = array_len { |
223 | 0 | for (subplace, subpat) in |
224 | 0 | prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix) |
225 | 0 | { |
226 | 0 | MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); |
227 | 0 | } |
228 | 0 | } else { |
229 | 0 | // If the array length couldn't be determined, ignore the |
230 | 0 | // subpatterns and delayed-assert that compilation will fail. |
231 | 0 | cx.tcx.dcx().span_delayed_bug( |
232 | 0 | pattern.span, |
233 | 0 | format!( |
234 | 0 | "array length in pattern couldn't be determined for ty={:?}", |
235 | 0 | pattern.ty |
236 | 0 | ), |
237 | 0 | ); |
238 | 0 | } |
239 | | |
240 | 0 | None |
241 | | } |
242 | 0 | PatKind::Slice { ref prefix, ref slice, ref suffix } => { |
243 | 0 | for (subplace, subpat) in |
244 | 0 | prefix_slice_suffix(&place_builder, None, prefix, slice, suffix) |
245 | 0 | { |
246 | 0 | MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); |
247 | 0 | } |
248 | | |
249 | 0 | if prefix.is_empty() && slice.is_some() && suffix.is_empty() { |
250 | | // This pattern is shaped like `[..]`. It can match a slice |
251 | | // of any length, so no length test is needed. |
252 | 0 | None |
253 | | } else { |
254 | | // Any other shape of slice pattern requires a length test. |
255 | | // Slice patterns with a `..` subpattern require a minimum |
256 | | // length; those without `..` require an exact length. |
257 | | Some(TestableCase::Slice { |
258 | 0 | len: u64::try_from(prefix.len() + suffix.len()).unwrap(), |
259 | 0 | op: if slice.is_some() { |
260 | 0 | SliceLenOp::GreaterOrEqual |
261 | | } else { |
262 | 0 | SliceLenOp::Equal |
263 | | }, |
264 | | }) |
265 | | } |
266 | | } |
267 | | |
268 | 21.5k | PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => { |
269 | 21.5k | let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)` |
270 | 21.5k | for &FieldPat { field, pattern: ref subpat } in subpatterns { |
271 | 12.3k | let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty)); |
272 | 12.3k | MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); |
273 | 12.3k | } |
274 | | |
275 | | // We treat non-exhaustive enums the same independent of the crate they are |
276 | | // defined in, to avoid differences in the operational semantics between crates. |
277 | 21.5k | let refutable = |
278 | 21.5k | adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive(); |
279 | 21.5k | if refutable { |
280 | 21.3k | Some(TestableCase::Variant { adt_def, variant_index }) |
281 | | } else { |
282 | 187 | None |
283 | | } |
284 | | } |
285 | | |
286 | 7.82k | PatKind::Leaf { ref subpatterns } => { |
287 | 15.7k | for &FieldPat { field, pattern: ref subpat } in subpatterns { |
288 | 15.7k | let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty)); |
289 | 15.7k | MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data); |
290 | 15.7k | } |
291 | 7.82k | None |
292 | | } |
293 | | |
294 | 0 | PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => { |
295 | 0 | let pinned_ref_ty = match pattern.ty.pinned_ty() { |
296 | 0 | Some(p_ty) if p_ty.is_ref() => p_ty, |
297 | 0 | _ => span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty), |
298 | | }; |
299 | 0 | MatchPairTree::for_pattern( |
300 | | // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`. |
301 | 0 | place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(), |
302 | 0 | subpattern, |
303 | 0 | cx, |
304 | 0 | &mut subpairs, |
305 | 0 | extra_data, |
306 | | ); |
307 | | |
308 | 0 | None |
309 | | } |
310 | | |
311 | 2.09k | PatKind::Deref { pin: Pinnedness::Not, ref subpattern } |
312 | 7 | | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => { |
313 | 2.09k | MatchPairTree::for_pattern( |
314 | 2.09k | place_builder.deref(), |
315 | 2.09k | subpattern, |
316 | 2.09k | cx, |
317 | 2.09k | &mut subpairs, |
318 | 2.09k | extra_data, |
319 | | ); |
320 | 2.09k | None |
321 | | } |
322 | | |
323 | | PatKind::DerefPattern { |
324 | 0 | ref subpattern, |
325 | 0 | borrow: DerefPatBorrowMode::Borrow(mutability), |
326 | | } => { |
327 | | // Create a new temporary for each deref pattern. |
328 | | // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? |
329 | 0 | let temp = cx.temp( |
330 | 0 | Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability), |
331 | 0 | pattern.span, |
332 | | ); |
333 | 0 | MatchPairTree::for_pattern( |
334 | 0 | PlaceBuilder::from(temp).deref(), |
335 | 0 | subpattern, |
336 | 0 | cx, |
337 | 0 | &mut subpairs, |
338 | 0 | extra_data, |
339 | | ); |
340 | 0 | Some(TestableCase::Deref { temp, mutability }) |
341 | | } |
342 | | |
343 | | PatKind::Guard { .. } => { |
344 | | // FIXME(guard_patterns) |
345 | 0 | None |
346 | | } |
347 | | |
348 | 0 | PatKind::Never => Some(TestableCase::Never), |
349 | | }; |
350 | | |
351 | 101k | if let Some(testable_case) = testable_case { |
352 | | // This pattern is refutable, so push a new match-pair node. |
353 | | // |
354 | | // Note: unless test_case is TestCase::Or, place must not be None. |
355 | | // This means that the closure capture analysis in |
356 | | // rustc_hir_typeck::upvar, and in particular the pattern handling |
357 | | // code of ExprUseVisitor, must capture all of the places we'll use. |
358 | | // Make sure to keep these two parts in sync! |
359 | 21.4k | match_pairs.push(MatchPairTree { |
360 | 21.4k | place, |
361 | 21.4k | testable_case, |
362 | 21.4k | subpairs, |
363 | 21.4k | pattern_span: pattern.span, |
364 | 21.4k | }) |
365 | 80.0k | } else { |
366 | 80.0k | // This pattern is irrefutable, so it doesn't need its own match-pair node. |
367 | 80.0k | // Just push its refutable subpatterns instead, if any. |
368 | 80.0k | match_pairs.extend(subpairs); |
369 | 80.0k | } |
370 | 101k | } |
371 | | } |