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