|
18 | 18 | use build::Builder;
|
19 | 19 | use build::matches::{Candidate, MatchPair, Test, TestKind};
|
20 | 20 | use hair::*;
|
| 21 | +use hair::pattern::compare_const_vals; |
21 | 22 | use rustc_data_structures::bit_set::BitSet;
|
22 | 23 | use rustc_data_structures::fx::FxHashMap;
|
23 | 24 | use rustc::ty::{self, Ty};
|
@@ -71,16 +72,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
71 | 72 | }
|
72 | 73 | }
|
73 | 74 |
|
74 |
| - PatternKind::Range { lo, hi, ty, end } => { |
75 |
| - assert!(ty == match_pair.pattern.ty); |
| 75 | + PatternKind::Range(range) => { |
| 76 | + assert!(range.ty == match_pair.pattern.ty); |
76 | 77 | Test {
|
77 | 78 | span: match_pair.pattern.span,
|
78 |
| - kind: TestKind::Range { |
79 |
| - lo, |
80 |
| - hi, |
81 |
| - ty, |
82 |
| - end, |
83 |
| - }, |
| 79 | + kind: TestKind::Range(range), |
84 | 80 | }
|
85 | 81 | }
|
86 | 82 |
|
@@ -136,7 +132,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
136 | 132 | PatternKind::Variant { .. } => {
|
137 | 133 | panic!("you should have called add_variants_to_switch instead!");
|
138 | 134 | }
|
139 |
| - PatternKind::Range { .. } | |
| 135 | + PatternKind::Range(range) => { |
| 136 | + // Check that none of the switch values are in the range. |
| 137 | + self.values_not_contained_in_range(range, indices) |
| 138 | + .unwrap_or(false) |
| 139 | + } |
140 | 140 | PatternKind::Slice { .. } |
|
141 | 141 | PatternKind::Array { .. } |
|
142 | 142 | PatternKind::Wild |
|
@@ -200,20 +200,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
200 | 200 | for (idx, discr) in adt_def.discriminants(tcx) {
|
201 | 201 | target_blocks.push(if variants.contains(idx) {
|
202 | 202 | values.push(discr.val);
|
203 |
| - targets.push(self.cfg.start_new_block()); |
204 |
| - *targets.last().unwrap() |
| 203 | + let block = self.cfg.start_new_block(); |
| 204 | + targets.push(block); |
| 205 | + block |
205 | 206 | } else {
|
206 |
| - if otherwise_block.is_none() { |
207 |
| - otherwise_block = Some(self.cfg.start_new_block()); |
208 |
| - } |
209 |
| - otherwise_block.unwrap() |
| 207 | + *otherwise_block |
| 208 | + .get_or_insert_with(|| self.cfg.start_new_block()) |
210 | 209 | });
|
211 | 210 | }
|
212 |
| - if let Some(otherwise_block) = otherwise_block { |
213 |
| - targets.push(otherwise_block); |
214 |
| - } else { |
215 |
| - targets.push(self.unreachable_block()); |
216 |
| - } |
| 211 | + targets.push( |
| 212 | + otherwise_block |
| 213 | + .unwrap_or_else(|| self.unreachable_block()), |
| 214 | + ); |
217 | 215 | debug!("num_enum_variants: {}, tested variants: {:?}, variants: {:?}",
|
218 | 216 | num_enum_variants, values, variants);
|
219 | 217 | let discr_ty = adt_def.repr.discr_type().to_ty(tcx);
|
@@ -378,7 +376,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
378 | 376 | }
|
379 | 377 | }
|
380 | 378 |
|
381 |
| - TestKind::Range { ref lo, ref hi, ty, ref end } => { |
| 379 | + TestKind::Range(PatternRange { ref lo, ref hi, ty, ref end }) => { |
382 | 380 | // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
383 | 381 | let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
|
384 | 382 | let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
|
@@ -490,8 +488,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
490 | 488 | // away.)
|
491 | 489 | let tested_match_pair = candidate.match_pairs.iter()
|
492 | 490 | .enumerate()
|
493 |
| - .filter(|&(_, mp)| mp.place == *test_place) |
494 |
| - .next(); |
| 491 | + .find(|&(_, mp)| mp.place == *test_place); |
495 | 492 | let (match_pair_index, match_pair) = match tested_match_pair {
|
496 | 493 | Some(pair) => pair,
|
497 | 494 | None => {
|
@@ -532,6 +529,24 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
532 | 529 | resulting_candidates[index].push(new_candidate);
|
533 | 530 | true
|
534 | 531 | }
|
| 532 | + |
| 533 | + (&TestKind::SwitchInt { switch_ty: _, ref options, ref indices }, |
| 534 | + &PatternKind::Range(range)) => { |
| 535 | + let not_contained = self |
| 536 | + .values_not_contained_in_range(range, indices) |
| 537 | + .unwrap_or(false); |
| 538 | + |
| 539 | + if not_contained { |
| 540 | + // No switch values are contained in the pattern range, |
| 541 | + // so the pattern can be matched only if this test fails. |
| 542 | + let otherwise = options.len(); |
| 543 | + resulting_candidates[otherwise].push(candidate.clone()); |
| 544 | + true |
| 545 | + } else { |
| 546 | + false |
| 547 | + } |
| 548 | + } |
| 549 | + |
535 | 550 | (&TestKind::SwitchInt { .. }, _) => false,
|
536 | 551 |
|
537 | 552 |
|
@@ -610,8 +625,63 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
610 | 625 | }
|
611 | 626 | }
|
612 | 627 |
|
| 628 | + (&TestKind::Range(test), |
| 629 | + &PatternKind::Range(pat)) => { |
| 630 | + if test == pat { |
| 631 | + resulting_candidates[0] |
| 632 | + .push(self.candidate_without_match_pair( |
| 633 | + match_pair_index, |
| 634 | + candidate, |
| 635 | + )); |
| 636 | + return true; |
| 637 | + } |
| 638 | + |
| 639 | + let no_overlap = (|| { |
| 640 | + use std::cmp::Ordering::*; |
| 641 | + use rustc::hir::RangeEnd::*; |
| 642 | + |
| 643 | + let param_env = ty::ParamEnv::empty().and(test.ty); |
| 644 | + let tcx = self.hir.tcx(); |
| 645 | + |
| 646 | + let lo = compare_const_vals(tcx, test.lo, pat.hi, param_env)?; |
| 647 | + let hi = compare_const_vals(tcx, test.hi, pat.lo, param_env)?; |
| 648 | + |
| 649 | + match (test.end, pat.end, lo, hi) { |
| 650 | + // pat < test |
| 651 | + (_, _, Greater, _) | |
| 652 | + (_, Excluded, Equal, _) | |
| 653 | + // pat > test |
| 654 | + (_, _, _, Less) | |
| 655 | + (Excluded, _, _, Equal) => Some(true), |
| 656 | + _ => Some(false), |
| 657 | + } |
| 658 | + })(); |
| 659 | + |
| 660 | + if no_overlap == Some(true) { |
| 661 | + // Testing range does not overlap with pattern range, |
| 662 | + // so the pattern can be matched only if this test fails. |
| 663 | + resulting_candidates[1].push(candidate.clone()); |
| 664 | + true |
| 665 | + } else { |
| 666 | + false |
| 667 | + } |
| 668 | + } |
| 669 | + |
| 670 | + (&TestKind::Range(range), &PatternKind::Constant { ref value }) => { |
| 671 | + if self.const_range_contains(range, value) == Some(false) { |
| 672 | + // `value` is not contained in the testing range, |
| 673 | + // so `value` can be matched only if this test fails. |
| 674 | + resulting_candidates[1].push(candidate.clone()); |
| 675 | + true |
| 676 | + } else { |
| 677 | + false |
| 678 | + } |
| 679 | + } |
| 680 | + |
| 681 | + (&TestKind::Range { .. }, _) => false, |
| 682 | + |
| 683 | + |
613 | 684 | (&TestKind::Eq { .. }, _) |
|
614 |
| - (&TestKind::Range { .. }, _) | |
615 | 685 | (&TestKind::Len { .. }, _) => {
|
616 | 686 | // These are all binary tests.
|
617 | 687 | //
|
@@ -722,6 +792,40 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
722 | 792 | "simplifyable pattern found: {:?}",
|
723 | 793 | match_pair.pattern)
|
724 | 794 | }
|
| 795 | + |
| 796 | + fn const_range_contains( |
| 797 | + &self, |
| 798 | + range: PatternRange<'tcx>, |
| 799 | + value: &'tcx ty::Const<'tcx>, |
| 800 | + ) -> Option<bool> { |
| 801 | + use std::cmp::Ordering::*; |
| 802 | + |
| 803 | + let param_env = ty::ParamEnv::empty().and(range.ty); |
| 804 | + let tcx = self.hir.tcx(); |
| 805 | + |
| 806 | + let a = compare_const_vals(tcx, range.lo, value, param_env)?; |
| 807 | + let b = compare_const_vals(tcx, value, range.hi, param_env)?; |
| 808 | + |
| 809 | + match (b, range.end) { |
| 810 | + (Less, _) | |
| 811 | + (Equal, RangeEnd::Included) if a != Greater => Some(true), |
| 812 | + _ => Some(false), |
| 813 | + } |
| 814 | + } |
| 815 | + |
| 816 | + fn values_not_contained_in_range( |
| 817 | + &self, |
| 818 | + range: PatternRange<'tcx>, |
| 819 | + indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>, |
| 820 | + ) -> Option<bool> { |
| 821 | + for val in indices.keys() { |
| 822 | + if self.const_range_contains(range, val)? { |
| 823 | + return Some(false); |
| 824 | + } |
| 825 | + } |
| 826 | + |
| 827 | + Some(true) |
| 828 | + } |
725 | 829 | }
|
726 | 830 |
|
727 | 831 | fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {
|
|
0 commit comments