Skip to content

Commit ce86b2a

Browse files
committed
Move MatchPair tree creation to its own module
This makes it easier to see that `MatchPair::new` has only one non-recursive caller, because the recursive callers are all in this module.
1 parent 00167ab commit ce86b2a

File tree

3 files changed

+248
-240
lines changed

3 files changed

+248
-240
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
use rustc_middle::mir::*;
2+
use rustc_middle::thir::{self, *};
3+
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
4+
5+
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
6+
use crate::build::matches::{FlatPat, MatchPair, TestCase};
7+
use crate::build::Builder;
8+
9+
impl<'a, 'tcx> Builder<'a, 'tcx> {
10+
fn field_match_pairs<'pat>(
11+
&mut self,
12+
place: PlaceBuilder<'tcx>,
13+
subpatterns: &'pat [FieldPat<'tcx>],
14+
) -> Vec<MatchPair<'pat, 'tcx>> {
15+
subpatterns
16+
.iter()
17+
.map(|fieldpat| {
18+
let place =
19+
place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
20+
MatchPair::new(place, &fieldpat.pattern, self)
21+
})
22+
.collect()
23+
}
24+
25+
fn prefix_slice_suffix<'pat>(
26+
&mut self,
27+
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
28+
place: &PlaceBuilder<'tcx>,
29+
prefix: &'pat [Box<Pat<'tcx>>],
30+
opt_slice: &'pat Option<Box<Pat<'tcx>>>,
31+
suffix: &'pat [Box<Pat<'tcx>>],
32+
) {
33+
let tcx = self.tcx;
34+
let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
35+
match place_resolved.ty(&self.local_decls, tcx).ty.kind() {
36+
ty::Array(_, length) => (length.eval_target_usize(tcx, self.param_env), true),
37+
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
38+
}
39+
} else {
40+
((prefix.len() + suffix.len()).try_into().unwrap(), false)
41+
};
42+
43+
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
44+
let elem =
45+
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
46+
MatchPair::new(place.clone_project(elem), subpattern, self)
47+
}));
48+
49+
if let Some(subslice_pat) = opt_slice {
50+
let suffix_len = suffix.len() as u64;
51+
let subslice = place.clone_project(PlaceElem::Subslice {
52+
from: prefix.len() as u64,
53+
to: if exact_size { min_length - suffix_len } else { suffix_len },
54+
from_end: !exact_size,
55+
});
56+
match_pairs.push(MatchPair::new(subslice, subslice_pat, self));
57+
}
58+
59+
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
60+
let end_offset = (idx + 1) as u64;
61+
let elem = ProjectionElem::ConstantIndex {
62+
offset: if exact_size { min_length - end_offset } else { end_offset },
63+
min_length,
64+
from_end: !exact_size,
65+
};
66+
let place = place.clone_project(elem);
67+
MatchPair::new(place, subpattern, self)
68+
}));
69+
}
70+
}
71+
72+
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
73+
/// Recursively builds a `MatchPair` tree for the given pattern and its
74+
/// subpatterns.
75+
pub(in crate::build) fn new(
76+
mut place_builder: PlaceBuilder<'tcx>,
77+
pattern: &'pat Pat<'tcx>,
78+
cx: &mut Builder<'_, 'tcx>,
79+
) -> MatchPair<'pat, 'tcx> {
80+
// Force the place type to the pattern's type.
81+
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
82+
if let Some(resolved) = place_builder.resolve_upvar(cx) {
83+
place_builder = resolved;
84+
}
85+
86+
// Only add the OpaqueCast projection if the given place is an opaque type and the
87+
// expected type from the pattern is not.
88+
let may_need_cast = match place_builder.base() {
89+
PlaceBase::Local(local) => {
90+
let ty =
91+
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty;
92+
ty != pattern.ty && ty.has_opaque_types()
93+
}
94+
_ => true,
95+
};
96+
if may_need_cast {
97+
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
98+
}
99+
100+
let place = place_builder.try_to_place(cx);
101+
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
102+
let mut subpairs = Vec::new();
103+
let test_case = match pattern.kind {
104+
PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
105+
106+
PatKind::Or { ref pats } => TestCase::Or {
107+
pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
108+
},
109+
110+
PatKind::Range(ref range) => {
111+
if range.is_full_range(cx.tcx) == Some(true) {
112+
default_irrefutable()
113+
} else {
114+
TestCase::Range(range)
115+
}
116+
}
117+
118+
PatKind::Constant { value } => TestCase::Constant { value },
119+
120+
PatKind::AscribeUserType {
121+
ascription: thir::Ascription { ref annotation, variance },
122+
ref subpattern,
123+
..
124+
} => {
125+
// Apply the type ascription to the value at `match_pair.place`
126+
let ascription = place.map(|source| super::Ascription {
127+
annotation: annotation.clone(),
128+
source,
129+
variance,
130+
});
131+
132+
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
133+
TestCase::Irrefutable { ascription, binding: None }
134+
}
135+
136+
PatKind::Binding { mode, var, ref subpattern, .. } => {
137+
let binding = place.map(|source| super::Binding {
138+
span: pattern.span,
139+
source,
140+
var_id: var,
141+
binding_mode: mode,
142+
});
143+
144+
if let Some(subpattern) = subpattern.as_ref() {
145+
// this is the `x @ P` case; have to keep matching against `P` now
146+
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
147+
}
148+
TestCase::Irrefutable { ascription: None, binding }
149+
}
150+
151+
PatKind::InlineConstant { subpattern: ref pattern, def, .. } => {
152+
// Apply a type ascription for the inline constant to the value at `match_pair.place`
153+
let ascription = place.map(|source| {
154+
let span = pattern.span;
155+
let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
156+
let args = ty::InlineConstArgs::new(
157+
cx.tcx,
158+
ty::InlineConstArgsParts {
159+
parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
160+
ty: cx.infcx.next_ty_var(span),
161+
},
162+
)
163+
.args;
164+
let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
165+
def.to_def_id(),
166+
ty::UserArgs { args, user_self_ty: None },
167+
));
168+
let annotation = ty::CanonicalUserTypeAnnotation {
169+
inferred_ty: pattern.ty,
170+
span,
171+
user_ty: Box::new(user_ty),
172+
};
173+
super::Ascription { annotation, source, variance: ty::Contravariant }
174+
});
175+
176+
subpairs.push(MatchPair::new(place_builder, pattern, cx));
177+
TestCase::Irrefutable { ascription, binding: None }
178+
}
179+
180+
PatKind::Array { ref prefix, ref slice, ref suffix } => {
181+
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
182+
default_irrefutable()
183+
}
184+
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
185+
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
186+
187+
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
188+
default_irrefutable()
189+
} else {
190+
TestCase::Slice {
191+
len: prefix.len() + suffix.len(),
192+
variable_length: slice.is_some(),
193+
}
194+
}
195+
}
196+
197+
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
198+
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
199+
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
200+
201+
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
202+
i == variant_index || {
203+
(cx.tcx.features().exhaustive_patterns
204+
|| cx.tcx.features().min_exhaustive_patterns)
205+
&& !v
206+
.inhabited_predicate(cx.tcx, adt_def)
207+
.instantiate(cx.tcx, args)
208+
.apply_ignore_module(cx.tcx, cx.param_env)
209+
}
210+
}) && (adt_def.did().is_local()
211+
|| !adt_def.is_variant_list_non_exhaustive());
212+
if irrefutable {
213+
default_irrefutable()
214+
} else {
215+
TestCase::Variant { adt_def, variant_index }
216+
}
217+
}
218+
219+
PatKind::Leaf { ref subpatterns } => {
220+
subpairs = cx.field_match_pairs(place_builder, subpatterns);
221+
default_irrefutable()
222+
}
223+
224+
PatKind::Deref { ref subpattern } => {
225+
subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx));
226+
default_irrefutable()
227+
}
228+
229+
PatKind::DerefPattern { ref subpattern, mutability } => {
230+
// Create a new temporary for each deref pattern.
231+
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
232+
let temp = cx.temp(
233+
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
234+
pattern.span,
235+
);
236+
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
237+
TestCase::Deref { temp, mutability }
238+
}
239+
240+
PatKind::Never => TestCase::Never,
241+
};
242+
243+
MatchPair { place, test_case, subpairs, pattern }
244+
}
245+
}

compiler/rustc_mir_build/src/build/matches/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use tracing::{debug, instrument};
2424
use util::visit_bindings;
2525

2626
// helper functions, broken out by category:
27+
mod match_pair;
2728
mod simplify;
2829
mod test;
2930
mod util;

0 commit comments

Comments
 (0)