rustc_mir_build/builder/matches/
match_pair.rs

1use std::sync::Arc;
2
3use rustc_middle::mir::*;
4use rustc_middle::thir::*;
5use rustc_middle::ty::{self, Ty, TypeVisitableExt};
6
7use crate::builder::Builder;
8use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
9use crate::builder::matches::{FlatPat, MatchPairTree, PatternExtraData, TestCase};
10
11impl<'a, 'tcx> Builder<'a, 'tcx> {
12    /// Builds and pushes [`MatchPairTree`] subtrees, one for each pattern in
13    /// `subpatterns`, representing the fields of a [`PatKind::Variant`] or
14    /// [`PatKind::Leaf`].
15    ///
16    /// Used internally by [`MatchPairTree::for_pattern`].
17    fn field_match_pairs(
18        &mut self,
19        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
20        extra_data: &mut PatternExtraData<'tcx>,
21        place: PlaceBuilder<'tcx>,
22        subpatterns: &[FieldPat<'tcx>],
23    ) {
24        for fieldpat in subpatterns {
25            let place = place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
26            MatchPairTree::for_pattern(place, &fieldpat.pattern, self, match_pairs, extra_data);
27        }
28    }
29
30    /// Builds [`MatchPairTree`] subtrees for the prefix/middle/suffix parts of an
31    /// array pattern or slice pattern, and adds those trees to `match_pairs`.
32    ///
33    /// Used internally by [`MatchPairTree::for_pattern`].
34    fn prefix_slice_suffix(
35        &mut self,
36        match_pairs: &mut Vec<MatchPairTree<'tcx>>,
37        extra_data: &mut PatternExtraData<'tcx>,
38        place: &PlaceBuilder<'tcx>,
39        prefix: &[Pat<'tcx>],
40        opt_slice: &Option<Box<Pat<'tcx>>>,
41        suffix: &[Pat<'tcx>],
42    ) {
43        let tcx = self.tcx;
44        let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
45            match place_resolved.ty(&self.local_decls, tcx).ty.kind() {
46                ty::Array(_, length) => (
47                    length
48                        .try_to_target_usize(tcx)
49                        .expect("expected len of array pat to be definite"),
50                    true,
51                ),
52                _ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
53            }
54        } else {
55            ((prefix.len() + suffix.len()).try_into().unwrap(), false)
56        };
57
58        for (idx, subpattern) in prefix.iter().enumerate() {
59            let elem =
60                ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
61            let place = place.clone_project(elem);
62            MatchPairTree::for_pattern(place, subpattern, self, match_pairs, extra_data)
63        }
64
65        if let Some(subslice_pat) = opt_slice {
66            let suffix_len = suffix.len() as u64;
67            let subslice = place.clone_project(PlaceElem::Subslice {
68                from: prefix.len() as u64,
69                to: if exact_size { min_length - suffix_len } else { suffix_len },
70                from_end: !exact_size,
71            });
72            MatchPairTree::for_pattern(subslice, subslice_pat, self, match_pairs, extra_data);
73        }
74
75        for (idx, subpattern) in suffix.iter().rev().enumerate() {
76            let end_offset = (idx + 1) as u64;
77            let elem = ProjectionElem::ConstantIndex {
78                offset: if exact_size { min_length - end_offset } else { end_offset },
79                min_length,
80                from_end: !exact_size,
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        // 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).ty;
110                ty != pattern.ty && ty.has_opaque_types()
111            }
112            _ => true,
113        };
114        if may_need_cast {
115            place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
116        }
117
118        // Place can be none if the pattern refers to a non-captured place in a closure.
119        let place = place_builder.try_to_place(cx);
120        let mut subpairs = Vec::new();
121        let test_case = match pattern.kind {
122            PatKind::Wild | PatKind::Error(_) => None,
123
124            PatKind::Or { ref pats } => Some(TestCase::Or {
125                pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
126            }),
127
128            PatKind::Range(ref range) => {
129                if range.is_full_range(cx.tcx) == Some(true) {
130                    None
131                } else {
132                    Some(TestCase::Range(Arc::clone(range)))
133                }
134            }
135
136            PatKind::Constant { value } => Some(TestCase::Constant { value }),
137
138            PatKind::AscribeUserType {
139                ascription: Ascription { ref annotation, variance },
140                ref subpattern,
141                ..
142            } => {
143                MatchPairTree::for_pattern(
144                    place_builder,
145                    subpattern,
146                    cx,
147                    &mut subpairs,
148                    extra_data,
149                );
150
151                // Apply the type ascription to the value at `match_pair.place`
152                if let Some(source) = place {
153                    let annotation = annotation.clone();
154                    extra_data.ascriptions.push(super::Ascription { source, annotation, variance });
155                }
156
157                None
158            }
159
160            PatKind::Binding { mode, var, ref subpattern, .. } => {
161                // In order to please the borrow checker, when lowering a pattern
162                // like `x @ subpat` we must establish any bindings in `subpat`
163                // before establishing the binding for `x`.
164                //
165                // For example (from #69971):
166                //
167                // ```ignore (illustrative)
168                // struct NonCopyStruct {
169                //     copy_field: u32,
170                // }
171                //
172                // fn foo1(x: NonCopyStruct) {
173                //     let y @ NonCopyStruct { copy_field: z } = x;
174                //     // the above should turn into
175                //     let z = x.copy_field;
176                //     let y = x;
177                // }
178                // ```
179
180                // First, recurse into the subpattern, if any.
181                if let Some(subpattern) = subpattern.as_ref() {
182                    // this is the `x @ P` case; have to keep matching against `P` now
183                    MatchPairTree::for_pattern(
184                        place_builder,
185                        subpattern,
186                        cx,
187                        &mut subpairs,
188                        extra_data,
189                    );
190                }
191
192                // Then push this binding, after any bindings in the subpattern.
193                if let Some(source) = place {
194                    extra_data.bindings.push(super::Binding {
195                        span: pattern.span,
196                        source,
197                        var_id: var,
198                        binding_mode: mode,
199                    });
200                }
201
202                None
203            }
204
205            PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => {
206                MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
207                None
208            }
209            PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => {
210                MatchPairTree::for_pattern(place_builder, pattern, cx, &mut subpairs, extra_data);
211
212                // Apply a type ascription for the inline constant to the value at `match_pair.place`
213                if let Some(source) = place {
214                    let span = pattern.span;
215                    let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
216                    let args = ty::InlineConstArgs::new(
217                        cx.tcx,
218                        ty::InlineConstArgsParts {
219                            parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
220                            ty: cx.infcx.next_ty_var(span),
221                        },
222                    )
223                    .args;
224                    let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::new(
225                        ty::UserTypeKind::TypeOf(def_id, ty::UserArgs { args, user_self_ty: None }),
226                    ));
227                    let annotation = ty::CanonicalUserTypeAnnotation {
228                        inferred_ty: pattern.ty,
229                        span,
230                        user_ty: Box::new(user_ty),
231                    };
232                    let variance = ty::Contravariant;
233                    extra_data.ascriptions.push(super::Ascription { annotation, source, variance });
234                }
235
236                None
237            }
238
239            PatKind::Array { ref prefix, ref slice, ref suffix } => {
240                cx.prefix_slice_suffix(
241                    &mut subpairs,
242                    extra_data,
243                    &place_builder,
244                    prefix,
245                    slice,
246                    suffix,
247                );
248                None
249            }
250            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
251                cx.prefix_slice_suffix(
252                    &mut subpairs,
253                    extra_data,
254                    &place_builder,
255                    prefix,
256                    slice,
257                    suffix,
258                );
259
260                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
261                    None
262                } else {
263                    Some(TestCase::Slice {
264                        len: prefix.len() + suffix.len(),
265                        variable_length: slice.is_some(),
266                    })
267                }
268            }
269
270            PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
271                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
272                cx.field_match_pairs(&mut subpairs, extra_data, downcast_place, subpatterns);
273
274                let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
275                    i == variant_index
276                        || !v.inhabited_predicate(cx.tcx, adt_def).instantiate(cx.tcx, args).apply(
277                            cx.tcx,
278                            cx.infcx.typing_env(cx.param_env),
279                            cx.def_id.into(),
280                        )
281                }) && !adt_def.variant_list_has_applicable_non_exhaustive();
282                if irrefutable { None } else { Some(TestCase::Variant { adt_def, variant_index }) }
283            }
284
285            PatKind::Leaf { ref subpatterns } => {
286                cx.field_match_pairs(&mut subpairs, extra_data, place_builder, subpatterns);
287                None
288            }
289
290            PatKind::Deref { ref subpattern } => {
291                MatchPairTree::for_pattern(
292                    place_builder.deref(),
293                    subpattern,
294                    cx,
295                    &mut subpairs,
296                    extra_data,
297                );
298                None
299            }
300
301            PatKind::DerefPattern { ref subpattern, mutability } => {
302                // Create a new temporary for each deref pattern.
303                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
304                let temp = cx.temp(
305                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
306                    pattern.span,
307                );
308                MatchPairTree::for_pattern(
309                    PlaceBuilder::from(temp).deref(),
310                    subpattern,
311                    cx,
312                    &mut subpairs,
313                    extra_data,
314                );
315                Some(TestCase::Deref { temp, mutability })
316            }
317
318            PatKind::Never => Some(TestCase::Never),
319        };
320
321        if let Some(test_case) = test_case {
322            // This pattern is refutable, so push a new match-pair node.
323            match_pairs.push(MatchPairTree {
324                place: place.expect("refutable patterns should always have a place to inspect"),
325                test_case,
326                subpairs,
327                pattern_ty: pattern.ty,
328                pattern_span: pattern.span,
329            })
330        } else {
331            // This pattern is irrefutable, so it doesn't need its own match-pair node.
332            // Just push its refutable subpatterns instead, if any.
333            match_pairs.extend(subpairs);
334        }
335    }
336}