Skip to content

Commit 568ca85

Browse files
committed
Auto merge of #119031 - Nadrieril:two-phase-match-lowering, r=<try>
[Experiment] Play with match lowering Match lowering to MIR has the reputation of being the most intricate piece of the compiler, and after banging my head on it for a bit I sure hope there isn't anything more intricate somewhere else. It's good quality code but I hope to unentangle it. This PR is me wrestling with it and asking `rustc-timer` for its opinion. r? `@ghost`
2 parents d9d89fd + b005fea commit 568ca85

File tree

4 files changed

+125
-149
lines changed

4 files changed

+125
-149
lines changed

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

+43-10
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
2222
use rustc_span::symbol::Symbol;
2323
use rustc_span::{BytePos, Pos, Span};
2424
use rustc_target::abi::VariantIdx;
25-
use smallvec::{smallvec, SmallVec};
26-
2725
// helper functions, broken out by category:
2826
mod simplify;
2927
mod test;
@@ -951,12 +949,15 @@ struct Candidate<'pat, 'tcx> {
951949
has_guard: bool,
952950

953951
/// All of these must be satisfied...
954-
match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
952+
// Invariant: all the `MatchPair`s are recursively simplified.
953+
match_pairs: Vec<MatchPair<'pat, 'tcx>>,
955954

956955
/// ...these bindings established...
956+
// Invariant: not mutated during match tree construction.
957957
bindings: Vec<Binding<'tcx>>,
958958

959959
/// ...and these types asserted...
960+
// Invariant: not mutated during match tree construction.
960961
ascriptions: Vec<Ascription<'tcx>>,
961962

962963
/// ...and if this is non-empty, one of these subcandidates also has to match...
@@ -976,19 +977,27 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
976977
place: PlaceBuilder<'tcx>,
977978
pattern: &'pat Pat<'tcx>,
978979
has_guard: bool,
979-
cx: &Builder<'_, 'tcx>,
980+
cx: &mut Builder<'_, 'tcx>,
980981
) -> Self {
981-
Candidate {
982+
let mut candidate = Candidate {
982983
span: pattern.span,
983984
has_guard,
984-
match_pairs: smallvec![MatchPair::new(place, pattern, cx)],
985+
match_pairs: vec![MatchPair::new(place, pattern, cx)],
985986
bindings: Vec::new(),
986987
ascriptions: Vec::new(),
987988
subcandidates: Vec::new(),
988989
otherwise_block: None,
989990
pre_binding_block: None,
990991
next_candidate_pre_binding_block: None,
991-
}
992+
};
993+
994+
cx.simplify_candidate(
995+
&mut candidate.bindings,
996+
&mut candidate.ascriptions,
997+
&mut candidate.match_pairs,
998+
);
999+
1000+
candidate
9921001
}
9931002

9941003
/// Visit the leaf candidates (those with no subcandidates) contained in
@@ -1044,13 +1053,17 @@ struct Ascription<'tcx> {
10441053
variance: ty::Variance,
10451054
}
10461055

1047-
#[derive(Clone, Debug)]
1056+
#[derive(Debug)]
10481057
pub(crate) struct MatchPair<'pat, 'tcx> {
1049-
// this place...
1058+
// This place...
10501059
place: PlaceBuilder<'tcx>,
10511060

10521061
// ... must match this pattern.
1062+
// Invariant: this pattern must be simplified, i.e. requires a test.
10531063
pattern: &'pat Pat<'tcx>,
1064+
1065+
/// Precomputed sub-match pairs.
1066+
subpairs: Vec<Self>,
10541067
}
10551068

10561069
/// See [`Test`] for more.
@@ -1172,7 +1185,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
11721185
// be a switch or pattern comparison.
11731186
let mut split_or_candidate = false;
11741187
for candidate in &mut *candidates {
1175-
split_or_candidate |= self.simplify_candidate(candidate);
1188+
split_or_candidate |= {
1189+
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] =
1190+
&*candidate.match_pairs
1191+
{
1192+
// Split a candidate in which the only match-pair is an or-pattern into multiple
1193+
// candidates. This is so that
1194+
//
1195+
// match x {
1196+
// 0 | 1 => { ... },
1197+
// 2 | 3 => { ... },
1198+
// }
1199+
//
1200+
// only generates a single switch.
1201+
candidate.subcandidates =
1202+
self.create_or_subcandidates(place, pats, candidate.has_guard);
1203+
candidate.match_pairs.pop();
1204+
true
1205+
} else {
1206+
false
1207+
}
1208+
};
11761209
}
11771210

