Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0471b32

Browse files
committedNov 11, 2015
Reorganize match construction to be O(n) instead of O(n^2). Whereas
before we iterated over the test and each outcome thereof, and then checked processed every candidate against this outcome, we now organize the walk differently. Instead, we visit each candidate and say "Here is the test being performed. Figure out the resulting candidates for each possible outcome and add yourself into the appropriate places."
1 parent 61884e5 commit 0471b32

File tree

2 files changed

+160
-233
lines changed

2 files changed

+160
-233
lines changed
 

‎src/librustc_mir/build/matches/mod.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -345,17 +345,20 @@ impl<'a,'tcx> Builder<'a,'tcx> {
345345
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
346346
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
347347

348-
for (outcome, target_block) in target_blocks.into_iter().enumerate() {
349-
let applicable_candidates: Vec<_> =
350-
candidates.iter()
351-
.filter_map(|candidate| {
352-
self.candidate_under_assumption(&match_pair.lvalue,
353-
&test.kind,
354-
outcome,
355-
candidate)
356-
})
357-
.collect();
358-
self.match_candidates(span, arm_blocks, applicable_candidates, target_block);
348+
let mut target_candidates: Vec<_> = (0..target_blocks.len()).map(|_| vec![]).collect();
349+
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
358+
target_blocks.into_iter()
359+
.zip(target_candidates.into_iter())
360+
{
361+
self.match_candidates(span, arm_blocks, target_candidates, target_block);
359362
}
360363
}
361364

‎src/librustc_mir/build/matches/test.rs

Lines changed: 146 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use hair::*;
2121
use repr::*;
2222
use rustc_data_structures::fnv::FnvHashMap;
2323
use rustc::middle::const_eval::ConstVal;
24-
use rustc::middle::ty::Ty;
24+
use rustc::middle::ty::{self, Ty};
2525
use syntax::codemap::Span;
2626

2727
impl<'a,'tcx> Builder<'a,'tcx> {
@@ -271,248 +271,172 @@ impl<'a,'tcx> Builder<'a,'tcx> {
271271
target_blocks
272272
}
273273

274-
/// Given a candidate and the outcome of a test we have performed,
275-
/// transforms the candidate into a new candidate that reflects
276-
/// further tests still needed. Returns `None` if this candidate
277-
/// has now been ruled out.
274+
/// Given that we are performing `test` against `test_lvalue`,
275+
/// this job sorts out what the status of `candidate` will be
276+
/// after the test. The `resulting_candidates` vector stores, for
277+
/// each possible outcome of `test`, a vector of the candidates
278+
/// that will result. This fn should add a (possibly modified)
279+
/// clone of candidate into `resulting_candidates` wherever
280+
/// appropriate.
278281
///
279-
/// For example, if a candidate included the patterns `[x.0 @
280-
/// Ok(P1), x.1 @ 22]`, and we did a switch test on `x.0` and
281-
/// found the variant `Err` (as indicated by the `test_outcome`
282-
/// parameter), we would return `None`. But if the test_outcome
283-
/// were `Ok`, we would return `Some([x.0.downcast<Ok>.0 @ P1, x.1
284-
/// @ 22])`.
285-
pub fn candidate_under_assumption<'pat>(&mut self,
286-
test_lvalue: &Lvalue<'tcx>,
287-
test_kind: &TestKind<'tcx>,
288-
test_outcome: usize,
289-
candidate: &Candidate<'pat, 'tcx>)
290-
-> Option<Candidate<'pat, 'tcx>> {
291-
let result = self.match_pairs_under_assumption(test_lvalue,
292-
test_kind,
293-
test_outcome,
294-
&candidate.match_pairs);
295-
match result {
296-
Some(match_pairs) => Some(Candidate { match_pairs: match_pairs,
297-
bindings: candidate.bindings.clone(),
298-
guard: candidate.guard.clone(),
299-
arm_index: candidate.arm_index }),
300-
None => None,
301-
}
302-
}
303-
304-
/// Helper for candidate_under_assumption that does the actual
305-
/// work of transforming the list of match pairs.
306-
fn match_pairs_under_assumption<'pat>(&mut self,
307-
test_lvalue: &Lvalue<'tcx>,
308-
test_kind: &TestKind<'tcx>,
309-
test_outcome: usize,
310-
match_pairs: &[MatchPair<'pat, 'tcx>])
311-
-> Option<Vec<MatchPair<'pat, 'tcx>>> {
312-
let mut result = vec![];
313-
314-
for match_pair in match_pairs {
315-
// If the match pair is either:
316-
// (1) testing a different lvalue; or,
317-
// (2) the test doesn't tell us anything about this match-pair,
318-
// then we have to retain it as for after the test is complete.
319-
if
320-
match_pair.lvalue != *test_lvalue || // (1)
321-
!self.test_informs_match_pair(match_pair, test_kind, test_outcome) // (2)
322-
{
323-
result.push(match_pair.clone());
324-
continue;
325-
}
326-
327-
// otherwise, build up the consequence match pairs
328-
let opt_consequent_match_pairs =
329-
self.consequent_match_pairs_under_assumption(match_pair,
330-
test_kind,
331-
test_outcome);
332-
match opt_consequent_match_pairs {
333-
None => {
334-
// Right kind of test, but wrong outcome. That
335-
// means this **entire candidate** is
336-
// inapplicable, since the candidate is only
337-
// applicable if all of its match-pairs apply (and
338-
// this one doesn't).
339-
return None;
340-
}
341-
342-
Some(consequent_match_pairs) => {
343-
// Test passed; add any new patterns we have to test to the final result.
344-
result.extend(consequent_match_pairs)
345-
}
282+
/// So, for example, if this candidate is `x @ Some(P0)` and the
283+
/// test is a variant test, then we would add `(x as Option).0 @
284+
/// P0` to the `resulting_candidates` entry corresponding to the
285+
/// variant `Some`.
286+
///
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).
294+
pub fn sort_candidate<'pat>(&mut self,
295+
test_lvalue: &Lvalue<'tcx>,
296+
test: &Test<'tcx>,
297+
candidate: &Candidate<'pat, 'tcx>,
298+
resulting_candidates: &mut Vec<Vec<Candidate<'pat, 'tcx>>>) {
299+
// Find the match_pair for this lvalue (if any). At present,
300+
// afaik, there can be at most one. (In the future, if we
301+
// adopted a more general `@` operator, there might be more
302+
// than one, but it'd be very unusual to have two sides that
303+
// both require tests; you'd expect one side to be simplified
304+
// away.)
305+
let tested_match_pair = candidate.match_pairs.iter()
306+
.enumerate()
307+
.filter(|&(_, mp)| mp.lvalue == *test_lvalue)
308+
.next();
309+
let (match_pair_index, match_pair) = match tested_match_pair {
310+
Some(pair) => pair,
311+
None => {
312+
// We are not testing this lvalue. Therefore, this
313+
// candidate applies to ALL outcomes.
314+
return self.add_to_all_candidate_sets(candidate, resulting_candidates);
346315
}
347-
}
348-
349-
Some(result)
350-
}
316+
};
351317

