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