Skip to content

Commit 9daa823

Browse files
committed
Improve match MIR generation for ranges
Makes testing a range rule out ranges/constant covered by the range that is being tested
1 parent d2ac11c commit 9daa823

File tree

3 files changed

+207
-5
lines changed

3 files changed

+207
-5
lines changed

src/librustc_mir/build/matches/test.rs

+118-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use build::Builder;
1919
use build::matches::{Candidate, MatchPair, Test, TestKind};
2020
use hair::*;
21+
use hair::pattern::compare_const_vals;
2122
use rustc_data_structures::bit_set::BitSet;
2223
use rustc_data_structures::fx::FxHashMap;
2324
use rustc::ty::{self, Ty};
@@ -136,7 +137,15 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
136137
PatternKind::Variant { .. } => {
137138
panic!("you should have called add_variants_to_switch instead!");
138139
}
139-
PatternKind::Range { .. } |
140+
PatternKind::Range { ty, lo, hi, end } => {
141+
indices
142+
.keys()
143+
.all(|value| {
144+
!self
145+
.const_range_contains(ty, lo, hi, end, value)
146+
.unwrap_or(true)
147+
})
148+
}
140149
PatternKind::Slice { .. } |
141150
PatternKind::Array { .. } |
142151
PatternKind::Wild |
@@ -529,6 +538,28 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
529538
resulting_candidates[index].push(new_candidate);
530539
true
531540
}
541+
542+
(&TestKind::SwitchInt { switch_ty: _, ref options, ref indices },
543+
&PatternKind::Range { ty, lo, hi, end }) => {
544+
let not_contained = indices
545+
.keys()
546+
.all(|value| {
547+
!self
548+
.const_range_contains(ty, lo, hi, end, value)
549+
.unwrap_or(true)
550+
});
551+
552+
if not_contained {
553+
// No values are contained in the pattern range,
554+
// so the pattern can be matched only if this test fails.
555+
let otherwise = options.len();
556+
resulting_candidates[otherwise].push(candidate.clone());
557+
true
558+
} else {
559+
false
560+
}
561+
}
562+
532563
(&TestKind::SwitchInt { .. }, _) => false,
533564

534565

@@ -607,8 +638,70 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
607638
}
608639
}
609640

641+
(&TestKind::Range {
642+
lo: test_lo, hi: test_hi, ty: test_ty, end: test_end,
643+
}, &PatternKind::Range {
644+
lo: pat_lo, hi: pat_hi, ty: _, end: pat_end,
645+
}) => {
646+
if (test_lo, test_hi, test_end) == (pat_lo, pat_hi, pat_end) {
647+
resulting_candidates[0]
648+
.push(self.candidate_without_match_pair(
649+
match_pair_index,
650+
candidate,
651+
));
652+
return true;
653+
}
654+
655+
let no_overlap = (|| {
656+
use std::cmp::Ordering::*;
657+
use rustc::hir::RangeEnd::*;
658+
659+
let param_env = ty::ParamEnv::empty().and(test_ty);
660+
let tcx = self.hir.tcx();
661+
662+
let lo = compare_const_vals(tcx, test_lo, pat_hi, param_env)?;
663+
let hi = compare_const_vals(tcx, test_hi, pat_lo, param_env)?;
664+
665+
match (test_end, pat_end, lo, hi) {
666+
// pat < test
667+
(_, _, Greater, _) |
668+
(_, Excluded, Equal, _) |
669+
// pat > test
670+
(_, _, _, Less) |
671+
(Excluded, _, _, Equal) => Some(true),
672+
_ => Some(false),
673+
}
674+
})();
675+
676+
if no_overlap == Some(true) {
677+
// Testing range does not overlap with pattern range,
678+
// so the pattern can be matched only if this test fails.
679+
resulting_candidates[1].push(candidate.clone());
680+
true
681+
} else {
682+
false
683+
}
684+
}
685+
686+
(&TestKind::Range {
687+
lo, hi, ty, end
688+
}, &PatternKind::Constant {
689+
ref value
690+
}) => {
691+
if self.const_range_contains(ty, lo, hi, end, value) == Some(false) {
692+
// `value` is not contained in the testing range,
693+
// so `value` can be matched only if this test fails.
694+
resulting_candidates[1].push(candidate.clone());
695+
true
696+
} else {
697+
false
698+
}
699+
}
700+
701+
(&TestKind::Range { .. }, _) => false,
702+
703+
610704
(&TestKind::Eq { .. }, _) |
611-
(&TestKind::Range { .. }, _) |
612705
(&TestKind::Len { .. }, _) => {
613706
// These are all binary tests.
614707
//
@@ -719,6 +812,29 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
719812
"simplifyable pattern found: {:?}",
720813
match_pair.pattern)
721814
}
815+
816+
fn const_range_contains(
817+
&self,
818+
ty: Ty<'tcx>,
819+
lo: &'tcx ty::Const<'tcx>,
820+
hi: &'tcx ty::Const<'tcx>,
821+
end: RangeEnd,
822+
value: &'tcx ty::Const<'tcx>,
823+
) -> Option<bool> {
824+
use std::cmp::Ordering::*;
825+
826+
let param_env = ty::ParamEnv::empty().and(ty);
827+
let tcx = self.hir.tcx();
828+
829+
let a = compare_const_vals(tcx, lo, value, param_env)?;
830+
let b = compare_const_vals(tcx, value, hi, param_env)?;
831+
832+
match (b, end) {
833+
(Less, _) |
834+
(Equal, RangeEnd::Included) if a != Greater => Some(true),
835+
_ => Some(false),
836+
}
837+
}
722838
}
723839

