diff --git a/src/doc/book/iterators.md b/src/doc/book/iterators.md index 0c4f804126631..c174d2d6bacb6 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 6782bdb4985ec..53f38cd77e4a4 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 6fb8de780942c..c2b22ef3e72c2 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2277,6 +2277,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. @@ -2747,6 +2751,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, diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs index e6cf5aa668357..922e1b0fc5d6d 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/range.rs b/src/libcollections/range.rs index afcd779ddf19f..4e39191b472ee 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/libcollections/string.rs b/src/libcollections/string.rs index 62ae7938e15e3..cae6520bdb286 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 efcb5d2ceb3aa..934a9f3d61452 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1226,6 +1226,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 { @@ -1255,6 +1273,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/iter.rs b/src/libcore/iter.rs index 37fe4d39db86f..377a8b6a05f5e 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; @@ -4297,7 +4297,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")] @@ -4306,7 +4306,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. /// @@ -4366,8 +4366,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> { @@ -4386,95 +4422,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; @@ -4512,10 +4459,83 @@ 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")] impl ExactSizeIterator for ops::Range<$t> { } + + #[unstable(feature = "inclusive_range", + reason = "recently added, follows RFC", + issue = "28237")] + impl ExactSizeIterator for ops::RangeInclusive<$t> { } )*) } @@ -4579,6 +4599,107 @@ 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 } => { + 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); + + // 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, A::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 } => { + 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, A::one())), None) + } + } + }; + + if let Some(start) = finishing { + *self = Empty { at: start }; + } + + 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 0f5584a952f54..a6dbec6c39dab 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'. @@ -1445,7 +1448,7 @@ pub trait IndexMut: Index { /// An unbounded range. #[derive(Copy, Clone, PartialEq, Eq)] -#[lang = "range_full"] +#[cfg_attr(stage0, lang = "range_full")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFull; @@ -1458,7 +1461,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")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct Range { /// The lower bound of the range (inclusive). @@ -1478,7 +1481,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")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFrom { /// The lower bound of the range (inclusive). @@ -1495,7 +1498,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")] // FIXME remove attribute after next snapshot #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeTo { /// The upper bound of the range (exclusive). @@ -1510,6 +1513,90 @@ 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) + #[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, + }, +} + +#[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/libcore/slice.rs b/src/libcore/slice.rs index fb15533f33c54..8acd0c8f2cf06 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]; @@ -558,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")] @@ -567,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")] @@ -580,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 4d3eebf 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] @@ -601,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")] @@ -609,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")] @@ -620,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 4d3eebf 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 2d898b50e0c3a..458318c5250a2 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1460,6 +1460,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 4d3eebf 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 4d3eebf 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/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index a6fb5c02f54c3..1fb27261c4dbf 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 8b042e73e796a..5ddea6917435e 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -398,11 +398,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/lang_items.rs b/src/librustc/middle/lang_items.rs index 6cbb90627eab5..8d407d0aa4473 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/middle/liveness.rs b/src/librustc/middle/liveness.rs index 81f0cb1fbb2a9..5abfd2b7d2c5b 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 11ef1dbd70556..ab894a55b44fe 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 85035b2500144..042007f5fef05 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -2008,7 +2008,6 @@ impl<'tcx> TyCtxt<'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 b29116309912a..b01b80b813399 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 75a1363fcf9ef..beedb3d70b699 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -1090,10 +1090,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/hir.rs b/src/librustc_front/hir.rs index cc562b0f7b20a..44e7fa05073a1 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -776,8 +776,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/intravisit.rs b/src/librustc_front/intravisit.rs index e6f448654acc5..d71e392f521e7 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/lowering.rs b/src/librustc_front/lowering.rs index 1bfcb29858633..acc7c6164b54a 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; @@ -1217,9 +1218,79 @@ 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), + signal_block_expr(lctx, + hir_vec![], + lower_expr(lctx, &**e), + e.span, + hir::PopUnstableBlock, + None), + 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 }| { @@ -1627,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) @@ -1676,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 { diff --git a/src/librustc_front/print/pprust.rs b/src/librustc_front/print/pprust.rs index 49fbcea3dbf14..143dfce09b602 100644 --- a/src/librustc_front/print/pprust.rs +++ b/src/librustc_front/print/pprust.rs @@ -1449,15 +1449,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 ac1cff527fe91..cb54cadfcd16b 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 3703e602746c6..810712bb0cf18 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 73fdbd54b29d0..4ba103c0c0d08 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 591d8a9f76c97..e3e53607829e6 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: &TyCtxt, 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 9be46cf4053b1..eee3d23283107 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3673,87 +3673,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, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 0ff6dcccde698..01f7d6b456582 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, diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e062f9040afc4..fa07b0f761b4e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -232,6 +232,7 @@ #![feature(fnbox)] #![feature(heap_api)] #![feature(hashmap_hasher)] +#![feature(inclusive_range)] #![feature(int_error_internals)] #![feature(into_cow)] #![feature(lang_items)] @@ -246,7 +247,6 @@ #![feature(optin_builtin_traits)] #![feature(placement_in_syntax)] #![feature(rand)] -#![feature(range_inclusive)] #![feature(raw)] #![feature(repr_simd)] #![feature(reflect_marker)] diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 23bb6fd141a4e..0dbfb2c7be654 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/feature_gate.rs b/src/libsyntax/feature_gate.rs index 3f2fb2d3d1704..2302548914223 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 @@ -583,6 +587,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(), @@ -991,6 +996,11 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> { self.gate_feature("type_ascription", e.span, "type ascription is experimental"); } + ast::ExprKind::Range(_, _, ast::RangeLimits::Closed) => { + self.gate_feature("inclusive_range_syntax", + e.span, + "inclusive range syntax is experimental"); + } _ => {} } visit::walk_expr(self, e); @@ -1177,6 +1187,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, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index d75e8f796ae89..591c1295d6641 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 e166a36721967..d9714cc1e25e0 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}; @@ -2059,9 +2059,10 @@ impl<'a> Parser<'a> { pub fn mk_range(&mut self, start: Option>, - end: Option>) + end: Option>, + limits: RangeLimits) -> ast::ExprKind { - ExprKind::Range(start, end) + ExprKind::Range(start, end, limits) } pub fn mk_field(&mut self, expr: P, ident: ast::SpannedIdent) -> ast::ExprKind { @@ -2899,7 +2900,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)) @@ -2945,32 +2946,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() { @@ -2986,8 +2987,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| { @@ -3028,6 +3029,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") } @@ -3059,18 +3065,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|{ @@ -3080,7 +3087,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 6593b3ea53200..294cbf358954f 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 b4e08d65a0a8d..55c1af44cab85 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::ExprKind::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 6fb81bb6a7684..df4eb1c9ed7d8 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 f26610b8b8d14..73ad488e55c93 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); } diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs index b839902c6832a..e4ab5829f4168 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 c9053328572bc..94967693ecf91 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/compile-fail/range_inclusive_gate.rs b/src/test/compile-fail/range_inclusive_gate.rs new file mode 100644 index 0000000000000..deac152ec85bc --- /dev/null +++ b/src/test/compile-fail/range_inclusive_gate.rs @@ -0,0 +1,25 @@ +// 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<_> = { 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 +} + + diff --git a/src/test/parse-fail/range_inclusive.rs b/src/test/parse-fail/range_inclusive.rs new file mode 100644 index 0000000000000..5fd6f1834e02d --- /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 0000000000000..021b6dd3e260f --- /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.rs b/src/test/run-pass/range.rs index 24261772add03..4c249bbe1f73e 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. diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs new file mode 100644 index 0000000000000..07233a43b88e2 --- /dev/null +++ b/src/test/run-pass/range_inclusive.rs @@ -0,0 +1,129 @@ +// 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, step_by)] + +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); + + 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 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; + 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 }); + + // check underflow + 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; + 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 0000000000000..5e0ec19d6b325 --- /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); +} +