From c5d58de665819f7330b3d64bdd084d25a412830a Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 16:48:58 -0500 Subject: [PATCH 01/19] core: add inclusive ranges to core::ops Since it removes the old iter::{range_inclusive, RangeInclusive} which were unstable and deprecated, this is a [breaking-change] on nightly. --- src/libcore/iter.rs | 185 +++++++++++++++++++++++--------------------- src/libcore/ops.rs | 72 ++++++++++++++++- src/libstd/lib.rs | 1 - 3 files changed, 167 insertions(+), 91 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 96302acb8d9..1f5ef6be0bc 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -4375,95 +4375,6 @@ impl Iterator for StepBy> where } } -/// An iterator over the range [start, stop] -#[derive(Clone)] -#[unstable(feature = "range_inclusive", - reason = "likely to be replaced by range notation and adapters", - issue = "27777")] -#[rustc_deprecated(since = "1.5.0", reason = "replaced with ... syntax")] -#[allow(deprecated)] -pub struct RangeInclusive { - range: ops::Range, - done: bool, -} - -/// Returns an iterator over the range [start, stop]. -#[inline] -#[unstable(feature = "range_inclusive", - reason = "likely to be replaced by range notation and adapters", - issue = "27777")] -#[rustc_deprecated(since = "1.5.0", reason = "replaced with ... syntax")] -#[allow(deprecated)] -pub fn range_inclusive(start: A, stop: A) -> RangeInclusive - where A: Step + One + Clone -{ - RangeInclusive { - range: start..stop, - done: false, - } -} - -#[unstable(feature = "range_inclusive", - reason = "likely to be replaced by range notation and adapters", - issue = "27777")] -#[rustc_deprecated(since = "1.5.0", reason = "replaced with ... syntax")] -#[allow(deprecated)] -impl Iterator for RangeInclusive where - A: PartialEq + Step + One + Clone, - for<'a> &'a A: Add<&'a A, Output = A> -{ - type Item = A; - - #[inline] - fn next(&mut self) -> Option { - self.range.next().or_else(|| { - if !self.done && self.range.start == self.range.end { - self.done = true; - Some(self.range.end.clone()) - } else { - None - } - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (lo, hi) = self.range.size_hint(); - if self.done { - (lo, hi) - } else { - let lo = lo.saturating_add(1); - let hi = hi.and_then(|x| x.checked_add(1)); - (lo, hi) - } - } -} - -#[unstable(feature = "range_inclusive", - reason = "likely to be replaced by range notation and adapters", - issue = "27777")] -#[rustc_deprecated(since = "1.5.0", reason = "replaced with ... syntax")] -#[allow(deprecated)] -impl DoubleEndedIterator for RangeInclusive where - A: PartialEq + Step + One + Clone, - for<'a> &'a A: Add<&'a A, Output = A>, - for<'a> &'a A: Sub -{ - #[inline] - fn next_back(&mut self) -> Option { - if self.range.end > self.range.start { - let result = self.range.end.clone(); - self.range.end = &self.range.end - &A::one(); - Some(result) - } else if !self.done && self.range.start == self.range.end { - self.done = true; - Some(self.range.end.clone()) - } else { - None - } - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Iterator for StepBy> { type Item = A; @@ -4505,6 +4416,9 @@ macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for ops::Range<$t> { } + + #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] + impl ExactSizeIterator for ops::RangeInclusive<$t> { } )*) } @@ -4568,6 +4482,99 @@ impl Iterator for ops::RangeFrom where } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl Iterator for ops::RangeInclusive where + for<'a> &'a A: Add<&'a A, Output = A> +{ + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + use ops::RangeInclusive::*; + + // this function has a sort of odd structure due to borrowck issues + // we may need to replace self, so borrows of self.start and self.end need to end early + + let (finishing, n) = match *self { + Empty { .. } => (None, None), // empty iterators yield no values + + NonEmpty { ref mut start, ref mut end } => { + let one = A::one(); + if start <= end { + let mut n = &*start + &one; + mem::swap(&mut n, start); + + // if the iterator is done iterating, it will change from NonEmpty to Empty + // to avoid unnecessary drops or clones, we'll reuse either start or end + // (they are equal now, so it doesn't matter which) + // to pull out end, we need to swap something back in -- use the previously + // created A::one() as a dummy value + + (if n == *end { Some(mem::replace(end, one)) } else { None }, + // ^ are we done yet? + Some(n)) // < the value to output + } else { + (Some(mem::replace(start, one)), None) + } + } + }; + + // turn into an empty iterator if this is the last value + if let Some(end) = finishing { + *self = Empty { at: end }; + } + + n + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + use ops::RangeInclusive::*; + + match *self { + Empty { .. } => (0, Some(0)), + + NonEmpty { ref start, ref end } => + match Step::steps_between(start, end, &A::one()) { + Some(hint) => (hint.saturating_add(1), hint.checked_add(1)), + None => (0, None), + } + } + } +} + +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl DoubleEndedIterator for ops::RangeInclusive where + for<'a> &'a A: Add<&'a A, Output = A>, + for<'a> &'a A: Sub<&'a A, Output = A> +{ + #[inline] + fn next_back(&mut self) -> Option { + use ops::RangeInclusive::*; + + // see Iterator::next for comments + + let (finishing, n) = match *self { + Empty { .. } => return None, + + NonEmpty { ref mut start, ref mut end } => { + let one = A::one(); + let mut n = &*end - &one; + mem::swap(&mut n, end); + + (if n == *start { Some(mem::replace(start, one)) } else { None }, + n) + } + }; + + if let Some(start) = finishing { + *self = Empty { at: start }; + } + + Some(n) + } +} + /// An iterator that repeats an element endlessly. /// /// This `struct` is created by the [`repeat()`] function. See its documentation for more. diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index d1c5b175bb0..cf8f4279a1d 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -67,8 +67,11 @@ #![stable(feature = "rust1", since = "1.0.0")] -use marker::{Sized, Unsize}; +use cmp::PartialOrd; use fmt; +use convert::From; +use marker::{Sized, Unsize}; +use num::One; /// The `Drop` trait is used to run some code when a value goes out of scope. /// This is sometimes called a 'destructor'. @@ -1530,6 +1533,73 @@ impl fmt::Debug for RangeTo { } } +/// An inclusive range which is bounded at both ends. +#[derive(Copy, Clone, PartialEq, Eq)] +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +pub enum RangeInclusive { + /// Empty range (iteration has finished) + Empty { + /// The point at which iteration finished + at: Idx + }, + /// Non-empty range (iteration will yield value(s)) + NonEmpty { + /// The lower bound of the range (inclusive). + start: Idx, + /// The upper bound of the range (inclusive). + end: Idx, + }, +} + +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl fmt::Debug for RangeInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + use self::RangeInclusive::*; + + match *self { + Empty { ref at } => write!(fmt, "[empty range @ {:?}]", at), + NonEmpty { ref start, ref end } => write!(fmt, "{:?}...{:?}", start, end), + } + } +} + +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl> From> for RangeInclusive { + fn from(range: Range) -> RangeInclusive { + use self::RangeInclusive::*; + + if range.start < range.end { + NonEmpty { + start: range.start, + end: range.end - Idx::one() // can't underflow because end > start >= MIN + } + } else { + Empty { + at: range.start + } + } + } +} + +/// An inclusive range which is only bounded above. +#[derive(Copy, Clone, PartialEq, Eq)] +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +pub struct RangeToInclusive { + /// The upper bound of the range (inclusive) + #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] + pub end: Idx, +} + +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl fmt::Debug for RangeToInclusive { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "...{:?}", self.end) + } +} + +// RangeToInclusive cannot impl From> +// because underflow would be possible with (..0).into() + /// The `Deref` trait is used to specify the functionality of dereferencing /// operations, like `*v`. /// diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f9e7c1fede2..dd84cba370c 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -247,7 +247,6 @@ #![feature(optin_builtin_traits)] #![feature(placement_in_syntax)] #![feature(rand)] -#![feature(range_inclusive)] #![feature(raw)] #![feature(repr_simd)] #![feature(reflect_marker)] From 5daf13cae371ce4ee90450a1d3006b53395a40d7 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:23:31 -0500 Subject: [PATCH 02/19] libsyntax: parse inclusive ranges --- src/libsyntax/ast.rs | 13 +++++- src/libsyntax/fold.rs | 5 +- src/libsyntax/parse/parser.rs | 87 ++++++++++++++++++++--------------- src/libsyntax/parse/token.rs | 2 +- src/libsyntax/print/pprust.rs | 8 +++- src/libsyntax/util/parser.rs | 14 ++++-- src/libsyntax/visit.rs | 2 +- 7 files changed, 81 insertions(+), 50 deletions(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 23bb6fd141a..0dbfb2c7be6 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -886,6 +886,15 @@ impl fmt::Debug for Expr { } } +/// Limit types of a range (inclusive or exclusive) +#[derive(Copy, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub enum RangeLimits { + /// Inclusive at the beginning, exclusive at the end + HalfOpen, + /// Inclusive at the beginning and end + Closed, +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum ExprKind { /// A `box x` expression. @@ -974,8 +983,8 @@ pub enum ExprKind { TupField(P, Spanned), /// An indexing operation (`foo[2]`) Index(P, P), - /// A range (`1..2`, `1..`, or `..2`) - Range(Option>, Option>), + /// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`) + Range(Option>, Option>, RangeLimits), /// Variable reference, possibly containing `::` and/or type /// parameters, e.g. foo::bar::. diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d75e8f796ae..591c1295d66 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1273,9 +1273,10 @@ pub fn noop_fold_expr(Expr {id, node, span, attrs}: Expr, folder: &mu ExprKind::Index(el, er) => { ExprKind::Index(folder.fold_expr(el), folder.fold_expr(er)) } - ExprKind::Range(e1, e2) => { + ExprKind::Range(e1, e2, lim) => { ExprKind::Range(e1.map(|x| folder.fold_expr(x)), - e2.map(|x| folder.fold_expr(x))) + e2.map(|x| folder.fold_expr(x)), + lim) } ExprKind::Path(qself, path) => { let qself = qself.map(|QSelf { ty, position }| { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b5d29a0d6db..8b563ef00f3 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -20,7 +20,7 @@ use ast::{BlockCheckMode, CaptureBy}; use ast::{Constness, Crate, CrateConfig}; use ast::{Decl, DeclKind}; use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf}; -use ast::{Expr, ExprKind}; +use ast::{Expr, ExprKind, RangeLimits}; use ast::{Field, FnDecl}; use ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; use ast::{Ident, ImplItem, Item, ItemKind}; @@ -2054,9 +2054,10 @@ impl<'a> Parser<'a> { pub fn mk_range(&mut self, start: Option>, - end: Option>) - -> ast::ExprKind { - ExprKind::Range(start, end) + end: Option>, + limits: RangeLimits) + -> ast::Expr_ { + ExprKind::Range(start, end, limits) } pub fn mk_field(&mut self, expr: P, ident: ast::SpannedIdent) -> ast::ExprKind { @@ -2894,7 +2895,7 @@ impl<'a> Parser<'a> { LhsExpr::AttributesParsed(attrs) => Some(attrs), _ => None, }; - if self.token == token::DotDot { + if self.token == token::DotDot || self.token == token::DotDotDot { return self.parse_prefix_range_expr(attrs); } else { try!(self.parse_prefix_expr(attrs)) @@ -2940,32 +2941,32 @@ impl<'a> Parser<'a> { ExprKind::Type(lhs, rhs), None); continue } else if op == AssocOp::DotDot { - // If we didn’t have to handle `x..`, it would be pretty easy to generalise - // it to the Fixity::None code. - // - // We have 2 alternatives here: `x..y` and `x..` The other two variants are - // handled with `parse_prefix_range_expr` call above. - let rhs = if self.is_at_start_of_range_notation_rhs() { - let rhs = self.parse_assoc_expr_with(op.precedence() + 1, - LhsExpr::NotYetParsed); - match rhs { - Ok(e) => Some(e), - Err(mut e) => { - e.cancel(); - None - } + // If we didn’t have to handle `x..`, it would be pretty easy to generalise + // it to the Fixity::None code. + // + // We have 2 alternatives here: `x..y` and `x..` The other two variants are + // handled with `parse_prefix_range_expr` call above. + let rhs = if self.is_at_start_of_range_notation_rhs() { + let rhs = self.parse_assoc_expr_with(op.precedence() + 1, + LhsExpr::NotYetParsed); + match rhs { + Ok(e) => Some(e), + Err(mut e) => { + e.cancel(); + None } - } else { - None - }; - let (lhs_span, rhs_span) = (lhs_span, if let Some(ref x) = rhs { - x.span - } else { - cur_op_span - }); - let r = self.mk_range(Some(lhs), rhs); - lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None); - break + } + } else { + None + }; + let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs { + x.span + } else { + cur_op_span + }); + let r = self.mk_range(Some(lhs), rhs, RangeLimits::HalfOpen); + lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None); + break } let rhs = try!(match op.fixity() { @@ -2981,8 +2982,8 @@ impl<'a> Parser<'a> { this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed) }), - // We currently have no non-associative operators that are not handled above by - // the special cases. The code is here only for future convenience. + // the only operator handled here is `...` (the other non-associative operators are + // special-cased above) Fixity::None => self.with_res( restrictions - Restrictions::RESTRICTION_STMT_EXPR, |this| { @@ -3023,6 +3024,11 @@ impl<'a> Parser<'a> { let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None) } + AssocOp::DotDotDot => { + let (lhs_span, rhs_span) = (lhs.span, rhs.span); + let r = self.mk_range(Some(lhs), Some(rhs), RangeLimits::Closed); + self.mk_expr(lhs_span.lo, rhs_span.hi, r, None) + } AssocOp::As | AssocOp::Colon | AssocOp::DotDot => { self.bug("As, Colon or DotDot branch reached") } @@ -3054,18 +3060,19 @@ impl<'a> Parser<'a> { } } - /// Parse prefix-forms of range notation: `..expr` and `..` + /// Parse prefix-forms of range notation: `..expr`, `..`, `...expr` fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option) -> PResult<'a, P> { - debug_assert!(self.token == token::DotDot); + debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot); + let tok = self.token.clone(); let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs)); let lo = self.span.lo; let mut hi = self.span.hi; self.bump(); let opt_end = if self.is_at_start_of_range_notation_rhs() { - // RHS must be parsed with more associativity than DotDot. - let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1; + // RHS must be parsed with more associativity than the dots. + let next_prec = AssocOp::from_token(&tok).unwrap().precedence() + 1; Some(try!(self.parse_assoc_expr_with(next_prec, LhsExpr::NotYetParsed) .map(|x|{ @@ -3075,7 +3082,13 @@ impl<'a> Parser<'a> { } else { None }; - let r = self.mk_range(None, opt_end); + let r = self.mk_range(None, + opt_end, + if tok == token::DotDot { + RangeLimits::HalfOpen + } else { + RangeLimits::Closed + }); Ok(self.mk_expr(lo, hi, r, attrs)) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index accbb54c629..0c59c240bcd 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -196,7 +196,7 @@ impl Token { BinOp(Or) => true, // in lambda syntax OrOr => true, // in lambda syntax AndAnd => true, // double borrow - DotDot => true, // range notation + DotDot | DotDotDot => true, // range notation ModSep => true, Interpolated(NtExpr(..)) => true, Interpolated(NtIdent(..)) => true, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index b4e08d65a0a..8d81787d922 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2163,11 +2163,15 @@ impl<'a> State<'a> { try!(self.print_expr(&index)); try!(word(&mut self.s, "]")); } - ast::ExprKind::Range(ref start, ref end) => { + ast::ExprKing::Range(ref start, ref end, limits) => { if let &Some(ref e) = start { try!(self.print_expr(&e)); } - try!(word(&mut self.s, "..")); + if limits == ast::RangeLimits::HalfOpen { + try!(word(&mut self.s, "..")); + } else { + try!(word(&mut self.s, "...")); + } if let &Some(ref e) = end { try!(self.print_expr(&e)); } diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index 6fb81bb6a76..df4eb1c9ed7 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -61,6 +61,8 @@ pub enum AssocOp { As, /// `..` range DotDot, + /// `...` range + DotDotDot, /// `:` Colon, } @@ -102,6 +104,7 @@ impl AssocOp { Token::AndAnd => Some(LAnd), Token::OrOr => Some(LOr), Token::DotDot => Some(DotDot), + Token::DotDotDot => Some(DotDotDot), Token::Colon => Some(Colon), _ if t.is_keyword(keywords::As) => Some(As), _ => None @@ -147,7 +150,7 @@ impl AssocOp { Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, LAnd => 6, LOr => 5, - DotDot => 4, + DotDot | DotDotDot => 4, Inplace => 3, Assign | AssignOp(_) => 2, } @@ -162,7 +165,7 @@ impl AssocOp { As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | LAnd | LOr | Colon => Fixity::Left, - DotDot => Fixity::None + DotDot | DotDotDot => Fixity::None } } @@ -171,7 +174,8 @@ impl AssocOp { match *self { Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract | - ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | Colon => false + ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | + DotDot | DotDotDot | Colon => false } } @@ -181,7 +185,7 @@ impl AssocOp { Assign | AssignOp(_) | Inplace => true, Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | - LOr | DotDot | Colon => false + LOr | DotDot | DotDotDot | Colon => false } } @@ -206,7 +210,7 @@ impl AssocOp { BitOr => Some(BinOpKind::BitOr), LAnd => Some(BinOpKind::And), LOr => Some(BinOpKind::Or), - Inplace | Assign | AssignOp(_) | As | DotDot | Colon => None + Inplace | Assign | AssignOp(_) | As | DotDot | DotDotDot | Colon => None } } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index f26610b8b8d..73ad488e55c 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -763,7 +763,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } - ExprKind::Range(ref start, ref end) => { + ExprKind::Range(ref start, ref end, _) => { walk_list!(visitor, visit_expr, start); walk_list!(visitor, visit_expr, end); } From a3312784518b9fa95e43f9a2d773acb0c083147a Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:24:34 -0500 Subject: [PATCH 03/19] HIR: add inclusive ranges, desugar all ranges (remove ExprRange) --- src/librustc_front/hir.rs | 2 - src/librustc_front/lowering.rs | 92 ++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index dbc1d71517b..cf9952248a8 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -777,8 +777,6 @@ pub enum Expr_ { ExprTupField(P, Spanned), /// An indexing operation (`foo[2]`) ExprIndex(P, P), - /// A range (`1..2`, `1..`, or `..2`) - ExprRange(Option>, Option>), /// Variable reference, possibly containing `::` and/or type /// parameters, e.g. foo::bar::. diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index 0e7d9db37fd..5e39a7c817a 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -65,6 +65,7 @@ use hir; use std::collections::BTreeMap; use std::collections::HashMap; +use std::iter; use syntax::ast::*; use syntax::attr::{ThinAttributes, ThinAttributesExt}; use syntax::ext::mtwt; @@ -1213,9 +1214,74 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { ExprKind::Index(ref el, ref er) => { hir::ExprIndex(lower_expr(lctx, el), lower_expr(lctx, er)) } - ExprKind::Range(ref e1, ref e2) => { - hir::ExprRange(e1.as_ref().map(|x| lower_expr(lctx, x)), - e2.as_ref().map(|x| lower_expr(lctx, x))) + ExprKind::Range(ref e1, ref e2, lims) => { + fn make_struct(lctx: &LoweringContext, + ast_expr: &Expr, + path: &[&str], + fields: &[(&str, &P)]) -> P { + let strs = std_path(lctx, &iter::once(&"ops") + .chain(path) + .map(|s| *s) + .collect::>()); + + let structpath = path_global(ast_expr.span, strs); + + let hir_expr = if fields.len() == 0 { + expr_path(lctx, + structpath, + ast_expr.attrs.clone()) + } else { + expr_struct(lctx, + ast_expr.span, + structpath, + fields.into_iter().map(|&(s, e)| { + field(token::intern(s), + lower_expr(lctx, &**e), + ast_expr.span) + }).collect(), + None, + ast_expr.attrs.clone()) + }; + + signal_block_expr(lctx, + hir_vec![], + hir_expr, + ast_expr.span, + hir::PushUnstableBlock, + None) + } + + return cache_ids(lctx, e.id, |lctx| { + use syntax::ast::RangeLimits::*; + + match (e1, e2, lims) { + (&None, &None, HalfOpen) => + make_struct(lctx, e, &["RangeFull"], + &[]), + + (&Some(ref e1), &None, HalfOpen) => + make_struct(lctx, e, &["RangeFrom"], + &[("start", e1)]), + + (&None, &Some(ref e2), HalfOpen) => + make_struct(lctx, e, &["RangeTo"], + &[("end", e2)]), + + (&Some(ref e1), &Some(ref e2), HalfOpen) => + make_struct(lctx, e, &["Range"], + &[("start", e1), ("end", e2)]), + + (&None, &Some(ref e2), Closed) => + make_struct(lctx, e, &["RangeToInclusive"], + &[("end", e2)]), + + (&Some(ref e1), &Some(ref e2), Closed) => + make_struct(lctx, e, &["RangeInclusive", "NonEmpty"], + &[("start", e1), ("end", e2)]), + + _ => panic!("impossible range in AST"), + } + }); } ExprKind::Path(ref qself, ref path) => { let hir_qself = qself.as_ref().map(|&QSelf { ref ty, position }| { @@ -1632,6 +1698,17 @@ fn arm(pats: hir::HirVec>, expr: P) -> hir::Arm { } } +fn field(name: Name, expr: P, span: Span) -> hir::Field { + hir::Field { + name: Spanned { + node: name, + span: span, + }, + span: span, + expr: expr, + } +} + fn expr_break(lctx: &LoweringContext, span: Span, attrs: ThinAttributes) -> P { expr(lctx, span, hir::ExprBreak(None), attrs) @@ -1681,6 +1758,15 @@ fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: hir::HirVec> expr(lctx, sp, hir::ExprTup(exprs), attrs) } +fn expr_struct(lctx: &LoweringContext, + sp: Span, + path: hir::Path, + fields: hir::HirVec, + e: Option>, + attrs: ThinAttributes) -> P { + expr(lctx, sp, hir::ExprStruct(path, fields, e), attrs) +} + fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_, attrs: ThinAttributes) -> P { P(hir::Expr { From d792183fdeea5623c7ec909d5aa2eccf9171582c Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:26:36 -0500 Subject: [PATCH 04/19] fallout from removing hir::ExprRange A whole bunch of stuff gets folded into struct handling! Plus, removes an ugly hack from trans and accidentally fixes a bug with constructing ranges from references (see later commits with tests). --- src/librustc/middle/cfg/construct.rs | 6 -- src/librustc/middle/expr_use_visitor.rs | 5 -- src/librustc/middle/liveness.rs | 9 +-- src/librustc/middle/mem_categorization.rs | 2 +- src/librustc/middle/ty/mod.rs | 1 - src/librustc_back/svh.rs | 2 - src/librustc_front/fold.rs | 4 - src/librustc_front/intravisit.rs | 4 - src/librustc_front/print/pprust.rs | 9 --- src/librustc_mir/hair/cx/expr.rs | 33 -------- src/librustc_passes/consts.rs | 3 - .../trans/debuginfo/create_scope_map.rs | 5 -- src/librustc_trans/trans/expr.rs | 56 ------------- src/librustc_typeck/check/mod.rs | 81 ------------------- 14 files changed, 3 insertions(+), 217 deletions(-) diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index 701a4596908..4d955173e80 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -317,12 +317,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { self.call(expr, pred, &l, Some(&**r).into_iter()) } - hir::ExprRange(ref start, ref end) => { - let fields = start.as_ref().map(|e| &**e).into_iter() - .chain(end.as_ref().map(|e| &**e)); - self.straightline(expr, pred, fields) - } - hir::ExprUnary(_, ref e) if self.tcx.is_method_call(expr.id) => { self.call(expr, pred, &e, None::.iter()) } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 93cc158cd12..48e9d73c9db 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -399,11 +399,6 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { } } - hir::ExprRange(ref start, ref end) => { - start.as_ref().map(|e| self.consume_expr(&e)); - end.as_ref().map(|e| self.consume_expr(&e)); - } - hir::ExprCall(ref callee, ref args) => { // callee(args) self.walk_callee(expr, &callee); self.consume_exprs(args); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index a487ddbc2b1..8d7f49fdb9c 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -498,7 +498,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { hir::ExprBlock(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) | hir::ExprStruct(..) | hir::ExprRepeat(..) | hir::ExprInlineAsm(..) | hir::ExprBox(..) | - hir::ExprRange(..) | hir::ExprType(..) => { + hir::ExprType(..) => { intravisit::walk_expr(ir, expr); } } @@ -1154,11 +1154,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&l, r_succ) } - hir::ExprRange(ref e1, ref e2) => { - let succ = e2.as_ref().map_or(succ, |e| self.propagate_through_expr(&e, succ)); - e1.as_ref().map_or(succ, |e| self.propagate_through_expr(&e, succ)) - } - hir::ExprBox(ref e) | hir::ExprAddrOf(_, ref e) | hir::ExprCast(ref e, _) | @@ -1446,7 +1441,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { hir::ExprBlock(..) | hir::ExprAddrOf(..) | hir::ExprStruct(..) | hir::ExprRepeat(..) | hir::ExprClosure(..) | hir::ExprPath(..) | hir::ExprBox(..) | - hir::ExprRange(..) | hir::ExprType(..) => { + hir::ExprType(..) => { intravisit::walk_expr(this, expr); } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index fef35764e1c..c79c6d91e7f 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -526,7 +526,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { hir::ExprAddrOf(..) | hir::ExprCall(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) | hir::ExprClosure(..) | hir::ExprRet(..) | - hir::ExprUnary(..) | hir::ExprRange(..) | + hir::ExprUnary(..) | hir::ExprMethodCall(..) | hir::ExprCast(..) | hir::ExprVec(..) | hir::ExprTup(..) | hir::ExprIf(..) | hir::ExprBinary(..) | hir::ExprWhile(..) | diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 00a011c6b5d..84b57585c6f 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -2002,7 +2002,6 @@ impl<'tcx> ctxt<'tcx> { hir::ExprCall(..) | hir::ExprMethodCall(..) | hir::ExprStruct(..) | - hir::ExprRange(..) | hir::ExprTup(..) | hir::ExprIf(..) | hir::ExprMatch(..) | diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index b2911630991..b01b80b8133 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -243,7 +243,6 @@ mod svh_visitor { SawExprAssign, SawExprAssignOp(hir::BinOp_), SawExprIndex, - SawExprRange, SawExprPath(Option), SawExprAddrOf(hir::Mutability), SawExprRet, @@ -275,7 +274,6 @@ mod svh_visitor { ExprField(_, name) => SawExprField(name.node.as_str()), ExprTupField(_, id) => SawExprTupField(id.node), ExprIndex(..) => SawExprIndex, - ExprRange(..) => SawExprRange, ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)), ExprAddrOf(m, _) => SawExprAddrOf(m), ExprBreak(id) => SawExprBreak(id.map(|id| id.node.name.as_str())), diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs index b5e56edb6e4..84d291e5375 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -1092,10 +1092,6 @@ pub fn noop_fold_expr(Expr { id, node, span, attrs }: Expr, folder: & ExprIndex(el, er) => { ExprIndex(folder.fold_expr(el), folder.fold_expr(er)) } - ExprRange(e1, e2) => { - ExprRange(e1.map(|x| folder.fold_expr(x)), - e2.map(|x| folder.fold_expr(x))) - } ExprPath(qself, path) => { let qself = qself.map(|QSelf { ty, position }| { QSelf { diff --git a/src/librustc_front/intravisit.rs b/src/librustc_front/intravisit.rs index c1bcaab9d68..a4423c3a991 100644 --- a/src/librustc_front/intravisit.rs +++ b/src/librustc_front/intravisit.rs @@ -784,10 +784,6 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } - ExprRange(ref start, ref end) => { - walk_list!(visitor, visit_expr, start); - walk_list!(visitor, visit_expr, end); - } ExprPath(ref maybe_qself, ref path) => { if let Some(ref qself) = *maybe_qself { visitor.visit_ty(&qself.ty); diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs index d837ab0f8f6..d5931dcd14d 100644 --- a/src/librustc_front/print/pprust.rs +++ b/src/librustc_front/print/pprust.rs @@ -1459,15 +1459,6 @@ impl<'a> State<'a> { try!(self.print_expr(&index)); try!(word(&mut self.s, "]")); } - hir::ExprRange(ref start, ref end) => { - if let &Some(ref e) = start { - try!(self.print_expr(&e)); - } - try!(word(&mut self.s, "..")); - if let &Some(ref e) = end { - try!(self.print_expr(&e)); - } - } hir::ExprPath(None, ref path) => { try!(self.print_path(path, true, 0)) } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index ac1cff527fe..cb54cadfcd1 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -22,7 +22,6 @@ use rustc::middle::ty::{self, VariantDef, Ty}; use rustc::mir::repr::*; use rustc_front::hir; use rustc_front::util as hir_util; -use syntax::parse::token; use syntax::ptr::P; impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { @@ -324,38 +323,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { } } - hir::ExprRange(ref start, ref end) => { - let range_ty = cx.tcx.expr_ty(self); - let (adt_def, substs) = match range_ty.sty { - ty::TyStruct(adt_def, substs) => (adt_def, substs), - _ => { - cx.tcx.sess.span_bug(self.span, "unexpanded ast"); - } - }; - - let field_expr_ref = |s: &'tcx P, name: &str| { - let name = token::intern(name); - let index = adt_def.variants[0].index_of_field_named(name).unwrap(); - FieldExprRef { name: Field::new(index), expr: s.to_ref() } - }; - - let start_field = start.as_ref() - .into_iter() - .map(|s| field_expr_ref(s, "start")); - - let end_field = end.as_ref() - .into_iter() - .map(|e| field_expr_ref(e, "end")); - - ExprKind::Adt { - adt_def: adt_def, - variant_index: 0, - substs: substs, - fields: start_field.chain(end_field).collect(), - base: None, - } - } - hir::ExprPath(..) => { convert_path_expr(cx, self) } diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index b0d459063ef..f6e4f3191ef 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -747,9 +747,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, hir::ExprAgain(_) | hir::ExprRet(_) | - // Miscellaneous expressions that could be implemented. - hir::ExprRange(..) | - // Expressions with side-effects. hir::ExprAssign(..) | hir::ExprAssignOp(..) | diff --git a/src/librustc_trans/trans/debuginfo/create_scope_map.rs b/src/librustc_trans/trans/debuginfo/create_scope_map.rs index 73fdbd54b29..4ba103c0c0d 100644 --- a/src/librustc_trans/trans/debuginfo/create_scope_map.rs +++ b/src/librustc_trans/trans/debuginfo/create_scope_map.rs @@ -346,11 +346,6 @@ fn walk_expr(cx: &CrateContext, walk_expr(cx, &rhs, scope_stack, scope_map); } - hir::ExprRange(ref start, ref end) => { - start.as_ref().map(|e| walk_expr(cx, &e, scope_stack, scope_map)); - end.as_ref().map(|e| walk_expr(cx, &e, scope_stack, scope_map)); - } - hir::ExprVec(ref init_expressions) | hir::ExprTup(ref init_expressions) => { for ie in init_expressions { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 782e38d3af2..ec6c2cdde59 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -86,7 +86,6 @@ use rustc_front::hir; use syntax::{ast, codemap}; use syntax::parse::token::InternedString; use syntax::ptr::P; -use syntax::parse::token; use std::mem; // Destinations @@ -1059,7 +1058,6 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, -> Block<'blk, 'tcx> { let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); let mut bcx = bcx; - let tcx = bcx.tcx(); debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); @@ -1088,59 +1086,6 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, node_id_type(bcx, expr.id), dest) } - hir::ExprRange(ref start, ref end) => { - // FIXME it is just not right that we are synthesising ast nodes in - // trans. Shudder. - fn make_field(field_name: &str, expr: P) -> hir::Field { - hir::Field { - name: codemap::dummy_spanned(token::intern(field_name)), - expr: expr, - span: codemap::DUMMY_SP, - } - } - - // A range just desugars into a struct. - // Note that the type of the start and end may not be the same, but - // they should only differ in their lifetime, which should not matter - // in trans. - let (did, fields, ty_params) = match (start, end) { - (&Some(ref start), &Some(ref end)) => { - // Desugar to Range - let fields = vec![make_field("start", start.clone()), - make_field("end", end.clone())]; - (tcx.lang_items.range_struct(), fields, vec![node_id_type(bcx, start.id)]) - } - (&Some(ref start), &None) => { - // Desugar to RangeFrom - let fields = vec![make_field("start", start.clone())]; - (tcx.lang_items.range_from_struct(), fields, vec![node_id_type(bcx, start.id)]) - } - (&None, &Some(ref end)) => { - // Desugar to RangeTo - let fields = vec![make_field("end", end.clone())]; - (tcx.lang_items.range_to_struct(), fields, vec![node_id_type(bcx, end.id)]) - } - _ => { - // Desugar to RangeFull - (tcx.lang_items.range_full_struct(), vec![], vec![]) - } - }; - - if let Some(did) = did { - let substs = Substs::new_type(ty_params, vec![]); - trans_struct(bcx, - &fields, - None, - expr.span, - expr.id, - tcx.mk_struct(tcx.lookup_adt_def(did), - tcx.mk_substs(substs)), - dest) - } else { - tcx.sess.span_bug(expr.span, - "No lang item for ranges (how did we get this far?)") - } - } hir::ExprTup(ref args) => { let numbered_fields: Vec<(usize, &hir::Expr)> = args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect(); @@ -2625,7 +2570,6 @@ fn expr_kind(tcx: &ty::ctxt, expr: &hir::Expr) -> ExprKind { hir::ExprCall(..) | hir::ExprMethodCall(..) | hir::ExprStruct(..) | - hir::ExprRange(..) | hir::ExprTup(..) | hir::ExprIf(..) | hir::ExprMatch(..) | diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 7ab4975c8b8..0eb1d2ee2fd 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3677,87 +3677,6 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, } } } - hir::ExprRange(ref start, ref end) => { - let t_start = start.as_ref().map(|e| { - check_expr(fcx, &e); - fcx.expr_ty(&e) - }); - let t_end = end.as_ref().map(|e| { - check_expr(fcx, &e); - fcx.expr_ty(&e) - }); - - let idx_type = match (t_start, t_end) { - (Some(ty), None) | (None, Some(ty)) => { - Some(ty) - } - (Some(t_start), Some(t_end)) if (t_start.references_error() || - t_end.references_error()) => { - Some(fcx.tcx().types.err) - } - (Some(t_start), Some(t_end)) => { - Some(infer::common_supertype(fcx.infcx(), - TypeOrigin::RangeExpression(expr.span), - true, - t_start, - t_end)) - } - _ => None - }; - - // Note that we don't check the type of start/end satisfy any - // bounds because right now the range structs do not have any. If we add - // some bounds, then we'll need to check `t_start` against them here. - - let range_type = match idx_type { - Some(idx_type) if idx_type.references_error() => { - fcx.tcx().types.err - } - Some(idx_type) => { - // Find the did from the appropriate lang item. - let did = match (start, end) { - (&Some(_), &Some(_)) => tcx.lang_items.range_struct(), - (&Some(_), &None) => tcx.lang_items.range_from_struct(), - (&None, &Some(_)) => tcx.lang_items.range_to_struct(), - (&None, &None) => { - tcx.sess.span_bug(expr.span, "full range should be dealt with above") - } - }; - - if let Some(did) = did { - let def = tcx.lookup_adt_def(did); - let predicates = tcx.lookup_predicates(did); - let substs = Substs::new_type(vec![idx_type], vec![]); - let bounds = fcx.instantiate_bounds(expr.span, &substs, &predicates); - fcx.add_obligations_for_parameters( - traits::ObligationCause::new(expr.span, - fcx.body_id, - traits::ItemObligation(did)), - &bounds); - - tcx.mk_struct(def, tcx.mk_substs(substs)) - } else { - span_err!(tcx.sess, expr.span, E0236, "no lang item for range syntax"); - fcx.tcx().types.err - } - } - None => { - // Neither start nor end => RangeFull - if let Some(did) = tcx.lang_items.range_full_struct() { - tcx.mk_struct( - tcx.lookup_adt_def(did), - tcx.mk_substs(Substs::empty()) - ) - } else { - span_err!(tcx.sess, expr.span, E0237, "no lang item for range syntax"); - fcx.tcx().types.err - } - } - }; - - fcx.write_ty(id, range_type); - } - } debug!("type of expr({}) {} is...", expr.id, From bd3197c748e7bfa652fb636fbb78fa7a42d826ca Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:27:07 -0500 Subject: [PATCH 05/19] remove range lang items The range desugaring does not use the lang items. Hence I did not add lang items for inclusive ranges. This cleanup commit removes the old unused ones as well. Whether the desugaring _should_ use lang items is another question: see #30809. But if we decide on a strategy there we can add back these lang items, and new ones for inclusive ranges. For stage0 we need to keep the attributes as the lang items still exist even if they are never used. This is surprisingly not a breaking change. Unused #[lang] attributes do not even trigger a lint (see #30881). --- src/libcore/ops.rs | 8 ++++---- src/librustc/middle/lang_items.rs | 4 ---- src/librustc_typeck/diagnostics.rs | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index cf8f4279a1d..b5bec316947 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1468,7 +1468,7 @@ pub trait IndexMut: Index { /// An unbounded range. #[derive(Copy, Clone, PartialEq, Eq)] -#[lang = "range_full"] +#[cfg_attr(stage0, lang = "range_full")] // TODO remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFull; @@ -1481,7 +1481,7 @@ impl fmt::Debug for RangeFull { /// A (half-open) range which is bounded at both ends. #[derive(Clone, PartialEq, Eq)] -#[lang = "range"] +#[cfg_attr(stage0, lang = "range")] // TODO remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct Range { /// The lower bound of the range (inclusive). @@ -1501,7 +1501,7 @@ impl fmt::Debug for Range { /// A range which is only bounded below. #[derive(Clone, PartialEq, Eq)] -#[lang = "range_from"] +#[cfg_attr(stage0, lang = "range_from")] // TODO remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFrom { /// The lower bound of the range (inclusive). @@ -1518,7 +1518,7 @@ impl fmt::Debug for RangeFrom { /// A range which is only bounded above. #[derive(Copy, Clone, PartialEq, Eq)] -#[lang = "range_to"] +#[cfg_attr(stage0, lang = "range_to")] // TODO remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeTo { /// The upper bound of the range (exclusive). diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 6cbb90627ea..8d407d0aa44 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -302,10 +302,6 @@ lets_do_this! { ShrAssignTraitLangItem, "shr_assign", shr_assign_trait; IndexTraitLangItem, "index", index_trait; IndexMutTraitLangItem, "index_mut", index_mut_trait; - RangeStructLangItem, "range", range_struct; - RangeFromStructLangItem, "range_from", range_from_struct; - RangeToStructLangItem, "range_to", range_to_struct; - RangeFullStructLangItem, "range_full", range_full_struct; UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 0ff6dcccde6..01f7d6b4565 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3643,8 +3643,8 @@ register_diagnostics! { // E0233, // E0234, // E0235, // structure constructor specifies a structure of type but - E0236, // no lang item for range syntax - E0237, // no lang item for range syntax +// E0236, // no lang item for range syntax +// E0237, // no lang item for range syntax E0238, // parenthesized parameters may only be used with a trait // E0239, // `next` method of `Iterator` trait has unexpected type // E0240, From 37a4cb32121bed9165f9bac696953f055f691aef Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:27:40 -0500 Subject: [PATCH 06/19] feature-gate inclusive range syntax --- src/libsyntax/feature_gate.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 70bd85c00d4..9be52fb153d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -241,7 +241,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status ("cfg_target_thread_local", "1.7.0", Some(29594), Active), // rustc internal - ("abi_vectorcall", "1.7.0", None, Active) + ("abi_vectorcall", "1.7.0", None, Active), + + // a...b and ...b + ("inclusive_range_syntax", "1.7.0", Some(28237), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -549,6 +552,7 @@ pub struct Features { pub allow_placement_in: bool, pub allow_box: bool, pub allow_pushpop_unsafe: bool, + pub allow_inclusive_range: bool, pub simd_ffi: bool, pub unmarked_api: bool, /// spans of #![feature] attrs for stable language features. for error reporting @@ -585,6 +589,7 @@ impl Features { allow_placement_in: false, allow_box: false, allow_pushpop_unsafe: false, + allow_inclusive_range: false, simd_ffi: false, unmarked_api: false, declared_stable_lang_features: Vec::new(), @@ -998,6 +1003,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { self.gate_feature("type_ascription", e.span, "type ascription is experimental"); } + ast::ExprRange(_, _, ast::RangeLimits::Closed) => { + self.gate_feature("inclusive_range_syntax", + e.span, + "inclusive range syntax is experimental"); + } _ => {} } visit::walk_expr(self, e); @@ -1184,6 +1194,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &Handler, allow_placement_in: cx.has_feature("placement_in_syntax"), allow_box: cx.has_feature("box_syntax"), allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"), + allow_inclusive_range: cx.has_feature("inclusive_range_syntax"), simd_ffi: cx.has_feature("simd_ffi"), unmarked_api: cx.has_feature("unmarked_api"), declared_stable_lang_features: accepted_features, From 69719df6116b584b71ee5a8d22974835c33b13be Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:28:29 -0500 Subject: [PATCH 07/19] test inclusive ranges Mostly copy the tests from half-open ranges, adding some more for DoubleEndedIterator and ExactSizeIterator. Also thoroughly (I think) test that the feature gates are working. --- src/test/compile-fail/range_inclusive_gate.rs | 21 ++++ src/test/parse-fail/range_inclusive.rs | 18 ++++ src/test/parse-fail/range_inclusive_gate.rs | 74 +++++++++++++ src/test/run-pass/range_inclusive.rs | 100 ++++++++++++++++++ src/test/run-pass/range_inclusive_gate.rs | 23 ++++ 5 files changed, 236 insertions(+) create mode 100644 src/test/compile-fail/range_inclusive_gate.rs create mode 100644 src/test/parse-fail/range_inclusive.rs create mode 100644 src/test/parse-fail/range_inclusive_gate.rs create mode 100644 src/test/run-pass/range_inclusive.rs create mode 100644 src/test/run-pass/range_inclusive_gate.rs diff --git a/src/test/compile-fail/range_inclusive_gate.rs b/src/test/compile-fail/range_inclusive_gate.rs new file mode 100644 index 00000000000..630679f27e3 --- /dev/null +++ b/src/test/compile-fail/range_inclusive_gate.rs @@ -0,0 +1,21 @@ +// Copyright 2016 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. + +// Make sure that #![feature(inclusive_range)] is required. + +#![feature(inclusive_range_syntax)] +// #![feature(inclusive_range)] + +pub fn main() { + let _: std::ops::RangeInclusive<_> = 1...10; + //~^ ERROR use of unstable library feature 'inclusive_range' +} + + diff --git a/src/test/parse-fail/range_inclusive.rs b/src/test/parse-fail/range_inclusive.rs new file mode 100644 index 00000000000..5fd6f1834e0 --- /dev/null +++ b/src/test/parse-fail/range_inclusive.rs @@ -0,0 +1,18 @@ +// Copyright 2016 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. + +// Make sure that inclusive ranges with no end point don't parse. + +#![feature(inclusive_range_syntax, inclusive_range)] + +pub fn main() { + for _ in 1... {} +} //~ ERROR expected one of + diff --git a/src/test/parse-fail/range_inclusive_gate.rs b/src/test/parse-fail/range_inclusive_gate.rs new file mode 100644 index 00000000000..021b6dd3e26 --- /dev/null +++ b/src/test/parse-fail/range_inclusive_gate.rs @@ -0,0 +1,74 @@ +// Copyright 2016 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. + +// Make sure that #![feature(inclusive_range_syntax)] is required. + +// #![feature(inclusive_range_syntax, inclusive_range)] + +macro_rules! m { + () => { for _ in 1...10 {} } //~ ERROR inclusive range syntax is experimental +} + +#[cfg(nope)] +fn f() {} +#[cfg(not(nope))] +fn f() { + for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental +} + +#[cfg(nope)] +macro_rules! n { () => {} } +#[cfg(not(nope))] +macro_rules! n { + () => { for _ in 1...10 {} } //~ ERROR inclusive range syntax is experimental +} + +macro_rules! o { + () => {{ + #[cfg(nope)] + fn g() {} + #[cfg(not(nope))] + fn g() { + for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental + } + + g(); + }} +} + +#[cfg(nope)] +macro_rules! p { () => {} } +#[cfg(not(nope))] +macro_rules! p { + () => {{ + #[cfg(nope)] + fn h() {} + #[cfg(not(nope))] + fn h() { + for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental + } + + h(); + }} +} + +pub fn main() { + for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental + for _ in ...10 {} //~ ERROR inclusive range syntax is experimental + + f(); // not allowed in cfg'ed functions + + m!(); // not allowed in macros + n!(); // not allowed in cfg'ed macros + o!(); // not allowed in macros that output cfgs + p!(); // not allowed in cfg'ed macros that output cfgs +} + + diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs new file mode 100644 index 00000000000..e64c453a3c4 --- /dev/null +++ b/src/test/run-pass/range_inclusive.rs @@ -0,0 +1,100 @@ +// Copyright 2016 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. + +// Test inclusive range syntax. + +#![feature(inclusive_range_syntax, inclusive_range)] + +use std::ops::{RangeInclusive, RangeToInclusive}; + +fn foo() -> isize { 42 } + +// Test that range syntax works in return statements +fn return_range_to() -> RangeToInclusive { return ...1; } + +pub fn main() { + let mut count = 0; + for i in 0_usize...10 { + assert!(i >= 0 && i <= 10); + count += i; + } + assert_eq!(count, 55); + + let mut count = 0; + let mut range = 0_usize...10; + for i in range { + assert!(i >= 0 && i <= 10); + count += i; + } + assert_eq!(count, 55); + + /* FIXME + let mut count = 0; + for i in (0_usize...10).step_by(2) { + assert!(i >= 0 && i <= 10 && i % 2 == 0); + count += i; + } + assert_eq!(count, 30); + */ + + let _ = 0_usize...4+4-3; + let _ = 0...foo(); + + let _ = { &42...&100 }; // references to literals are OK + let _ = ...42_usize; + + // Test we can use two different types with a common supertype. + let x = &42; + { + let y = 42; + let _ = x...&y; + } + + // test the size hints and emptying + let mut long = 0...255u8; + let mut short = 42...42; + assert_eq!(long.size_hint(), (256, Some(256))); + assert_eq!(short.size_hint(), (1, Some(1))); + long.next(); + short.next(); + assert_eq!(long.size_hint(), (255, Some(255))); + assert_eq!(short.size_hint(), (0, Some(0))); + assert_eq!(short, RangeInclusive::Empty { at: 42 }); + + assert_eq!(long.len(), 255); + assert_eq!(short.len(), 0); + + // test iterating backwards + assert_eq!(long.next_back(), Some(255)); + assert_eq!(long.next_back(), Some(254)); + assert_eq!(long.next_back(), Some(253)); + assert_eq!(long.next(), Some(1)); + assert_eq!(long.next(), Some(2)); + assert_eq!(long.next_back(), Some(252)); + for i in 3...251 { + assert_eq!(long.next(), Some(i)); + } + assert_eq!(long, RangeInclusive::Empty { at: 251 }); + + // what happens if you have a nonsense range? + let mut nonsense = 10...5; + assert_eq!(nonsense.next(), None); + assert_eq!(nonsense, RangeInclusive::Empty { at: 10 }); + + // conversion + assert_eq!(0...9, (0..10).into()); + assert_eq!(0...0, (0..1).into()); + assert_eq!(RangeInclusive::Empty { at: 1 }, (1..0).into()); + + // output + assert_eq!(format!("{:?}", 0...10), "0...10"); + assert_eq!(format!("{:?}", ...10), "...10"); + assert_eq!(format!("{:?}", long), "[empty range @ 251]"); +} diff --git a/src/test/run-pass/range_inclusive_gate.rs b/src/test/run-pass/range_inclusive_gate.rs new file mode 100644 index 00000000000..5e0ec19d6b3 --- /dev/null +++ b/src/test/run-pass/range_inclusive_gate.rs @@ -0,0 +1,23 @@ +// Copyright 2016 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. + +// Test that you only need the syntax gate if you don't mention the structs. + +#![feature(inclusive_range_syntax)] + +fn main() { + let mut count = 0; + for i in 0_usize...10 { + assert!(i >= 0 && i <= 10); + count += i; + } + assert_eq!(count, 55); +} + From e10614a77789f4b69ccc8cb777aaec035c99ae6f Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:29:39 -0500 Subject: [PATCH 08/19] adjust range tests Since the desugaring removed special handling for ranges, the error message changed and so I had to adjust `range-1`. Turns out there was a bug where borrowck was too restrictive in some rare cases of constructing ranges from literals. The `range-2` test enshrined this bug -- now it's adjusted to test a case that's actually wrong. --- src/test/compile-fail/range-1.rs | 2 +- src/test/compile-fail/range-2.rs | 10 ++++++---- src/test/run-pass/range.rs | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs index b839902c683..e4ab5829f41 100644 --- a/src/test/compile-fail/range-1.rs +++ b/src/test/compile-fail/range-1.rs @@ -13,7 +13,7 @@ pub fn main() { // Mixed types. let _ = 0u32..10i32; - //~^ ERROR start and end of range have incompatible types + //~^ ERROR mismatched types // Bool => does not implement iterator. for i in false..true {} diff --git a/src/test/compile-fail/range-2.rs b/src/test/compile-fail/range-2.rs index c9053328572..94967693ecf 100644 --- a/src/test/compile-fail/range-2.rs +++ b/src/test/compile-fail/range-2.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -12,8 +12,10 @@ pub fn main() { let r = { - &42..&42 - //~^ ERROR borrowed value does not live long enough - //~^^ ERROR borrowed value does not live long enough + let a = 42; + let b = 42; + &a..&b + //~^ ERROR `a` does not live long enough + //~^^ ERROR `b` does not live long enough }; } diff --git a/src/test/run-pass/range.rs b/src/test/run-pass/range.rs index 24261772add..4c249bbe1f7 100644 --- a/src/test/run-pass/range.rs +++ b/src/test/run-pass/range.rs @@ -1,4 +1,4 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -44,6 +44,7 @@ pub fn main() { let _ = 0_usize..4+4-3; let _ = 0..foo(); + let _ = { &42..&100 }; // references to literals are OK let _ = ..42_usize; // Test we can use two different types with a common supertype. From 15a8a296b724599a1eda807c3057338b11cb94bf Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 01:29:47 -0500 Subject: [PATCH 09/19] document inclusive range syntax --- src/doc/book/iterators.md | 5 +++++ src/doc/book/syntax-index.md | 4 +++- src/doc/reference.md | 23 +++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/doc/book/iterators.md b/src/doc/book/iterators.md index 0c4f8041266..c174d2d6bac 100644 --- a/src/doc/book/iterators.md +++ b/src/doc/book/iterators.md @@ -14,6 +14,11 @@ Now that you know more Rust, we can talk in detail about how this works. Ranges (the `0..10`) are 'iterators'. An iterator is something that we can call the `.next()` method on repeatedly, and it gives us a sequence of things. +(By the way, a range with two dots like `0..10` is inclusive on the left (so it +starts at 0) and exclusive on the right (so it ends at 9). A mathematician +would write "[0, 10)". To get a range that goes all the way up to 10 you can +write `0...10`.) + Like this: ```rust diff --git a/src/doc/book/syntax-index.md b/src/doc/book/syntax-index.md index 6782bdb4985..53f38cd77e4 100644 --- a/src/doc/book/syntax-index.md +++ b/src/doc/book/syntax-index.md @@ -66,7 +66,8 @@ * `..` (`..`, `expr..`, `..expr`, `expr..expr`): right-exclusive range literal. * `..` (`..expr`): struct literal update syntax. See [Structs (Update syntax)]. * `..` (`variant(x, ..)`, `struct_type { x, .. }`): "and the rest" pattern binding. See [Patterns (Ignoring bindings)]. -* `...` (`expr ... expr`): inclusive range pattern. See [Patterns (Ranges)]. +* `...` (`...expr`, `expr...expr`) *in an expression*: inclusive range expression. See [Iterators]. +* `...` (`expr...expr`) *in a pattern*: inclusive range pattern. See [Patterns (Ranges)]. * `/` (`expr / expr`): arithmetic division. Overloadable (`Div`). * `/=` (`var /= expr`): arithmetic division & assignment. * `:` (`pat: type`, `ident: type`): constraints. See [Variable Bindings], [Functions], [Structs], [Traits]. @@ -205,6 +206,7 @@ [Functions (Early Returns)]: functions.html#early-returns [Functions]: functions.html [Generics]: generics.html +[Iterators]: iterators.html [Lifetimes]: lifetimes.html [Loops (`for`)]: loops.html#for [Loops (`loop`)]: loops.html#loop diff --git a/src/doc/reference.md b/src/doc/reference.md index 6262618a030..f88843b642e 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2279,6 +2279,10 @@ The currently implemented features of the reference compiler are: `#[derive_Foo] #[derive_Bar]`, which can be user-defined syntax extensions. +* `inclusive_range_syntax` - Allows use of the `a...b` and `...b` syntax for inclusive ranges. + +* `inclusive_range` - Allows use of the types that represent desugared inclusive ranges. + * `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics are inherently unstable and no promise about them is made. @@ -2750,6 +2754,25 @@ let y = 0..10; assert_eq!(x, y); ``` +Similarly, the `...` operator will construct an object of one of the +`std::ops::RangeInclusive` variants. + +``` +# #![feature(inclusive_range_syntax)] +1...2; // std::ops::RangeInclusive +...4; // std::ops::RangeToInclusive +``` + +The following expressions are equivalent. + +``` +# #![feature(inclusive_range_syntax, inclusive_range)] +let x = std::ops::RangeInclusive::NonEmpty {start: 0, end: 10}; +let y = 0...10; + +assert_eq!(x, y); +``` + ### Unary operator expressions Rust defines the following unary operators. They are all written as prefix operators, From 24cc90262bd5ec52aa421103ef7c89a0697b046d Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Wed, 13 Jan 2016 13:12:16 -0500 Subject: [PATCH 10/19] note work still to be done In particular, uses of inclusive ranges within the standard library are still waiting. Slices and collections can be sliced with `usize` and `Range*`, but not yet `Range*Inclusive`. Also, we need to figure out what to do about `RangeArgument`. Currently it has `start()` and `end()` methods which are pretty much identical to `Range::start` and `Range::end`. For the same reason as Range itself, these methods can't express a range such as `0...255u8` without overflow. The easiest choice, it seems to me, is either changing the meaning of `end()` to be inclusive, or adding a new method, say `last()`, that is inclusive and specifying that `end()` returns `None` in cases where it would overflow. Changing the semantics would be a breaking change, but `RangeArgument` is unstable so maybe we should do it anyway. --- src/libcollections/range.rs | 1 + src/libcore/iter.rs | 4 +++- src/libcore/ops.rs | 12 +++++++----- src/libcore/slice.rs | 2 ++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libcollections/range.rs b/src/libcollections/range.rs index afcd779ddf1..4e39191b472 100644 --- a/src/libcollections/range.rs +++ b/src/libcollections/range.rs @@ -35,6 +35,7 @@ pub trait RangeArgument { } } +// FIXME add inclusive ranges to RangeArgument impl RangeArgument for RangeFull {} diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 1f5ef6be0bc..305f32e2637 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -4417,7 +4417,9 @@ macro_rules! range_exact_iter_impl { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for ops::Range<$t> { } - #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] impl ExactSizeIterator for ops::RangeInclusive<$t> { } )*) } diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index b5bec316947..e5a51c98aca 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1468,7 +1468,7 @@ pub trait IndexMut: Index { /// An unbounded range. #[derive(Copy, Clone, PartialEq, Eq)] -#[cfg_attr(stage0, lang = "range_full")] // TODO remove attribute after next snapshot +#[cfg_attr(stage0, lang = "range_full")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFull; @@ -1481,7 +1481,7 @@ impl fmt::Debug for RangeFull { /// A (half-open) range which is bounded at both ends. #[derive(Clone, PartialEq, Eq)] -#[cfg_attr(stage0, lang = "range")] // TODO remove attribute after next snapshot +#[cfg_attr(stage0, lang = "range")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct Range { /// The lower bound of the range (inclusive). @@ -1501,7 +1501,7 @@ impl fmt::Debug for Range { /// A range which is only bounded below. #[derive(Clone, PartialEq, Eq)] -#[cfg_attr(stage0, lang = "range_from")] // TODO remove attribute after next snapshot +#[cfg_attr(stage0, lang = "range_from")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFrom { /// The lower bound of the range (inclusive). @@ -1518,7 +1518,7 @@ impl fmt::Debug for RangeFrom { /// A range which is only bounded above. #[derive(Copy, Clone, PartialEq, Eq)] -#[cfg_attr(stage0, lang = "range_to")] // TODO remove attribute after next snapshot +#[cfg_attr(stage0, lang = "range_to")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeTo { /// The upper bound of the range (exclusive). @@ -1586,7 +1586,9 @@ impl> From> for RangeInclusiv #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] pub struct RangeToInclusive { /// The upper bound of the range (inclusive) - #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] pub end: Idx, } diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index afda70f4fcc..73466a849dc 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -533,6 +533,8 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! { panic!("slice index starts at {} but ends at {}", index, end); } +// FIXME implement indexing with inclusive ranges + #[stable(feature = "rust1", since = "1.0.0")] impl ops::Index> for [T] { type Output = [T]; From b1b4f50678ecd6aaeed9991b860c272be010fc9f Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Thu, 28 Jan 2016 00:25:43 -0500 Subject: [PATCH 11/19] add StepBy for RangeInclusive --- src/libcore/iter.rs | 112 ++++++++++++++++++++++++++- src/test/run-pass/range_inclusive.rs | 4 +- 2 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 305f32e2637..8cea98d2a21 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -306,7 +306,7 @@ use default::Default; use marker; use mem; use num::{Zero, One}; -use ops::{self, Add, Sub, FnMut, Mul, RangeFrom}; +use ops::{self, Add, Sub, FnMut, Mul}; use option::Option::{self, Some, None}; use marker::Sized; use usize; @@ -4286,7 +4286,7 @@ step_impl_no_between!(u64 i64); /// /// The resulting iterator handles overflow by stopping. The `A` /// parameter is the type being iterated over, while `R` is the range -/// type (usually one of `std::ops::{Range, RangeFrom}`. +/// type (usually one of `std::ops::{Range, RangeFrom, RangeInclusive}`. #[derive(Clone)] #[unstable(feature = "step_by", reason = "recent addition", issue = "27741")] @@ -4295,7 +4295,7 @@ pub struct StepBy { range: R, } -impl RangeFrom { +impl ops::RangeFrom { /// Creates an iterator starting at the same point, but stepping by /// the given amount at each iteration. /// @@ -4355,8 +4355,44 @@ impl ops::Range { } } +impl ops::RangeInclusive { + /// Creates an iterator with the same range, but stepping by the + /// given amount at each iteration. + /// + /// The resulting iterator handles overflow by stopping. + /// + /// # Examples + /// + /// ``` + /// #![feature(step_by, inclusive_range_syntax)] + /// + /// for i in (0...10).step_by(2) { + /// println!("{}", i); + /// } + /// ``` + /// + /// This prints: + /// + /// ```text + /// 0 + /// 2 + /// 4 + /// 6 + /// 8 + /// 10 + /// ``` + #[unstable(feature = "step_by", reason = "recent addition", + issue = "27741")] + pub fn step_by(self, by: A) -> StepBy { + StepBy { + step_by: by, + range: self + } + } +} + #[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for StepBy> where +impl Iterator for StepBy> where A: Clone, for<'a> &'a A: Add<&'a A, Output = A> { @@ -4412,6 +4448,74 @@ impl Iterator for StepBy> { } } +#[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] +impl Iterator for StepBy> { + type Item = A; + + #[inline] + fn next(&mut self) -> Option { + use ops::RangeInclusive::*; + + // this function has a sort of odd structure due to borrowck issues + // we may need to replace self.range, so borrows of start and end need to end early + + let (finishing, n) = match self.range { + Empty { .. } => return None, // empty iterators yield no values + + NonEmpty { ref mut start, ref mut end } => { + let zero = A::zero(); + let rev = self.step_by < zero; + + // march start towards (maybe past!) end and yield the old value + if (rev && start >= end) || + (!rev && start <= end) + { + match start.step(&self.step_by) { + Some(mut n) => { + mem::swap(start, &mut n); + (None, Some(n)) // yield old value, remain non-empty + }, + None => { + let mut n = end.clone(); + mem::swap(start, &mut n); + (None, Some(n)) // yield old value, remain non-empty + } + } + } else { + // found range in inconsistent state (start at or past end), so become empty + (Some(mem::replace(end, zero)), None) + } + } + }; + + // turn into an empty iterator if we've reached the end + if let Some(end) = finishing { + self.range = Empty { at: end }; + } + + n + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + use ops::RangeInclusive::*; + + match self.range { + Empty { .. } => (0, Some(0)), + + NonEmpty { ref start, ref end } => + match Step::steps_between(start, + end, + &self.step_by) { + Some(hint) => (hint.saturating_add(1), hint.checked_add(1)), + None => (0, None) + } + } + } +} + macro_rules! range_exact_iter_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index e64c453a3c4..c1bc6adf7b8 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -10,7 +10,7 @@ // Test inclusive range syntax. -#![feature(inclusive_range_syntax, inclusive_range)] +#![feature(inclusive_range_syntax, inclusive_range, step_by)] use std::ops::{RangeInclusive, RangeToInclusive}; @@ -35,14 +35,12 @@ pub fn main() { } assert_eq!(count, 55); - /* FIXME let mut count = 0; for i in (0_usize...10).step_by(2) { assert!(i >= 0 && i <= 10 && i % 2 == 0); count += i; } assert_eq!(count, 30); - */ let _ = 0_usize...4+4-3; let _ = 0...foo(); From 7eb7c56bd43b2ae12ef8b92e7258d520099a5347 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Thu, 28 Jan 2016 11:20:48 -0500 Subject: [PATCH 12/19] add indexing with RangeInclusive in libcore and libcollections --- src/libcollections/lib.rs | 2 +- src/libcollections/string.rs | 34 ++++++++++++++++- src/libcollections/vec.rs | 32 ++++++++++++++++ src/libcore/slice.rs | 55 +++++++++++++++++++++++++-- src/libcore/str/mod.rs | 56 ++++++++++++++++++++++++++++ src/libstd/lib.rs | 1 + src/test/run-pass/range_inclusive.rs | 18 +++++++++ 7 files changed, 192 insertions(+), 6 deletions(-) diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index 9c6fdc217dc..2a81e7dcaf7 100644 --- a/src/libcollections/lib.rs +++ b/src/libcollections/lib.rs @@ -40,7 +40,7 @@ #![feature(fmt_internals)] #![feature(fmt_radix)] #![feature(heap_api)] -#![feature(iter_arith)] +#![feature(inclusive_range)] #![feature(iter_arith)] #![feature(lang_items)] #![feature(nonzero)] diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 62ae7938e15..cae6520bdb2 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -59,7 +59,7 @@ use core::fmt; use core::hash; use core::iter::FromIterator; use core::mem; -use core::ops::{self, Add}; +use core::ops::{self, Add, Index, IndexMut}; use core::ptr; use core::slice; use core::str::pattern::Pattern; @@ -1606,6 +1606,24 @@ impl ops::Index for String { unsafe { str::from_utf8_unchecked(&self.vec) } } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeInclusive) -> &str { + Index::index(&**self, index) + } +} +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::Index> for String { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeToInclusive) -> &str { + Index::index(&**self, index) + } +} #[stable(feature = "derefmut_for_string", since = "1.2.0")] impl ops::IndexMut> for String { @@ -1635,6 +1653,20 @@ impl ops::IndexMut for String { unsafe { mem::transmute(&mut *self.vec) } } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { + IndexMut::index_mut(&mut **self, index) + } +} +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::IndexMut> for String { + #[inline] + fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { + IndexMut::index_mut(&mut **self, index) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl ops::Deref for String { diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index ae442e155c0..841748f3881 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1225,6 +1225,24 @@ impl ops::Index for Vec { self } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::Index> for Vec { + type Output = [T]; + + #[inline] + fn index(&self, index: ops::RangeInclusive) -> &[T] { + Index::index(&**self, index) + } +} +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::Index> for Vec { + type Output = [T]; + + #[inline] + fn index(&self, index: ops::RangeToInclusive) -> &[T] { + Index::index(&**self, index) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl ops::IndexMut> for Vec { @@ -1254,6 +1272,20 @@ impl ops::IndexMut for Vec { self } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::IndexMut> for Vec { + #[inline] + fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut [T] { + IndexMut::index_mut(&mut **self, index) + } +} +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::IndexMut> for Vec { + #[inline] + fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut [T] { + IndexMut::index_mut(&mut **self, index) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl ops::Deref for Vec { diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 73466a849dc..2293e93eea7 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -560,7 +560,7 @@ impl ops::Index> for [T] { #[inline] fn index(&self, index: ops::RangeTo) -> &[T] { - self.index(ops::Range{ start: 0, end: index.end }) + self.index(0 .. index.end) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -569,7 +569,7 @@ impl ops::Index> for [T] { #[inline] fn index(&self, index: ops::RangeFrom) -> &[T] { - self.index(ops::Range{ start: index.start, end: self.len() }) + self.index(index.start .. self.len()) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -582,6 +582,32 @@ impl ops::Index for [T] { } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::Index> for [T] { + type Output = [T]; + + #[inline] + fn index(&self, index: ops::RangeInclusive) -> &[T] { + match index { + ops::RangeInclusive::Empty { .. } => &[], + ops::RangeInclusive::NonEmpty { end, .. } if end == usize::max_value() => + panic!("attempted to index slice up to maximum usize"), + ops::RangeInclusive::NonEmpty { start, end } => + self.index(start .. end+1) + } + } +} +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::Index> for [T] { + type Output = [T]; + + #[inline] + fn index(&self, index: ops::RangeToInclusive) -> &[T] { + // SNAP 3391630 change this to `0...index.end` + self.index(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl ops::IndexMut> for [T] { #[inline] @@ -603,7 +629,7 @@ impl ops::IndexMut> for [T] { impl ops::IndexMut> for [T] { #[inline] fn index_mut(&mut self, index: ops::RangeTo) -> &mut [T] { - self.index_mut(ops::Range{ start: 0, end: index.end }) + self.index_mut(0 .. index.end) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -611,7 +637,7 @@ impl ops::IndexMut> for [T] { #[inline] fn index_mut(&mut self, index: ops::RangeFrom) -> &mut [T] { let len = self.len(); - self.index_mut(ops::Range{ start: index.start, end: len }) + self.index_mut(index.start .. len) } } #[stable(feature = "rust1", since = "1.0.0")] @@ -622,6 +648,27 @@ impl ops::IndexMut for [T] { } } +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::IndexMut> for [T] { + #[inline] + fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut [T] { + match index { + ops::RangeInclusive::Empty { .. } => &mut [], + ops::RangeInclusive::NonEmpty { end, .. } if end == usize::max_value() => + panic!("attempted to index slice up to maximum usize"), + ops::RangeInclusive::NonEmpty { start, end } => + self.index_mut(start .. end+1) + } + } +} +#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +impl ops::IndexMut> for [T] { + #[inline] + fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut [T] { + // SNAP 3391630 change this to `0...index.end` + self.index_mut(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) + } +} //////////////////////////////////////////////////////////////////////////////// // Common traits diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 4d367cfd432..1c77ea84537 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1462,6 +1462,62 @@ mod traits { self } } + + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] + impl ops::Index> for str { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeInclusive) -> &str { + match index { + ops::RangeInclusive::Empty { .. } => "", + ops::RangeInclusive::NonEmpty { end, .. } if end == usize::max_value() => + panic!("attempted to index slice up to maximum usize"), + ops::RangeInclusive::NonEmpty { start, end } => + self.index(start .. end+1) + } + } + } + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] + impl ops::Index> for str { + type Output = str; + + #[inline] + fn index(&self, index: ops::RangeToInclusive) -> &str { + // SNAP 3391630 change this to `0...index.end` + self.index(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) + } + } + + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] + impl ops::IndexMut> for str { + #[inline] + fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { + match index { + ops::RangeInclusive::Empty { .. } => &mut self[0..0], // `&mut ""` doesn't work + ops::RangeInclusive::NonEmpty { end, .. } if end == usize::max_value() => + panic!("attempted to index str up to maximum usize"), + ops::RangeInclusive::NonEmpty { start, end } => + self.index_mut(start .. end+1) + } + } + } + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] + impl ops::IndexMut> for str { + #[inline] + fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { + // SNAP 3391630 change this to `0...index.end` + self.index_mut(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) + } + } } /// Methods for string slices diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index dd84cba370c..6ecf5c32bdb 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -233,6 +233,7 @@ #![feature(fnbox)] #![feature(heap_api)] #![feature(hashmap_hasher)] +#![feature(inclusive_range)] #![feature(int_error_internals)] #![feature(into_cow)] #![feature(lang_items)] diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index c1bc6adf7b8..52c8353d00e 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -55,6 +55,24 @@ pub fn main() { let _ = x...&y; } + // test collection indexing + let vec = (0...10).collect::>(); + let slice: &[_] = &*vec; + let string = String::from("hello world"); + let stir = "hello world"; + + assert_eq!(&vec[3...6], &[3, 4, 5, 6]); + assert_eq!(&vec[ ...6], &[0, 1, 2, 3, 4, 5, 6]); + + assert_eq!(&slice[3...6], &[3, 4, 5, 6]); + assert_eq!(&slice[ ...6], &[0, 1, 2, 3, 4, 5, 6]); + + assert_eq!(&string[3...6], "lo w"); + assert_eq!(&string[ ...6], "hello w"); + + assert_eq!(&stir[3...6], "lo w"); + assert_eq!(&stir[ ...6], "hello w"); + // test the size hints and emptying let mut long = 0...255u8; let mut short = 42...42; From 1ec3005e451137e51479b77ea3ab4f45dec56ed0 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Thu, 11 Feb 2016 12:52:39 -0500 Subject: [PATCH 13/19] fix fallout from libsyntax enumpocalypse --- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/parse/parser.rs | 2 +- src/libsyntax/print/pprust.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 9be52fb153d..94f9cf6546d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1003,7 +1003,7 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { self.gate_feature("type_ascription", e.span, "type ascription is experimental"); } - ast::ExprRange(_, _, ast::RangeLimits::Closed) => { + ast::ExprKind::Range(_, _, ast::RangeLimits::Closed) => { self.gate_feature("inclusive_range_syntax", e.span, "inclusive range syntax is experimental"); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8b563ef00f3..e772641aca5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2056,7 +2056,7 @@ impl<'a> Parser<'a> { start: Option>, end: Option>, limits: RangeLimits) - -> ast::Expr_ { + -> ast::ExprKind { ExprKind::Range(start, end, limits) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8d81787d922..55c1af44cab 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -2163,7 +2163,7 @@ impl<'a> State<'a> { try!(self.print_expr(&index)); try!(word(&mut self.s, "]")); } - ast::ExprKing::Range(ref start, ref end, limits) => { + ast::ExprKind::Range(ref start, ref end, limits) => { if let &Some(ref e) = start { try!(self.print_expr(&e)); } From f27a3a304fa10c77d4e944172b27f9f0776f9847 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Sat, 27 Feb 2016 01:56:19 -0500 Subject: [PATCH 14/19] fix underflow in DoubleEndedIterator::next_back --- src/libcore/iter.rs | 14 +++++++++----- src/test/run-pass/range_inclusive.rs | 5 +++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 8cea98d2a21..64933d0acf5 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -4665,11 +4665,15 @@ impl DoubleEndedIterator for ops::RangeInclusive where NonEmpty { ref mut start, ref mut end } => { let one = A::one(); - let mut n = &*end - &one; - mem::swap(&mut n, end); + if start <= end { + let mut n = &*end - &one; + mem::swap(&mut n, end); - (if n == *start { Some(mem::replace(start, one)) } else { None }, - n) + (if n == *start { Some(mem::replace(start, one)) } else { None }, + Some(n)) + } else { + (Some(mem::replace(end, one)), None) + } } }; @@ -4677,7 +4681,7 @@ impl DoubleEndedIterator for ops::RangeInclusive where *self = Empty { at: start }; } - Some(n) + n } } diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index 52c8353d00e..970ea7cc078 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -99,6 +99,11 @@ pub fn main() { } assert_eq!(long, RangeInclusive::Empty { at: 251 }); + // check underflow + let mut narrow = 1...0; + assert_eq!(narrow.next_back(), None); + assert_eq!(narrow, RangeInclusive::Empty { at: 0 }); + // what happens if you have a nonsense range? let mut nonsense = 10...5; assert_eq!(nonsense.next(), None); From 15303650e84e50f0731b2a3fe24d291d82c7bf2f Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Sat, 27 Feb 2016 01:56:38 -0500 Subject: [PATCH 15/19] fix stability hole --- src/librustc_front/lowering.rs | 7 ++++++- src/test/compile-fail/range_inclusive_gate.rs | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index 5e39a7c817a..665cc72b9fa 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -1236,7 +1236,12 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { structpath, fields.into_iter().map(|&(s, e)| { field(token::intern(s), - lower_expr(lctx, &**e), + signal_block_expr(lctx, + hir_vec![], + lower_expr(lctx, &**e), + e.span, + hir::PopUnstableBlock, + None), ast_expr.span) }).collect(), None, diff --git a/src/test/compile-fail/range_inclusive_gate.rs b/src/test/compile-fail/range_inclusive_gate.rs index 630679f27e3..deac152ec85 100644 --- a/src/test/compile-fail/range_inclusive_gate.rs +++ b/src/test/compile-fail/range_inclusive_gate.rs @@ -14,8 +14,12 @@ // #![feature(inclusive_range)] pub fn main() { - let _: std::ops::RangeInclusive<_> = 1...10; + let _: std::ops::RangeInclusive<_> = { use std::intrinsics; 1 } ... { use std::intrinsics; 2 }; //~^ ERROR use of unstable library feature 'inclusive_range' + //~^^ ERROR core_intrinsics + //~^^^ ERROR core_intrinsics + //~^^^^ WARN unused_imports + //~^^^^^ WARN unused_imports } From 54cb2d958681e0e643859fd324c5729fc9de6520 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Sat, 27 Feb 2016 02:03:02 -0500 Subject: [PATCH 16/19] update snapshot comments --- src/libcore/slice.rs | 4 ++-- src/libcore/str/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 2293e93eea7..3a285b7cffd 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -603,7 +603,7 @@ impl ops::Index> for [T] { #[inline] fn index(&self, index: ops::RangeToInclusive) -> &[T] { - // SNAP 3391630 change this to `0...index.end` + // SNAP 4d3eebf change this to `0...index.end` self.index(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) } } @@ -665,7 +665,7 @@ impl ops::IndexMut> for [T] { impl ops::IndexMut> for [T] { #[inline] fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut [T] { - // SNAP 3391630 change this to `0...index.end` + // SNAP 4d3eebf change this to `0...index.end` self.index_mut(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) } } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 1c77ea84537..d69ffebc073 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1488,7 +1488,7 @@ mod traits { #[inline] fn index(&self, index: ops::RangeToInclusive) -> &str { - // SNAP 3391630 change this to `0...index.end` + // SNAP 4d3eebf change this to `0...index.end` self.index(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) } } @@ -1514,7 +1514,7 @@ mod traits { impl ops::IndexMut> for str { #[inline] fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { - // SNAP 3391630 change this to `0...index.end` + // SNAP 4d3eebf change this to `0...index.end` self.index_mut(ops::RangeInclusive::NonEmpty { start: 0, end: index.end }) } } From 003120aa191845c1e57467dc4a2cb6140e5689a2 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Fri, 4 Mar 2016 18:39:25 -0500 Subject: [PATCH 17/19] add more unstable annotations --- src/libcore/ops.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index e5a51c98aca..6f8ac078807 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -1538,15 +1538,30 @@ impl fmt::Debug for RangeTo { #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] pub enum RangeInclusive { /// Empty range (iteration has finished) + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] Empty { /// The point at which iteration finished + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] at: Idx }, /// Non-empty range (iteration will yield value(s)) + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] NonEmpty { /// The lower bound of the range (inclusive). + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] start: Idx, /// The upper bound of the range (inclusive). + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] end: Idx, }, } From a928c8360c14660c6eeb4bfa29d6ad54ac0029f4 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Fri, 4 Mar 2016 18:57:22 -0500 Subject: [PATCH 18/19] add underflow/overflow tests --- src/test/run-pass/range_inclusive.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index 970ea7cc078..07233a43b88 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -103,6 +103,14 @@ pub fn main() { let mut narrow = 1...0; assert_eq!(narrow.next_back(), None); assert_eq!(narrow, RangeInclusive::Empty { at: 0 }); + let mut zero = 0u8...0; + assert_eq!(zero.next_back(), Some(0)); + assert_eq!(zero.next_back(), None); + assert_eq!(zero, RangeInclusive::Empty { at: 0 }); + let mut high = 255u8...255; + assert_eq!(high.next_back(), Some(255)); + assert_eq!(high.next_back(), None); + assert_eq!(high, RangeInclusive::Empty { at: 255 }); // what happens if you have a nonsense range? let mut nonsense = 10...5; From 430b3e19606cf76288a383b89428107756d4aa61 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Fri, 4 Mar 2016 18:57:43 -0500 Subject: [PATCH 19/19] remove under/overflow from next_back/next --- src/libcore/iter.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libcore/iter.rs b/src/libcore/iter.rs index 64933d0acf5..e37e52c6dc3 100644 --- a/src/libcore/iter.rs +++ b/src/libcore/iter.rs @@ -4605,8 +4605,10 @@ impl Iterator for ops::RangeInclusive where Empty { .. } => (None, None), // empty iterators yield no values NonEmpty { ref mut start, ref mut end } => { - let one = A::one(); - if start <= end { + if start == end { + (Some(mem::replace(end, A::one())), Some(mem::replace(start, A::one()))) + } else if start < end { + let one = A::one(); let mut n = &*start + &one; mem::swap(&mut n, start); @@ -4620,7 +4622,7 @@ impl Iterator for ops::RangeInclusive where // ^ are we done yet? Some(n)) // < the value to output } else { - (Some(mem::replace(start, one)), None) + (Some(mem::replace(start, A::one())), None) } } }; @@ -4664,15 +4666,17 @@ impl DoubleEndedIterator for ops::RangeInclusive where Empty { .. } => return None, NonEmpty { ref mut start, ref mut end } => { - let one = A::one(); - if start <= end { + if start == end { + (Some(mem::replace(start, A::one())), Some(mem::replace(end, A::one()))) + } else if start < end { + let one = A::one(); let mut n = &*end - &one; mem::swap(&mut n, end); (if n == *start { Some(mem::replace(start, one)) } else { None }, Some(n)) } else { - (Some(mem::replace(end, one)), None) + (Some(mem::replace(end, A::one())), None) } } };