724840
fn is_switch_ty<'tcx>(ty: Ty<'tcx>) -> bool {

src/librustc_mir/hair/pattern/mod.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use hair::constant::*;
2424
use rustc::mir::{fmt_const_val, Field, BorrowKind, Mutability};
2525
use rustc::mir::{ProjectionElem, UserTypeAnnotation, UserTypeProjection, UserTypeProjections};
2626
use rustc::mir::interpret::{Scalar, GlobalId, ConstValue, sign_extend};
27-
use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty};
27+
use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, Lift};
2828
use rustc::ty::subst::{Substs, Kind};
2929
use rustc::ty::layout::VariantIdx;
3030
use rustc::hir::{self, PatKind, RangeEnd};
@@ -1210,8 +1210,8 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
12101210
}
12111211
}
12121212

1213-
pub fn compare_const_vals<'a, 'tcx>(
1214-
tcx: TyCtxt<'a, 'tcx, 'tcx>,
1213+
pub fn compare_const_vals<'a, 'gcx, 'tcx>(
1214+
tcx: TyCtxt<'a, 'gcx, 'tcx>,
12151215
a: &'tcx ty::Const<'tcx>,
12161216
b: &'tcx ty::Const<'tcx>,
12171217
ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
@@ -1233,6 +1233,9 @@ pub fn compare_const_vals<'a, 'tcx>(
12331233
return fallback();
12341234
}
12351235

1236+
let tcx = tcx.global_tcx();
1237+
let (a, b, ty) = (a, b, ty).lift_to_tcx(tcx).unwrap();
1238+
12361239
// FIXME: This should use assert_bits(ty) instead of use_bits
12371240
// but triggers possibly bugs due to mismatching of arrays and slices
12381241
if let (Some(a), Some(b)) = (a.to_bits(tcx, ty), b.to_bits(tcx, ty)) {
+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#![feature(exclusive_range_pattern)]
2+
3+
// run-pass
4+
5+
fn main() {
6+
let incl_range = |x, b| {
7+
match x {
8+
0..=5 if b => 0,
9+
5..=10 if b => 1,
10+
1..=4 if !b => 2,
11+
_ => 3,
12+
}
13+
};
14+
assert_eq!(incl_range(3, false), 2);
15+
assert_eq!(incl_range(3, true), 0);
16+
assert_eq!(incl_range(5, false), 3);
17+
assert_eq!(incl_range(5, true), 0);
18+
19+
let excl_range = |x, b| {
20+
match x {
21+
0..5 if b => 0,
22+
5..10 if b => 1,
23+
1..4 if !b => 2,
24+
_ => 3,
25+
}
26+
};
27+
assert_eq!(excl_range(3, false), 2);
28+
assert_eq!(excl_range(3, true), 0);
29+
assert_eq!(excl_range(5, false), 3);
30+
assert_eq!(excl_range(5, true), 1);
31+
32+
let incl_range_vs_const = |x, b| {
33+
match x {
34+
0..=5 if b => 0,
35+
7 => 1,
36+
3 => 2,
37+
_ => 3,
38+
}
39+
};
40+
assert_eq!(incl_range_vs_const(5, false), 3);
41+
assert_eq!(incl_range_vs_const(5, true), 0);
42+
assert_eq!(incl_range_vs_const(3, false), 2);
43+
assert_eq!(incl_range_vs_const(3, true), 0);
44+
assert_eq!(incl_range_vs_const(7, false), 1);
45+
assert_eq!(incl_range_vs_const(7, true), 1);
46+
47+
let excl_range_vs_const = |x, b| {
48+
match x {
49+
0..5 if b => 0,
50+
7 => 1,
51+
3 => 2,
52+
_ => 3,
53+
}
54+
};
55+
assert_eq!(excl_range_vs_const(5, false), 3);
56+
assert_eq!(excl_range_vs_const(5, true), 3);
57+
assert_eq!(excl_range_vs_const(3, false), 2);
58+
assert_eq!(excl_range_vs_const(3, true), 0);
59+
assert_eq!(excl_range_vs_const(7, false), 1);
60+
assert_eq!(excl_range_vs_const(7, true), 1);
61+
62+
let const_vs_incl_range = |x, b| {
63+
match x {
64+
3 if b => 0,
65+
5..=7 => 2,
66+
1..=4 => 1,
67+
_ => 3,
68+
}
69+
};
70+
assert_eq!(const_vs_incl_range(3, false), 1);
71+
assert_eq!(const_vs_incl_range(3, true), 0);
72+
73+
let const_vs_excl_range = |x, b| {
74+
match x {
75+
3 if b => 0,
76+
5..7 => 2,
77+
1..4 => 1,
78+
_ => 3,
79+
}
80+
};
81+
assert_eq!(const_vs_excl_range(3, false), 1);
82+
assert_eq!(const_vs_excl_range(3, true), 0);
83+
}

0 commit comments

Comments
 (0)