Skip to content

Commit b7f6d72

Browse files
committed
Rollup merge of rust-lang#29776 - nikomatsakis:mir-29740, r=nrc
In previous PRs, I changed the match desugaring to generate more efficient code for ints/chars and the like. But this doesn't help when you're matching strings, ranges, or other crazy complex things (leading to rust-lang#29740). This commit restructures match desugaring *yet again* to handle that case better -- basically we now degenerate to an if-else-if chain in such cases. ~~Note that this builds on rust-lang#29763 which will hopefully land soon. So ignore the first few commits.~~ landed now r? @Aatch since he's been reviewing the other commits in this series
2 parents 35decad + a5e3625 commit b7f6d72

File tree

3 files changed

+535
-48
lines changed

3 files changed

+535
-48
lines changed

src/librustc_mir/build/matches/mod.rs

+184-23
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
8585

8686
// this will generate code to test discriminant_lvalue and
8787
// branch to the appropriate arm block
88-
self.match_candidates(span, &mut arm_blocks, candidates, block);
88+
let otherwise = self.match_candidates(span, &mut arm_blocks, candidates, block);
89+
90+
// because all matches are exhaustive, in principle we expect
91+
// an empty vector to be returned here, but the algorithm is
92+
// not entirely precise
93+
if !otherwise.is_empty() {
94+
let join_block = self.join_otherwise_blocks(otherwise);
95+
self.panic(join_block);
96+
}
8997

9098
// all the arm blocks will rejoin here
9199
let end_block = self.cfg.start_new_block();
@@ -279,11 +287,32 @@ struct Test<'tcx> {
279287
// Main matching algorithm
280288

281289
impl<'a,'tcx> Builder<'a,'tcx> {
290+
/// The main match algorithm. It begins with a set of candidates
291+
/// `candidates` and has the job of generating code to determine
292+
/// which of these candidates, if any, is the correct one. The
293+
/// candidates are sorted in inverse priority -- so the last item
294+
/// in the list has highest priority. When a candidate is found to
295+
/// match the value, we will generate a branch to the appropriate
296+
/// block found in `arm_blocks`.
297+
///
298+
/// The return value is a list of "otherwise" blocks. These are
299+
/// points in execution where we found that *NONE* of the
300+
/// candidates apply. In principle, this means that the input
301+
/// list was not exhaustive, though at present we sometimes are
302+
/// not smart enough to recognize all exhaustive inputs.
303+
///
304+
/// It might be surprising that the input can be inexhaustive.
305+
/// Indeed, initially, it is not, because all matches are
306+
/// exhaustive in Rust. But during processing we sometimes divide
307+
/// up the list of candidates and recurse with a non-exhaustive
308+
/// list. This is important to keep the size of the generated code
309+
/// under control. See `test_candidates` for more details.
282310
fn match_candidates<'pat>(&mut self,
283311
span: Span,
284312
arm_blocks: &mut ArmBlocks,
285313
mut candidates: Vec<Candidate<'pat, 'tcx>>,
286314
mut block: BasicBlock)
315+
-> Vec<BasicBlock>
287316
{
288317
debug!("matched_candidate(span={:?}, block={:?}, candidates={:?})",
289318
span, block, candidates);
@@ -311,17 +340,127 @@ impl<'a,'tcx> Builder<'a,'tcx> {
311340
} else {
312341
// if None is returned, then any remaining candidates
313342
// are unreachable (at least not through this path).
314-
return;
343+
return vec![];
315344
}
316345
}
317346

318347
// If there are no candidates that still need testing, we're done.
319348
// Since all matches are exhaustive, execution should never reach this point.
320349
if candidates.is_empty() {
321-
return self.panic(block);
350+
return vec![block];
351+
}
352+
353+
// Test candidates where possible.
354+
let (otherwise, tested_candidates) =
355+
self.test_candidates(span, arm_blocks, &candidates, block);
356+
357+
// If the target candidates were exhaustive, then we are done.
358+
if otherwise.is_empty() {
359+
return vec![];
360+
}
361+
362+
// If all candidates were sorted into `target_candidates` somewhere, then
363+
// the initial set was inexhaustive.
364+
let untested_candidates = candidates.len() - tested_candidates;
365+
if untested_candidates == 0 {
366+
return otherwise;
322367
}
323368

324-
// otherwise, extract the next match pair and construct tests
369+
// Otherwise, let's process those remaining candidates.
370+
let join_block = self.join_otherwise_blocks(otherwise);
371+
candidates.truncate(untested_candidates);
372+
self.match_candidates(span, arm_blocks, candidates, join_block)
373+
}
374+
375+
fn join_otherwise_blocks(&mut self,
376+
otherwise: Vec<BasicBlock>)
377+
-> BasicBlock
378+
{
379+
if otherwise.len() == 1 {
380+
otherwise[0]
381+
} else {
382+
let join_block = self.cfg.start_new_block();
383+
for block in otherwise {
384+
self.cfg.terminate(block, Terminator::Goto { target: join_block });
385+
}
386+
join_block
387+
}
388+
}
389+
390+
/// This is the most subtle part of the matching algorithm. At
391+
/// this point, the input candidates have been fully simplified,
392+
/// and so we know that all remaining match-pairs require some
393+
/// sort of test. To decide what test to do, we take the highest
394+
/// priority candidate (last one in the list) and extract the
395+
/// first match-pair from the list. From this we decide what kind
396+
/// of test is needed using `test`, defined in the `test` module.
397+
///
398+
/// *Note:* taking the first match pair is somewhat arbitrary, and
399+
/// we might do better here by choosing more carefully what to
400+
/// test.
401+
///
402+
/// For example, consider the following possible match-pairs:
403+
///
404+
/// 1. `x @ Some(P)` -- we will do a `Switch` to decide what variant `x` has
405+
/// 2. `x @ 22` -- we will do a `SwitchInt`
406+
/// 3. `x @ 3..5` -- we will do a range test
407+
/// 4. etc.
408+
///
409+
/// Once we know what sort of test we are going to perform, this
410+
/// test may also help us with other candidates. So we walk over
411+
/// the candidates (from high to low priority) and check. This
412+
/// gives us, for each outcome of the test, a transformed list of
413+
/// candidates. For example, if we are testing the current
414+
/// variant of `x.0`, and we have a candidate `{x.0 @ Some(v), x.1
415+
/// @ 22}`, then we would have a resulting candidate of `{(x.0 as
416+
/// Some).0 @ v, x.1 @ 22}`. Note that the first match-pair is now
417+
/// simpler (and, in fact, irrefutable).
418+
///
419+
/// But there may also be candidates that the test just doesn't
420+
/// apply to. For example, consider the case of #29740:
421+
///
422+
/// ```rust
423+
/// match x {
424+
/// "foo" => ...,
425+
/// "bar" => ...,
426+
/// "baz" => ...,
427+
/// _ => ...,
428+
/// }
429+
/// ```
430+
///
431+
/// Here the match-pair we are testing will be `x @ "foo"`, and we
432+
/// will generate an `Eq` test. Because `"bar"` and `"baz"` are different
433+
/// constants, we will decide that these later candidates are just not
434+
/// informed by the eq test. So we'll wind up with three candidate sets:
435+
///
436+
/// - If outcome is that `x == "foo"` (one candidate, derived from `x @ "foo"`)
437+
/// - If outcome is that `x != "foo"` (empty list of candidates)
438+
/// - Otherwise (three candidates, `x @ "bar"`, `x @ "baz"`, `x @
439+
/// _`). Here we have the invariant that everything in the
440+
/// otherwise list is of **lower priority** than the stuff in the
441+
/// other lists.
442+
///
443+
/// So we'll compile the test. For each outcome of the test, we
444+
/// recursively call `match_candidates` with the corresponding set
445+
/// of candidates. But note that this set is now inexhaustive: for
446+
/// example, in the case where the test returns false, there are
447+
/// NO candidates, even though there is stll a value to be
448+
/// matched. So we'll collect the return values from
449+
/// `match_candidates`, which are the blocks where control-flow
450+
/// goes if none of the candidates matched. At this point, we can
451+
/// continue with the "otherwise" list.
452+
///
453+
/// If you apply this to the above test, you basically wind up
454+
/// with an if-else-if chain, testing each candidate in turn,
455+
/// which is precisely what we want.
456+
fn test_candidates<'pat>(&mut self,
457+
span: Span,
458+
arm_blocks: &mut ArmBlocks,
459+
candidates: &[Candidate<'pat, 'tcx>],
460+
block: BasicBlock)
461+
-> (Vec<BasicBlock>, usize)
462+
{
463+
// extract the match-pair from the highest priority candidate
325464
let match_pair = &candidates.last().unwrap().match_pairs[0];
326465
let mut test = self.test(match_pair);
327466

@@ -331,35 +470,57 @@ impl<'a,'tcx> Builder<'a,'tcx> {
331470
// available
332471
match test.kind {
333472
TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
334-
for candidate in &candidates {
335-
self.add_cases_to_switch(&match_pair.lvalue,
336-
candidate,
337-
switch_ty,
338-
options,
339-
indices);
473+
for candidate in candidates.iter().rev() {
474+
if !self.add_cases_to_switch(&match_pair.lvalue,
475+
candidate,
476+
switch_ty,
477+
options,
478+
indices) {
479+
break;
480+
}
340481
}
341482
}
342483
_ => { }
343484
}
344485

486+
// perform the test, branching to one of N blocks. For each of
487+
// those N possible outcomes, create a (initially empty)
488+
// vector of candidates. Those are the candidates that still
489+
// apply if the test has that particular outcome.
345490
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
346491
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
347-
348492
let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect();
349493

350-
for candidate in &candidates {
351-
self.sort_candidate(&match_pair.lvalue,
352-
&test,
353-
candidate,
354-
&mut target_candidates);
355-
}
356-
357-
for (target_block, target_candidates) in
494+
// Sort the candidates into the appropriate vector in
495+
// `target_candidates`. Note that at some point we may
496+
// encounter a candidate where the test is not relevant; at
497+
// that point, we stop sorting.
498+
let tested_candidates =
499+
candidates.iter()
500+
.rev()
501+
.take_while(|c| self.sort_candidate(&match_pair.lvalue,
502+
&test,
503+
c,
504+
&mut target_candidates))
505+
.count();
506+
assert!(tested_candidates > 0); // at least the last candidate ought to be tested
507+
508+
// For each outcome of test, process the candidates that still
509+
// apply. Collect a list of blocks where control flow will
510+
// branch if one of the `target_candidate` sets is not
511+
// exhaustive.
512+
let otherwise: Vec<_> =
358513
target_blocks.into_iter()
359-
.zip(target_candidates.into_iter())
360-
{
361-
self.match_candidates(span, arm_blocks, target_candidates, target_block);
362-
}
514+
.zip(target_candidates)
515+
.flat_map(|(target_block, target_candidates)| {
516+
self.match_candidates(span,
517+
arm_blocks,
518+
target_candidates,
519+
target_block)
520+
})
521+
.collect();
522+
523+
(otherwise, tested_candidates)
363524
}
364525

365526
/// Initializes each of the bindings from the candidate by

src/librustc_mir/build/matches/test.rs

+33-25
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
105105
switch_ty: Ty<'tcx>,
106106
options: &mut Vec<ConstVal>,
107107
indices: &mut FnvHashMap<ConstVal, usize>)
108+
-> bool
108109
{
109110
let match_pair = match candidate.match_pairs.iter().find(|mp| mp.lvalue == *test_lvalue) {
110111
Some(match_pair) => match_pair,
111-
_ => { return; }
112+
_ => { return false; }
112113
};
113114

114115
match *match_pair.pattern.kind {
@@ -121,11 +122,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
121122
options.push(value.clone());
122123
options.len() - 1
123124
});
125+
true
124126
}
125127

