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        prefix: &[Pat<'tcx>],
43        opt_slice: &Option<Box<Pat<'tcx>>>,
44        suffix: &[Pat<'tcx>],
45    ) {
46        let tcx = self.tcx;
47        let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
48            let place_ty = place_resolved.ty(&self.local_decls, tcx).ty;
49            match place_ty.kind() {
50                ty::Array(_, length) => {
51                    if let Some(length) = length.try_to_target_usize(tcx) {
52                        (length, true)
53                    } else {
54                        // This can happen when the array length is a generic const
55                        // expression that couldn't be evaluated (e.g., due to an error).
56                        // Since there's already a compilation error, we use a fallback
57                        // to avoid an ICE.
58                        tcx.dcx().span_delayed_bug(
59                            tcx.def_span(self.def_id),
60                            "array length in pattern couldn't be evaluated",
61                        );
62                        ((prefix.len() + suffix.len()).try_into().unwrap(), false)
63                    }
64                }
65                _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
66            }
67        } else {
68            ((prefix.len() + suffix.len()).try_into().unwrap(), false)
69        };
70
71        for (idx, subpattern) in prefix.iter().enumerate() {
72            let elem =
73                ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
74            let place = place.clone_project(elem);
75            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
76        }
77
78        if let Some(subslice_pat) = opt_slice {
79            let suffix_len = suffix.len() as u64;
80            let subslice = place.clone_project(PlaceElem::Subslice {
81                from: prefix.len() as u64,
82                to: if exact_size { min_length - suffix_len } else { suffix_len },
83                from_end: !exact_size,
84            });
85            MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
86        }
87
88        for (idx, subpattern) in suffix.iter().rev().enumerate() {
89            let end_offset = (idx + 1) as u64;
90            let elem = ProjectionElem::ConstantIndex {
91                offset: if exact_size { min_length - end_offset } else { end_offset },
92                min_length,
93                from_end: !exact_size,
94            };
95            let place = place.clone_project(elem);
96            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
97        }
98    }
99}
100
101impl<'tcx> MatchPairTree<'tcx> {
102    /// Recursively builds a match pair tree for the given pattern and its
103    /// subpatterns.
104    pub(super) fn for_pattern(
105        mut place_builder: PlaceBuilder<'tcx>,
106        pattern: &Pat<'tcx>,
107        cx: &mut Builder<'_, 'tcx>,
108        match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
109        extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
110    ) {
111        // Force the place type to the pattern's type.
112        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
113        if let Some(resolved) = place_builder.resolve_upvar(cx) {
114            place_builder = resolved;
115        }
116
117        if !cx.tcx.next_trait_solver_globally() {
118            // Only add the OpaqueCast projection if the given place is an opaque type and the
119            // expected type from the pattern is not.
120            let may_need_cast = match place_builder.base() {
121                PlaceBase::Local(local) => {
122                    let ty =
123                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
124                            .ty;
125                    ty != pattern.ty && ty.has_opaque_types()
126                }
127                _ => true,
128            };
129            if may_need_cast {
130                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
131            }
132        }
133
134        let place = place_builder.try_to_place(cx);
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                // CAUTION: The type of the pattern node (`pattern.ty`) is
163                // _often_ the same as the type of the const value (`value.ty`),
164                // but there are some cases where those types differ
165                // (e.g. when `deref!(..)` patterns interact with `String`).
166
167                // Classify the constant-pattern into further kinds, to
168                // reduce the number of ad-hoc type tests needed later on.
169                let pat_ty = pattern.ty;
170                let const_kind = if pat_ty.is_bool() {
171                    PatConstKind::Bool
172                } else if pat_ty.is_integral() || pat_ty.is_char() {
173                    PatConstKind::IntOrChar
174                } else if pat_ty.is_floating_point() {
175                    PatConstKind::Float
176                } else {
177                    // FIXME(Zalathar): This still covers several different
178                    // categories (e.g. raw pointer, string, pattern-type)
179                    // which could be split out into their own kinds.
180                    PatConstKind::Other
181                };
182                Some(TestableCase::Constant { value, kind: const_kind })
183            }
184
185            PatKind::AscribeUserType {
186                ascription: Ascription { ref annotation, variance },
187                ref subpattern,
188                ..
189            } => {
190                MatchPairTree::for_pattern(
191                    place_builder,
192                    subpattern,
193                    cx,
194                    &mut subpairs,
195                    extra_data,
196                );
197
198                // Apply the type ascription to the value at `match_pair.place`
199                if let Some(source) = place {
200                    let annotation = annotation.clone();
201                    extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
202                }
203
204                None
205            }
206
207            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
208                // In order to please the borrow checker, when lowering a pattern
209                // like `x @ subpat` we must establish any bindings in `subpat`
210                // before establishing the binding for `x`.
211                //
212                // For example (from #69971):
213                //
214                // ```ignore (illustrative)
215                // struct NonCopyStruct {
216                //     copy_field: u32,
217                // }
218                //
219                // fn foo1(x: NonCopyStruct) {
220                //     let y @ NonCopyStruct { copy_field: z } = x;
221                //     // the above should turn into
222                //     let z = x.copy_field;
223                //     let y = x;
224                // }
225                // ```
226
227                // First, recurse into the subpattern, if any.
228                if let Some(subpattern) = subpattern.as_ref() {
229                    // this is the `x @ P` case; have to keep matching against `P` now
230                    MatchPairTree::for_pattern(
231                        place_builder,
232                        subpattern,
233                        cx,
234                        &mut subpairs,
235                        extra_data,
236                    );
237                }
238
239                // Then push this binding, after any bindings in the subpattern.
240                if let Some(source) = place {
241                    extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
242                        span: pattern.span,
243                        source,
244                        var_id: var,
245                        binding_mode: mode,
246                        is_shorthand,
247                    }));
248                }
249
250                None
251            }
252
253            PatKind::ExpandedConstant { subpattern: ref pattern, .. } => {
254                MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
255                None
256            }
257
258            PatKind::Array { ref prefix, ref slice, ref suffix } => {
259                cx.prefix_slice_suffix(
260                    &mut subpairs,
261                    extra_data,
262                    &place_builder,
263                    prefix,
264                    slice,
265                    suffix,
266                );
267                None
268            }
269            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
270                cx.prefix_slice_suffix(
271                    &mut subpairs,
272                    extra_data,
273                    &place_builder,
274                    prefix,
275                    slice,
276                    suffix,
277                );
278
279                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
280                    // This pattern is shaped like `[..]`. It can match a slice
281                    // of any length, so no length test is needed.
282                    None
283                } else {
284                    // Any other shape of slice pattern requires a length test.
285                    // Slice patterns with a `..` subpattern require a minimum
286                    // length; those without `..` require an exact length.
287                    Some(TestableCase::Slice {
288                        len: u64::try_from(prefix.len() + suffix.len()).unwrap(),
289                        op: if slice.is_some() {
290                            SliceLenOp::GreaterOrEqual
291                        } else {
292                            SliceLenOp::Equal
293                        },
294                    })
295                }
296            }
297
298            PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
299                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
300                cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
301
302                let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
303                    i == variant_index
304                        || !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
305                            cx.tcx,
306                            cx.infcx.typing_env(cx.param_env),
307                            cx.def_id.into(),
308                        )
309                }) && !adt_def.variant_list_has_applicable_non_exhaustive();
310                if irrefutable {
311                    None
312                } else {
313                    Some(TestableCase::Variant { adt_def, variant_index })
314                }
315            }
316
317            PatKind::Leaf { ref subpatterns } => {
318                cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
319                None
320            }
321
322            // FIXME: Pin-patterns should probably have their own pattern kind,
323            // instead of overloading `PatKind::Deref` via the pattern type.
324            PatKind::Deref { ref subpattern }
325                if let Some(ref_ty) = pattern.ty.pinned_ty()
326                    && ref_ty.is_ref() =>
327            {
328                MatchPairTree::for_pattern(
329                    place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
330                    subpattern,
331                    cx,
332                    &mut subpairs,
333                    extra_data,
334                );
335                None
336            }
337
338            PatKind::Deref { ref subpattern }
339            | PatKind::DerefPattern { ref subpattern, borrow: DerefPatBorrowMode::Box } => {
340                MatchPairTree::for_pattern(
341                    place_builder.deref(),
342                    subpattern,
343                    cx,
344                    &mut subpairs,
345                    extra_data,
346                );
347                None
348            }
349
350            PatKind::DerefPattern {
351                ref subpattern,
352                borrow: DerefPatBorrowMode::Borrow(mutability),
353            } => {
354                // Create a new temporary for each deref pattern.
355                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
356                let temp = cx.temp(
357                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
358                    pattern.span,
359                );
360                MatchPairTree::for_pattern(
361                    PlaceBuilder::from(temp).deref(),
362                    subpattern,
363                    cx,
364                    &mut subpairs,
365                    extra_data,
366                );
367                Some(TestableCase::Deref { temp, mutability })
368            }
369
370            PatKind::Never => Some(TestableCase::Never),
371        };
372
373        if let Some(testable_case) = testable_case {
374            // This pattern is refutable, so push a new match-pair node.
375            //
376            // Note: unless test_case is TestCase::Or, place must not be None.
377            // This means that the closure capture analysis in
378            // rustc_hir_typeck::upvar, and in particular the pattern handling
379            // code of ExprUseVisitor, must capture all of the places we'll use.
380            // Make sure to keep these two parts in sync!
381            match_pairs.push(MatchPairTree {
382                place,
383                testable_case,
384                subpairs,
385                pattern_ty: pattern.ty,
386                pattern_span: pattern.span,
387            })
388        } else {
389            // This pattern is irrefutable, so it doesn't need its own match-pair node.
390            // Just push its refutable subpatterns instead, if any.
391            match_pairs.extend(subpairs);
392        }
393    }
394}