Skip to content

Commit c0d0e68

Browse files
committed
Auto merge of #35712 - oli-obk:exclusive_range_patterns, r=nikomatsakis
exclusive range patterns adds `..` patterns to the language under a feature gate (`exclusive_range_pattern`). This allows turning ``` rust match i { 0...9 => {}, 10...19 => {}, 20...29 => {}, _ => {} } ``` into ``` rust match i { 0..10 => {}, 10..20 => {}, 20..30 => {}, _ => {} } ```
2 parents 83c2d95 + 98fef41 commit c0d0e68

25 files changed

+269
-67
lines changed

src/librustc/hir/intravisit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
661661
walk_list!(visitor, visit_pat, optional_subpattern);
662662
}
663663
PatKind::Lit(ref expression) => visitor.visit_expr(expression),
664-
PatKind::Range(ref lower_bound, ref upper_bound) => {
664+
PatKind::Range(ref lower_bound, ref upper_bound, _) => {
665665
visitor.visit_expr(lower_bound);
666666
visitor.visit_expr(upper_bound)
667667
}

src/librustc/hir/lowering.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1250,8 +1250,10 @@ impl<'a> LoweringContext<'a> {
12501250
PatKind::Ref(ref inner, mutbl) => {
12511251
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
12521252
}
1253-
PatKind::Range(ref e1, ref e2) => {
1254-
hir::PatKind::Range(P(self.lower_expr(e1)), P(self.lower_expr(e2)))
1253+
PatKind::Range(ref e1, ref e2, ref end) => {
1254+
hir::PatKind::Range(P(self.lower_expr(e1)),
1255+
P(self.lower_expr(e2)),
1256+
self.lower_range_end(end))
12551257
}
12561258
PatKind::Slice(ref before, ref slice, ref after) => {
12571259
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
@@ -1264,6 +1266,13 @@ impl<'a> LoweringContext<'a> {
12641266
})
12651267
}
12661268

1269+
fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
1270+
match *e {
1271+
RangeEnd::Included => hir::RangeEnd::Included,
1272+
RangeEnd::Excluded => hir::RangeEnd::Excluded,
1273+
}
1274+
}
1275+
12671276
fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
12681277
hir::Expr {
12691278
id: e.id,

src/librustc/hir/mod.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,12 @@ pub enum BindingMode {
572572
BindByValue(Mutability),
573573
}
574574

575+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
576+
pub enum RangeEnd {
577+
Included,
578+
Excluded,
579+
}
580+
575581
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
576582
pub enum PatKind {
577583
/// Represents a wildcard pattern (`_`)
@@ -603,8 +609,8 @@ pub enum PatKind {
603609
Ref(P<Pat>, Mutability),
604610
/// A literal
605611
Lit(P<Expr>),
606-
/// A range pattern, e.g. `1...2`
607-
Range(P<Expr>, P<Expr>),
612+
/// A range pattern, e.g. `1...2` or `1..2`
613+
Range(P<Expr>, P<Expr>, RangeEnd),
608614
/// `[a, b, ..i, y, z]` is represented as:
609615
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
610616
Slice(HirVec<P<Pat>>, Option<P<Pat>>, HirVec<P<Pat>>),

src/librustc/hir/print.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use syntax::symbol::keywords;
2424
use syntax_pos::{self, BytePos};
2525

2626
use hir;
27-
use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
27+
use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier, RangeEnd};
2828

2929
use std::io::{self, Write, Read};
3030

@@ -1732,10 +1732,13 @@ impl<'a> State<'a> {
17321732
self.print_pat(&inner)?;
17331733
}
17341734
PatKind::Lit(ref e) => self.print_expr(&e)?,
1735-
PatKind::Range(ref begin, ref end) => {
1735+
PatKind::Range(ref begin, ref end, ref end_kind) => {
17361736
self.print_expr(&begin)?;
17371737
space(&mut self.s)?;
1738-
word(&mut self.s, "...")?;
1738+
match *end_kind {
1739+
RangeEnd::Included => word(&mut self.s, "...")?,
1740+
RangeEnd::Excluded => word(&mut self.s, "..")?,
1741+
}
17391742
self.print_expr(&end)?;
17401743
}
17411744
PatKind::Slice(ref before, ref slice, ref after) => {

src/librustc_const_eval/_match.rs

+34-17
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use pattern::{FieldPattern, Pattern, PatternKind};
2424
use pattern::{PatternFoldable, PatternFolder};
2525

2626
use rustc::hir::def_id::DefId;
27+
use rustc::hir::RangeEnd;
2728
use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
2829

2930
use rustc::mir::Field;
@@ -206,8 +207,8 @@ pub enum Constructor {
206207
Variant(DefId),
207208
/// Literal values.
208209
ConstantValue(ConstVal),
209-
/// Ranges of literal values (2..5).
210-
ConstantRange(ConstVal, ConstVal),
210+
/// Ranges of literal values (`2...5` and `2..5`).
211+
ConstantRange(ConstVal, ConstVal, RangeEnd),
211212
/// Array patterns of length n.
212213
Slice(usize),
213214
}
@@ -686,8 +687,8 @@ fn pat_constructors(_cx: &mut MatchCheckCtxt,
686687
Some(vec![Variant(adt_def.variants[variant_index].did)]),
687688
PatternKind::Constant { ref value } =>
688689
Some(vec![ConstantValue(value.clone())]),
689-
PatternKind::Range { ref lo, ref hi } =>
690-
Some(vec![ConstantRange(lo.clone(), hi.clone())]),
690+
PatternKind::Range { ref lo, ref hi, ref end } =>
691+
Some(vec![ConstantRange(lo.clone(), hi.clone(), end.clone())]),
691692
PatternKind::Array { .. } => match pcx.ty.sty {
692693
ty::TyArray(_, length) => Some(vec![Slice(length)]),
693694
_ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty)
@@ -791,17 +792,33 @@ fn slice_pat_covered_by_constructor(_tcx: TyCtxt, _span: Span,
791792

792793
fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
793794
ctor: &Constructor,
794-
from: &ConstVal, to: &ConstVal)
795+
from: &ConstVal, to: &ConstVal,
796+
end: RangeEnd)
795797
-> Result<bool, ErrorReported> {
796-
let (c_from, c_to) = match *ctor {
797-
ConstantValue(ref value) => (value, value),
798-
ConstantRange(ref from, ref to) => (from, to),
799-
Single => return Ok(true),
800-
_ => bug!()
801-
};
802-
let cmp_from = compare_const_vals(tcx, span, c_from, from)?;
803-
let cmp_to = compare_const_vals(tcx, span, c_to, to)?;
804-
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
798+
let cmp_from = |c_from| Ok(compare_const_vals(tcx, span, c_from, from)? != Ordering::Less);
799+
let cmp_to = |c_to| compare_const_vals(tcx, span, c_to, to);
800+
match *ctor {
801+
ConstantValue(ref value) => {
802+
let to = cmp_to(value)?;
803+
let end = (to != Ordering::Greater) ||
804+
(end == RangeEnd::Excluded && to == Ordering::Equal);
805+
Ok(cmp_from(value)? && end)
806+
},
807+
ConstantRange(ref from, ref to, RangeEnd::Included) => {
808+
let to = cmp_to(to)?;
809+
let end = (to != Ordering::Greater) ||
810+
(end == RangeEnd::Excluded && to == Ordering::Equal);
811+
Ok(cmp_from(from)? && end)
812+
},
813+
ConstantRange(ref from, ref to, RangeEnd::Excluded) => {
814+
let to = cmp_to(to)?;
815+
let end = (to == Ordering::Less) ||
816+
(end == RangeEnd::Excluded && to == Ordering::Equal);
817+
Ok(cmp_from(from)? && end)
818+
}
819+
Single => Ok(true),
820+
_ => bug!(),
821+
}
805822
}
806823

807824
fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>(
@@ -872,7 +889,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
872889
},
873890
_ => {
874891
match range_covered_by_constructor(
875-
cx.tcx, pat.span, constructor, value, value
892+
cx.tcx, pat.span, constructor, value, value, RangeEnd::Included
876893
) {
877894
Ok(true) => Some(vec![]),
878895
Ok(false) => None,
@@ -882,9 +899,9 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
882899
}
883900
}
884901

885-
PatternKind::Range { ref lo, ref hi } => {
902+
PatternKind::Range { ref lo, ref hi, ref end } => {
886903
match range_covered_by_constructor(
887-
cx.tcx, pat.span, constructor, lo, hi
904+
cx.tcx, pat.span, constructor, lo, hi, end.clone()
888905
) {
889906
Ok(true) => Some(vec![]),
890907
Ok(false) => None,

src/librustc_const_eval/pattern.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc::middle::const_val::ConstVal;
1515
use rustc::mir::{Field, BorrowKind, Mutability};
1616
use rustc::ty::{self, TyCtxt, AdtDef, Ty, TypeVariants, Region};
1717
use rustc::ty::subst::{Substs, Kind};
18-
use rustc::hir::{self, PatKind};
18+
use rustc::hir::{self, PatKind, RangeEnd};
1919
use rustc::hir::def::{Def, CtorKind};
2020
use rustc::hir::pat_util::EnumerateAndAdjustIterator;
2121

@@ -90,6 +90,7 @@ pub enum PatternKind<'tcx> {
9090
Range {
9191
lo: ConstVal,
9292
hi: ConstVal,
93+
end: RangeEnd,
9394
},
9495

9596
/// matches against a slice, checking the length and extracting elements
@@ -228,9 +229,12 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
228229
PatternKind::Constant { ref value } => {
229230
print_const_val(value, f)
230231
}
231-
PatternKind::Range { ref lo, ref hi } => {
232+
PatternKind::Range { ref lo, ref hi, ref end } => {
232233
print_const_val(lo, f)?;
233-
write!(f, "...")?;
234+
match *end {
235+
RangeEnd::Included => write!(f, "...")?,
236+
RangeEnd::Excluded => write!(f, "..")?,
237+
}
234238
print_const_val(hi, f)
235239
}
236240
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
@@ -291,11 +295,11 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {
291295

292296
PatKind::Lit(ref value) => self.lower_lit(value),
293297

294-
PatKind::Range(ref lo, ref hi) => {
298+
PatKind::Range(ref lo, ref hi, ref end) => {
295299
match (self.lower_lit(lo), self.lower_lit(hi)) {
296300
(PatternKind::Constant { value: lo },
297301
PatternKind::Constant { value: hi }) => {
298-
PatternKind::Range { lo: lo, hi: hi }
302+
PatternKind::Range { lo: lo, hi: hi, end: end.clone() }
299303
}
300304
_ => PatternKind::Wild
301305
}
@@ -871,10 +875,12 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
871875
},
872876
PatternKind::Range {
873877
ref lo,
874-
ref hi
878+
ref hi,
879+
ref end,
875880
} => PatternKind::Range {
876881
lo: lo.fold_with(folder),
877-
hi: hi.fold_with(folder)
882+
hi: hi.fold_with(folder),
883+
end: end.clone(),
878884
},
879885
PatternKind::Slice {
880886
ref prefix,

src/librustc_mir/build/matches/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use rustc_data_structures::bitvec::BitVector;
1919
use rustc::middle::const_val::ConstVal;
2020
use rustc::ty::{AdtDef, Ty};
2121
use rustc::mir::*;
22+
use rustc::hir;
2223
use hair::*;
2324
use syntax::ast::{Name, NodeId};
2425
use syntax_pos::Span;
@@ -318,11 +319,12 @@ enum TestKind<'tcx> {
318319
ty: Ty<'tcx>,
319320
},
320321

321-
// test whether the value falls within an inclusive range
322+
// test whether the value falls within an inclusive or exclusive range
322323
Range {
323324
lo: Literal<'tcx>,
324325
hi: Literal<'tcx>,
325326
ty: Ty<'tcx>,
327+
end: hir::RangeEnd,
326328
},
327329

328330
// test length of the slice is equal to len

src/librustc_mir/build/matches/test.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_data_structures::bitvec::BitVector;
2323
use rustc::middle::const_val::ConstVal;
2424
use rustc::ty::{self, Ty};
2525
use rustc::mir::*;
26+
use rustc::hir::RangeEnd;
2627
use syntax_pos::Span;
2728
use std::cmp::Ordering;
2829

@@ -69,13 +70,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
6970
}
7071
}
7172

72-
PatternKind::Range { ref lo, ref hi } => {
73+
PatternKind::Range { ref lo, ref hi, ref end } => {
7374
Test {
7475
span: match_pair.pattern.span,
7576
kind: TestKind::Range {
7677
lo: Literal::Value { value: lo.clone() },
7778
hi: Literal::Value { value: hi.clone() },
7879
ty: match_pair.pattern.ty.clone(),
80+
end: end.clone(),
7981
},
8082
}
8183
}
@@ -324,15 +326,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
324326
}
325327
}
326328

327-
TestKind::Range { ref lo, ref hi, ty } => {
329+
TestKind::Range { ref lo, ref hi, ty, ref end } => {
328330
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
329331
let lo = self.literal_operand(test.span, ty.clone(), lo.clone());
330332
let hi = self.literal_operand(test.span, ty.clone(), hi.clone());
331333
let val = Operand::Consume(lvalue.clone());
332334

333335
let fail = self.cfg.start_new_block();
334336
let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone());
335-
let block = self.compare(block, fail, test.span, BinOp::Le, val, hi);
337+
let block = match *end {
338+
RangeEnd::Included => self.compare(block, fail, test.span, BinOp::Le, val, hi),
339+
RangeEnd::Excluded => self.compare(block, fail, test.span, BinOp::Lt, val, hi),
340+
};
336341

337342
vec![block, fail]
338343
}

src/librustc_passes/consts.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use rustc::util::common::ErrorReported;
4545
use rustc::util::nodemap::NodeSet;
4646
use rustc::lint::builtin::CONST_ERR;
4747

48-
use rustc::hir::{self, PatKind};
48+
use rustc::hir::{self, PatKind, RangeEnd};
4949
use syntax::ast;
5050
use syntax_pos::Span;
5151
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
@@ -157,7 +157,21 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
157157
PatKind::Lit(ref lit) => {
158158
self.check_const_eval(lit);
159159
}
160-
PatKind::Range(ref start, ref end) => {
160+
PatKind::Range(ref start, ref end, RangeEnd::Excluded) => {
161+
let const_cx = ConstContext::with_tables(self.tcx, self.tables);
162+
match const_cx.compare_lit_exprs(p.span, start, end) {
163+
Ok(Ordering::Less) => {}
164+
Ok(Ordering::Equal) |
165+
Ok(Ordering::Greater) => {
166+
span_err!(self.tcx.sess,
167+
start.span,
168+
E0579,
169+
"lower range bound must be less than upper");
170+
}
171+
Err(ErrorReported) => {}
172+
}
173+
}
174+
PatKind::Range(ref start, ref end, RangeEnd::Included) => {
161175
let const_cx = ConstContext::with_tables(self.tcx, self.tables);
162176
match const_cx.compare_lit_exprs(p.span, start, end) {
163177
Ok(Ordering::Less) |

src/librustc_passes/diagnostics.rs

+18
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,24 @@ pub impl Foo for Bar {
223223
```
224224
"##,
225225

226+
227+
E0579: r##"
228+
When matching against an exclusive range, the compiler verifies that the range
229+
is non-empty. Exclusive range patterns include the start point but not the end
230+
point, so this is equivalent to requiring the start of the range to be less
231+
than the end of the range.
232+
233+
For example:
234+
235+
```compile_fail
236+
match 5u32 {
237+
// This range is ok, albeit pointless.
238+
1 .. 2 => {}
239+
// This range is empty, and the compiler can tell.
240+
5 .. 5 => {}
241+
}
242+
```
243+
"##,
226244
}
227245

228246
register_diagnostics! {

src/librustc_typeck/check/_match.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
7676
self.demand_suptype(pat.span, expected, pat_ty);
7777
pat_ty
7878
}
79-
PatKind::Range(ref begin, ref end) => {
79+
PatKind::Range(ref begin, ref end, _) => {
8080
let lhs_ty = self.check_expr(begin);
8181
let rhs_ty = self.check_expr(end);
8282

src/libsyntax/ast.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,12 @@ pub enum BindingMode {
547547
ByValue(Mutability),
548548
}
549549

550+
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
551+
pub enum RangeEnd {
552+
Included,
553+
Excluded,
554+
}
555+
550556
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
551557
pub enum PatKind {
552558
/// Represents a wildcard pattern (`_`)
@@ -583,8 +589,8 @@ pub enum PatKind {
583589
Ref(P<Pat>, Mutability),
584590
/// A literal
585591
Lit(P<Expr>),
586-
/// A range pattern, e.g. `1...2`
587-
Range(P<Expr>, P<Expr>),
592+
/// A range pattern, e.g. `1...2` or `1..2`
593+
Range(P<Expr>, P<Expr>, RangeEnd),
588594
/// `[a, b, ..i, y, z]` is represented as:
589595
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
590596
Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),

0 commit comments

Comments
 (0)