rustc_mir_build/builder/matches/
match_pair.rs

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