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::span_bug;
6use rustc_middle::thir::{Ascription, DerefPatBorrowMode, FieldPat, Pat, PatKind};
7use rustc_middle::ty::{self, Ty, TypeVisitableExt};
8use rustc_span::Span;
9
10use crate::builder::Builder;
11use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
12use crate::builder::matches::{
13    FlatPat, 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    let InterPat {
111        place,
112        testable_case,
113        subpats,
114        or_subpats,
115        ascriptions,
116        binding,
117        pattern_span,
118        is_never: _, // Not needed by `MatchPairTree` forests.
119    } = inter_pat;
120
121    // Type ascriptions can appear regardless of whether the node is an or-pattern.
122    extra_data.ascriptions.extend(ascriptions);
123
124    // Or and non-or patterns have very different handling.
125    if let Some(or_subpats) = or_subpats {
126        // We're dealing with an or-pattern node.
127        if !testable_case.is_none() {
    ::core::panicking::panic("assertion failed: testable_case.is_none()")
};assert!(testable_case.is_none());
128        if !subpats.is_empty() {
    ::core::panicking::panic("assertion failed: subpats.is_empty()")
};assert!(subpats.is_empty());
129        if !binding.is_none() {
    ::core::panicking::panic("assertion failed: binding.is_none()")
};assert!(binding.is_none());
130
131        let or_subpats = or_subpats
132            .into_iter()
133            .map(|subpat| FlatPat::from_inter_pat(subpat))
134            .collect::<Box<[_]>>();
135
136        if !or_subpats[0].extra_data.bindings.is_empty() {
137            // Hold a place for any bindings established in (possibly-nested) or-patterns.
138            // By only holding a place when bindings are present, we skip over any
139            // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
140            // other words, we can assume this expands into subcandidates.
141            // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
142            extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
143        }
144
145        match_pairs.push(MatchPairTree {
146            // Or-patterns never need a place during MIR building.
147            place: None,
148            testable_case: TestableCase::Or { pats: or_subpats },
149            subpairs: ::alloc::vec::Vec::new()vec![],
150            pattern_span,
151        });
152    } else {
153        // We're dealing with a node that isn't an or-pattern.
154
155        // Recursively squash any subpatterns into refutable `MatchPairTree` forests.
156        // This must happen _before_ pushing the binding, as described by the binding step.
157        let mut subpairs = ::alloc::vec::Vec::new()vec![];
158        for subpat in subpats {
159            squash_inter_pat(subpat, &mut subpairs, extra_data);
160        }
161
162        if let Some(testable_case) = testable_case {
163            // This pattern is refutable, so push a new match-pair node.
164            //
165            // If this match is inside a closure, it's essential that the place
166            // we're testing was actually captured! Be sure to keep `ExprUseVisitor`
167            // in sync with the refutability checks in this module.
168            if !place.is_some() {
    ::core::panicking::panic("assertion failed: place.is_some()")
};assert!(place.is_some());
169            if !!#[allow(non_exhaustive_omitted_patterns)] match testable_case {
                TestableCase::Or { .. } => true,
                _ => false,
            } {
    ::core::panicking::panic("assertion failed: !matches!(testable_case, TestableCase::Or { .. })")
};assert!(!matches!(testable_case, TestableCase::Or { .. }));
170            match_pairs.push(MatchPairTree { place, testable_case, subpairs, pattern_span });
171        } else {
172            // This pattern is irrefutable, so it doesn't need its own match-pair node.
173            // Just push its refutable subpatterns instead, if any.
174            match_pairs.extend(subpairs);
175        }
176
177        // If present, the binding must be pushed _after_ traversing subpatterns.
178        // This is so that when lowering something like `x @ NonCopy { copy_field }`,
179        // the binding to `copy_field` will occur before the binding for `x`.
180        // See <https://github.com/rust-lang/rust/issues/69971> for more background.
181        if let Some(binding) = binding {
182            extra_data.bindings.push(super::SubpatternBindings::One(binding));
183        }
184    }
185}
186
187/// "Intermediate pattern", a partly-lowered THIR [`Pat`] that has not yet been
188/// squashed into a forest of refutable [`MatchPairTree`] nodes.
189///
190/// FIXME(Zalathar): This could potentially be split into different enum variants
191/// for or-patterns and non-or patterns, but for now the flat structure makes
192/// construction a bit easier, at the cost of more complicated invariants.
193struct InterPat<'tcx> {
194    /// Place that this pattern node will test.
195    ///
196    /// If `None`, we're in a closure that didn't capture the relevant place,
197    /// because it won't actually be tested.
198    place: Option<Place<'tcx>>,
199    /// Testable condition to compare the place to (e.g. "is 3" or "is Some").
200    ///
201    /// If `None`, this pattern node is irrefutable or an or-pattern,
202    /// though it might have refutable descendants.
203    testable_case: Option<TestableCase<'tcx>>,
204
205    /// Immediate subpatterns of a node that is *not* an or-pattern.
206    subpats: Vec<InterPat<'tcx>>,
207    /// Immediate subpatterns of an or-pattern node.
208    ///
209    /// Invariant: If this is Some, then fields `subpats`, `testable_case`,
210    /// and `binding` must all be empty.
211    or_subpats: Option<Box<[InterPat<'tcx>]>>,
212
213    ascriptions: Vec<super::Ascription<'tcx>>,
214    /// Binding to establish for a [`PatKind::Binding`] node.
215    binding: Option<super::Binding<'tcx>>,
216
217    /// Span field of the THIR pattern this node was created from.
218    pattern_span: Span,
219    /// True if this pattern can never match, because all of its alternatives
220    /// contain a `!` pattern.
221    is_never: bool,
222}
223
224impl<'tcx> InterPat<'tcx> {
225    fn lower_thir_pat(
226        cx: &mut Builder<'_, 'tcx>,
227        mut place_builder: PlaceBuilder<'tcx>,
228        pattern: &Pat<'tcx>,
229    ) -> Self {
230        // Force the place type to the pattern's type.
231        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
232        if let Some(resolved) = place_builder.resolve_upvar(cx) {
233            place_builder = resolved;
234        }
235
236        if !cx.tcx.next_trait_solver_globally() {
237            // Only add the OpaqueCast projection if the given place is an opaque type and the
238            // expected type from the pattern is not.
239            let may_need_cast = match place_builder.base() {
240                PlaceBase::Local(local) => {
241                    let ty =
242                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
243                            .ty;
244                    ty != pattern.ty && ty.has_opaque_types()
245                }
246                _ => true,
247            };
248            if may_need_cast {
249                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
250            }
251        }
252
253        // Variables that will become `InterPat` fields:
254        let place = place_builder.try_to_place(cx);
255        let mut subpats = ::alloc::vec::Vec::new()vec![];
256        let mut or_subpats = None;
257        let mut ascriptions = ::alloc::vec::Vec::new()vec![];
258        let mut binding = None;
259
260        // Apply any type ascriptions to the value at `match_pair.place`.
261        if let Some(place) = place
262            && let Some(extra) = &pattern.extra
263        {
264            for &Ascription { ref annotation, variance } in &extra.ascriptions {
265                ascriptions.push(super::Ascription {
266                    source: place,
267                    annotation: annotation.clone(),
268                    variance,
269                });
270            }
271        }
272
273        let testable_case = match pattern.kind {
274            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
275
276            PatKind::Or { ref pats } => {
277                or_subpats = Some(
278                    pats.iter()
279                        .map(|subpat| InterPat::lower_thir_pat(cx, place_builder.clone(), subpat))
280                        .collect::<Box<[_]>>(),
281                );
282                None
283            }
284
285            PatKind::Range(ref range) => {
286                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);
287                if range.is_full_range(cx.tcx) == Some(true) {
288                    None
289                } else {
290                    Some(TestableCase::Range(Arc::clone(range)))
291                }
292            }
293
294            PatKind::Constant { value } => {
295                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);
296
297                // Classify the constant-pattern into further kinds, to
298                // reduce the number of ad-hoc type tests needed later on.
299                let pat_ty = pattern.ty;
300                let const_kind = if pat_ty.is_bool() {
301                    PatConstKind::Bool
302                } else if pat_ty.is_integral() || pat_ty.is_char() {
303                    PatConstKind::IntOrChar
304                } else if pat_ty.is_floating_point() {
305                    PatConstKind::Float
306                } else if pat_ty.is_str() {
307                    PatConstKind::String
308                } else {
309                    // FIXME(Zalathar): This still covers several different
310                    // categories (e.g. raw pointer, pattern-type)
311                    // which could be split out into their own kinds.
312                    PatConstKind::Other
313                };
314                Some(TestableCase::Constant { value, kind: const_kind })
315            }
316
317            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
318                // First, recurse into the subpattern, if any.
319                if let Some(subpattern) = subpattern.as_ref() {
320                    // this is the `x @ P` case; have to keep matching against `P` now
321                    subpats.push(InterPat::lower_thir_pat(cx, place_builder, subpattern));
322                }
323
324                // Then push this binding, after any bindings in the subpattern.
325                if let Some(place) = place {
326                    binding = Some(super::Binding {
327                        span: pattern.span,
328                        source: place,
329                        var_id: var,
330                        binding_mode: mode,
331                        is_shorthand,
332                    });
333                }
334                None
335            }
336
337            PatKind::Array { ref prefix, ref slice, ref suffix } => {
338                // Determine the statically-known length of the array type being matched.
339                // This should always succeed for legal programs, but could fail for
340                // erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
341                // so take care not to ICE if this fails.
342                let array_len = match pattern.ty.kind() {
343                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
344                    _ => None,
345                };
346                if let Some(array_len) = array_len {
347                    for (subplace, subpat) in
348                        prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix)
349                    {
350                        subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
351                    }
352                } else {
353                    // If the array length couldn't be determined, ignore the
354                    // subpatterns and delayed-assert that compilation will fail.
355                    cx.tcx.dcx().span_delayed_bug(
356                        pattern.span,
357                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("array length in pattern couldn\'t be determined for ty={0:?}",
                pattern.ty))
    })format!(
358                            "array length in pattern couldn't be determined for ty={:?}",
359                            pattern.ty
360                        ),
361                    );
362                }
363
364                None
365            }
366            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
367                for (subplace, subpat) in
368                    prefix_slice_suffix(&place_builder, None, prefix, slice, suffix)
369                {
370                    subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
371                }
372
373                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
374                    // A slice pattern shaped like `[..]` is irrefutable.
375                    // It can match a slice of any length, so no length test is needed.
376                    None
377                } else {
378                    // Any other shape of slice pattern requires a length test.
379                    // Slice patterns with a `..` subpattern require a minimum
380                    // length; those without `..` require an exact length.
381                    Some(TestableCase::Slice {
382                        len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
383                        op: if slice.is_some() {
384                            SliceLenOp::GreaterOrEqual
385                        } else {
386                            SliceLenOp::Equal
387                        },
388                    })
389                }
390            }
391
392            PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
393                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
394                for &FieldPat { field, pattern: ref subpat } in subpatterns {
395                    let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty));
396                    subpats.push(InterPat::lower_thir_pat(cx, subplace, subpat));
397                }
398
399                // We treat non-exhaustive enums the same independent of the crate they are
400                // defined in, to avoid differences in the operational semantics between crates.
401                let refutable =
402                    adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
403                if refutable {
404                    Some(TestableCase::Variant { adt_def, variant_index })
405                } else {
406                    None
407                }
408            }
409
410            PatKind::Leaf { ref subpatterns } => {
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                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_middle::util::bug::span_bug_fmt(pattern.span,
    format_args!("bad type for pinned deref: {0:?}", pattern.ty))span_bug!(pattern.span, "bad type for pinned deref: {:?}", pattern.ty),
422                };
423                subpats.push(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                None
431            }
432
433            PatKind::Deref { pin: Pinnedness::Not, ref subpattern }
434            | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
435                subpats.push(InterPat::lower_thir_pat(cx, place_builder.deref(), subpattern));
436                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                subpats.push(InterPat::lower_thir_pat(
450                    cx,
451                    PlaceBuilder::from(temp).deref(),
452                    subpattern,
453                ));
454                Some(TestableCase::Deref { temp, mutability })
455            }
456
457            PatKind::Guard { .. } => {
458                // FIXME(guard_patterns)
459                None
460            }
461
462            PatKind::Never => Some(TestableCase::Never),
463        };
464
465        // A pattern node is guaranteed to never match if one of these is true:
466        // - The node itself is a never pattern (`!`).
467        // - It is not an or-pattern, and one of its subpatterns will never match.
468        // - It is an or-pattern, and _all_ of its or-subpatterns will never match.
469        let is_never = #[allow(non_exhaustive_omitted_patterns)] match pattern.kind {
    PatKind::Never => true,
    _ => false,
}matches!(pattern.kind, PatKind::Never)
470            || subpats.iter().any(|subpat| subpat.is_never)
471            || or_subpats
472                .as_ref()
473                .is_some_and(|or_subpats| or_subpats.iter().all(|subpat| subpat.is_never));
474
475        InterPat {
476            place,
477            testable_case,
478            subpats,
479            or_subpats,
480            ascriptions,
481            binding,
482            pattern_span: pattern.span,
483            is_never,
484        }
485    }
486}