about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-01-25 02:17:33 +0000
committerbors <bors@rust-lang.org>2017-01-25 02:17:33 +0000
commitc0d0e68be4f8bb9d8522bd72a7a1b8ef95b84c6c (patch)
tree2df7197e1d4cbf264e68bf7e9ce2df67b60f574a
parent83c2d95238e3545e7ae9af4873c48b1e3651c164 (diff)
parent98fef41d63a91759790f5bbe4ac746b5c1a670ba (diff)
downloadrust-c0d0e68be4f8bb9d8522bd72a7a1b8ef95b84c6c.tar.gz
rust-c0d0e68be4f8bb9d8522bd72a7a1b8ef95b84c6c.zip
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 => {},
    _ => {}
}
```
-rw-r--r--src/librustc/hir/intravisit.rs2
-rw-r--r--src/librustc/hir/lowering.rs13
-rw-r--r--src/librustc/hir/mod.rs10
-rw-r--r--src/librustc/hir/print.rs9
-rw-r--r--src/librustc_const_eval/_match.rs51
-rw-r--r--src/librustc_const_eval/pattern.rs20
-rw-r--r--src/librustc_mir/build/matches/mod.rs4
-rw-r--r--src/librustc_mir/build/matches/test.rs11
-rw-r--r--src/librustc_passes/consts.rs18
-rw-r--r--src/librustc_passes/diagnostics.rs18
-rw-r--r--src/librustc_typeck/check/_match.rs2
-rw-r--r--src/libsyntax/ast.rs10
-rw-r--r--src/libsyntax/feature_gate.rs9
-rw-r--r--src/libsyntax/fold.rs14
-rw-r--r--src/libsyntax/parse/parser.rs43
-rw-r--r--src/libsyntax/print/pprust.rs9
-rw-r--r--src/libsyntax/visit.rs4
-rw-r--r--src/test/compile-fail/exclusive_range_pattern_syntax_collision.rs18
-rw-r--r--src/test/compile-fail/exclusive_range_pattern_syntax_collision2.rs18
-rw-r--r--src/test/compile-fail/exclusive_range_pattern_syntax_collision3.rs18
-rw-r--r--src/test/compile-fail/feature-gate-exclusive-range-pattern.rs (renamed from src/test/parse-fail/pat-range-bad-dots.rs)4
-rw-r--r--src/test/compile-fail/match-range-fail-2.rs8
-rw-r--r--src/test/parse-fail/pat-ranges-4.rs2
-rw-r--r--src/test/parse-fail/pat-tuple-5.rs2
-rw-r--r--src/test/run-pass/match-range.rs19
25 files changed, 269 insertions, 67 deletions
diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs
index 7527d7fb318..fb8d6618e89 100644
--- a/src/librustc/hir/intravisit.rs
+++ b/src/librustc/hir/intravisit.rs
@@ -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)
         }
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 89afc08fa24..88d461cab9f 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -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(),
@@ -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,
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index b631d67e473..3d9fa4e5a06 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -573,6 +573,12 @@ pub enum BindingMode {
 }
 
 #[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 (`_`)
     Wild,
@@ -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>>),
diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs
index 58c677fb507..d4bb2d37091 100644
--- a/src/librustc/hir/print.rs
+++ b/src/librustc/hir/print.rs
@@ -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};
 
@@ -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) => {
diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs
index f4b3646fce0..bb5dacf71e1 100644
--- a/src/librustc_const_eval/_match.rs
+++ b/src/librustc_const_eval/_match.rs
@@ -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;
@@ -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),
 }
@@ -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)
@@ -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>(
@@ -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,
@@ -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,
diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs
index fbd15b6eb10..3235e06b806 100644
--- a/src/librustc_const_eval/pattern.rs
+++ b/src/librustc_const_eval/pattern.rs
@@ -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;
 
@@ -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
@@ -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 } |
@@ -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
                 }
@@ -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,
diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs
index 63bb1bf20c0..5c3d93ffda9 100644
--- a/src/librustc_mir/build/matches/mod.rs
+++ b/src/librustc_mir/build/matches/mod.rs
@@ -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;
@@ -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
diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs
index 8b4a013bad0..a35b2925ae2 100644
--- a/src/librustc_mir/build/matches/test.rs
+++ b/src/librustc_mir/build/matches/test.rs
@@ -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;
 
@@ -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(),
                     },
                 }
             }
@@ -324,7 +326,7 @@ 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());
@@ -332,7 +334,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
 
                 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]
             }
diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs
index 8f12817511a..4bdc113e516 100644
--- a/src/librustc_passes/consts.rs
+++ b/src/librustc_passes/consts.rs
@@ -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};
@@ -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) |
diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs
index b2ef1abd2a4..c414d718368 100644
--- a/src/librustc_passes/diagnostics.rs
+++ b/src/librustc_passes/diagnostics.rs
@@ -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! {
diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs
index 99c664cebea..3b3c0257c4a 100644
--- a/src/librustc_typeck/check/_match.rs
+++ b/src/librustc_typeck/check/_match.rs
@@ -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);
 
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 58487655ac1..d18215be3e6 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -548,6 +548,12 @@ pub enum BindingMode {
 }
 
 #[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 (`_`)
     Wild,
@@ -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>>),
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index c25020caf85..3ce3e36d3c2 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -26,7 +26,7 @@ use self::AttributeType::*;
 use self::AttributeGate::*;
 
 use abi::Abi;
-use ast::{self, NodeId, PatKind};
+use ast::{self, NodeId, PatKind, RangeEnd};
 use attr;
 use codemap::{CodeMap, Spanned};
 use syntax_pos::Span;
@@ -248,6 +248,9 @@ declare_features! (
     // a...b and ...b
     (active, inclusive_range_syntax, "1.7.0", Some(28237)),
 
+    // X..Y patterns
+    (active, exclusive_range_pattern, "1.11.0", Some(37854)),
+
     // impl specialization (RFC 1210)
     (active, specialization, "1.7.0", Some(31844)),
 
@@ -1285,6 +1288,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                     }
                 }
             }
+            PatKind::Range(_, _, RangeEnd::Excluded) => {
+                gate_feature_post!(&self, exclusive_range_pattern, pattern.span,
+                                   "exclusive range pattern syntax is experimental");
+            }
             _ => {}
         }
         visit::walk_pat(self, pattern)
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index c42bf24578f..1ee070cb92d 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -112,6 +112,10 @@ pub trait Folder : Sized {
         e.map(|e| noop_fold_expr(e, self))
     }
 
+    fn fold_range_end(&mut self, re: RangeEnd) -> RangeEnd {
+        noop_fold_range_end(re, self)
+    }
+
     fn fold_opt_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
         noop_fold_opt_expr(e, self)
     }
@@ -1093,8 +1097,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
             }
             PatKind::Box(inner) => PatKind::Box(folder.fold_pat(inner)),
             PatKind::Ref(inner, mutbl) => PatKind::Ref(folder.fold_pat(inner), mutbl),
-            PatKind::Range(e1, e2) => {
-                PatKind::Range(folder.fold_expr(e1), folder.fold_expr(e2))
+            PatKind::Range(e1, e2, end) => {
+                PatKind::Range(folder.fold_expr(e1),
+                               folder.fold_expr(e2),
+                               folder.fold_range_end(end))
             },
             PatKind::Slice(before, slice, after) => {
                 PatKind::Slice(before.move_map(|x| folder.fold_pat(x)),
@@ -1107,6 +1113,10 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
     })
 }
 
+pub fn noop_fold_range_end<T: Folder>(end: RangeEnd, _folder: &mut T) -> RangeEnd {
+    end
+}
+
 pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mut T) -> Expr {
     Expr {
         node: match node {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 2a0a7385998..b564cc337e4 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -38,6 +38,7 @@ use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
 use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
 use ast::{Visibility, WhereClause};
 use ast::{BinOpKind, UnOp};
+use ast::RangeEnd;
 use {ast, attr};
 use codemap::{self, CodeMap, Spanned, spanned, respan};
 use syntax_pos::{self, Span, Pos, BytePos, mk_sp};
@@ -3329,8 +3330,7 @@ impl<'a> Parser<'a> {
             }
 
             if before_slice {
-                if self.check(&token::DotDot) {
-                    self.bump();
+                if self.eat(&token::DotDot) {
 
                     if self.check(&token::Comma) ||
                             self.check(&token::CloseDelim(token::Bracket)) {
@@ -3346,8 +3346,7 @@ impl<'a> Parser<'a> {
             }
 
             let subpat = self.parse_pat()?;
-            if before_slice && self.check(&token::DotDot) {
-                self.bump();
+            if before_slice && self.eat(&token::DotDot) {
                 slice = Some(subpat);
                 before_slice = false;
             } else if before_slice {
@@ -3462,6 +3461,22 @@ impl<'a> Parser<'a> {
         }
     }
 
+    // helper function to decide whether to parse as ident binding or to try to do
+    // something more complex like range patterns
+    fn parse_as_ident(&mut self) -> bool {
+        self.look_ahead(1, |t| match *t {
+            token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) |
+            token::DotDotDot | token::ModSep | token::Not => Some(false),
+            // ensure slice patterns [a, b.., c] and [a, b, c..] don't go into the
+            // range pattern branch
+            token::DotDot => None,
+            _ => Some(true),
+        }).unwrap_or_else(|| self.look_ahead(2, |t| match *t {
+            token::Comma | token::CloseDelim(token::Bracket) => true,
+            _ => false,
+        }))
+    }
+
     /// Parse a pattern.
     pub fn parse_pat(&mut self) -> PResult<'a, P<Pat>> {
         maybe_whole!(self, NtPat, |x| x);
@@ -3511,11 +3526,7 @@ impl<'a> Parser<'a> {
                 let subpat = self.parse_pat()?;
                 pat = PatKind::Box(subpat);
             } else if self.token.is_ident() && !self.token.is_any_keyword() &&
-                      self.look_ahead(1, |t| match *t {
-                          token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) |
-                          token::DotDotDot | token::ModSep | token::Not => false,
-                          _ => true,
-                      }) {
+                      self.parse_as_ident() {
                 // Parse ident @ pat
                 // This can give false positives and parse nullary enums,
                 // they are dealt with later in resolve
@@ -3542,14 +3553,19 @@ impl<'a> Parser<'a> {
                         let mac = spanned(lo, self.prev_span.hi, Mac_ { path: path, tts: tts });
                         pat = PatKind::Mac(mac);
                     }
-                    token::DotDotDot => {
+                    token::DotDotDot | token::DotDot => {
+                        let end_kind = match self.token {
+                            token::DotDot => RangeEnd::Excluded,
+                            token::DotDotDot => RangeEnd::Included,
+                            _ => panic!("can only parse `..` or `...` for ranges (checked above)"),
+                        };
                         // Parse range
                         let hi = self.prev_span.hi;
                         let begin =
                               self.mk_expr(lo, hi, ExprKind::Path(qself, path), ThinVec::new());
                         self.bump();
                         let end = self.parse_pat_range_end()?;
-                        pat = PatKind::Range(begin, end);
+                        pat = PatKind::Range(begin, end, end_kind);
                     }
                     token::OpenDelim(token::Brace) => {
                         if qself.is_some() {
@@ -3583,7 +3599,10 @@ impl<'a> Parser<'a> {
                     Ok(begin) => {
                         if self.eat(&token::DotDotDot) {
                             let end = self.parse_pat_range_end()?;
-                            pat = PatKind::Range(begin, end);
+                            pat = PatKind::Range(begin, end, RangeEnd::Included);
+                        } else if self.eat(&token::DotDot) {
+                            let end = self.parse_pat_range_end()?;
+                            pat = PatKind::Range(begin, end, RangeEnd::Excluded);
                         } else {
                             pat = PatKind::Lit(begin);
                         }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 061e871fe52..e75671d27aa 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -11,7 +11,7 @@
 pub use self::AnnNode::*;
 
 use abi::{self, Abi};
-use ast::{self, BlockCheckMode, PatKind};
+use ast::{self, BlockCheckMode, PatKind, RangeEnd};
 use ast::{SelfKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
 use ast::Attribute;
 use util::parser::AssocOp;
@@ -2544,10 +2544,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) => {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 1fcfce18948..bfab7a250c6 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -440,9 +440,9 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a 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)
+            visitor.visit_expr(upper_bound);
         }
         PatKind::Wild => (),
         PatKind::Slice(ref prepatterns, ref slice_pattern, ref postpatterns) => {
diff --git a/src/test/compile-fail/exclusive_range_pattern_syntax_collision.rs b/src/test/compile-fail/exclusive_range_pattern_syntax_collision.rs
new file mode 100644
index 00000000000..69e5898ed4d
--- /dev/null
+++ b/src/test/compile-fail/exclusive_range_pattern_syntax_collision.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+    match [5..4, 99..105, 43..44] {
+        [_, 99.., _] => {}, //~ ERROR unexpected token: `,`
+        _ => {},
+    }
+}
diff --git a/src/test/compile-fail/exclusive_range_pattern_syntax_collision2.rs b/src/test/compile-fail/exclusive_range_pattern_syntax_collision2.rs
new file mode 100644
index 00000000000..9212ea86118
--- /dev/null
+++ b/src/test/compile-fail/exclusive_range_pattern_syntax_collision2.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+    match [5..4, 99..105, 43..44] {
+        [_, 99..] => {}, //~ ERROR unexpected token: `]`
+        _ => {},
+    }
+}
diff --git a/src/test/compile-fail/exclusive_range_pattern_syntax_collision3.rs b/src/test/compile-fail/exclusive_range_pattern_syntax_collision3.rs
new file mode 100644
index 00000000000..d83305857ac
--- /dev/null
+++ b/src/test/compile-fail/exclusive_range_pattern_syntax_collision3.rs
@@ -0,0 +1,18 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(exclusive_range_pattern)]
+
+fn main() {
+    match [5..4, 99..105, 43..44] {
+        [..9, 99..100, _] => {}, //~ ERROR expected one of `,` or `]`, found `9`
+        _ => {},
+    }
+}
diff --git a/src/test/parse-fail/pat-range-bad-dots.rs b/src/test/compile-fail/feature-gate-exclusive-range-pattern.rs
index d2afd26ea9a..e0b6d5986ff 100644
--- a/src/test/parse-fail/pat-range-bad-dots.rs
+++ b/src/test/compile-fail/feature-gate-exclusive-range-pattern.rs
@@ -8,11 +8,9 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// compile-flags: -Z parse-only
-
 pub fn main() {
     match 22 {
-        0 .. 3 => {} //~ ERROR expected one of `...`, `=>`, `if`, or `|`, found `..`
+        0 .. 3 => {} //~ ERROR exclusive range pattern syntax is experimental
         _ => {}
     }
 }
diff --git a/src/test/compile-fail/match-range-fail-2.rs b/src/test/compile-fail/match-range-fail-2.rs
index e30f783ce36..91ad2b3507a 100644
--- a/src/test/compile-fail/match-range-fail-2.rs
+++ b/src/test/compile-fail/match-range-fail-2.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![feature(exclusive_range_pattern)]
+
 fn main() {
     match 5 {
         6 ... 1 => { }
@@ -15,6 +17,12 @@ fn main() {
     };
     //~^^^ ERROR lower range bound must be less than or equal to upper
 
+    match 5 {
+        0 .. 0 => { }
+        _ => { }
+    };
+    //~^^^ ERROR lower range bound must be less than upper
+
     match 5u64 {
         0xFFFF_FFFF_FFFF_FFFF ... 1 => { }
         _ => { }
diff --git a/src/test/parse-fail/pat-ranges-4.rs b/src/test/parse-fail/pat-ranges-4.rs
index 50dcb899527..4bbf387d1c0 100644
--- a/src/test/parse-fail/pat-ranges-4.rs
+++ b/src/test/parse-fail/pat-ranges-4.rs
@@ -11,5 +11,5 @@
 // Parsing of range patterns
 
 fn main() {
-    let 10 - 3 ... 10 = 8; //~ error: expected one of `...`, `:`, `;`, or `=`, found `-`
+    let 10 - 3 ... 10 = 8; //~ error: expected one of `...`, `..`, `:`, `;`, or `=`, found `-`
 }
diff --git a/src/test/parse-fail/pat-tuple-5.rs b/src/test/parse-fail/pat-tuple-5.rs
index 145d1f9d8ec..d528b97b5de 100644
--- a/src/test/parse-fail/pat-tuple-5.rs
+++ b/src/test/parse-fail/pat-tuple-5.rs
@@ -12,6 +12,6 @@
 
 fn main() {
     match 0 {
-        (pat ..) => {} //~ ERROR expected one of `)`, `,`, or `@`, found `..`
+        (pat ..) => {} //~ ERROR unexpected token: `)`
     }
 }
diff --git a/src/test/run-pass/match-range.rs b/src/test/run-pass/match-range.rs
index 0b2e19d6c79..cf695a77ce1 100644
--- a/src/test/run-pass/match-range.rs
+++ b/src/test/run-pass/match-range.rs
@@ -7,17 +7,26 @@
 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
-//
+
+#![feature(exclusive_range_pattern)]
 
 pub fn main() {
     match 5_usize {
       1_usize...5_usize => {}
       _ => panic!("should match range"),
     }
+    match 1_usize {
+        1_usize..5_usize => {}
+        _ => panic!("should match range start"),
+    }
     match 5_usize {
       6_usize...7_usize => panic!("shouldn't match range"),
       _ => {}
     }
+    match 7_usize {
+        6_usize..7_usize => panic!("shouldn't match range end"),
+        _ => {},
+    }
     match 5_usize {
       1_usize => panic!("should match non-first range"),
       2_usize...6_usize => {}
@@ -39,4 +48,12 @@ pub fn main() {
       -3.6...3.6 => {}
       _ => panic!("should match negative float range")
     }
+    match 3.5 {
+        0.0..3.5 => panic!("should not match the range end"),
+        _ => {},
+    }
+    match 0.0 {
+        0.0..3.5 => {},
+        _ => panic!("should match the range start"),
+    }
 }