rustc_mir_build/builder/matches/match_pair.rs
1use std::sync::Arc;
2
3use rustc_abi::FieldIdx;
4use rustc_middle::mir::*;
5use rustc_middle::span_bug;
6use rustc_middle::thir::*;
7use rustc_middle::ty::{self, Ty, TypeVisitableExt};
8
9use crate::builder::Builder;
10use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
11use crate::builder::matches::{
12 FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase,
13};
14
15impl<'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 fn field_match_pairs(
22 &mut self,
23 match_pairs: &mut Vec<MatchPairTree<'tcx>>,
24 extra_data: &mut PatternExtraData<'tcx>,
25 place: PlaceBuilder<'tcx>,
26 subpatterns: &[FieldPat<'tcx>],
27 ) {
28 for fieldpat in subpatterns {
29 let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
30 MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data);
31 }
32 }
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 fn prefix_slice_suffix(
39 &mut self,
40 match_pairs: &mut Vec<MatchPairTree<'tcx>>,
41 extra_data: &mut PatternExtraData<'tcx>,
42 place: &PlaceBuilder<'tcx>,
43 prefix: &[Pat<'tcx>],
44 opt_slice: &Option<Box<Pat<'tcx>>>,
45 suffix: &[Pat<'tcx>],
46 ) {
47 let tcx = self.tcx;
48 let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
49 let place_ty = place_resolved.ty(&self.local_decls, tcx).ty;
50 match place_ty.kind() {
51 ty::Array(_, length) => {
52 if let Some(length) = length.try_to_target_usize(tcx) {
53 (length, true)
54 } else {
55 // This can happen when the array length is a generic const
56 // expression that couldn't be evaluated (e.g., due to an error).
57 // Since there's already a compilation error, we use a fallback
58 // to avoid an ICE.
59 tcx.dcx().span_delayed_bug(
60 tcx.def_span(self.def_id),
61 "array length in pattern couldn't be evaluated",
62 );
63 ((prefix.len() + suffix.len()).try_into().unwrap(), false)
64 }
65 }
66 _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
67 }
68 } else {
69 ((prefix.len() + suffix.len()).try_into().unwrap(), false)
70 };
71
72 for (idx, subpattern) in prefix.iter().enumerate() {
73 let elem =
74 ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
75 let place = place.clone_project(elem);
76 MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
77 }
78
79 if let Some(subslice_pat) = opt_slice {
80 let suffix_len = suffix.len() as u64;
81 let subslice = place.clone_project(PlaceElem::Subslice {
82 from: prefix.len() as u64,
83 to: if exact_size { min_length - suffix_len } else { suffix_len },
84 from_end: !exact_size,
85 });
86 MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
87 }
88
89 for (idx, subpattern) in suffix.iter().rev().enumerate() {
90 let end_offset = (idx + 1) as u64;
91 let elem = ProjectionElem::ConstantIndex {
92 offset: if exact_size { min_length - end_offset } else { end_offset },
93 min_length,
94 from_end: !exact_size,
95 };
96 let place = place.clone_project(elem);
97 MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
98 }
99 }
100}
101
102impl<'tcx> MatchPairTree<'tcx> {
103 /// Recursively builds a match pair tree for the given pattern and its
104 /// subpatterns.
105 pub(super) fn for_pattern(
106 mut place_builder: PlaceBuilder<'tcx>,
107 pattern: &Pat<'tcx>,
108 cx: &mut Builder<'_, 'tcx>,
109 match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
110 extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
111 ) {
112 // Force the place type to the pattern's type.
113 // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
114 if let Some(resolved) = place_builder.resolve_upvar(cx) {
115 place_builder = resolved;
116 }
117
118 if !cx.tcx.next_trait_solver_globally() {
119 // Only add the OpaqueCast projection if the given place is an opaque type and the
120 // expected type from the pattern is not.
121 let may_need_cast = match place_builder.base() {
122 PlaceBase::Local(local) => {
123 let ty =
124 Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
125 .ty;
126 ty != pattern.ty && ty.has_opaque_types()
127 }
128 _ => true,
129 };
130 if may_need_cast {
131 place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
132 }
133 }
134
135 let place = place_builder.try_to_place(cx);
136
137 // Apply any type ascriptions to the value at `match_pair.place`.
138 if let Some(place) = place
139 && let Some(extra) = &pattern.extra
140 {
141 for &Ascription { ref annotation, variance } in &extra.ascriptions {
142 extra_data.ascriptions.push(super::Ascription {
143 source: place,
144 annotation: annotation.clone(),
145 variance,
146 });
147 }
148 }
149
150 let mut subpairs = Vec::new();
151 let testable_case = match pattern.kind {
152 PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
153
154 PatKind::Or { ref pats } => {
155 let pats: Box<[FlatPat<'tcx>]> =
156 pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
157 if !pats[0].extra_data.bindings.is_empty() {
158 // Hold a place for any bindings established in (possibly-nested) or-patterns.
159 // By only holding a place when bindings are present, we skip over any
160 // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
161 // other words, we can assume this expands into subcandidates.
162 // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
163 extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
164 }
165 Some(TestableCase::Or { pats })
166 }
167
168 PatKind::Range(ref range) => {
169 if range.is_full_range(cx.tcx) == Some(true) {
170 None
171 } else {
172 Some(TestableCase::Range(Arc::clone(range)))
173 }
174 }
175
176 PatKind::Constant { value } => {
177 // CAUTION: The type of the pattern node (`pattern.ty`) is
178 // _often_ the same as the type of the const value (`value.ty`),
179 // but there are some cases where those types differ
180 // (e.g. when `deref!(..)` patterns interact with `String`).
181
182 // Classify the constant-pattern into further kinds, to
183 // reduce the number of ad-hoc type tests needed later on.
184 let pat_ty = pattern.ty;
185 let const_kind = if pat_ty.is_bool() {
186 PatConstKind::Bool
187 } else if pat_ty.is_integral() || pat_ty.is_char() {
188 PatConstKind::IntOrChar
189 } else if pat_ty.is_floating_point() {
190 PatConstKind::Float
191 } else if pat_ty.is_str() {
192 // Deref-patterns can cause string-literal patterns to have
193 // type `str` instead of the usual `&str`.
194 if !cx.tcx.features().deref_patterns() {
195 span_bug!(
196 pattern.span,
197 "const pattern has type `str` but deref_patterns is not enabled"
198 );
199 }
200 PatConstKind::String
201 } else if pat_ty.is_imm_ref_str() {
202 PatConstKind::String
203 } else {
204 // FIXME(Zalathar): This still covers several different
205 // categories (e.g. raw pointer, pattern-type)
206 // which could be split out into their own kinds.
207 PatConstKind::Other
208 };
209 Some(TestableCase::Constant { value, kind: const_kind })
210 }
211
212 PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
213 // In order to please the borrow checker, when lowering a pattern
214 // like `x @ subpat` we must establish any bindings in `subpat`
215 // before establishing the binding for `x`.
216 //
217 // For example (from #69971):
218 //
219 // ```ignore (illustrative)
220 // struct NonCopyStruct {
221 // copy_field: u32,
222 // }
223 //
224 // fn foo1(x: NonCopyStruct) {
225 // let y @ NonCopyStruct { copy_field: z } = x;
226 // // the above should turn into
227 // let z = x.copy_field;
228 // let y = x;
229 // }
230 // ```
231
232 // First, recurse into the subpattern, if any.
233 if let Some(subpattern) = subpattern.as_ref() {
234 // this is the `x @ P` case; have to keep matching against `P` now
235 MatchPairTree::for_pattern(
236 place_builder,
237 subpattern,
238 cx,
239 &mut subpairs,
240 extra_data,
241 );
242 }
243
244 // Then push this binding, after any bindings in the subpattern.
245 if let Some(source) = place {
246 extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
247 span: pattern.span,
248 source,
249 var_id: var,
250 binding_mode: mode,
251 is_shorthand,
252 }));
253 }
254
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}