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