Skip to main content

rustc_mir_build/builder/matches/
match_pair.rs

1use std::sync::Arc;
2
3use rustc_abi::FieldIdx;
4use rustc_middle::mir::{Pinnedness, Place, PlaceElem, ProjectionElem};
5use rustc_middle::thir::{Ascription, DerefPatBorrowMode, FieldPat, Pat, PatKind};
6use rustc_middle::ty::{self, Ty, TypeVisitableExt};
7use rustc_span::{Span, span_bug};
8
9use crate::builder::Builder;
10use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
11use crate::builder::matches::{
12    FlatPat, MatchPairKind, 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`].
17fn prefix_slice_suffix<'a, 'tcx>(
18    place: &PlaceBuilder<'tcx>,
19    array_len: Option<u64>, // Some for array patterns; None for slice patterns
20    prefix: &'a [Pat<'tcx>],
21    opt_slice: &'a Option<Box<Pat<'tcx>>>,
22    suffix: &'a [Pat<'tcx>],
23) -> Vec<(PlaceBuilder<'tcx>, &'a Pat<'tcx>)> {
24    let prefix_len = u64::try_from(prefix.len()).unwrap();
25    let suffix_len = u64::try_from(suffix.len()).unwrap();
26
27    let mut output_pairs =
28        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    let (min_length, is_array) = match array_len {
36        Some(len) => (len, true),
37        None => (prefix_len + suffix_len, false),
38    };
39
40    for (offset, prefix_subpat) in (0u64..).zip(prefix) {
41        let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
42        let subplace = place.clone_project(elem);
43        output_pairs.push((subplace, prefix_subpat));
44    }
45
46    if let Some(slice_subpat) = opt_slice {
47        let elem = PlaceElem::Subslice {
48            from: prefix_len,
49            to: if is_array { min_length - suffix_len } else { suffix_len },
50            from_end: !is_array,
51        };
52        let subplace = place.clone_project(elem);
53        output_pairs.push((subplace, slice_subpat));
54    }
55
56    for (offset_from_end, suffix_subpat) in (1u64..).zip(suffix.iter().rev()) {
57        let elem = ProjectionElem::ConstantIndex {
58            offset: if is_array { min_length - offset_from_end } else { offset_from_end },
59            min_length,
60            from_end: !is_array,
61        };
62        let subplace = place.clone_project(elem);
63        output_pairs.push((subplace, suffix_subpat));
64    }
65
66    output_pairs
67}
68
69impl<'tcx> FlatPat<'tcx> {
70    /// Creates a `FlatPat` containing a simplified [`MatchPairTree`] list/forest
71    /// for the given pattern.
72    pub(crate) fn new(
73        place: PlaceBuilder<'tcx>,
74        pattern: &Pat<'tcx>,
75        cx: &mut Builder<'_, 'tcx>,
76    ) -> Self {
77        // Recursively lower the THIR pattern into an intermediate form,
78        // then flatten into a `FlatPat`.
79        let inter_pat = InterPat::lower_thir_pat(cx, place, pattern);
80        FlatPat::from_inter_pat(inter_pat)
81    }
82
83    /// Squashes an [`InterPat`] into a [`FlatPat`].
84    ///
85    /// This is a separate function because it also needs to be called recursively
86    /// when squashing any or-patterns.
87    fn from_inter_pat(inter_pat: InterPat<'tcx>) -> Self {
88        let mut match_pairs = ::alloc::vec::Vec::new()vec![];
89        let mut extra_data = PatternExtraData {
90            span: inter_pat.pattern_span,
91            bindings: ::alloc::vec::Vec::new()vec![],
92            ascriptions: ::alloc::vec::Vec::new()vec![],
93            is_never: inter_pat.is_never,
94        };
95        squash_inter_pat(inter_pat, &mut match_pairs, &mut extra_data);
96
97        FlatPat { match_pairs, extra_data }
98    }
99}
100
101/// Recursively squashes an [`InterPat`] into a forest of refutable [`MatchPairTree`]
102/// nodes, while accumulating ascriptions and bindings.
103fn squash_inter_pat<'tcx>(
104    inter_pat: InterPat<'tcx>,
105    match_pairs: &mut Vec<MatchPairTree<'tcx>>, // Newly-created nodes are added to this vector
106    extra_data: &mut PatternExtraData<'tcx>,    // Bindings/ascriptions are added here
107) {
108    // Destructure exhaustively to make sure we don't miss any fields.
109    // The `is_never` field is not needed by `MatchPairTree` forests.
110    let InterPat { kind, ascriptions, pattern_span, is_never: _ } = inter_pat;
111
112    // Type ascriptions can appear regardless of whether the node is an or-pattern.
113    extra_data.ascriptions.extend(ascriptions);
114
115    // Or patterns, refutable patterns, and irrefutable patterns all have different handling.
116    match kind {
117        InterPatKind::Or { or_subpats } => {
118            let or_subpats = or_subpats
119                .into_iter()
120                .map(|subpat| FlatPat::from_inter_pat(subpat))
121                .collect::<Box<[_]>>();
122
123            if !or_subpats[0].extra_data.bindings.is_empty() {
124                // Hold a place for any bindings established in (possibly-nested) or-patterns.
125                // By only holding a place when bindings are present, we skip over any
126                // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
127                // other words, we can assume this expands into subcandidates.
128                // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
129                extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
130            }
131
132            match_pairs
133                .push(MatchPairTree { kind: MatchPairKind::Or { or_subpats }, pattern_span });
134        }
135
136        InterPatKind::Refutable { place, testable_case, subpats } => {
137            // Recursively squash any subpatterns into refutable `MatchPairTree` forests,
138            // which will become the children of a new node.
139            let mut subpairs = ::alloc::vec::Vec::new()vec![];
140            for subpat in subpats {
141                squash_inter_pat(subpat, &mut subpairs, extra_data);
142            }
143
144            // This pattern is refutable, so push a new match-pair node.
145            match_pairs.push(MatchPairTree {
146                kind: MatchPairKind::Testable { place, testable_case, subpairs },
147                pattern_span,
148            });
149        }
150
151        InterPatKind::Irrefutable { subpats, binding } => {
152            // Recursively squash any subpatterns into refutable `MatchPairTree` forests.
153            // This must happen _before_ pushing the binding, as described by the binding step.
154            for subpat in subpats {
155                // For irrefutable nodes, squash directly into the caller's match pairs.
156                squash_inter_pat(subpat, match_pairs, extra_data);
157            }
158
159            // If present, the binding must be pushed _after_ traversing subpatterns.
160            // This is so that when lowering something like `x @ NonCopy { copy_field }`,
161            // the binding to `copy_field` will occur before the binding for `x`.
162            // See <https://github.com/rust-lang/rust/issues/69971> for more background.
163            if let Some(binding) = binding {
164                extra_data.bindings.push(super::SubpatternBindings::One(binding));
165            }
166        }
167    }
168}
169
170/// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been
171/// squashed into a forest of refutable [`MatchPairTree`] nodes.
172struct InterPat<'tcx> {
173    kind: InterPatKind<'tcx>,
174
175    ascriptions: Vec<super::Ascription<'tcx>>,
176    /// Span field of the THIR pattern this node was created from.
177    pattern_span: Span,
178    /// True if this pattern can never match, because all of its alternatives
179    /// contain a `!` pattern.
180    is_never: bool,
181}
182
183enum InterPatKind<'tcx> {
184    Or {
185        /// The alternatives of an or-pattern, e.g. `P` and `Q` in `P | Q`.
186        or_subpats: Box<[InterPat<'tcx>]>,
187    },
188
189    /// Pattern node that performs some kind of test on a place.
190    Refutable {
191        /// Place that this pattern node will test.
192        place: Place<'tcx>,
193        /// Testable condition to compare the place to (e.g. "is 3" or "is Some").
194        testable_case: TestableCase<'tcx>,
195        /// Immediate subpatterns.
196        subpats: Vec<InterPat<'tcx>>,
197    },
198
199    /// Pattern node that doesn't test anything, though it might have refutable descendants.
200    Irrefutable {
201        /// Immediate subpatterns.
202        subpats: Vec<InterPat<'tcx>>,
203        /// Binding to establish for a [`PatKind::Binding`] node.
204        binding: Option<super::Binding<'tcx>>,
205    },
206}
207
208impl<'tcx> InterPat<'tcx> {
209    fn lower_thir_pat(
210        cx: &mut Builder<'_, 'tcx>,
211        mut place_builder: PlaceBuilder<'tcx>,
212        pattern: &Pat<'tcx>,
213    ) -> Self {
214        // Force the place type to the pattern's type.
215        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
216        if let Some(resolved) = place_builder.resolve_upvar(cx) {
217            place_builder = resolved;
218        }
219
220        if !cx.tcx.next_trait_solver_globally() {
221            // Only add the OpaqueCast projection if the given place is an opaque type and the
222            // expected type from the pattern is not.
223            let may_need_cast = match place_builder.base() {
224                PlaceBase::Local(local) => {
225                    let ty =
226                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
227                            .ty;
228                    ty != pattern.ty && ty.has_opaque_types()
229                }
230                _ => true,
231            };
232            if may_need_cast {
233                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
234            }
235        }
236
237        let place = place_builder.try_to_place(cx);
238
239        // Apply any type ascriptions to the value at `match_pair.place`.
240        let mut ascriptions = ::alloc::vec::Vec::new()vec![];
241        if let Some(place) = place
242            && let Some(extra) = &pattern.extra
243        {
244            ascriptions.extend(extra.ascriptions.iter().map(
245                |&Ascription { ref annotation, variance }| super::Ascription {
246                    source: place,
247                    annotation: annotation.clone(),
248                    variance,
249                },
250            ));
251        }
252
253        // For refutable nodes a place must be available, either because it is not a
254        // closure upvar or because it was captured.
255        let unwrap_place = || place.expect("refutable patterns must have captured a place");
256
257        let kind: InterPatKind<'_> = match pattern.kind {
258            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => {
259                InterPatKind::Irrefutable { subpats: ::alloc::vec::Vec::new()vec![], binding: None }
260            }
261
262            PatKind::Or { ref pats } => {
263                let or_subpats = pats
264                    .iter()
265                    .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat))
266                    .collect::<Box<[_]>>();
267                InterPatKind::Or { or_subpats }
268            }
269
270            PatKind::Range(ref range) => {
271                {
    match (&pattern.ty, &range.ty) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(pattern.ty, range.ty);
272                if range.is_full_range(cx.tcx) == Some(true) {
273                    InterPatKind::Irrefutable { subpats: ::alloc::vec::Vec::new()vec![], binding: None }
274                } else {
275                    InterPatKind::Refutable {
276                        place: unwrap_place(),
277                        testable_case: TestableCase::Range(Arc::clone(range)),
278                        subpats: ::alloc::vec::Vec::new()vec![],
279                    }
280                }
281            }
282
283            PatKind::Constant { value } => {
284                {
    match (&pattern.ty, &value.ty) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};assert_eq!(pattern.ty, value.ty);
285
286                // Classify the constant-pattern into further kinds, to
287                // reduce the number of ad-hoc type tests needed later on.
288                let pat_ty = pattern.ty;
289                let const_kind = if pat_ty.is_bool() {
290                    PatConstKind::Bool
291                } else if pat_ty.is_integral() || pat_ty.is_char() {
292                    PatConstKind::IntOrChar
293                } else if pat_ty.is_floating_point() {
294                    PatConstKind::Float
295                } else if pat_ty.is_str() {
296                    PatConstKind::String
297                } else {
298                    // FIXME(Zalathar): This still covers several different
299                    // categories (e.g. raw pointer, pattern-type)
300                    // which could be split out into their own kinds.
301                    PatConstKind::Other
302                };
303
304                InterPatKind::Refutable {
305                    place: unwrap_place(),
306                    testable_case: TestableCase::Constant { value, kind: const_kind },
307                    subpats: ::alloc::vec::Vec::new()vec![],
308                }
309            }
310
311            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
312                // First, recurse into the subpattern, if any.
313                // This is the `x @ P` case; have to keep matching against `P` now.
314                let subpat: Option<InterPat<'_>> = subpattern
315                    .as_deref()
316                    .map(|subpattern| InterPat::lower_thir_pat(cx, place_builder, subpattern));
317
318                // Then push this binding, after any bindings in the subpattern.
319                let binding = place.map(|place| super::Binding {
320                    span: pattern.span,
321                    source: place,
322                    var_id: var,
323                    binding_mode: mode,
324                    is_shorthand,
325                });
326                InterPatKind::Irrefutable { subpats: Vec::from_iter(subpat), binding }
327            }
328
329            PatKind::Array { ref prefix, ref slice, ref suffix } => {
330                // Determine the statically-known length of the array type being matched.
331                // This should always succeed for legal programs, but could fail for
332                // erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
333                // so take care not to ICE if this fails.
334                let array_len = match pattern.ty.kind() {
335                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
336                    _ => None,
337                };
338
339                let mut subpats = ::alloc::vec::Vec::new()vec![];
340                if let Some(array_len) = array_len {
341                    for (subplace, subpat) in
342                        prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix)
343                    {
344                        subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
345                    }
346                } else {
347                    // If the array length couldn't be determined, ignore the
348                    // subpatterns and delayed-assert that compilation will fail.
349                    cx.tcx.dcx().span_delayed_bug(
350                        pattern.span,
351                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("array length in pattern couldn\'t be determined for ty={0:?}",
                pattern.ty))
    })format!(
352                            "array length in pattern couldn't be determined for ty={:?}",
353                            pattern.ty
354                        ),
355                    );
356                }
357
358                InterPatKind::Irrefutable { subpats, binding: None }
359            }
360            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
361                let mut subpats = ::alloc::vec::Vec::new()vec![];
362                for (subplace, subpat) in
363                    prefix_slice_suffix(&place_builder, None, prefix, slice, suffix)
364                {
365                    subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
366                }
367
368                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
369                    // A slice pattern shaped like `[..]` is irrefutable.
370                    // It can match a slice of any length, so no length test is needed.
371                    InterPatKind::Irrefutable { subpats, binding: None }
372                } else {
373                    // Any other shape of slice pattern requires a length test.
374                    // Slice patterns with a `..` subpattern require a minimum
375                    // length; those without `..` require an exact length.
376                    let testable_case = TestableCase::Slice {
377                        len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
378                        op: if slice.is_some() {
379                            SliceLenOp::GreaterOrEqual
380                        } else {
381                            SliceLenOp::Equal
382                        },
383                    };
384                    InterPatKind::Refutable { place: unwrap_place(), testable_case, subpats }
385                }
386            }
387
388            PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
389                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
390                let mut subpats = ::alloc::vec::Vec::new()vec![];
391                for &FieldPat { field, pattern: ref subpat } in subpatterns {
392                    let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty));
393                    subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
394                }
395
396                // We treat non-exhaustive enums the same independent of the crate they are
397                // defined in, to avoid differences in the operational semantics between crates.
398                let refutable =
399                    adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
400                if refutable {
401                    let testable_case = TestableCase::Variant { adt_def, variant_index };
402                    InterPatKind::Refutable { place: unwrap_place(), testable_case, subpats }
403                } else {
404                    InterPatKind::Irrefutable { subpats, binding: None }
405                }
406            }
407
408            PatKind::Leaf { ref subpatterns } => {
409                let mut subpats = ::alloc::vec::Vec::new()vec![];
410                for &FieldPat { field, pattern: ref subpat } in subpatterns {
411                    let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty));
412                    subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
413                }
414                InterPatKind::Irrefutable { subpats, binding: None }
415            }
416
417            PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => {
418                let pinned_ref_ty = match pattern.ty.pinned_ty() {
419                    Some(p_ty) if p_ty.is_ref() => p_ty,
420                    _ => bug_impl(Some(pattern.span),
    format_args!("bad type for pinned deref: {0:?}", pattern.ty),
    Location::caller())span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty),
421                };
422                let subpat = InterPat::lower_thir_pat(
423                    cx,
424                    // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`.
425                    place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(),
426                    subpattern,
427                );
428
429                InterPatKind::Irrefutable { subpats: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [subpat]))vec![subpat], binding: None }
430            }
431
432            PatKind::Deref { pin: Pinnedness::Not, ref subpattern }
433            | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
434                let subpat = InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern);
435                InterPatKind::Irrefutable { subpats: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [subpat]))vec![subpat], binding: None }
436            }
437
438            PatKind::DerefPattern {
439                ref subpattern,
440                borrow: DerefPatBorrowMode::Borrow(mutability),
441            } => {
442                // Create a new temporary for each deref pattern.
443                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
444                let temp = cx.temp(
445                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
446                    pattern.span,
447                );
448                let subpat =
449                    InterPat::lower_thir_pat(cx, PlaceBuilder::from(temp).deref(), subpattern);
450                InterPatKind::Refutable {
451                    place: unwrap_place(),
452                    testable_case: TestableCase::Deref { temp, mutability },
453                    subpats: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [subpat]))vec![subpat],
454                }
455            }
456
457            PatKind::Guard { .. } => {
458                // FIXME(guard_patterns)
459                InterPatKind::Irrefutable { subpats: ::alloc::vec::Vec::new()vec![], binding: None }
460            }
461
462            PatKind::Never => InterPatKind::Refutable {
463                place: unwrap_place(),
464                testable_case: TestableCase::Never,
465                subpats: ::alloc::vec::Vec::new()vec![],
466            },
467        };
468
469        // A pattern node is guaranteed to never match if one of these is true:
470        // - The node itself is a never pattern (`!`).
471        // - It is not an or-pattern, and one of its subpatterns will never match.
472        // - It is an or-pattern, and _all_ of its or-subpatterns will never match.
473        let is_never = match &kind {
474            InterPatKind::Refutable { testable_case: TestableCase::Never, .. } => true,
475            InterPatKind::Refutable { subpats, .. } | InterPatKind::Irrefutable { subpats, .. } => {
476                subpats.iter().any(|subpat| subpat.is_never)
477            }
478            InterPatKind::Or { or_subpats } => or_subpats.iter().all(|subpat| subpat.is_never),
479        };
480
481        InterPat { kind, ascriptions, pattern_span: pattern.span, is_never }
482    }
483}