rustc_mir_build/builder/matches/
match_pair.rs

1use std::sync::Arc;
2
3use rustc_middle::mir::*;
4use rustc_middle::thir::{self, *};
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, TestCase};
10
11impl<'a, 'tcx> Builder<'a, 'tcx> {
12    /// Builds and returns [`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        place: PlaceBuilder<'tcx>,
20        subpatterns: &[FieldPat<'tcx>],
21    ) -> Vec<MatchPairTree<'tcx>> {
22        subpatterns
23            .iter()
24            .map(|fieldpat| {
25                let place =
26                    place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
27                MatchPairTree::for_pattern(place, &fieldpat.pattern, self)
28            })
29            .collect()
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        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        match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
60            let elem =
61                ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
62            MatchPairTree::for_pattern(place.clone_project(elem), subpattern, self)
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            match_pairs.push(MatchPairTree::for_pattern(subslice, subslice_pat, self));
73        }
74
75        match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
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)
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(in crate::builder) fn for_pattern(
92        mut place_builder: PlaceBuilder<'tcx>,
93        pattern: &Pat<'tcx>,
94        cx: &mut Builder<'_, 'tcx>,
95    ) -> MatchPairTree<'tcx> {
96        // Force the place type to the pattern's type.
97        // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
98        if let Some(resolved) = place_builder.resolve_upvar(cx) {
99            place_builder = resolved;
100        }
101
102        // Only add the OpaqueCast projection if the given place is an opaque type and the
103        // expected type from the pattern is not.
104        let may_need_cast = match place_builder.base() {
105            PlaceBase::Local(local) => {
106                let ty =
107                    Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty;
108                ty != pattern.ty && ty.has_opaque_types()
109            }
110            _ => true,
111        };
112        if may_need_cast {
113            place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
114        }
115
116        let place = place_builder.try_to_place(cx);
117        let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
118        let mut subpairs = Vec::new();
119        let test_case = match pattern.kind {
120            PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
121
122            PatKind::Or { ref pats } => TestCase::Or {
123                pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
124            },
125
126            PatKind::Range(ref range) => {
127                if range.is_full_range(cx.tcx) == Some(true) {
128                    default_irrefutable()
129                } else {
130                    TestCase::Range(Arc::clone(range))
131                }
132            }
133
134            PatKind::Constant { value } => TestCase::Constant { value },
135
136            PatKind::AscribeUserType {
137                ascription: thir::Ascription { ref annotation, variance },
138                ref subpattern,
139                ..
140            } => {
141                // Apply the type ascription to the value at `match_pair.place`
142                let ascription = place.map(|source| super::Ascription {
143                    annotation: annotation.clone(),
144                    source,
145                    variance,
146                });
147
148                subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx));
149                TestCase::Irrefutable { ascription, binding: None }
150            }
151
152            PatKind::Binding { mode, var, ref subpattern, .. } => {
153                let binding = place.map(|source| super::Binding {
154                    span: pattern.span,
155                    source,
156                    var_id: var,
157                    binding_mode: mode,
158                });
159
160                if let Some(subpattern) = subpattern.as_ref() {
161                    // this is the `x @ P` case; have to keep matching against `P` now
162                    subpairs.push(MatchPairTree::for_pattern(place_builder, subpattern, cx));
163                }
164                TestCase::Irrefutable { ascription: None, binding }
165            }
166
167            PatKind::ExpandedConstant { subpattern: ref pattern, def_id: _, is_inline: false } => {
168                subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx));
169                default_irrefutable()
170            }
171            PatKind::ExpandedConstant { subpattern: ref pattern, def_id, is_inline: true } => {
172                // Apply a type ascription for the inline constant to the value at `match_pair.place`
173                let ascription = place.map(|source| {
174                    let span = pattern.span;
175                    let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
176                    let args = ty::InlineConstArgs::new(
177                        cx.tcx,
178                        ty::InlineConstArgsParts {
179                            parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
180                            ty: cx.infcx.next_ty_var(span),
181                        },
182                    )
183                    .args;
184                    let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::new(
185                        ty::UserTypeKind::TypeOf(def_id, ty::UserArgs { args, user_self_ty: None }),
186                    ));
187                    let annotation = ty::CanonicalUserTypeAnnotation {
188                        inferred_ty: pattern.ty,
189                        span,
190                        user_ty: Box::new(user_ty),
191                    };
192                    super::Ascription { annotation, source, variance: ty::Contravariant }
193                });
194
195                subpairs.push(MatchPairTree::for_pattern(place_builder, pattern, cx));
196                TestCase::Irrefutable { ascription, binding: None }
197            }
198
199            PatKind::Array { ref prefix, ref slice, ref suffix } => {
200                cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
201                default_irrefutable()
202            }
203            PatKind::Slice { ref prefix, ref slice, ref suffix } => {
204                cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
205
206                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
207                    default_irrefutable()
208                } else {
209                    TestCase::Slice {
210                        len: prefix.len() + suffix.len(),
211                        variable_length: slice.is_some(),
212                    }
213                }
214            }
215
216            PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
217                let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
218                subpairs = cx.field_match_pairs(downcast_place, subpatterns);
219
220                let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
221                    i == variant_index
222                        || !v
223                            .inhabited_predicate(cx.tcx, adt_def)
224                            .instantiate(cx.tcx, args)
225                            .apply_ignore_module(cx.tcx, cx.infcx.typing_env(cx.param_env))
226                }) && (adt_def.did().is_local()
227                    || !adt_def.is_variant_list_non_exhaustive());
228                if irrefutable {
229                    default_irrefutable()
230                } else {
231                    TestCase::Variant { adt_def, variant_index }
232                }
233            }
234
235            PatKind::Leaf { ref subpatterns } => {
236                subpairs = cx.field_match_pairs(place_builder, subpatterns);
237                default_irrefutable()
238            }
239
240            PatKind::Deref { ref subpattern } => {
241                subpairs.push(MatchPairTree::for_pattern(place_builder.deref(), subpattern, cx));
242                default_irrefutable()
243            }
244
245            PatKind::DerefPattern { ref subpattern, mutability } => {
246                // Create a new temporary for each deref pattern.
247                // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
248                let temp = cx.temp(
249                    Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
250                    pattern.span,
251                );
252                subpairs.push(MatchPairTree::for_pattern(
253                    PlaceBuilder::from(temp).deref(),
254                    subpattern,
255                    cx,
256                ));
257                TestCase::Deref { temp, mutability }
258            }
259
260            PatKind::Never => TestCase::Never,
261        };
262
263        MatchPairTree {
264            place,
265            test_case,
266            subpairs,
267            pattern_ty: pattern.ty,
268            pattern_span: pattern.span,
269        }
270    }
271}