Skip to content

Commit

Permalink
Auto merge of #35712 - oli-obk:exclusive_range_patterns, r=nikomatsakis
Browse files Browse the repository at this point in the history
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 => {},
    _ => {}
}
```
  • Loading branch information
bors committed Jan 25, 2017
2 parents 83c2d95 + 98fef41 commit c0d0e68
Show file tree
Hide file tree
Showing 25 changed files with 269 additions and 67 deletions.
2 changes: 1 addition & 1 deletion src/librustc/hir/intravisit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat) {
walk_list!(visitor, visit_pat, optional_subpattern);
}
PatKind::Lit(ref expression) => visitor.visit_expr(expression),
PatKind::Range(ref lower_bound, ref upper_bound) => {
PatKind::Range(ref lower_bound, ref upper_bound, _) => {
visitor.visit_expr(lower_bound);
visitor.visit_expr(upper_bound)
}
Expand Down
13 changes: 11 additions & 2 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1250,8 +1250,10 @@ impl<'a> LoweringContext<'a> {
PatKind::Ref(ref inner, mutbl) => {
hir::PatKind::Ref(self.lower_pat(inner), self.lower_mutability(mutbl))
}
PatKind::Range(ref e1, ref e2) => {
hir::PatKind::Range(P(self.lower_expr(e1)), P(self.lower_expr(e2)))
PatKind::Range(ref e1, ref e2, ref end) => {
hir::PatKind::Range(P(self.lower_expr(e1)),
P(self.lower_expr(e2)),
self.lower_range_end(end))
}
PatKind::Slice(ref before, ref slice, ref after) => {
hir::PatKind::Slice(before.iter().map(|x| self.lower_pat(x)).collect(),
Expand All @@ -1264,6 +1266,13 @@ impl<'a> LoweringContext<'a> {
})
}

fn lower_range_end(&mut self, e: &RangeEnd) -> hir::RangeEnd {
match *e {
RangeEnd::Included => hir::RangeEnd::Included,
RangeEnd::Excluded => hir::RangeEnd::Excluded,
}
}

fn lower_expr(&mut self, e: &Expr) -> hir::Expr {
hir::Expr {
id: e.id,
Expand Down
10 changes: 8 additions & 2 deletions src/librustc/hir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,12 @@ pub enum BindingMode {
BindByValue(Mutability),
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum RangeEnd {
Included,
Excluded,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum PatKind {
/// Represents a wildcard pattern (`_`)
Expand Down Expand Up @@ -603,8 +609,8 @@ pub enum PatKind {
Ref(P<Pat>, Mutability),
/// A literal
Lit(P<Expr>),
/// A range pattern, e.g. `1...2`
Range(P<Expr>, P<Expr>),
/// A range pattern, e.g. `1...2` or `1..2`
Range(P<Expr>, P<Expr>, RangeEnd),
/// `[a, b, ..i, y, z]` is represented as:
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
Slice(HirVec<P<Pat>>, Option<P<Pat>>, HirVec<P<Pat>>),
Expand Down
9 changes: 6 additions & 3 deletions src/librustc/hir/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use syntax::symbol::keywords;
use syntax_pos::{self, BytePos};

use hir;
use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier, RangeEnd};

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

Expand Down Expand Up @@ -1732,10 +1732,13 @@ impl<'a> State<'a> {
self.print_pat(&inner)?;
}
PatKind::Lit(ref e) => self.print_expr(&e)?,
PatKind::Range(ref begin, ref end) => {
PatKind::Range(ref begin, ref end, ref end_kind) => {
self.print_expr(&begin)?;
space(&mut self.s)?;
word(&mut self.s, "...")?;
match *end_kind {
RangeEnd::Included => word(&mut self.s, "...")?,
RangeEnd::Excluded => word(&mut self.s, "..")?,
}
self.print_expr(&end)?;
}
PatKind::Slice(ref before, ref slice, ref after) => {
Expand Down
51 changes: 34 additions & 17 deletions src/librustc_const_eval/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use pattern::{FieldPattern, Pattern, PatternKind};
use pattern::{PatternFoldable, PatternFolder};

use rustc::hir::def_id::DefId;
use rustc::hir::RangeEnd;
use rustc::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};

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

fn range_covered_by_constructor(tcx: TyCtxt, span: Span,
ctor: &Constructor,
from: &ConstVal, to: &ConstVal)
from: &ConstVal, to: &ConstVal,
end: RangeEnd)
-> Result<bool, ErrorReported> {
let (c_from, c_to) = match *ctor {
ConstantValue(ref value) => (value, value),
ConstantRange(ref from, ref to) => (from, to),
Single => return Ok(true),
_ => bug!()
};
let cmp_from = compare_const_vals(tcx, span, c_from, from)?;
let cmp_to = compare_const_vals(tcx, span, c_to, to)?;
Ok(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
let cmp_from = |c_from| Ok(compare_const_vals(tcx, span, c_from, from)? != Ordering::Less);
let cmp_to = |c_to| compare_const_vals(tcx, span, c_to, to);
match *ctor {
ConstantValue(ref value) => {
let to = cmp_to(value)?;
let end = (to != Ordering::Greater) ||
(end == RangeEnd::Excluded && to == Ordering::Equal);
Ok(cmp_from(value)? && end)
},
ConstantRange(ref from, ref to, RangeEnd::Included) => {
let to = cmp_to(to)?;
let end = (to != Ordering::Greater) ||
(end == RangeEnd::Excluded && to == Ordering::Equal);
Ok(cmp_from(from)? && end)
},
ConstantRange(ref from, ref to, RangeEnd::Excluded) => {
let to = cmp_to(to)?;
let end = (to == Ordering::Less) ||
(end == RangeEnd::Excluded && to == Ordering::Equal);
Ok(cmp_from(from)? && end)
}
Single => Ok(true),
_ => bug!(),
}
}

fn patterns_for_variant<'p, 'a: 'p, 'tcx: 'a>(
Expand Down Expand Up @@ -872,7 +889,7 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
},
_ => {
match range_covered_by_constructor(
cx.tcx, pat.span, constructor, value, value
cx.tcx, pat.span, constructor, value, value, RangeEnd::Included
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
Expand All @@ -882,9 +899,9 @@ fn specialize<'p, 'a: 'p, 'tcx: 'a>(
}
}

PatternKind::Range { ref lo, ref hi } => {
PatternKind::Range { ref lo, ref hi, ref end } => {
match range_covered_by_constructor(
cx.tcx, pat.span, constructor, lo, hi
cx.tcx, pat.span, constructor, lo, hi, end.clone()
) {
Ok(true) => Some(vec![]),
Ok(false) => None,
Expand Down
20 changes: 13 additions & 7 deletions src/librustc_const_eval/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rustc::middle::const_val::ConstVal;
use rustc::mir::{Field, BorrowKind, Mutability};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, TypeVariants, Region};
use rustc::ty::subst::{Substs, Kind};
use rustc::hir::{self, PatKind};
use rustc::hir::{self, PatKind, RangeEnd};
use rustc::hir::def::{Def, CtorKind};
use rustc::hir::pat_util::EnumerateAndAdjustIterator;

Expand Down Expand Up @@ -90,6 +90,7 @@ pub enum PatternKind<'tcx> {
Range {
lo: ConstVal,
hi: ConstVal,
end: RangeEnd,
},

/// matches against a slice, checking the length and extracting elements
Expand Down Expand Up @@ -228,9 +229,12 @@ impl<'tcx> fmt::Display for Pattern<'tcx> {
PatternKind::Constant { ref value } => {
print_const_val(value, f)
}
PatternKind::Range { ref lo, ref hi } => {
PatternKind::Range { ref lo, ref hi, ref end } => {
print_const_val(lo, f)?;
write!(f, "...")?;
match *end {
RangeEnd::Included => write!(f, "...")?,
RangeEnd::Excluded => write!(f, "..")?,
}
print_const_val(hi, f)
}
PatternKind::Slice { ref prefix, ref slice, ref suffix } |
Expand Down Expand Up @@ -291,11 +295,11 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> {

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

PatKind::Range(ref lo, ref hi) => {
PatKind::Range(ref lo, ref hi, ref end) => {
match (self.lower_lit(lo), self.lower_lit(hi)) {
(PatternKind::Constant { value: lo },
PatternKind::Constant { value: hi }) => {
PatternKind::Range { lo: lo, hi: hi }
PatternKind::Range { lo: lo, hi: hi, end: end.clone() }
}
_ => PatternKind::Wild
}
Expand Down Expand Up @@ -871,10 +875,12 @@ impl<'tcx> PatternFoldable<'tcx> for PatternKind<'tcx> {
},
PatternKind::Range {
ref lo,
ref hi
ref hi,
ref end,
} => PatternKind::Range {
lo: lo.fold_with(folder),
hi: hi.fold_with(folder)
hi: hi.fold_with(folder),
end: end.clone(),
},
PatternKind::Slice {
ref prefix,
Expand Down
4 changes: 3 additions & 1 deletion src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use rustc_data_structures::bitvec::BitVector;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{AdtDef, Ty};
use rustc::mir::*;
use rustc::hir;
use hair::*;
use syntax::ast::{Name, NodeId};
use syntax_pos::Span;
Expand Down Expand Up @@ -318,11 +319,12 @@ enum TestKind<'tcx> {
ty: Ty<'tcx>,
},

// test whether the value falls within an inclusive range
// test whether the value falls within an inclusive or exclusive range
Range {
lo: Literal<'tcx>,
hi: Literal<'tcx>,
ty: Ty<'tcx>,
end: hir::RangeEnd,
},

// test length of the slice is equal to len
Expand Down
11 changes: 8 additions & 3 deletions src/librustc_mir/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use rustc_data_structures::bitvec::BitVector;
use rustc::middle::const_val::ConstVal;
use rustc::ty::{self, Ty};
use rustc::mir::*;
use rustc::hir::RangeEnd;
use syntax_pos::Span;
use std::cmp::Ordering;

Expand Down Expand Up @@ -69,13 +70,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
}

PatternKind::Range { ref lo, ref hi } => {
PatternKind::Range { ref lo, ref hi, ref end } => {
Test {
span: match_pair.pattern.span,
kind: TestKind::Range {
lo: Literal::Value { value: lo.clone() },
hi: Literal::Value { value: hi.clone() },
ty: match_pair.pattern.ty.clone(),
end: end.clone(),
},
}
}
Expand Down Expand Up @@ -324,15 +326,18 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
}
}

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

let fail = self.cfg.start_new_block();
let block = self.compare(block, fail, test.span, BinOp::Le, lo, val.clone());
let block = self.compare(block, fail, test.span, BinOp::Le, val, hi);
let block = match *end {
RangeEnd::Included => self.compare(block, fail, test.span, BinOp::Le, val, hi),
RangeEnd::Excluded => self.compare(block, fail, test.span, BinOp::Lt, val, hi),
};

vec![block, fail]
}
Expand Down
18 changes: 16 additions & 2 deletions src/librustc_passes/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ use rustc::util::common::ErrorReported;
use rustc::util::nodemap::NodeSet;
use rustc::lint::builtin::CONST_ERR;

use rustc::hir::{self, PatKind};
use rustc::hir::{self, PatKind, RangeEnd};
use syntax::ast;
use syntax_pos::Span;
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
Expand Down Expand Up @@ -157,7 +157,21 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckCrateVisitor<'a, 'tcx> {
PatKind::Lit(ref lit) => {
self.check_const_eval(lit);
}
PatKind::Range(ref start, ref end) => {
PatKind::Range(ref start, ref end, RangeEnd::Excluded) => {
let const_cx = ConstContext::with_tables(self.tcx, self.tables);
match const_cx.compare_lit_exprs(p.span, start, end) {
Ok(Ordering::Less) => {}
Ok(Ordering::Equal) |
Ok(Ordering::Greater) => {
span_err!(self.tcx.sess,
start.span,
E0579,
"lower range bound must be less than upper");
}
Err(ErrorReported) => {}
}
}
PatKind::Range(ref start, ref end, RangeEnd::Included) => {
let const_cx = ConstContext::with_tables(self.tcx, self.tables);
match const_cx.compare_lit_exprs(p.span, start, end) {
Ok(Ordering::Less) |
Expand Down
18 changes: 18 additions & 0 deletions src/librustc_passes/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,24 @@ pub impl Foo for Bar {
```
"##,