126-
PatternKind::Range { .. } => {
127-
}
128-
128+
PatternKind::Range { .. } |
129129
PatternKind::Constant { .. } |
130130
PatternKind::Variant { .. } |
131131
PatternKind::Slice { .. } |
@@ -134,6 +134,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
134134
PatternKind::Binding { .. } |
135135
PatternKind::Leaf { .. } |
136136
PatternKind::Deref { .. } => {
137+
// don't know how to add these patterns to a switch
138+
false
137139
}
138140
}
139141
}
@@ -284,18 +286,29 @@ impl<'a,'tcx> Builder<'a,'tcx> {
284286
/// P0` to the `resulting_candidates` entry corresponding to the
285287
/// variant `Some`.
286288
///
287-
/// In many cases we will add the `candidate` to more than one
288-
/// outcome. For example, say that the test is `x == 22`, but the
289-
/// candidate is `x @ 13..55`. In that case, if the test is true,
290-
/// then we know that the candidate applies (without this match
291-
/// pair, potentially, though we don't optimize this due to
292-
/// #29623). If the test is false, the candidate may also apply
293-
/// (with the match pair, still).
289+
/// However, in some cases, the test may just not be relevant to
290+
/// candidate. For example, suppose we are testing whether `foo.x == 22`,
291+
/// but in one match arm we have `Foo { x: _, ... }`... in that case,
292+
/// the test for what value `x` has has no particular relevance
293+
/// to this candidate. In such cases, this function just returns false
294+
/// without doing anything. This is used by the overall `match_candidates`
295+
/// algorithm to structure the match as a whole. See `match_candidates` for
296+
/// more details.
297+
///
298+
/// FIXME(#29623). In some cases, we have some tricky choices to
299+
/// make. for example, if we are testing that `x == 22`, but the
300+
/// candidate is `x @ 13..55`, what should we do? In the event
301+
/// that the test is true, we know that the candidate applies, but
302+
/// in the event of false, we don't know that it *doesn't*
303+
/// apply. For now, we return false, indicate that the test does
304+
/// not apply to this candidate, but it might be we can get
305+
/// tighter match code if we do something a bit different.
294306
pub fn sort_candidate<'pat>(&mut self,
295307
test_lvalue: &Lvalue<'tcx>,
296308
test: &Test<'tcx>,
297309
candidate: &Candidate<'pat, 'tcx>,
298-
resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>]) {
310+
resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>])
311+
-> bool {
299312
// Find the match_pair for this lvalue (if any). At present,
300313
// afaik, there can be at most one. (In the future, if we
301314
// adopted a more general `@` operator, there might be more
@@ -311,7 +324,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
311324
None => {
312325
// We are not testing this lvalue. Therefore, this
313326
// candidate applies to ALL outcomes.
314-
return self.add_to_all_candidate_sets(candidate, resulting_candidates);
327+
return false;
315328
}
316329
};
317330

