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