|
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