rustc_mir_build/builder/matches/
match_pair.rs

1use std::sync::Arc;
2
3use rustc_abi::FieldIdx;
4use rustc_middle::mir::*;
5use rustc_middle::thir::*;
6use rustc_middle::ty::{self, Ty, TypeVisitableExt};
7
8use crate::builder::Builder;
9use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
10use crate::builder::matches::{
11    FlatPat, MatchPairTree, PatConstKind, PatternExtraData, SliceLenOp, TestableCase,
12};
13
14impl<'a, 'tcx> Builder<'a, 'tcx> {
15    /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
16    /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or
17    /// [`PatKind::Leaf`].
18    ///
19    /// Used internally by [`MatchPairTree::for_pattern`].
20    fn field_match_pairs(
21        &mut self,
22        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
23        extra_data: &mut PatternExtraData<'tcx>,
24        place: PlaceBuilder<'tcx>,
25        subpatterns: &[FieldPat<'tcx>],
26    ) {
27        for fieldpat in subpatterns {
28            let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
29            MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data);
30        }
31    }
32
33    /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an
34    /// array pattern or slice pattern, and adds those trees to `match_pairs`.
35    ///
36    /// Used internally by [`MatchPairTree::for_pattern`].
37    fn prefix_slice_suffix(
38        &mut self,
39        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
40        extra_data: &mut PatternExtraData<'tcx>,
41        place: &PlaceBuilder<'tcx>,
42        array_len: Option<u64>,
43        prefix: &[Pat<'tcx>],
44        opt_slice: &Option<Box<Pat<'tcx>>>,
45        suffix: &[Pat<'tcx>],
46    ) {
47        let prefix_len = u64::try_from(prefix.len()).unwrap();
48        let suffix_len = u64::try_from(suffix.len()).unwrap();
49
50        // For slice patterns with a `..` followed by 0 or more suffix subpatterns,
51        // the actual slice index of those subpatterns isn't statically known, so
52        // we have to index them relative to the end of the slice.
53        //
54        // For array patterns, all subpatterns are indexed relative to the start.
55        let (min_length, is_array) = match array_len {
56            Some(len) => (len, true),
57            None => (prefix_len + suffix_len, false),
58        };
59
60        for (offset, subpattern) in (0u64..).zip(prefix) {
61            let elem = ProjectionElem::ConstantIndex { offset, min_length, from_end: false };
62            let place = place.clone_project(elem);
63            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
64        }
65
66        if let Some(subslice_pat) = opt_slice {
67            let subslice = place.clone_project(PlaceElem::Subslice {
68                from: prefix_len,
69                to: if is_array { min_length - suffix_len } else { suffix_len },
70                from_end: !is_array,
71            });
72            MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
73        }
74
75        for (end_offset, subpattern) in (1u64..).zip(suffix.iter().rev()) {
76            let elem = ProjectionElem::ConstantIndex {
77                offset: if is_array { min_length - end_offset } else { end_offset },
78                min_length,
79                from_end: !is_array,
80            };
81            let place = place.clone_project(elem);
82            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
83        }
84    }
85}
86
87impl<'tcx> MatchPairTree<'tcx> {
88    /// Recursively builds a match pair tree for the given pattern and its
89    /// subpatterns.
90    pub(super) fn for_pattern(
91        mut place_builder: PlaceBuilder<'tcx>,
92        pattern: &Pat<'tcx>,
93        cx: &mut Builder<'_, 'tcx>,
94        match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
95        extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
96    ) {
97        // Force the place type to the pattern's type.
98        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
99        if let Some(resolved) = place_builder.resolve_upvar(cx) {
100            place_builder = resolved;
101        }
102
103        if !cx.tcx.next_trait_solver_globally() {
104            // Only add the OpaqueCast projection if the given place is an opaque type and the
105            // expected type from the pattern is not.
106            let may_need_cast = match place_builder.base() {
107                PlaceBase::Local(local) => {
108                    let ty =
109                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
110                            .ty;
111                    ty != pattern.ty && ty.has_opaque_types()
112                }
113                _ => true,
114            };
115            if may_need_cast {
116                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
117            }
118        }
119
120        let place = place_builder.try_to_place(cx);
121
122        // Apply any type ascriptions to the value at `match_pair.place`.
123        if let Some(place) = place
124            && let Some(extra) = &pattern.extra
125        {
126            for &Ascription { ref annotation, variance } in &extra.ascriptions {
127                extra_data.ascriptions.push(super::Ascription {
128                    source: place,
129                    annotation: annotation.clone(),
130                    variance,
131                });
132            }
133        }
134
135        let mut subpairs = Vec::new();
136        let testable_case = match pattern.kind {
137            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
138
139            PatKind::Or { ref pats } => {
140                let pats: Box<[FlatPat<'tcx>]> =
141                    pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
142                if !pats[0].extra_data.bindings.is_empty() {
143                    // Hold a place for any bindings established in (possibly-nested) or-patterns.
144                    // By only holding a place when bindings are present, we skip over any
145                    // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
146                    // other words, we can assume this expands into subcandidates.
147                    // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
148                    extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
149                }
150                Some(TestableCase::Or { pats })
151            }
152
153            PatKind::Range(ref range) => {
154                if range.is_full_range(cx.tcx) == Some(true) {
155                    None
156                } else {
157                    Some(TestableCase::Range(Arc::clone(range)))
158                }
159            }
160
161            PatKind::Constant { value } => {
162                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);
163
164                // Classify the constant-pattern into further kinds, to
165                // reduce the number of ad-hoc type tests needed later on.
166                let pat_ty = pattern.ty;
167                let const_kind = if pat_ty.is_bool() {
168                    PatConstKind::Bool
169                } else if pat_ty.is_integral() || pat_ty.is_char() {
170                    PatConstKind::IntOrChar
171                } else if pat_ty.is_floating_point() {
172                    PatConstKind::Float
173                } else if pat_ty.is_str() {
174                    PatConstKind::String
175                } else {
176                    // FIXME(Zalathar): This still covers several different
177                    // categories (e.g. raw pointer, pattern-type)
178                    // which could be split out into their own kinds.
179                    PatConstKind::Other
180                };
181                Some(TestableCase::Constant { value, kind: const_kind })
182            }
183
184            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
185                // In order to please the borrow checker, when lowering a pattern
186                // like `x @ subpat` we must establish any bindings in `subpat`
187                // before establishing the binding for `x`.
188                //
189                // For example (from #69971):
190                //
191                // ```ignore (illustrative)
192                // struct NonCopyStruct {
193                //     copy_field: u32,
194                // }
195                //
196                // fn foo1(x: NonCopyStruct) {
197                //     let y @ NonCopyStruct { copy_field: z } = x;
198                //     // the above should turn into
199                //     let z = x.copy_field;
200                //     let y = x;
201                // }
202                // ```
203
204                // First, recurse into the subpattern, if any.
205                if let Some(subpattern) = subpattern.as_ref() {
206                    // this is the `x @ P` case; have to keep matching against `P` now
207                    MatchPairTree::for_pattern(
208                        place_builder,
209                        subpattern,
210                        cx,
211                        &mut subpairs,
212                        extra_data,
213                    );
214                }
215
216                // Then push this binding, after any bindings in the subpattern.
217                if let Some(source) = place {
218                    extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
219                        span: pattern.span,
220                        source,
221                        var_id: var,
222                        binding_mode: mode,
223                        is_shorthand,
224                    }));
225                }
226
227                None
228            }
229
230            PatKind::Array { ref prefix, ref slice, ref suffix } => {
231                // Determine the statically-known length of the array type being matched.
232                // This should always succeed for legal programs, but could fail for
233                // erroneous programs (e.g. the type is `[u8; const { panic!() }]`),
234                // so take care not to ICE if this fails.
235                let array_len = match pattern.ty.kind() {
236                    ty::Array(_, len) => len.try_to_target_usize(cx.tcx),
237                    _ => None,
238                };
239                if let Some(array_len) = array_len {
240                    cx.prefix_slice_suffix(
241                        &mut subpairs,
242                        extra_data,
243                        &place_builder,
244                        Some(array_len),
245                        prefix,
246                        slice,
247                        suffix,
248                    );
249                } else {
250                    // If the array length couldn't be determined, ignore the
251                    // subpatterns and delayed-assert that compilation will fail.
252                    cx.tcx.dcx().span_delayed_bug(
253                        pattern.span,
254                        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("array length in pattern couldn\'t be determined for ty={0:?}",
                pattern.ty))
    })format!(
255                            "array length in pattern couldn't be determined for ty={:?}",
256                            pattern.ty
257                        ),
258                    );
259                }
260
261                None
262            }
263            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
264                cx.prefix_slice_suffix(
265                    &mut subpairs,
266                    extra_data,
267                    &place_builder,
268                    None,
269                    prefix,
270                    slice,
271                    suffix,
272                );
273
274                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
275                    // This pattern is shaped like `[..]`. It can match a slice
276                    // of any length, so no length test is needed.
277                    None
278                } else {
279                    // Any other shape of slice pattern requires a length test.
280                    // Slice patterns with a `..` subpattern require a minimum
281                    // length; those without `..` require an exact length.
282                    Some(TestableCase::Slice {
283                        len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
284                        op: if slice.is_some() {
285                            SliceLenOp::GreaterOrEqual
286                        } else {
287                            SliceLenOp::Equal
288                        },
289                    })
290                }
291            }
292
293            PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
294                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
295                cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
296
297                let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
298                    i == variant_index
299                        || !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
300                            cx.tcx,
301                            cx.infcx.typing_env(cx.param_env),
302                            cx.def_id.into(),
303                        )
304                }) && !adt_def.variant_list_has_applicable_non_exhaustive();
305                if irrefutable {
306                    None
307                } else {
308                    Some(TestableCase::Variant { adt_def, variant_index })
309                }
310            }
311
312            PatKind::Leaf { ref subpatterns } => {
313                cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
314                None
315            }
316
317            // FIXME: Pin-patterns should probably have their own pattern kind,
318            // instead of overloading `PatKind::Deref` via the pattern type.
319            PatKind::Deref { ref subpattern }
320                if let Some(ref_ty) = pattern.ty.pinned_ty()
321                    && ref_ty.is_ref() =>
322            {
323                MatchPairTree::for_pattern(
324                    place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
325                    subpattern,
326                    cx,
327                    &mut subpairs,
328                    extra_data,
329                );
330                None
331            }
332
333            PatKind::Deref { ref subpattern }
334            | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
335                MatchPairTree::for_pattern(
336                    place_builder.deref(),
337                    subpattern,
338                    cx,
339                    &mut subpairs,
340                    extra_data,
341                );
342                None
343            }
344
345            PatKind::DerefPattern {
346                ref subpattern,
347                borrow: DerefPatBorrowMode::Borrow(mutability),
348            } => {
349                // Create a new temporary for each deref pattern.
350                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
351                let temp = cx.temp(
352                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
353                    pattern.span,
354                );
355                MatchPairTree::for_pattern(
356                    PlaceBuilder::from(temp).deref(),
357                    subpattern,
358                    cx,
359                    &mut subpairs,
360                    extra_data,
361                );
362                Some(TestableCase::Deref { temp, mutability })
363            }
364
365            PatKind::Never => Some(TestableCase::Never),
366        };
367
368        if let Some(testable_case) = testable_case {
369            // This pattern is refutable, so push a new match-pair node.
370            //
371            // Note: unless test_case is TestCase::Or, place must not be None.
372            // This means that the closure capture analysis in
373            // rustc_hir_typeck::upvar, and in particular the pattern handling
374            // code of ExprUseVisitor, must capture all of the places we'll use.
375            // Make sure to keep these two parts in sync!
376            match_pairs.push(MatchPairTree {
377                place,
378                testable_case,
379                subpairs,
380                pattern_ty: pattern.ty,
381                pattern_span: pattern.span,
382            })
383        } else {
384            // This pattern is irrefutable, so it doesn't need its own match-pair node.
385            // Just push its refutable subpatterns instead, if any.
386            match_pairs.extend(subpairs);
387        }
388    }
389}