rustc_mir_build/builder/matches/
match_pair.rs

1use std::sync::Arc;
2
3use rustc_hir::ByRef;
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::{FlatPat, MatchPairTree, PatternExtraData, TestCase};
11
12impl<'a, 'tcx> Builder<'a, 'tcx> {
13    /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
14    /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or
15    /// [`PatKind::Leaf`].
16    ///
17    /// Used internally by [`MatchPairTree::for_pattern`].
18    fn field_match_pairs(
19        &mut self,
20        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
21        extra_data: &mut PatternExtraData<'tcx>,
22        place: PlaceBuilder<'tcx>,
23        subpatterns: &[FieldPat<'tcx>],
24    ) {
25        for fieldpat in subpatterns {
26            let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
27            MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data);
28        }
29    }
30
31    /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an
32    /// array pattern or slice pattern, and adds those trees to `match_pairs`.
33    ///
34    /// Used internally by [`MatchPairTree::for_pattern`].
35    fn prefix_slice_suffix(
36        &mut self,
37        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
38        extra_data: &mut PatternExtraData<'tcx>,
39        place: &PlaceBuilder<'tcx>,
40        prefix: &[Pat<'tcx>],
41        opt_slice: &Option<Box<Pat<'tcx>>>,
42        suffix: &[Pat<'tcx>],
43    ) {
44        let tcx = self.tcx;
45        let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
46            match place_resolved.ty(&self.local_decls, tcx).ty.kind() {
47                ty::Array(_, length) => (
48                    length
49                        .try_to_target_usize(tcx)
50                        .expect("expected len of array pat to be definite"),
51                    true,
52                ),
53                _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
54            }
55        } else {
56            ((prefix.len() + suffix.len()).try_into().unwrap(), false)
57        };
58
59        for (idx, subpattern) in prefix.iter().enumerate() {
60            let elem =
61                ProjectionElem::ConstantIndex { offset: idx as u64, 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 suffix_len = suffix.len() as u64;
68            let subslice = place.clone_project(PlaceElem::Subslice {
69                from: prefix.len() as u64,
70                to: if exact_size { min_length - suffix_len } else { suffix_len },
71                from_end: !exact_size,
72            });
73            MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
74        }
75
76        for (idx, subpattern) in suffix.iter().rev().enumerate() {
77            let end_offset = (idx + 1) as u64;
78            let elem = ProjectionElem::ConstantIndex {
79                offset: if exact_size { min_length - end_offset } else { end_offset },
80                min_length,
81                from_end: !exact_size,
82            };
83            let place = place.clone_project(elem);
84            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
85        }
86    }
87}
88
89impl<'tcx> MatchPairTree<'tcx> {
90    /// Recursively builds a match pair tree for the given pattern and its
91    /// subpatterns.
92    pub(super) fn for_pattern(
93        mut place_builder: PlaceBuilder<'tcx>,
94        pattern: &Pat<'tcx>,
95        cx: &mut Builder<'_, 'tcx>,
96        match_pairs: &mut Vec<Self>, // Newly-created nodes are added to this vector
97        extra_data: &mut PatternExtraData<'tcx>, // Bindings/ascriptions are added here
98    ) {
99        // Force the place type to the pattern's type.
100        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
101        if let Some(resolved) = place_builder.resolve_upvar(cx) {
102            place_builder = resolved;
103        }
104
105        if !cx.tcx.next_trait_solver_globally() {
106            // Only add the OpaqueCast projection if the given place is an opaque type and the
107            // expected type from the pattern is not.
108            let may_need_cast = match place_builder.base() {
109                PlaceBase::Local(local) => {
110                    let ty =
111                        Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx)
112                            .ty;
113                    ty != pattern.ty && ty.has_opaque_types()
114                }
115                _ => true,
116            };
117            if may_need_cast {
118                place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
119            }
120        }
121
122        let place = place_builder.try_to_place(cx);
123        let mut subpairs = Vec::new();
124        let test_case = match pattern.kind {
125            PatKind::Missing | PatKind::Wild | PatKind::Error(_) => None,
126
127            PatKind::Or { ref pats } => {
128                let pats: Box<[FlatPat<'tcx>]> =
129                    pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
130                if !pats[0].extra_data.bindings.is_empty() {
131                    // Hold a place for any bindings established in (possibly-nested) or-patterns.
132                    // By only holding a place when bindings are present, we skip over any
133                    // or-patterns that will be simplified by `merge_trivial_subcandidates`. In
134                    // other words, we can assume this expands into subcandidates.
135                    // FIXME(@dianne): this needs updating/removing if we always merge or-patterns
136                    extra_data.bindings.push(super::SubpatternBindings::FromOrPattern);
137                }
138                Some(TestCase::Or { pats })
139            }
140
141            PatKind::Range(ref range) => {
142                if range.is_full_range(cx.tcx) == Some(true) {
143                    None
144                } else {
145                    Some(TestCase::Range(Arc::clone(range)))
146                }
147            }
148
149            PatKind::Constant { value } => Some(TestCase::Constant { value }),
150
151            PatKind::AscribeUserType {
152                ascription: Ascription { ref annotation, variance },
153                ref subpattern,
154                ..
155            } => {
156                MatchPairTree::for_pattern(
157                    place_builder,
158                    subpattern,
159                    cx,
160                    &mut subpairs,
161                    extra_data,
162                );
163
164                // Apply the type ascription to the value at `match_pair.place`
165                if let Some(source) = place {
166                    let annotation = annotation.clone();
167                    extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
168                }
169
170                None
171            }
172
173            PatKind::Binding { mode, var, ref subpattern, .. } => {
174                // In order to please the borrow checker, when lowering a pattern
175                // like `x @ subpat` we must establish any bindings in `subpat`
176                // before establishing the binding for `x`.
177                //
178                // For example (from #69971):
179                //
180                // ```ignore (illustrative)
181                // struct NonCopyStruct {
182                //     copy_field: u32,
183                // }
184                //
185                // fn foo1(x: NonCopyStruct) {
186                //     let y @ NonCopyStruct { copy_field: z } = x;
187                //     // the above should turn into
188                //     let z = x.copy_field;
189                //     let y = x;
190                // }
191                // ```
192
193                // First, recurse into the subpattern, if any.
194                if let Some(subpattern) = subpattern.as_ref() {
195                    // this is the `x @ P` case; have to keep matching against `P` now
196                    MatchPairTree::for_pattern(
197                        place_builder,
198                        subpattern,
199                        cx,
200                        &mut subpairs,
201                        extra_data,
202                    );
203                }
204
205                // Then push this binding, after any bindings in the subpattern.
206                if let Some(source) = place {
207                    extra_data.bindings.push(super::SubpatternBindings::One(super::Binding {
208                        span: pattern.span,
209                        source,
210                        var_id: var,
211                        binding_mode: mode,
212                    }));
213                }
214
215                None
216            }
217
218            PatKind::ExpandedConstant { subpattern: ref pattern, .. } => {
219                MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
220                None
221            }
222
223            PatKind::Array { ref prefix, ref slice, ref suffix } => {
224                cx.prefix_slice_suffix(
225                    &mut subpairs,
226                    extra_data,
227                    &place_builder,
228                    prefix,
229                    slice,
230                    suffix,
231                );
232                None
233            }
234            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
235                cx.prefix_slice_suffix(
236                    &mut subpairs,
237                    extra_data,
238                    &place_builder,
239                    prefix,
240                    slice,
241                    suffix,
242                );
243
244                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
245                    None
246                } else {
247                    Some(TestCase::Slice {
248                        len: prefix.len() + suffix.len(),
249                        variable_length: slice.is_some(),
250                    })
251                }
252            }
253
254            PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
255                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
256                cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
257
258                let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
259                    i == variant_index
260                        || !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
261                            cx.tcx,
262                            cx.infcx.typing_env(cx.param_env),
263                            cx.def_id.into(),
264                        )
265                }) && !adt_def.variant_list_has_applicable_non_exhaustive();
266                if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
267            }
268
269            PatKind::Leaf { ref subpatterns } => {
270                cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
271                None
272            }
273
274            PatKind::Deref { ref subpattern }
275            | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => {
276                if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) {
277                    // Only deref patterns on boxes can be lowered using a built-in deref.
278                    debug_assert!(pattern.ty.is_box());
279                }
280
281                MatchPairTree::for_pattern(
282                    place_builder.deref(),
283                    subpattern,
284                    cx,
285                    &mut subpairs,
286                    extra_data,
287                );
288                None
289            }
290
291            PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => {
292                // Create a new temporary for each deref pattern.
293                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
294                let temp = cx.temp(
295                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
296                    pattern.span,
297                );
298                MatchPairTree::for_pattern(
299                    PlaceBuilder::from(temp).deref(),
300                    subpattern,
301                    cx,
302                    &mut subpairs,
303                    extra_data,
304                );
305                Some(TestCase::Deref { temp, mutability })
306            }
307
308            PatKind::Never => Some(TestCase::Never),
309        };
310
311        if let Some(test_case) = test_case {
312            // This pattern is refutable, so push a new match-pair node.
313            match_pairs.push(MatchPairTree {
314                place,
315                test_case,
316                subpairs,
317                pattern_ty: pattern.ty,
318                pattern_span: pattern.span,
319            })
320        } else {
321            // This pattern is irrefutable, so it doesn't need its own match-pair node.
322            // Just push its refutable subpatterns instead, if any.
323            match_pairs.extend(subpairs);
324        }
325    }
326}