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        prefix: &[Pat<'tcx>],
44        opt_slice: &Option<Box<Pat<'tcx>>>,
45        suffix: &[Pat<'tcx>],
46    ) {
47        let tcx = self.tcx;
48        let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
49            let place_ty = place_resolved.ty(&self.local_decls, tcx).ty;
50            match place_ty.kind() {
51                ty::Array(_, length) => {
52                    if let Some(length) = length.try_to_target_usize(tcx) {
53                        (length, true)
54                    } else {
55                        // This can happen when the array length is a generic const
56                        // expression that couldn't be evaluated (e.g., due to an error).
57                        // Since there's already a compilation error, we use a fallback
58                        // to avoid an ICE.
59                        tcx.dcx().span_delayed_bug(
60                            tcx.def_span(self.def_id),
61                            "array length in pattern couldn't be evaluated",
62                        );
63                        ((prefix.len() + suffix.len()).try_into().unwrap(), false)
64                    }
65                }
66                _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
67            }
68        } else {
69            ((prefix.len() + suffix.len()).try_into().unwrap(), false)
70        };
71
72        for (idx, subpattern) in prefix.iter().enumerate() {
73            let elem =
74                ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
75            let place = place.clone_project(elem);
76            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
77        }
78
79        if let Some(subslice_pat) = opt_slice {
80            let suffix_len = suffix.len() as u64;
81            let subslice = place.clone_project(PlaceElem::Subslice {
82                from: prefix.len() as u64,
83                to: if exact_size { min_length - suffix_len } else { suffix_len },
84                from_end: !exact_size,
85            });
86            MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
87        }
88
89        for (idx, subpattern) in suffix.iter().rev().enumerate() {
90            let end_offset = (idx + 1) as u64;
91            let elem = ProjectionElem::ConstantIndex {
92                offset: if exact_size { min_length - end_offset } else { end_offset },
93                min_length,
94                from_end: !exact_size,
95            };
96            let place = place.clone_project(elem);
97            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
98        }
99    }
100}
101
102impl<'tcx> MatchPairTree<'tcx> {
103    /// Recursively builds a match pair tree for the given pattern and its
104    /// subpatterns.
105    pub(super) fn for_pattern(
106        mut place_builder: PlaceBuilder<'tcx>,
107        pattern: &Pat<'tcx>,
108        cx: &mut Builder<'_, 'tcx>,
109        match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
110        extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
111    ) {
112        // Force the place type to the pattern's type.
113        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
114        if let Some(resolved) = place_builder.resolve_upvar(cx) {
115            place_builder = resolved;
116        }
117
118        if !cx.tcx.next_trait_solver_globally() {
119            // Only add the OpaqueCast projection if the given place is an opaque type and the
120            // expected type from the pattern is not.
121            let may_need_cast = match place_builder.base() {
122                PlaceBase::Local(local) => {
123                    let ty =
124                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
125                            .ty;
126                    ty != pattern.ty && ty.has_opaque_types()
127                }
128                _ => true,
129            };
130            if may_need_cast {
131                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
132            }
133        }
134
135        let place = place_builder.try_to_place(cx);
136
137        // Apply any type ascriptions to the value at `match_pair.place`.
138        if let Some(place) = place
139            && let Some(extra) = &pattern.extra
140        {
141            for &Ascription { ref annotation, variance } in &extra.ascriptions {
142                extra_data.ascriptions.push(super::Ascription {
143                    source: place,
144                    annotation: annotation.clone(),
145                    variance,
146                });
147            }
148        }
149
150        let mut subpairs = Vec::new();
151        let testable_case = match pattern.kind {
152            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
153
154            PatKind::Or { ref pats } => {
155                let pats: Box<[FlatPat<'tcx>]> =
156                    pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
157                if !pats[0].extra_data.bindings.is_empty() {
158                    // Hold a place for any bindings established in (possibly-nested) or-patterns.
159                    // By only holding a place when bindings are present, we skip over any
160                    // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
161                    // other words, we can assume this expands into subcandidates.
162                    // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
163                    extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
164                }
165                Some(TestableCase::Or { pats })
166            }
167
168            PatKind::Range(ref range) => {
169                if range.is_full_range(cx.tcx) == Some(true) {
170                    None
171                } else {
172                    Some(TestableCase::Range(Arc::clone(range)))
173                }
174            }
175
176            PatKind::Constant { value } => {
177                // CAUTION: The type of the pattern node (`pattern.ty`) is
178                // _often_ the same as the type of the const value (`value.ty`),
179                // but there are some cases where those types differ
180                // (e.g. when `deref!(..)` patterns interact with `String`).
181
182                // Classify the constant-pattern into further kinds, to
183                // reduce the number of ad-hoc type tests needed later on.
184                let pat_ty = pattern.ty;
185                let const_kind = if pat_ty.is_bool() {
186                    PatConstKind::Bool
187                } else if pat_ty.is_integral() || pat_ty.is_char() {
188                    PatConstKind::IntOrChar
189                } else if pat_ty.is_floating_point() {
190                    PatConstKind::Float
191                } else if pat_ty.is_str() {
192                    // Deref-patterns can cause string-literal patterns to have
193                    // type `str` instead of the usual `&str`.
194                    if !cx.tcx.features().deref_patterns() {
195                        span_bug!(
196                            pattern.span,
197                            "const pattern has type `str` but deref_patterns is not enabled"
198                        );
199                    }
200                    PatConstKind::String
201                } else if pat_ty.is_imm_ref_str() {
202                    PatConstKind::String
203                } else {
204                    // FIXME(Zalathar): This still covers several different
205                    // categories (e.g. raw pointer, pattern-type)
206                    // which could be split out into their own kinds.
207                    PatConstKind::Other
208                };
209                Some(TestableCase::Constant { value, kind: const_kind })
210            }
211
212            PatKind::Binding { mode, var, is_shorthand, ref subpattern, .. } => {
213                // In order to please the borrow checker, when lowering a pattern
214                // like `x @ subpat` we must establish any bindings in `subpat`
215                // before establishing the binding for `x`.
216                //
217                // For example (from #69971):
218                //
219                // ```ignore (illustrative)
220                // struct NonCopyStruct {
221                //     copy_field: u32,
222                // }
223                //
224                // fn foo1(x: NonCopyStruct) {
225                //     let y @ NonCopyStruct { copy_field: z } = x;
226                //     // the above should turn into
227                //     let z = x.copy_field;
228                //     let y = x;
229                // }
230                // ```
231
232                // First, recurse into the subpattern, if any.
233                if let Some(subpattern) = subpattern.as_ref() {
234                    // this is the `x @ P` case; have to keep matching against `P` now
235                    MatchPairTree::for_pattern(
236                        place_builder,
237                        subpattern,
238                        cx,
239                        &mut subpairs,
240                        extra_data,
241                    );
242                }
243
244                // Then push this binding, after any bindings in the subpattern.
245                if let Some(source) = place {
246                    extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
247                        span: pattern.span,
248                        source,
249                        var_id: var,
250                        binding_mode: mode,
251                        is_shorthand,
252                    }));
253                }
254
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}