11781211
ensure_sufficient_stack(|| {

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

+70-60
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,20 @@
1515
use crate::build::expr::as_place::PlaceBuilder;
1616
use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
1717
use crate::build::Builder;
18+
use rustc_middle::mir::PlaceElem;
1819
use rustc_middle::thir::{self, *};
19-
2020
use std::mem;
2121

2222
impl<'a, 'tcx> Builder<'a, 'tcx> {
2323
/// Simplify a candidate so that all match pairs require a test.
24-
///
25-
/// This method will also split a candidate, in which the only
26-
/// match-pair is an or-pattern, into multiple candidates.
27-
/// This is so that
28-
///
29-
/// match x {
30-
/// 0 | 1 => { ... },
31-
/// 2 | 3 => { ... },
32-
/// }
33-
///
34-
/// only generates a single switch. If this happens this method returns
35-
/// `true`.
36-
#[instrument(skip(self, candidate), level = "debug")]
24+
#[instrument(skip(self), level = "debug")]
3725
pub(super) fn simplify_candidate<'pat>(
3826
&mut self,
39-
candidate: &mut Candidate<'pat, 'tcx>,
40-
) -> bool {
27+
candidate_bindings: &mut Vec<Binding<'tcx>>,
28+
candidate_ascriptions: &mut Vec<Ascription<'tcx>>,
29+
candidate_match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
30+
) {
4131
// repeatedly simplify match pairs until fixed point is reached
42-
debug!("{candidate:#?}");
4332

4433
// existing_bindings and new_bindings exists to keep the semantics in order.
4534
// Reversing the binding order for bindings after `@` changes the binding order in places
@@ -59,28 +48,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
5948
// binding in iter 2: [6, 7]
6049
//
6150
// final binding: [1, 2, 3, 6, 7, 4, 5]
62-
let mut existing_bindings = mem::take(&mut candidate.bindings);
51+
let mut existing_bindings = mem::take(candidate_bindings);
6352
let mut new_bindings = Vec::new();
6453
loop {
65-
let match_pairs = mem::take(&mut candidate.match_pairs);
54+
let mut match_pairs = mem::take(candidate_match_pairs);
6655

67-
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] =
68-
&*match_pairs
56+
if let [MatchPair { pattern: Pat { kind: PatKind::Or { .. }, .. }, .. }] = &*match_pairs
6957
{
7058
existing_bindings.extend_from_slice(&new_bindings);
71-
mem::swap(&mut candidate.bindings, &mut existing_bindings);
72-
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
73-
return true;
59+
mem::swap(candidate_bindings, &mut existing_bindings);
60+
mem::swap(candidate_match_pairs, &mut match_pairs);
61+
return;
7462
}
7563

7664
let mut changed = false;
7765
for match_pair in match_pairs {
78-
match self.simplify_match_pair(match_pair, candidate) {
66+
match self.simplify_match_pair(
67+
match_pair,
68+
candidate_bindings,
69+
candidate_ascriptions,
70+
candidate_match_pairs,
71+
) {
7972
Ok(()) => {
8073
changed = true;
8174
}
8275
Err(match_pair) => {
83-
candidate.match_pairs.push(match_pair);
76+
candidate_match_pairs.push(match_pair);
8477
}
8578
}
8679
}
@@ -97,38 +90,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
9790
// let z = x.copy_field;
9891
// let y = x;
9992
// }
100-
candidate.bindings.extend_from_slice(&new_bindings);
101-
mem::swap(&mut candidate.bindings, &mut new_bindings);
102-
candidate.bindings.clear();
93+
candidate_bindings.extend_from_slice(&new_bindings);
94+
mem::swap(candidate_bindings, &mut new_bindings);
95+
candidate_bindings.clear();
10396

10497
if !changed {
10598
existing_bindings.extend_from_slice(&new_bindings);
106-
mem::swap(&mut candidate.bindings, &mut existing_bindings);
99+
mem::swap(candidate_bindings, &mut existing_bindings);
107100
// Move or-patterns to the end, because they can result in us
108101
// creating additional candidates, so we want to test them as
109102
// late as possible.
110-
candidate
111-
.match_pairs
103+
candidate_match_pairs
112104
.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
113-
debug!(simplified = ?candidate, "simplify_candidate");
114-
return false; // if we were not able to simplify any, done.
105+
debug!(simplified = ?candidate_match_pairs, "simplify_candidate");
106+
return; // if we were not able to simplify any, done.
115107
}
116108
}
117109
}
118110

119111
/// Given `candidate` that has a single or-pattern for its match-pairs,
120112
/// creates a fresh candidate for each of its input subpatterns passed via
121113
/// `pats`.
122-
fn create_or_subcandidates<'pat>(
114+
pub(super) fn create_or_subcandidates<'pat>(
123115
&mut self,
124-
candidate: &Candidate<'pat, 'tcx>,
125116
place: &PlaceBuilder<'tcx>,
126117
pats: &'pat [Box<Pat<'tcx>>],
118+
has_guard: bool,
127119
) -> Vec<Candidate<'pat, 'tcx>> {
128120
pats.iter()
129121
.map(|box pat| {
130-
let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self);
131-
self.simplify_candidate(&mut candidate);
122+
let mut candidate = Candidate::new(place.clone(), pat, has_guard, self);
123+
if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] =
124+
&*candidate.match_pairs
125+
{
126+
candidate.subcandidates =
127+
self.create_or_subcandidates(place, pats, candidate.has_guard);
128+
candidate.match_pairs.pop();
129+
}
132130
candidate
133131
})
134132
.collect()
@@ -141,8 +139,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
141139
/// candidate.
142140
fn simplify_match_pair<'pat>(
143141
&mut self,
144-
match_pair: MatchPair<'pat, 'tcx>,
145-
candidate: &mut Candidate<'pat, 'tcx>,
142+
mut match_pair: MatchPair<'pat, 'tcx>,
143+
bindings: &mut Vec<Binding<'tcx>>,
144+
ascriptions: &mut Vec<Ascription<'tcx>>,
145+
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
146146
) -> Result<(), MatchPair<'pat, 'tcx>> {
147147
match match_pair.pattern.kind {
148148
PatKind::AscribeUserType {
@@ -151,14 +151,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
151151
} => {
152152
// Apply the type ascription to the value at `match_pair.place`, which is the
153153
if let Some(source) = match_pair.place.try_to_place(self) {
154-
candidate.ascriptions.push(Ascription {
154+
ascriptions.push(Ascription {
155155
annotation: annotation.clone(),
156156
source,
157157
variance,
158158
});
159159
}
160160

161-
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
161+
match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
162162

163163
Ok(())
164164
}
@@ -178,7 +178,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
178178
is_primary: _,
179179
} => {
180180
if let Some(source) = match_pair.place.try_to_place(self) {
181-
candidate.bindings.push(Binding {
181+
bindings.push(Binding {
182182
span: match_pair.pattern.span,
183183
source,
184184
var_id: var,
@@ -188,7 +188,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
188188

189189
if let Some(subpattern) = subpattern.as_ref() {
190190
// this is the `x @ P` case; have to keep matching against `P` now
191-
candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
191+
match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
192192
}
193193

194194
Ok(())
@@ -206,7 +206,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
206206
}
207207

208208
PatKind::InlineConstant { subpattern: ref pattern, def: _ } => {
209-
candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
209+
match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
210210

211211
Ok(())
212212
}
@@ -222,15 +222,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
222222
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
223223
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
224224
// irrefutable
225+
self.prefix_slice_suffix(match_pairs, &match_pair.place, prefix, slice, suffix);
226+
Ok(())
227+
} else {
225228
self.prefix_slice_suffix(
226-
&mut candidate.match_pairs,
229+
&mut match_pair.subpairs,
227230
&match_pair.place,
228231
prefix,
229232
slice,
230233
suffix,
231234
);
232-
Ok(())
233-
} else {
235+
self.simplify_candidate(bindings, ascriptions, &mut match_pair.subpairs);
234236
Err(match_pair)
235237
}
236238
}
@@ -248,35 +250,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
248250
|| !adt_def.is_variant_list_non_exhaustive());
249251
if irrefutable {
250252
let place_builder = match_pair.place.downcast(adt_def, variant_index);
251-
candidate
252-
.match_pairs
253-
.extend(self.field_match_pairs(place_builder, subpatterns));
253+
match_pairs.extend(self.field_match_pairs(place_builder, subpatterns));
254254
Ok(())
255255
} else {
256+
// If we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
257+
// we want to create a set of derived match-patterns like
258+
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
259+
let downcast_place = match_pair.place.clone().downcast(adt_def, variant_index); // `(x as Variant)`
260+
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
261+
// e.g., `(x as Variant).0`
262+
let place = downcast_place.clone_project(PlaceElem::Field(
263+
subpattern.field,
264+
subpattern.pattern.ty,
265+
));
266+
// e.g., `(x as Variant).0 @ P1`
267+
MatchPair::new(place, &subpattern.pattern, self)
268+
});
269+
270+
match_pair.subpairs.extend(consequent_match_pairs);
271+
self.simplify_candidate(bindings, ascriptions, &mut match_pair.subpairs);
256272
Err(match_pair)
257273
}
258274
}
259275

260276
PatKind::Array { ref prefix, ref slice, ref suffix } => {
261-
self.prefix_slice_suffix(
262-
&mut candidate.match_pairs,
263-
&match_pair.place,
264-
prefix,
265-
slice,
266-
suffix,
267-
);
277+
self.prefix_slice_suffix(match_pairs, &match_pair.place, prefix, slice, suffix);
268278
Ok(())
269279
}
270280

271281
PatKind::Leaf { ref subpatterns } => {
272282
// tuple struct, match subpats (if any)
273-
candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
283+
match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
274284
Ok(())
275285
}
276286

277287
PatKind::Deref { ref subpattern } => {
278288
let place_builder = match_pair.place.deref();
279-
candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self));
289+
match_pairs.push(MatchPair::new(place_builder, subpattern, self));
280290
Ok(())
281291
}
282292

0 commit comments

Comments
 (0)