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