352-
/// Given that we executed `test` to `match_pair.lvalue` with
353-
/// outcome `test_outcome`, does that tell us anything about
354-
/// whether `match_pair` applies?
355-
///
356-
/// Often it does not. For example, if we are testing whether
357-
/// the discriminant equals 4, and we find that it does not,
358-
/// but the `match_pair` is testing if the discriminant equals 5,
359-
/// that does not help us.
360-
fn test_informs_match_pair<'pat>(&mut self,
361-
match_pair: &MatchPair<'pat, 'tcx>,
362-
test_kind: &TestKind<'tcx>,
363-
_test_outcome: usize)
364-
-> bool {
365-
match *match_pair.pattern.kind {
366-
PatternKind::Variant { .. } => {
367-
match *test_kind {
368-
TestKind::Switch { .. } => true,
369-
_ => false,
318+
match test.kind {
319+
// If we are performing a variant switch, then this
320+
// informs variant patterns, but nothing else.
321+
TestKind::Switch { adt_def: tested_adt_def } => {
322+
match *match_pair.pattern.kind {
323+
PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
324+
assert_eq!(adt_def, tested_adt_def);
325+
let new_candidate =
326+
self.candidate_after_variant_switch(match_pair_index,
327+
adt_def,
328+
variant_index,
329+
subpatterns,
330+
candidate);
331+
resulting_candidates[variant_index].push(new_candidate);
332+
}
333+
_ => {
334+
self.add_to_all_candidate_sets(candidate, resulting_candidates);
335+
}
370336
}
371337
}
372338

373-
PatternKind::Constant { value: Literal::Value { .. } }
374-
if is_switch_ty(match_pair.pattern.ty) => {
375-
match *test_kind {
376-
TestKind::SwitchInt { .. } => true,
377-
378-
// Did not do an integer equality test (which is always a SwitchInt).
379-
// So we learned nothing relevant to this match-pair.
380-
//
381-
// FIXME(#29623) we could use TestKind::Range to rule
382-
// things out here, in some cases.
383-
_ => false,
339+
// If we are performing a switch over integers, then this informs integer
340+
// equality, but nothing else.
341+
//
342+
// FIXME(#29623) we could use TestKind::Range to rule
343+
// things out here, in some cases.
344+
TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => {
345+
match *match_pair.pattern.kind {
346+
PatternKind::Constant { value: Literal::Value { ref value } }
347+
if is_switch_ty(match_pair.pattern.ty) => {
348+
let index = indices[value];
349+
let new_candidate = self.candidate_without_match_pair(match_pair_index,
350+
candidate);
351+
resulting_candidates[index].push(new_candidate);
352+
}
353+
_ => {
354+
self.add_to_all_candidate_sets(candidate, resulting_candidates);
355+
}
384356
}
385357
}
386358

387-
PatternKind::Constant { .. } |
388-
PatternKind::Range { .. } |
389-
PatternKind::Slice { .. } => {
359+
TestKind::Eq { .. } |
360+
TestKind::Range { .. } |
361+
TestKind::Len { .. } => {
362+
// These are all binary tests.
363+
//
364+
// FIXME(#29623) we can be more clever here
390365
let pattern_test = self.test(&match_pair);
391-
if pattern_test.kind == *test_kind {
392-
true
366+
if pattern_test.kind == test.kind {
367+
let new_candidate = self.candidate_without_match_pair(match_pair_index,
368+
candidate);
369+
resulting_candidates[0].push(new_candidate);
393370
} else {
394-
// FIXME(#29623) in all 3 cases, we could sometimes do
395-
// better here. For example, if we are checking
396-
// whether the value is equal to X, and we find
397-
// that it is, that (may) imply value is not equal
398-
// to Y. Or, if the range tested is `3..5`, and
399-
// our range is `4..5`, then we know that our
400-
// range also does not apply. Similarly, if we
401-
// test that length is >= 5, and it fails, we also
402-
// know that length is not >= 7. etc.
403-
false
371+
self.add_to_all_candidate_sets(candidate, resulting_candidates);
404372
}
405373
}
406-
407-
PatternKind::Array { .. } |
408-
PatternKind::Wild |
409-
PatternKind::Binding { .. } |
410-
PatternKind::Leaf { .. } |
411-
PatternKind::Deref { .. } => {
412-
self.error_simplifyable(&match_pair)
413-
}
414374
}
415375
}
416376

417-
/// Given that we executed `test` with outcome `test_outcome`,
418-
/// what are the resulting match pairs? This can return either:
419-
///
420-
/// - None, meaning that the test indicated that this outcome
421-
/// means that this match-pair is not the current one for the
422-
/// current discriminant (which rules out the enclosing
423-
/// candidate);
424-
/// - Some(...), meaning that either the test didn't tell us whether this
425-
/// match-pair is correct or not, or that we DID match and now have
426-
/// subsequent matches to perform.
427-
///
428-
/// As an example, consider:
429-
///
430-
/// ```
431-
/// match option {
432-
/// Ok(<pattern>) => ...,
433-
/// Err(_) => ...,
434-
/// }
435-
/// ```
436-
///
437-
/// Suppose that the `test` is a `Switch` and the outcome is
438-
/// `Ok`. Then in that case, the first arm will have a match-pair
439-
/// of `option @ Ok(<pattern>)`. In that case we will return
440-
/// `Some(vec![(option as Ok) @ <pattern>])`. The `Some` reuslt
441-
/// indicates that the match-pair still applies, and we now have
442-
/// to test `(option as Ok) @ <pattern>`.
443-
///
444-
/// On the second arm, a `None` will be returned, because if we
445-
/// observed that `option` has the discriminant `Ok`, then the
446-
/// second arm cannot apply.
447-
pub fn consequent_match_pairs_under_assumption<'pat>(&mut self,
448-
match_pair: &MatchPair<'pat, 'tcx>,
449-
test_kind: &TestKind<'tcx>,
450-
test_outcome: usize)
451-
-> Option<Vec<MatchPair<'pat, 'tcx>>> {
452-
match *match_pair.pattern.kind {
453-
PatternKind::Variant { adt_def, variant_index, ref subpatterns } => {
454-
assert!(match *test_kind { TestKind::Switch { .. } => true,
455-
_ => false });
456-
457-
if test_outcome != variant_index {
458-
return None; // Tested, but found the wrong variant.
459-
}
377+
fn candidate_without_match_pair<'pat>(&mut self,
378+
match_pair_index: usize,
379+
candidate: &Candidate<'pat, 'tcx>)
380+
-> Candidate<'pat, 'tcx> {
381+
let other_match_pairs =
382+
candidate.match_pairs.iter()
383+
.enumerate()
384+
.filter(|&(index, _)| index != match_pair_index)
385+
.map(|(_, mp)| mp.clone())
386+
.collect();
387+
Candidate {
388+
match_pairs: other_match_pairs,
389+
bindings: candidate.bindings.clone(),
390+
guard: candidate.guard.clone(),
391+
arm_index: candidate.arm_index,
392+
}
393+
}
460394

461-
// Correct variant. Extract the subitems and match
462-
// those. The lvalue goes gets downcast, so
463-
// e.g. `foo.bar` becomes `foo.bar as Variant`.
464-
let elem = ProjectionElem::Downcast(adt_def, variant_index);
465-
let downcast_lvalue = match_pair.lvalue.clone().elem(elem);
466-
let consequent_match_pairs =
467-
subpatterns.iter()
468-
.map(|subpattern| {
469-
let lvalue =
470-
downcast_lvalue.clone().field(
471-
subpattern.field);
472-
MatchPair::new(lvalue, &subpattern.pattern)
473-
})
474-
.collect();
475-
Some(consequent_match_pairs)
476-
}
395+
fn add_to_all_candidate_sets<'pat>(&mut self,
396+
candidate: &Candidate<'pat, 'tcx>,
397+
resulting_candidates: &mut Vec<Vec<Candidate<'pat, 'tcx>>>) {
398+
for resulting_candidate in resulting_candidates {
399+
resulting_candidate.push(candidate.clone());
400+
}
401+
}
477402

478-
PatternKind::Constant { value: Literal::Value { ref value } }
479-
if is_switch_ty(match_pair.pattern.ty) => {
480-
match *test_kind {
481-
TestKind::SwitchInt { switch_ty: _, options: _, ref indices } => {
482-
let index = indices[value];
483-
if index == test_outcome {
484-
Some(vec![]) // this value, nothing left to test
485-
} else {
486-
None // some other value, candidate is inapplicable
487-
}
488-
}
403+
fn candidate_after_variant_switch<'pat>(&mut self,
404+
match_pair_index: usize,
405+
adt_def: ty::AdtDef<'tcx>,
406+
variant_index: usize,
407+
subpatterns: &'pat [FieldPattern<'tcx>],
408+
candidate: &Candidate<'pat, 'tcx>)
409+
-> Candidate<'pat, 'tcx> {
410+
let match_pair = &candidate.match_pairs[match_pair_index];
411+
412+
// So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
413+
// we want to create a set of derived match-patterns like
414+
// `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
415+
let elem = ProjectionElem::Downcast(adt_def, variant_index);
416+
let downcast_lvalue = match_pair.lvalue.clone().elem(elem); // `(x as Variant)`
417+
let consequent_match_pairs =
418+
subpatterns.iter()
419+
.map(|subpattern| {
420+
// e.g., `(x as Variant).0`
421+
let lvalue = downcast_lvalue.clone().field(subpattern.field);
422+
// e.g., `(x as Variant).0 @ P1`
423+
MatchPair::new(lvalue, &subpattern.pattern)
424+
});
489425

490-
_ => {
491-
self.hir.span_bug(
492-
match_pair.pattern.span,
493-
&format!("did a switch-int, but value {:?} not found in cases",
494-
value));
495-
}
496-
}
497-
}
426+
// In addition, we need all the other match pairs from the old candidate.
427+
let other_match_pairs =
428+
candidate.match_pairs.iter()
429+
.enumerate()
430+
.filter(|&(index, _)| index != match_pair_index)
431+
.map(|(_, mp)| mp.clone());
498432

499-
PatternKind::Constant { .. } |
500-
PatternKind::Range { .. } |
501-
PatternKind::Slice { .. } => {
502-
if test_outcome == 0 {
503-
Some(vec![])
504-
} else {
505-
None
506-
}
507-
}
433+
let all_match_pairs = consequent_match_pairs.chain(other_match_pairs).collect();
508434

509-
PatternKind::Array { .. } |
510-
PatternKind::Wild |
511-
PatternKind::Binding { .. } |
512-
PatternKind::Leaf { .. } |
513-
PatternKind::Deref { .. } => {
514-
self.error_simplifyable(match_pair)
515-
}
435+
Candidate {
436+
match_pairs: all_match_pairs,
437+
bindings: candidate.bindings.clone(),
438+
guard: candidate.guard.clone(),
439+
arm_index: candidate.arm_index,
516440
}
517441
}
518442

0 commit comments

Comments
 (0)
Please sign in to comment.