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::*;
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
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> MatchPairTree<'tcx> {
70    /// Recursively builds a match pair tree for the given pattern and its
71    /// subpatterns.
72    pub(super) fn for_pattern(
73        mut place_builder: PlaceBuilder<'tcx>,
74        pattern: &Pat<'tcx>,
75        cx: &mut Builder<'_, 'tcx>,
76        match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
77        extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
78    ) {
79        // Force the place type to the pattern's type.
80        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
81        if let Some(resolved) = place_builder.resolve_upvar(cx) {
82            place_builder = resolved;
83        }
84
85        if !cx.tcx.next_trait_solver_globally() {
86            // Only add the OpaqueCast projection if the given place is an opaque type and the
87            // expected type from the pattern is not.
88            let may_need_cast = match place_builder.base() {
89                PlaceBase::Local(local) => {
90                    let ty =
91                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
92                            .ty;
93                    ty != pattern.ty && ty.has_opaque_types()
94                }
95                _ => true,
96            };
97            if may_need_cast {
98                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
99            }
100        }
101
102        let place = place_builder.try_to_place(cx);
103
104        // Apply any type ascriptions to the value at `match_pair.place`.
105        if let Some(place) = place
106            && let Some(extra) = &pattern.extra
107        {
108            for &Ascription { ref annotation, variance } in &extra.ascriptions {
109                extra_data.ascriptions.push(super::Ascription {
110                    source: place,
111                    annotation: annotation.clone(),
112                    variance,
113                });
114            }
115        }
116
117        let mut subpairs = Vec::new();
118        let testable_case = match pattern.kind {
119            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
120
121            PatKind::Or { ref pats } => {
122                let pats: Box<[FlatPat<'tcx>]> =
123                    pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
124                if !pats[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                Some(TestableCase::Or { pats })
133            }
134
135            PatKind::Range(ref range) => {
136                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);
137                if range.is_full_range(cx.tcx) == Some(true) {
138                    None
139                } else {
140                    Some(TestableCase::Range(Arc::clone(range)))
141                }
142            }
143
144            PatKind::Constant { value } => {
145                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);
146
147                // Classify the constant-pattern into further kinds, to
148                // reduce the number of ad-hoc type tests needed later on.
149                let pat_ty = pattern.ty;
150                let const_kind = if pat_ty.is_bool() {
151                    PatConstKind::Bool
152                } else if pat_ty.is_integral() || pat_ty.is_char() {
153                    PatConstKind::IntOrChar
154                } else if pat_ty.is_floating_point() {
155                    PatConstKind::Float
156                } else if pat_ty.is_str() {
157                    PatConstKind::String
158                } else {
159                    // FIXME(Zalathar): This still covers several different
160                    // categories (e.g. raw pointer, pattern-type)
161                    // which could be split out into their own kinds.
162                    PatConstKind::Other
163                };
164                Some(TestableCase::Constant { value, kind: const_kind })
165            }
166
167            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
168                // In order to please the borrow checker, when lowering a pattern
169                // like `x @ subpat` we must establish any bindings in `subpat`
170                // before establishing the binding for `x`.
171                //
172                // For example (from #69971):
173                //
174                // ```ignore (illustrative)
175                // struct NonCopyStruct {
176                //     copy_field: u32,
177                // }
178                //
179                // fn foo1(x: NonCopyStruct) {
180                //     let y @ NonCopyStruct { copy_field: z } = x;
181                //     // the above should turn into
182                //     let z = x.copy_field;
183                //     let y = x;
184                // }
185                // ```
186
187                // First, recurse into the subpattern, if any.
188                if let Some(subpattern) = subpattern.as_ref() {
189                    // this is the `x @ P` case; have to keep matching against `P` now
190                    MatchPairTree::for_pattern(
191                        place_builder,
192                        subpattern,
193                        cx,
194                        &mut subpairs,
195                        extra_data,
196                    );
197                }
198
199                // Then push this binding, after any bindings in the subpattern.
200                if let Some(source) = place {
201                    extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
202                        span: pattern.span,
203                        source,
204                        var_id: var,
205                        binding_mode: mode,
206                        is_shorthand,
207                    }));
208                }
209
210                None
211            }
212
213            PatKind::Array { ref prefix, ref slice, ref suffix } => {
214                // Determine the statically-known length of the array type being matched.
215                // This should always succeed for legal programs, but could fail for
216                // erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
217                // so take care not to ICE if this fails.
218                let array_len = match pattern.ty.kind() {
219                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
220                    _ => None,
221                };
222                if let Some(array_len) = array_len {
223                    for (subplace, subpat) in
224                        prefix_slice_suffix(&place_builder, Some(array_len), prefix, slice, suffix)
225                    {
226                        MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data);
227                    }
228                } else {
229                    // If the array length couldn't be determined, ignore the
230                    // subpatterns and delayed-assert that compilation will fail.
231                    cx.tcx.dcx().span_delayed_bug(
232                        pattern.span,
233                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("array length in pattern couldn\'t be determined for ty={0:?}",
                pattern.ty))
    })format!(
234                            "array length in pattern couldn't be determined for ty={:?}",
235                            pattern.ty
236                        ),
237                    );
238                }
239
240                None
241            }
242            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
243                for (subplace, subpat) in
244                    prefix_slice_suffix(&place_builder, None, prefix, slice, suffix)
245                {
246                    MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data);
247                }
248
249                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
250                    // This pattern is shaped like `[..]`. It can match a slice
251                    // of any length, so no length test is needed.
252                    None
253                } else {
254                    // Any other shape of slice pattern requires a length test.
255                    // Slice patterns with a `..` subpattern require a minimum
256                    // length; those without `..` require an exact length.
257                    Some(TestableCase::Slice {
258                        len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
259                        op: if slice.is_some() {
260                            SliceLenOp::GreaterOrEqual
261                        } else {
262                            SliceLenOp::Equal
263                        },
264                    })
265                }
266            }
267
268            PatKind::Variant { adt_def, variant_index, args: _, ref subpatterns } => {
269                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
270                for &FieldPat { field, pattern: ref subpat } in subpatterns {
271                    let subplace = downcast_place.clone_project(PlaceElem::Field(field, subpat.ty));
272                    MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data);
273                }
274
275                // We treat non-exhaustive enums the same independent of the crate they are
276                // defined in, to avoid differences in the operational semantics between crates.
277                let refutable =
278                    adt_def.variants().len() > 1 || adt_def.is_variant_list_non_exhaustive();
279                if refutable {
280                    Some(TestableCase::Variant { adt_def, variant_index })
281                } else {
282                    None
283                }
284            }
285
286            PatKind::Leaf { ref subpatterns } => {
287                for &FieldPat { field, pattern: ref subpat } in subpatterns {
288                    let subplace = place_builder.clone_project(PlaceElem::Field(field, subpat.ty));
289                    MatchPairTree::for_pattern(subplace, subpat, cx, &mut subpairs, extra_data);
290                }
291                None
292            }
293
294            PatKind::Deref { pin: Pinnedness::Pinned, ref subpattern } => {
295                let pinned_ref_ty = match pattern.ty.pinned_ty() {
296                    Some(p_ty) if p_ty.is_ref() => p_ty,
297                    _ => ::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),
298                };
299                MatchPairTree::for_pattern(
300                    // Project into the `Pin(_)` struct, then deref the inner `&` or `&mut`.
301                    place_builder.field(FieldIdx::ZERO, pinned_ref_ty).deref(),
302                    subpattern,
303                    cx,
304                    &mut subpairs,
305                    extra_data,
306                );
307
308                None
309            }
310
311            PatKind::Deref { pin: Pinnedness::Not, ref subpattern }
312            | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
313                MatchPairTree::for_pattern(
314                    place_builder.deref(),
315                    subpattern,
316                    cx,
317                    &mut subpairs,
318                    extra_data,
319                );
320                None
321            }
322
323            PatKind::DerefPattern {
324                ref subpattern,
325                borrow: DerefPatBorrowMode::Borrow(mutability),
326            } => {
327                // Create a new temporary for each deref pattern.
328                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
329                let temp = cx.temp(
330                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
331                    pattern.span,
332                );
333                MatchPairTree::for_pattern(
334                    PlaceBuilder::from(temp).deref(),
335                    subpattern,
336                    cx,
337                    &mut subpairs,
338                    extra_data,
339                );
340                Some(TestableCase::Deref { temp, mutability })
341            }
342
343            PatKind::Guard { .. } => {
344                // FIXME(guard_patterns)
345                None
346            }
347
348            PatKind::Never => Some(TestableCase::Never),
349        };
350
351        if let Some(testable_case) = testable_case {
352            // This pattern is refutable, so push a new match-pair node.
353            //
354            // Note: unless test_case is TestCase::Or, place must not be None.
355            // This means that the closure capture analysis in
356            // rustc_hir_typeck::upvar, and in particular the pattern handling
357            // code of ExprUseVisitor, must capture all of the places we'll use.
358            // Make sure to keep these two parts in sync!
359            match_pairs.push(MatchPairTree {
360                place,
361                testable_case,
362                subpairs,
363                pattern_span: pattern.span,
364            })
365        } else {
366            // This pattern is irrefutable, so it doesn't need its own match-pair node.
367            // Just push its refutable subpatterns instead, if any.
368            match_pairs.extend(subpairs);
369        }
370    }
371}