@@ -329,9 +342,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
329342
subpatterns,
330343
candidate);
331344
resulting_candidates[variant_index].push(new_candidate);
345+
true
332346
}
333347
_ => {
334-
self.add_to_all_candidate_sets(candidate, resulting_candidates);
348+
false
335349
}
336350
}
337351
}
@@ -349,9 +363,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
349363
let new_candidate = self.candidate_without_match_pair(match_pair_index,
350364
candidate);
351365
resulting_candidates[index].push(new_candidate);
366+
true
352367
}
353368
_ => {
354-
self.add_to_all_candidate_sets(candidate, resulting_candidates);
369+
false
355370
}
356371
}
357372
}
@@ -367,8 +382,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
367382
let new_candidate = self.candidate_without_match_pair(match_pair_index,
368383
candidate);
369384
resulting_candidates[0].push(new_candidate);
385+
true
370386
} else {
371-
self.add_to_all_candidate_sets(candidate, resulting_candidates);
387+
false
372388
}
373389
}
374390
}
@@ -392,14 +408,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
392408
}
393409
}
394410

395-
fn add_to_all_candidate_sets<'pat>(&mut self,
396-
candidate: &Candidate<'pat, 'tcx>,
397-
resulting_candidates: &mut [Vec<Candidate<'pat, 'tcx>>]) {
398-
for resulting_candidate in resulting_candidates {
399-
resulting_candidate.push(candidate.clone());
400-
}
401-
}
402-
403411
fn candidate_after_variant_switch<'pat>(&mut self,
404412
match_pair_index: usize,
405413
adt_def: ty::AdtDef<'tcx>,
@@ -447,5 +455,5 @@ impl<'a,'tcx> Builder<'a,'tcx> {
447455
}
448456

449457
fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
450-
ty.is_integral() || ty.is_char()
458+
ty.is_integral() || ty.is_char() || ty.is_bool()
451459
}

0 commit comments

Comments
 (0)