E0579: r##"
When matching against an exclusive range, the compiler verifies that the range
is non-empty. Exclusive range patterns include the start point but not the end
point, so this is equivalent to requiring the start of the range to be less
than the end of the range.
For example:
```compile_fail
match 5u32 {
// This range is ok, albeit pointless.
1 .. 2 => {}
// This range is empty, and the compiler can tell.
5 .. 5 => {}
}
```
"##,
}

register_diagnostics! {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_typeck/check/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
self.demand_suptype(pat.span, expected, pat_ty);
pat_ty
}
PatKind::Range(ref begin, ref end) => {
PatKind::Range(ref begin, ref end, _) => {
let lhs_ty = self.check_expr(begin);
let rhs_ty = self.check_expr(end);

Expand Down
10 changes: 8 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,12 @@ pub enum BindingMode {
ByValue(Mutability),
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum RangeEnd {
Included,
Excluded,
}

#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum PatKind {
/// Represents a wildcard pattern (`_`)
Expand Down Expand Up @@ -583,8 +589,8 @@ pub enum PatKind {
Ref(P<Pat>, Mutability),
/// A literal
Lit(P<Expr>),
/// A range pattern, e.g. `1...2`
Range(P<Expr>, P<Expr>),
/// A range pattern, e.g. `1...2` or `1..2`
Range(P<Expr>, P<Expr>, RangeEnd),
/// `[a, b, ..i, y, z]` is represented as:
/// `PatKind::Slice(box [a, b], Some(i), box [y, z])`
Slice(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
Expand Down
Loading

0 comments on commit c0d0e68

Please sign in to comment.