From c951341a78a9d3cc7c73130ca08cbc273af72082 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 10 Jan 2017 22:13:53 +0100 Subject: [PATCH 1/3] add exclusive range patterns under a feature gate --- src/librustc/hir/intravisit.rs | 2 +- src/librustc/hir/lowering.rs | 13 ++++- src/librustc/hir/mod.rs | 10 +++- src/librustc/hir/print.rs | 9 ++-- src/librustc_const_eval/_match.rs | 51 ++++++++++++------- src/librustc_const_eval/pattern.rs | 20 +++++--- src/librustc_mir/build/matches/mod.rs | 4 +- src/librustc_mir/build/matches/test.rs | 11 ++-- src/librustc_passes/consts.rs | 18 ++++++- src/librustc_passes/diagnostics.rs | 18 +++++++ src/librustc_typeck/check/_match.rs | 2 +- src/libsyntax/ast.rs | 10 +++- src/libsyntax/feature_gate.rs | 9 +++- src/libsyntax/fold.rs | 14 ++++- src/libsyntax/parse/parser.rs | 43 +++++++++++----- src/libsyntax/print/pprust.rs | 9 ++-- src/libsyntax/visit.rs | 4 +- src/test/compile-fail/match-range-fail-2.rs | 8 +++ .../pat-range-bad-dots.rs | 4 +- src/test/parse-fail/pat-ranges-4.rs | 2 +- src/test/parse-fail/pat-tuple-5.rs | 2 +- src/test/run-pass/match-range.rs | 19 ++++++- 22 files changed, 215 insertions(+), 67 deletions(-) rename src/test/{parse-fail => compile-fail}/pat-range-bad-dots.rs (81%) diff --git a/src/librustc/hir/intravisit.rs b/src/librustc/hir/intravisit.rs index 7527d7fb318dd..fb8d6618e89a5 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 69ffbd9968aba..fb403cc22e614 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1249,8 +1249,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(), @@ -1263,6 +1265,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 b631d67e47365..3d9fa4e5a0621 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -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 (`_`) @@ -603,8 +609,8 @@ pub enum PatKind { Ref(P, Mutability), /// A literal Lit(P), - /// A range pattern, e.g. `1...2` - Range(P, P), + /// A range pattern, e.g. `1...2` or `1..2` + Range(P, P, RangeEnd), /// `[a, b, ..i, y, z]` is represented as: /// `PatKind::Slice(box [a, b], Some(i), box [y, z])` Slice(HirVec>, Option>, HirVec>), diff --git a/src/librustc/hir/print.rs b/src/librustc/hir/print.rs index 377f2ea26aba9..8ce5fc87f691e 100644 --- a/src/librustc/hir/print.rs +++ b/src/librustc/hir/print.rs @@ -24,7 +24,7 @@ use syntax_pos::{self, BytePos}; use errors; use hir; -use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; +use hir::{PatKind, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier, RangeEnd}; use std::io::{self, Write, Read}; @@ -1735,10 +1735,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 f4b3646fce02c..bb5dacf71e175 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 { - 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 fbd15b6eb1035..3235e06b8065f 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 63bb1bf20c06c..5c3d93ffda9c1 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 8b4a013bad0a3..a35b2925ae271 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 8f12817511ad3..4bdc113e51698 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 b2ef1abd2a4e7..c414d71836818 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 99c664cebeaea..3b3c0257c4a41 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 58487655ac187..d18215be3e638 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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 (`_`) @@ -583,8 +589,8 @@ pub enum PatKind { Ref(P, Mutability), /// A literal Lit(P), - /// A range pattern, e.g. `1...2` - Range(P, P), + /// A range pattern, e.g. `1...2` or `1..2` + Range(P, P, RangeEnd), /// `[a, b, ..i, y, z]` is represented as: /// `PatKind::Slice(box [a, b], Some(i), box [y, z])` Slice(Vec>, Option>, Vec>), diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ba6e752d310e0..17b9fdf2e7ccf 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)), @@ -1262,6 +1265,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 2e5ce739fb34a..bc8d13d800c96 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) -> Option> { noop_fold_opt_expr(e, self) } @@ -1095,8 +1099,10 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P { } 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)), @@ -1109,6 +1115,10 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P { }) } +pub fn noop_fold_range_end(end: RangeEnd, _folder: &mut T) -> RangeEnd { + end +} + pub fn noop_fold_expr(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 d1a683b0bd5b8..1ddfa16e62251 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}; @@ -3445,8 +3446,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)) { @@ -3462,8 +3462,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 { @@ -3578,6 +3577,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> { maybe_whole!(self, NtPat, |x| x); @@ -3627,11 +3642,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 @@ -3658,14 +3669,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() { @@ -3699,7 +3715,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 baa3ebec85469..2c513df88de6a 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; @@ -2553,10 +2553,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 1fcfce1894859..bfab7a250c67f 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/match-range-fail-2.rs b/src/test/compile-fail/match-range-fail-2.rs index e30f783ce3637..91ad2b3507abd 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-range-bad-dots.rs b/src/test/compile-fail/pat-range-bad-dots.rs similarity index 81% rename from src/test/parse-fail/pat-range-bad-dots.rs rename to src/test/compile-fail/pat-range-bad-dots.rs index d2afd26ea9a3d..e0b6d5986ff37 100644 --- a/src/test/parse-fail/pat-range-bad-dots.rs +++ b/src/test/compile-fail/pat-range-bad-dots.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/parse-fail/pat-ranges-4.rs b/src/test/parse-fail/pat-ranges-4.rs index 50dcb8995279c..4bbf387d1c0da 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 145d1f9d8ec76..d528b97b5de89 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 0b2e19d6c7930..cf695a77ce1f1 100644 --- a/src/test/run-pass/match-range.rs +++ b/src/test/run-pass/match-range.rs @@ -7,17 +7,26 @@ // , 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"), + } } From 63a8eb937099e8281b4fd93332a57fdaed9ca16c Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 19 Jan 2017 15:13:44 +0100 Subject: [PATCH 2/3] check exclusive_range_pattern feature gate --- ...-range-bad-dots.rs => feature-gate-exclusive-range-pattern.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/compile-fail/{pat-range-bad-dots.rs => feature-gate-exclusive-range-pattern.rs} (100%) diff --git a/src/test/compile-fail/pat-range-bad-dots.rs b/src/test/compile-fail/feature-gate-exclusive-range-pattern.rs similarity index 100% rename from src/test/compile-fail/pat-range-bad-dots.rs rename to src/test/compile-fail/feature-gate-exclusive-range-pattern.rs From 98fef41d63a91759790f5bbe4ac746b5c1a670ba Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Thu, 19 Jan 2017 15:14:02 +0100 Subject: [PATCH 3/3] test slice patterns with exclusive range patterns --- ...exclusive_range_pattern_syntax_collision.rs | 18 ++++++++++++++++++ ...xclusive_range_pattern_syntax_collision2.rs | 18 ++++++++++++++++++ ...xclusive_range_pattern_syntax_collision3.rs | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/test/compile-fail/exclusive_range_pattern_syntax_collision.rs create mode 100644 src/test/compile-fail/exclusive_range_pattern_syntax_collision2.rs create mode 100644 src/test/compile-fail/exclusive_range_pattern_syntax_collision3.rs 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 0000000000000..69e5898ed4d16 --- /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 or the MIT license +// , 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 0000000000000..9212ea8611862 --- /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 or the MIT license +// , 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 0000000000000..d83305857aca6 --- /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 or the MIT license +// , 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` + _ => {}, + } +}