1
Fork 0

Auto merge of #30884 - durka:inclusive-ranges, r=aturon

This PR implements [RFC 1192](https://github.com/rust-lang/rfcs/blob/master/text/1192-inclusive-ranges.md), which is triple-dot syntax for inclusive range expressions. The new stuff is behind two feature gates (one for the syntax and one for the std::ops types). This replaces the deprecated functionality in std::iter. Along the way I simplified the desugaring for all ranges.

This is my first contribution to rust which changes more than one character outside of a test or comment, so please review carefully! Some of the individual commit messages have more of my notes. Also thanks for putting up with my dumb questions in #rust-internals.

- For implementing `std::ops::RangeInclusive`, I took @Stebalien's suggestion from https://github.com/rust-lang/rfcs/pull/1192#issuecomment-137864421. It seemed to me to make the implementation easier and increase type safety. If that stands, the RFC should be amended to avoid confusion.
- I also kind of like @glaebhoerl's [idea](https://github.com/rust-lang/rfcs/pull/1254#issuecomment-147815299), which is unified inclusive/exclusive range syntax something like `x>..=y`. We can experiment with this while everything is behind a feature gate.
- There are a couple of FIXMEs left (see the last commit). I didn't know what to do about `RangeArgument` and I haven't added `Index` impls yet. Those should be discussed/finished before merging.

cc @Gankro since you [complained](https://www.reddit.com/r/rust/comments/3xkfro/what_happened_to_inclusive_ranges/cy5j0yq)
cc #27777 #30877 rust-lang/rust#1192 rust-lang/rfcs#1254
relevant to #28237 (tracking issue)
This commit is contained in:
bors 2016-03-06 07:16:41 +00:00
commit 8484831d29
46 changed files with 983 additions and 390 deletions

View file

@ -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 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. 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: Like this:
```rust ```rust

View file

@ -66,7 +66,8 @@
* `..` (`..`, `expr..`, `..expr`, `expr..expr`): right-exclusive range literal. * `..` (`..`, `expr..`, `..expr`, `expr..expr`): right-exclusive range literal.
* `..` (`..expr`): struct literal update syntax. See [Structs (Update syntax)]. * `..` (`..expr`): struct literal update syntax. See [Structs (Update syntax)].
* `..` (`variant(x, ..)`, `struct_type { x, .. }`): "and the rest" pattern binding. See [Patterns (Ignoring bindings)]. * `..` (`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`). * `/` (`expr / expr`): arithmetic division. Overloadable (`Div`).
* `/=` (`var /= expr`): arithmetic division & assignment. * `/=` (`var /= expr`): arithmetic division & assignment.
* `:` (`pat: type`, `ident: type`): constraints. See [Variable Bindings], [Functions], [Structs], [Traits]. * `:` (`pat: type`, `ident: type`): constraints. See [Variable Bindings], [Functions], [Structs], [Traits].
@ -205,6 +206,7 @@
[Functions (Early Returns)]: functions.html#early-returns [Functions (Early Returns)]: functions.html#early-returns
[Functions]: functions.html [Functions]: functions.html
[Generics]: generics.html [Generics]: generics.html
[Iterators]: iterators.html
[Lifetimes]: lifetimes.html [Lifetimes]: lifetimes.html
[Loops (`for`)]: loops.html#for [Loops (`for`)]: loops.html#for
[Loops (`loop`)]: loops.html#loop [Loops (`loop`)]: loops.html#loop

View file

@ -2277,6 +2277,10 @@ The currently implemented features of the reference compiler are:
`#[derive_Foo] #[derive_Bar]`, which can be user-defined syntax `#[derive_Foo] #[derive_Bar]`, which can be user-defined syntax
extensions. 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 * `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
are inherently unstable and no promise about them is made. are inherently unstable and no promise about them is made.
@ -2747,6 +2751,25 @@ let y = 0..10;
assert_eq!(x, y); 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 ### Unary operator expressions
Rust defines the following unary operators. They are all written as prefix operators, Rust defines the following unary operators. They are all written as prefix operators,

View file

@ -40,7 +40,7 @@
#![feature(fmt_internals)] #![feature(fmt_internals)]
#![feature(fmt_radix)] #![feature(fmt_radix)]
#![feature(heap_api)] #![feature(heap_api)]
#![feature(iter_arith)] #![feature(inclusive_range)]
#![feature(iter_arith)] #![feature(iter_arith)]
#![feature(lang_items)] #![feature(lang_items)]
#![feature(nonzero)] #![feature(nonzero)]

View file

@ -35,6 +35,7 @@ pub trait RangeArgument<T> {
} }
} }
// FIXME add inclusive ranges to RangeArgument
impl<T> RangeArgument<T> for RangeFull {} impl<T> RangeArgument<T> for RangeFull {}

View file

@ -59,7 +59,7 @@ use core::fmt;
use core::hash; use core::hash;
use core::iter::FromIterator; use core::iter::FromIterator;
use core::mem; use core::mem;
use core::ops::{self, Add}; use core::ops::{self, Add, Index, IndexMut};
use core::ptr; use core::ptr;
use core::slice; use core::slice;
use core::str::pattern::Pattern; use core::str::pattern::Pattern;
@ -1606,6 +1606,24 @@ impl ops::Index<ops::RangeFull> for String {
unsafe { str::from_utf8_unchecked(&self.vec) } unsafe { str::from_utf8_unchecked(&self.vec) }
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl ops::Index<ops::RangeInclusive<usize>> for String {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &str {
Index::index(&**self, index)
}
}
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl ops::Index<ops::RangeToInclusive<usize>> for String {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &str {
Index::index(&**self, index)
}
}
#[stable(feature = "derefmut_for_string", since = "1.2.0")] #[stable(feature = "derefmut_for_string", since = "1.2.0")]
impl ops::IndexMut<ops::Range<usize>> for String { impl ops::IndexMut<ops::Range<usize>> for String {
@ -1635,6 +1653,20 @@ impl ops::IndexMut<ops::RangeFull> for String {
unsafe { mem::transmute(&mut *self.vec) } unsafe { mem::transmute(&mut *self.vec) }
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl ops::IndexMut<ops::RangeInclusive<usize>> for String {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut str {
IndexMut::index_mut(&mut **self, index)
}
}
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl ops::IndexMut<ops::RangeToInclusive<usize>> for String {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut str {
IndexMut::index_mut(&mut **self, index)
}
}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl ops::Deref for String { impl ops::Deref for String {

View file

@ -1226,6 +1226,24 @@ impl<T> ops::Index<ops::RangeFull> for Vec<T> {
self self
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<T> ops::Index<ops::RangeInclusive<usize>> for Vec<T> {
type Output = [T];
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &[T] {
Index::index(&**self, index)
}
}
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<T> ops::Index<ops::RangeToInclusive<usize>> for Vec<T> {
type Output = [T];
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &[T] {
Index::index(&**self, index)
}
}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T> ops::IndexMut<ops::Range<usize>> for Vec<T> { impl<T> ops::IndexMut<ops::Range<usize>> for Vec<T> {
@ -1255,6 +1273,20 @@ impl<T> ops::IndexMut<ops::RangeFull> for Vec<T> {
self self
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<T> ops::IndexMut<ops::RangeInclusive<usize>> for Vec<T> {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &mut [T] {
IndexMut::index_mut(&mut **self, index)
}
}
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<T> ops::IndexMut<ops::RangeToInclusive<usize>> for Vec<T> {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut [T] {
IndexMut::index_mut(&mut **self, index)
}
}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T> ops::Deref for Vec<T> { impl<T> ops::Deref for Vec<T> {

View file

@ -306,7 +306,7 @@ use default::Default;
use marker; use marker;
use mem; use mem;
use num::{Zero, One}; 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 option::Option::{self, Some, None};
use marker::Sized; use marker::Sized;
use usize; use usize;
@ -4297,7 +4297,7 @@ step_impl_no_between!(u64 i64);
/// ///
/// The resulting iterator handles overflow by stopping. The `A` /// The resulting iterator handles overflow by stopping. The `A`
/// parameter is the type being iterated over, while `R` is the range /// 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)] #[derive(Clone)]
#[unstable(feature = "step_by", reason = "recent addition", #[unstable(feature = "step_by", reason = "recent addition",
issue = "27741")] issue = "27741")]
@ -4306,7 +4306,7 @@ pub struct StepBy<A, R> {
range: R, range: R,
} }
impl<A: Step> RangeFrom<A> { impl<A: Step> ops::RangeFrom<A> {
/// Creates an iterator starting at the same point, but stepping by /// Creates an iterator starting at the same point, but stepping by
/// the given amount at each iteration. /// the given amount at each iteration.
/// ///
@ -4366,8 +4366,44 @@ impl<A: Step> ops::Range<A> {
} }
} }
impl<A: Step> ops::RangeInclusive<A> {
/// 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<A, Self> {
StepBy {
step_by: by,
range: self
}
}
}
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<A> Iterator for StepBy<A, RangeFrom<A>> where impl<A> Iterator for StepBy<A, ops::RangeFrom<A>> where
A: Clone, A: Clone,
for<'a> &'a A: Add<&'a A, Output = A> for<'a> &'a A: Add<&'a A, Output = A>
{ {
@ -4386,95 +4422,6 @@ impl<A> Iterator for StepBy<A, RangeFrom<A>> 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<A> {
range: ops::Range<A>,
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<A>(start: A, stop: A) -> RangeInclusive<A>
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<A> Iterator for RangeInclusive<A> where
A: PartialEq + Step + One + Clone,
for<'a> &'a A: Add<&'a A, Output = A>
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
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<usize>) {
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<A> DoubleEndedIterator for RangeInclusive<A> where
A: PartialEq + Step + One + Clone,
for<'a> &'a A: Add<&'a A, Output = A>,
for<'a> &'a A: Sub<Output=A>
{
#[inline]
fn next_back(&mut self) -> Option<A> {
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")] #[stable(feature = "rust1", since = "1.0.0")]
impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::Range<A>> { impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::Range<A>> {
type Item = A; type Item = A;
@ -4512,10 +4459,83 @@ impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::Range<A>> {
} }
} }
#[unstable(feature = "inclusive_range",
reason = "recently added, follows RFC",
issue = "28237")]
impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::RangeInclusive<A>> {
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
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<usize>) {
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 { macro_rules! range_exact_iter_impl {
($($t:ty)*) => ($( ($($t:ty)*) => ($(
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl ExactSizeIterator for ops::Range<$t> { } 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<A: Step + One> Iterator for ops::RangeFrom<A> where
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<A: Step + One> Iterator for ops::RangeInclusive<A> where
for<'a> &'a A: Add<&'a A, Output = A>
{
type Item = A;
#[inline]
fn next(&mut self) -> Option<A> {
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<usize>) {
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<A: Step + One> DoubleEndedIterator for ops::RangeInclusive<A> 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<A> {
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. /// An iterator that repeats an element endlessly.
/// ///
/// This `struct` is created by the [`repeat()`] function. See its documentation for more. /// This `struct` is created by the [`repeat()`] function. See its documentation for more.

View file

@ -67,8 +67,11 @@
#![stable(feature = "rust1", since = "1.0.0")] #![stable(feature = "rust1", since = "1.0.0")]
use marker::{Sized, Unsize}; use cmp::PartialOrd;
use fmt; 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. /// The `Drop` trait is used to run some code when a value goes out of scope.
/// This is sometimes called a 'destructor'. /// This is sometimes called a 'destructor'.
@ -1445,7 +1448,7 @@ pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
/// An unbounded range. /// An unbounded range.
#[derive(Copy, Clone, PartialEq, Eq)] #[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")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct RangeFull; pub struct RangeFull;
@ -1458,7 +1461,7 @@ impl fmt::Debug for RangeFull {
/// A (half-open) range which is bounded at both ends. /// A (half-open) range which is bounded at both ends.
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
#[lang = "range"] #[cfg_attr(stage0, lang = "range")] // FIXME remove attribute after next snapshot
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct Range<Idx> { pub struct Range<Idx> {
/// The lower bound of the range (inclusive). /// The lower bound of the range (inclusive).
@ -1478,7 +1481,7 @@ impl<Idx: fmt::Debug> fmt::Debug for Range<Idx> {
/// A range which is only bounded below. /// A range which is only bounded below.
#[derive(Clone, PartialEq, Eq)] #[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")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct RangeFrom<Idx> { pub struct RangeFrom<Idx> {
/// The lower bound of the range (inclusive). /// The lower bound of the range (inclusive).
@ -1495,7 +1498,7 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeFrom<Idx> {
/// A range which is only bounded above. /// A range which is only bounded above.
#[derive(Copy, Clone, PartialEq, Eq)] #[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")] #[stable(feature = "rust1", since = "1.0.0")]
pub struct RangeTo<Idx> { pub struct RangeTo<Idx> {
/// The upper bound of the range (exclusive). /// The upper bound of the range (exclusive).
@ -1510,6 +1513,90 @@ impl<Idx: fmt::Debug> fmt::Debug for RangeTo<Idx> {
} }
} }
/// 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<Idx> {
/// 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<Idx: fmt::Debug> fmt::Debug for RangeInclusive<Idx> {
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<Idx: PartialOrd + One + Sub<Output=Idx>> From<Range<Idx>> for RangeInclusive<Idx> {
fn from(range: Range<Idx>) -> RangeInclusive<Idx> {
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<Idx> {
/// 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<Idx: fmt::Debug> fmt::Debug for RangeToInclusive<Idx> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "...{:?}", self.end)
}
}
// RangeToInclusive<Idx> cannot impl From<RangeTo<Idx>>
// because underflow would be possible with (..0).into()
/// The `Deref` trait is used to specify the functionality of dereferencing /// The `Deref` trait is used to specify the functionality of dereferencing
/// operations, like `*v`. /// operations, like `*v`.
/// ///

View file

@ -533,6 +533,8 @@ fn slice_index_order_fail(index: usize, end: usize) -> ! {
panic!("slice index starts at {} but ends at {}", index, end); panic!("slice index starts at {} but ends at {}", index, end);
} }
// FIXME implement indexing with inclusive ranges
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T> ops::Index<ops::Range<usize>> for [T] { impl<T> ops::Index<ops::Range<usize>> for [T] {
type Output = [T]; type Output = [T];
@ -558,7 +560,7 @@ impl<T> ops::Index<ops::RangeTo<usize>> for [T] {
#[inline] #[inline]
fn index(&self, index: ops::RangeTo<usize>) -> &[T] { fn index(&self, index: ops::RangeTo<usize>) -> &[T] {
self.index(ops::Range{ start: 0, end: index.end }) self.index(0 .. index.end)
} }
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -567,7 +569,7 @@ impl<T> ops::Index<ops::RangeFrom<usize>> for [T] {
#[inline] #[inline]
fn index(&self, index: ops::RangeFrom<usize>) -> &[T] { fn index(&self, index: ops::RangeFrom<usize>) -> &[T] {
self.index(ops::Range{ start: index.start, end: self.len() }) self.index(index.start .. self.len())
} }
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -580,6 +582,32 @@ impl<T> ops::Index<RangeFull> for [T] {
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<T> ops::Index<ops::RangeInclusive<usize>> for [T] {
type Output = [T];
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &[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<T> ops::Index<ops::RangeToInclusive<usize>> for [T] {
type Output = [T];
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &[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")] #[stable(feature = "rust1", since = "1.0.0")]
impl<T> ops::IndexMut<ops::Range<usize>> for [T] { impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
#[inline] #[inline]
@ -601,7 +629,7 @@ impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] { impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] {
#[inline] #[inline]
fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &mut [T] { fn index_mut(&mut self, index: ops::RangeTo<usize>) -> &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")] #[stable(feature = "rust1", since = "1.0.0")]
@ -609,7 +637,7 @@ impl<T> ops::IndexMut<ops::RangeFrom<usize>> for [T] {
#[inline] #[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut [T] { fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &mut [T] {
let len = self.len(); 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")] #[stable(feature = "rust1", since = "1.0.0")]
@ -620,6 +648,27 @@ impl<T> ops::IndexMut<RangeFull> for [T] {
} }
} }
#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")]
impl<T> ops::IndexMut<ops::RangeInclusive<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &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<T> ops::IndexMut<ops::RangeToInclusive<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &mut [T] {
// SNAP 4d3eebf change this to `0...index.end`
self.index_mut(ops::RangeInclusive::NonEmpty { start: 0, end: index.end })
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Common traits // Common traits

View file

@ -1460,6 +1460,62 @@ mod traits {
self self
} }
} }
#[unstable(feature = "inclusive_range",
reason = "recently added, follows RFC",
issue = "28237")]
impl ops::Index<ops::RangeInclusive<usize>> for str {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeInclusive<usize>) -> &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<ops::RangeToInclusive<usize>> for str {
type Output = str;
#[inline]
fn index(&self, index: ops::RangeToInclusive<usize>) -> &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<ops::RangeInclusive<usize>> for str {
#[inline]
fn index_mut(&mut self, index: ops::RangeInclusive<usize>) -> &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<ops::RangeToInclusive<usize>> for str {
#[inline]
fn index_mut(&mut self, index: ops::RangeToInclusive<usize>) -> &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 /// Methods for string slices

View file

@ -317,12 +317,6 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
self.call(expr, pred, &l, Some(&**r).into_iter()) 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) => { hir::ExprUnary(_, ref e) if self.tcx.is_method_call(expr.id) => {
self.call(expr, pred, &e, None::<hir::Expr>.iter()) self.call(expr, pred, &e, None::<hir::Expr>.iter())
} }

View file

@ -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) hir::ExprCall(ref callee, ref args) => { // callee(args)
self.walk_callee(expr, &callee); self.walk_callee(expr, &callee);
self.consume_exprs(args); self.consume_exprs(args);

View file

@ -302,10 +302,6 @@ lets_do_this! {
ShrAssignTraitLangItem, "shr_assign", shr_assign_trait; ShrAssignTraitLangItem, "shr_assign", shr_assign_trait;
IndexTraitLangItem, "index", index_trait; IndexTraitLangItem, "index", index_trait;
IndexMutTraitLangItem, "index_mut", index_mut_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; UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type;

View file

@ -498,7 +498,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
hir::ExprBlock(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) | hir::ExprBlock(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) |
hir::ExprStruct(..) | hir::ExprRepeat(..) | hir::ExprStruct(..) | hir::ExprRepeat(..) |
hir::ExprInlineAsm(..) | hir::ExprBox(..) | hir::ExprInlineAsm(..) | hir::ExprBox(..) |
hir::ExprRange(..) | hir::ExprType(..) => { hir::ExprType(..) => {
intravisit::walk_expr(ir, expr); intravisit::walk_expr(ir, expr);
} }
} }
@ -1154,11 +1154,6 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&l, r_succ) 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::ExprBox(ref e) |
hir::ExprAddrOf(_, ref e) | hir::ExprAddrOf(_, ref e) |
hir::ExprCast(ref e, _) | hir::ExprCast(ref e, _) |
@ -1446,7 +1441,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
hir::ExprBlock(..) | hir::ExprAddrOf(..) | hir::ExprBlock(..) | hir::ExprAddrOf(..) |
hir::ExprStruct(..) | hir::ExprRepeat(..) | hir::ExprStruct(..) | hir::ExprRepeat(..) |
hir::ExprClosure(..) | hir::ExprPath(..) | hir::ExprBox(..) | hir::ExprClosure(..) | hir::ExprPath(..) | hir::ExprBox(..) |
hir::ExprRange(..) | hir::ExprType(..) => { hir::ExprType(..) => {
intravisit::walk_expr(this, expr); intravisit::walk_expr(this, expr);
} }
} }

View file

@ -526,7 +526,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
hir::ExprAddrOf(..) | hir::ExprCall(..) | hir::ExprAddrOf(..) | hir::ExprCall(..) |
hir::ExprAssign(..) | hir::ExprAssignOp(..) | hir::ExprAssign(..) | hir::ExprAssignOp(..) |
hir::ExprClosure(..) | hir::ExprRet(..) | hir::ExprClosure(..) | hir::ExprRet(..) |
hir::ExprUnary(..) | hir::ExprRange(..) | hir::ExprUnary(..) |
hir::ExprMethodCall(..) | hir::ExprCast(..) | hir::ExprMethodCall(..) | hir::ExprCast(..) |
hir::ExprVec(..) | hir::ExprTup(..) | hir::ExprIf(..) | hir::ExprVec(..) | hir::ExprTup(..) | hir::ExprIf(..) |
hir::ExprBinary(..) | hir::ExprWhile(..) | hir::ExprBinary(..) | hir::ExprWhile(..) |

View file

@ -2008,7 +2008,6 @@ impl<'tcx> TyCtxt<'tcx> {
hir::ExprCall(..) | hir::ExprCall(..) |
hir::ExprMethodCall(..) | hir::ExprMethodCall(..) |
hir::ExprStruct(..) | hir::ExprStruct(..) |
hir::ExprRange(..) |
hir::ExprTup(..) | hir::ExprTup(..) |
hir::ExprIf(..) | hir::ExprIf(..) |
hir::ExprMatch(..) | hir::ExprMatch(..) |

View file

@ -243,7 +243,6 @@ mod svh_visitor {
SawExprAssign, SawExprAssign,
SawExprAssignOp(hir::BinOp_), SawExprAssignOp(hir::BinOp_),
SawExprIndex, SawExprIndex,
SawExprRange,
SawExprPath(Option<usize>), SawExprPath(Option<usize>),
SawExprAddrOf(hir::Mutability), SawExprAddrOf(hir::Mutability),
SawExprRet, SawExprRet,
@ -275,7 +274,6 @@ mod svh_visitor {
ExprField(_, name) => SawExprField(name.node.as_str()), ExprField(_, name) => SawExprField(name.node.as_str()),
ExprTupField(_, id) => SawExprTupField(id.node), ExprTupField(_, id) => SawExprTupField(id.node),
ExprIndex(..) => SawExprIndex, ExprIndex(..) => SawExprIndex,
ExprRange(..) => SawExprRange,
ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)), ExprPath(ref qself, _) => SawExprPath(qself.as_ref().map(|q| q.position)),
ExprAddrOf(m, _) => SawExprAddrOf(m), ExprAddrOf(m, _) => SawExprAddrOf(m),
ExprBreak(id) => SawExprBreak(id.map(|id| id.node.name.as_str())), ExprBreak(id) => SawExprBreak(id.map(|id| id.node.name.as_str())),

View file

@ -1090,10 +1090,6 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span, attrs }: Expr, folder: &
ExprIndex(el, er) => { ExprIndex(el, er) => {
ExprIndex(folder.fold_expr(el), folder.fold_expr(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) => { ExprPath(qself, path) => {
let qself = qself.map(|QSelf { ty, position }| { let qself = qself.map(|QSelf { ty, position }| {
QSelf { QSelf {

View file

@ -776,8 +776,6 @@ pub enum Expr_ {
ExprTupField(P<Expr>, Spanned<usize>), ExprTupField(P<Expr>, Spanned<usize>),
/// An indexing operation (`foo[2]`) /// An indexing operation (`foo[2]`)
ExprIndex(P<Expr>, P<Expr>), ExprIndex(P<Expr>, P<Expr>),
/// A range (`1..2`, `1..`, or `..2`)
ExprRange(Option<P<Expr>>, Option<P<Expr>>),
/// Variable reference, possibly containing `::` and/or type /// Variable reference, possibly containing `::` and/or type
/// parameters, e.g. foo::bar::<baz>. /// parameters, e.g. foo::bar::<baz>.

View file

@ -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(main_expression);
visitor.visit_expr(index_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) => { ExprPath(ref maybe_qself, ref path) => {
if let Some(ref qself) = *maybe_qself { if let Some(ref qself) = *maybe_qself {
visitor.visit_ty(&qself.ty); visitor.visit_ty(&qself.ty);

View file

@ -65,6 +65,7 @@ use hir;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
use std::iter;
use syntax::ast::*; use syntax::ast::*;
use syntax::attr::{ThinAttributes, ThinAttributesExt}; use syntax::attr::{ThinAttributes, ThinAttributesExt};
use syntax::ext::mtwt; use syntax::ext::mtwt;
@ -1217,9 +1218,79 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
ExprKind::Index(ref el, ref er) => { ExprKind::Index(ref el, ref er) => {
hir::ExprIndex(lower_expr(lctx, el), lower_expr(lctx, er)) hir::ExprIndex(lower_expr(lctx, el), lower_expr(lctx, er))
} }
ExprKind::Range(ref e1, ref e2) => { ExprKind::Range(ref e1, ref e2, lims) => {
hir::ExprRange(e1.as_ref().map(|x| lower_expr(lctx, x)), fn make_struct(lctx: &LoweringContext,
e2.as_ref().map(|x| lower_expr(lctx, x))) ast_expr: &Expr,
path: &[&str],
fields: &[(&str, &P<Expr>)]) -> P<hir::Expr> {
let strs = std_path(lctx, &iter::once(&"ops")
.chain(path)
.map(|s| *s)
.collect::<Vec<_>>());
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) => { ExprKind::Path(ref qself, ref path) => {
let hir_qself = qself.as_ref().map(|&QSelf { ref ty, position }| { let hir_qself = qself.as_ref().map(|&QSelf { ref ty, position }| {
@ -1627,6 +1698,17 @@ fn arm(pats: hir::HirVec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
} }
} }
fn field(name: Name, expr: P<hir::Expr>, span: Span) -> hir::Field {
hir::Field {
name: Spanned {
node: name,
span: span,
},
span: span,
expr: expr,
}
}
fn expr_break(lctx: &LoweringContext, span: Span, fn expr_break(lctx: &LoweringContext, span: Span,
attrs: ThinAttributes) -> P<hir::Expr> { attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, span, hir::ExprBreak(None), attrs) expr(lctx, span, hir::ExprBreak(None), attrs)
@ -1676,6 +1758,15 @@ fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: hir::HirVec<P<hir::Expr>>
expr(lctx, sp, hir::ExprTup(exprs), attrs) expr(lctx, sp, hir::ExprTup(exprs), attrs)
} }
fn expr_struct(lctx: &LoweringContext,
sp: Span,
path: hir::Path,
fields: hir::HirVec<hir::Field>,
e: Option<P<hir::Expr>>,
attrs: ThinAttributes) -> P<hir::Expr> {
expr(lctx, sp, hir::ExprStruct(path, fields, e), attrs)
}
fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_, fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_,
attrs: ThinAttributes) -> P<hir::Expr> { attrs: ThinAttributes) -> P<hir::Expr> {
P(hir::Expr { P(hir::Expr {

View file

@ -1449,15 +1449,6 @@ impl<'a> State<'a> {
try!(self.print_expr(&index)); try!(self.print_expr(&index));
try!(word(&mut self.s, "]")); 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) => { hir::ExprPath(None, ref path) => {
try!(self.print_path(path, true, 0)) try!(self.print_path(path, true, 0))
} }

View file

@ -22,7 +22,6 @@ use rustc::middle::ty::{self, VariantDef, Ty};
use rustc::mir::repr::*; use rustc::mir::repr::*;
use rustc_front::hir; use rustc_front::hir;
use rustc_front::util as hir_util; use rustc_front::util as hir_util;
use syntax::parse::token;
use syntax::ptr::P; use syntax::ptr::P;
impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { 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<hir::Expr>, 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(..) => { hir::ExprPath(..) => {
convert_path_expr(cx, self) convert_path_expr(cx, self)
} }

View file

@ -747,9 +747,6 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
hir::ExprAgain(_) | hir::ExprAgain(_) |
hir::ExprRet(_) | hir::ExprRet(_) |
// Miscellaneous expressions that could be implemented.
hir::ExprRange(..) |
// Expressions with side-effects. // Expressions with side-effects.
hir::ExprAssign(..) | hir::ExprAssign(..) |
hir::ExprAssignOp(..) | hir::ExprAssignOp(..) |

View file

@ -346,11 +346,6 @@ fn walk_expr(cx: &CrateContext,
walk_expr(cx, &rhs, scope_stack, scope_map); 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::ExprVec(ref init_expressions) |
hir::ExprTup(ref init_expressions) => { hir::ExprTup(ref init_expressions) => {
for ie in init_expressions { for ie in init_expressions {

View file

@ -86,7 +86,6 @@ use rustc_front::hir;
use syntax::{ast, codemap}; use syntax::{ast, codemap};
use syntax::parse::token::InternedString; use syntax::parse::token::InternedString;
use syntax::ptr::P; use syntax::ptr::P;
use syntax::parse::token;
use std::mem; use std::mem;
// Destinations // Destinations
@ -1059,7 +1058,6 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
-> Block<'blk, 'tcx> { -> Block<'blk, 'tcx> {
let _icx = push_ctxt("trans_rvalue_dps_unadjusted"); let _icx = push_ctxt("trans_rvalue_dps_unadjusted");
let mut bcx = bcx; let mut bcx = bcx;
let tcx = bcx.tcx();
debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); 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), node_id_type(bcx, expr.id),
dest) 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::Expr>) -> 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) => { hir::ExprTup(ref args) => {
let numbered_fields: Vec<(usize, &hir::Expr)> = let numbered_fields: Vec<(usize, &hir::Expr)> =
args.iter().enumerate().map(|(i, arg)| (i, &**arg)).collect(); 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::ExprCall(..) |
hir::ExprMethodCall(..) | hir::ExprMethodCall(..) |
hir::ExprStruct(..) | hir::ExprStruct(..) |
hir::ExprRange(..) |
hir::ExprTup(..) | hir::ExprTup(..) |
hir::ExprIf(..) | hir::ExprIf(..) |
hir::ExprMatch(..) | hir::ExprMatch(..) |

View file

@ -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, debug!("type of expr({}) {} is...", expr.id,

View file

@ -3643,8 +3643,8 @@ register_diagnostics! {
// E0233, // E0233,
// E0234, // E0234,
// E0235, // structure constructor specifies a structure of type but // E0235, // structure constructor specifies a structure of type but
E0236, // no lang item for range syntax // E0236, // no lang item for range syntax
E0237, // no lang item for range syntax // E0237, // no lang item for range syntax
E0238, // parenthesized parameters may only be used with a trait E0238, // parenthesized parameters may only be used with a trait
// E0239, // `next` method of `Iterator` trait has unexpected type // E0239, // `next` method of `Iterator` trait has unexpected type
// E0240, // E0240,

View file

@ -232,6 +232,7 @@
#![feature(fnbox)] #![feature(fnbox)]
#![feature(heap_api)] #![feature(heap_api)]
#![feature(hashmap_hasher)] #![feature(hashmap_hasher)]
#![feature(inclusive_range)]
#![feature(int_error_internals)] #![feature(int_error_internals)]
#![feature(into_cow)] #![feature(into_cow)]
#![feature(lang_items)] #![feature(lang_items)]
@ -246,7 +247,6 @@
#![feature(optin_builtin_traits)] #![feature(optin_builtin_traits)]
#![feature(placement_in_syntax)] #![feature(placement_in_syntax)]
#![feature(rand)] #![feature(rand)]
#![feature(range_inclusive)]
#![feature(raw)] #![feature(raw)]
#![feature(repr_simd)] #![feature(repr_simd)]
#![feature(reflect_marker)] #![feature(reflect_marker)]

View file

@ -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)] #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum ExprKind { pub enum ExprKind {
/// A `box x` expression. /// A `box x` expression.
@ -974,8 +983,8 @@ pub enum ExprKind {
TupField(P<Expr>, Spanned<usize>), TupField(P<Expr>, Spanned<usize>),
/// An indexing operation (`foo[2]`) /// An indexing operation (`foo[2]`)
Index(P<Expr>, P<Expr>), Index(P<Expr>, P<Expr>),
/// A range (`1..2`, `1..`, or `..2`) /// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`)
Range(Option<P<Expr>>, Option<P<Expr>>), Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
/// Variable reference, possibly containing `::` and/or type /// Variable reference, possibly containing `::` and/or type
/// parameters, e.g. foo::bar::<baz>. /// parameters, e.g. foo::bar::<baz>.

View file

@ -241,7 +241,10 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
("cfg_target_thread_local", "1.7.0", Some(29594), Active), ("cfg_target_thread_local", "1.7.0", Some(29594), Active),
// rustc internal // 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) // (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_placement_in: bool,
pub allow_box: bool, pub allow_box: bool,
pub allow_pushpop_unsafe: bool, pub allow_pushpop_unsafe: bool,
pub allow_inclusive_range: bool,
pub simd_ffi: bool, pub simd_ffi: bool,
pub unmarked_api: bool, pub unmarked_api: bool,
/// spans of #![feature] attrs for stable language features. for error reporting /// spans of #![feature] attrs for stable language features. for error reporting
@ -583,6 +587,7 @@ impl Features {
allow_placement_in: false, allow_placement_in: false,
allow_box: false, allow_box: false,
allow_pushpop_unsafe: false, allow_pushpop_unsafe: false,
allow_inclusive_range: false,
simd_ffi: false, simd_ffi: false,
unmarked_api: false, unmarked_api: false,
declared_stable_lang_features: Vec::new(), 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, self.gate_feature("type_ascription", e.span,
"type ascription is experimental"); "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); visit::walk_expr(self, e);
@ -1177,6 +1187,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &Handler,
allow_placement_in: cx.has_feature("placement_in_syntax"), allow_placement_in: cx.has_feature("placement_in_syntax"),
allow_box: cx.has_feature("box_syntax"), allow_box: cx.has_feature("box_syntax"),
allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"), allow_pushpop_unsafe: cx.has_feature("pushpop_unsafe"),
allow_inclusive_range: cx.has_feature("inclusive_range_syntax"),
simd_ffi: cx.has_feature("simd_ffi"), simd_ffi: cx.has_feature("simd_ffi"),
unmarked_api: cx.has_feature("unmarked_api"), unmarked_api: cx.has_feature("unmarked_api"),
declared_stable_lang_features: accepted_features, declared_stable_lang_features: accepted_features,

View file

@ -1273,9 +1273,10 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mu
ExprKind::Index(el, er) => { ExprKind::Index(el, er) => {
ExprKind::Index(folder.fold_expr(el), folder.fold_expr(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)), 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) => { ExprKind::Path(qself, path) => {
let qself = qself.map(|QSelf { ty, position }| { let qself = qself.map(|QSelf { ty, position }| {

View file

@ -20,7 +20,7 @@ use ast::{BlockCheckMode, CaptureBy};
use ast::{Constness, Crate, CrateConfig}; use ast::{Constness, Crate, CrateConfig};
use ast::{Decl, DeclKind}; use ast::{Decl, DeclKind};
use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf}; use ast::{EMPTY_CTXT, EnumDef, ExplicitSelf};
use ast::{Expr, ExprKind}; use ast::{Expr, ExprKind, RangeLimits};
use ast::{Field, FnDecl}; use ast::{Field, FnDecl};
use ast::{ForeignItem, ForeignItemKind, FunctionRetTy}; use ast::{ForeignItem, ForeignItemKind, FunctionRetTy};
use ast::{Ident, ImplItem, Item, ItemKind}; use ast::{Ident, ImplItem, Item, ItemKind};
@ -2059,9 +2059,10 @@ impl<'a> Parser<'a> {
pub fn mk_range(&mut self, pub fn mk_range(&mut self,
start: Option<P<Expr>>, start: Option<P<Expr>>,
end: Option<P<Expr>>) end: Option<P<Expr>>,
limits: RangeLimits)
-> ast::ExprKind { -> ast::ExprKind {
ExprKind::Range(start, end) ExprKind::Range(start, end, limits)
} }
pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::ExprKind { pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::ExprKind {
@ -2899,7 +2900,7 @@ impl<'a> Parser<'a> {
LhsExpr::AttributesParsed(attrs) => Some(attrs), LhsExpr::AttributesParsed(attrs) => Some(attrs),
_ => None, _ => None,
}; };
if self.token == token::DotDot { if self.token == token::DotDot || self.token == token::DotDotDot {
return self.parse_prefix_range_expr(attrs); return self.parse_prefix_range_expr(attrs);
} else { } else {
try!(self.parse_prefix_expr(attrs)) try!(self.parse_prefix_expr(attrs))
@ -2945,32 +2946,32 @@ impl<'a> Parser<'a> {
ExprKind::Type(lhs, rhs), None); ExprKind::Type(lhs, rhs), None);
continue continue
} else if op == AssocOp::DotDot { } else if op == AssocOp::DotDot {
// If we didnt have to handle `x..`, it would be pretty easy to generalise // If we didnt have to handle `x..`, it would be pretty easy to generalise
// it to the Fixity::None code. // it to the Fixity::None code.
// //
// We have 2 alternatives here: `x..y` and `x..` The other two variants are // We have 2 alternatives here: `x..y` and `x..` The other two variants are
// handled with `parse_prefix_range_expr` call above. // handled with `parse_prefix_range_expr` call above.
let rhs = if self.is_at_start_of_range_notation_rhs() { let rhs = if self.is_at_start_of_range_notation_rhs() {
let rhs = self.parse_assoc_expr_with(op.precedence() + 1, let rhs = self.parse_assoc_expr_with(op.precedence() + 1,
LhsExpr::NotYetParsed); LhsExpr::NotYetParsed);
match rhs { match rhs {
Ok(e) => Some(e), Ok(e) => Some(e),
Err(mut e) => { Err(mut e) => {
e.cancel(); e.cancel();
None None
}
} }
} else { }
None } else {
}; None
let (lhs_span, rhs_span) = (lhs_span, if let Some(ref x) = rhs { };
x.span let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs {
} else { x.span
cur_op_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); let r = self.mk_range(Some(lhs), rhs, RangeLimits::HalfOpen);
break lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
break
} }
let rhs = try!(match op.fixity() { let rhs = try!(match op.fixity() {
@ -2986,8 +2987,8 @@ impl<'a> Parser<'a> {
this.parse_assoc_expr_with(op.precedence() + 1, this.parse_assoc_expr_with(op.precedence() + 1,
LhsExpr::NotYetParsed) LhsExpr::NotYetParsed)
}), }),
// We currently have no non-associative operators that are not handled above by // the only operator handled here is `...` (the other non-associative operators are
// the special cases. The code is here only for future convenience. // special-cased above)
Fixity::None => self.with_res( Fixity::None => self.with_res(
restrictions - Restrictions::RESTRICTION_STMT_EXPR, restrictions - Restrictions::RESTRICTION_STMT_EXPR,
|this| { |this| {
@ -3028,6 +3029,11 @@ impl<'a> Parser<'a> {
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); 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) 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 => { AssocOp::As | AssocOp::Colon | AssocOp::DotDot => {
self.bug("As, Colon or DotDot branch reached") 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, fn parse_prefix_range_expr(&mut self,
already_parsed_attrs: Option<ThinAttributes>) already_parsed_attrs: Option<ThinAttributes>)
-> PResult<'a, P<Expr>> { -> PResult<'a, P<Expr>> {
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 attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
let lo = self.span.lo; let lo = self.span.lo;
let mut hi = self.span.hi; let mut hi = self.span.hi;
self.bump(); self.bump();
let opt_end = if self.is_at_start_of_range_notation_rhs() { let opt_end = if self.is_at_start_of_range_notation_rhs() {
// RHS must be parsed with more associativity than DotDot. // RHS must be parsed with more associativity than the dots.
let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1; let next_prec = AssocOp::from_token(&tok).unwrap().precedence() + 1;
Some(try!(self.parse_assoc_expr_with(next_prec, Some(try!(self.parse_assoc_expr_with(next_prec,
LhsExpr::NotYetParsed) LhsExpr::NotYetParsed)
.map(|x|{ .map(|x|{
@ -3080,7 +3087,13 @@ impl<'a> Parser<'a> {
} else { } else {
None 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)) Ok(self.mk_expr(lo, hi, r, attrs))
} }

View file

@ -196,7 +196,7 @@ impl Token {
BinOp(Or) => true, // in lambda syntax BinOp(Or) => true, // in lambda syntax
OrOr => true, // in lambda syntax OrOr => true, // in lambda syntax
AndAnd => true, // double borrow AndAnd => true, // double borrow
DotDot => true, // range notation DotDot | DotDotDot => true, // range notation
ModSep => true, ModSep => true,
Interpolated(NtExpr(..)) => true, Interpolated(NtExpr(..)) => true,
Interpolated(NtIdent(..)) => true, Interpolated(NtIdent(..)) => true,

View file

@ -2163,11 +2163,15 @@ impl<'a> State<'a> {
try!(self.print_expr(&index)); try!(self.print_expr(&index));
try!(word(&mut self.s, "]")); 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 { if let &Some(ref e) = start {
try!(self.print_expr(&e)); 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 { if let &Some(ref e) = end {
try!(self.print_expr(&e)); try!(self.print_expr(&e));
} }

View file

@ -61,6 +61,8 @@ pub enum AssocOp {
As, As,
/// `..` range /// `..` range
DotDot, DotDot,
/// `...` range
DotDotDot,
/// `:` /// `:`
Colon, Colon,
} }
@ -102,6 +104,7 @@ impl AssocOp {
Token::AndAnd => Some(LAnd), Token::AndAnd => Some(LAnd),
Token::OrOr => Some(LOr), Token::OrOr => Some(LOr),
Token::DotDot => Some(DotDot), Token::DotDot => Some(DotDot),
Token::DotDotDot => Some(DotDotDot),
Token::Colon => Some(Colon), Token::Colon => Some(Colon),
_ if t.is_keyword(keywords::As) => Some(As), _ if t.is_keyword(keywords::As) => Some(As),
_ => None _ => None
@ -147,7 +150,7 @@ impl AssocOp {
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
LAnd => 6, LAnd => 6,
LOr => 5, LOr => 5,
DotDot => 4, DotDot | DotDotDot => 4,
Inplace => 3, Inplace => 3,
Assign | AssignOp(_) => 2, Assign | AssignOp(_) => 2,
} }
@ -162,7 +165,7 @@ impl AssocOp {
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd |
BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual |
LAnd | LOr | Colon => Fixity::Left, LAnd | LOr | Colon => Fixity::Left,
DotDot => Fixity::None DotDot | DotDotDot => Fixity::None
} }
} }
@ -171,7 +174,8 @@ impl AssocOp {
match *self { match *self {
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract | 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, Assign | AssignOp(_) | Inplace => true,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide |
Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | 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), BitOr => Some(BinOpKind::BitOr),
LAnd => Some(BinOpKind::And), LAnd => Some(BinOpKind::And),
LOr => Some(BinOpKind::Or), LOr => Some(BinOpKind::Or),
Inplace | Assign | AssignOp(_) | As | DotDot | Colon => None Inplace | Assign | AssignOp(_) | As | DotDot | DotDotDot | Colon => None
} }
} }
} }

View file

@ -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(main_expression);
visitor.visit_expr(index_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, start);
walk_list!(visitor, visit_expr, end); walk_list!(visitor, visit_expr, end);
} }

View file

@ -13,7 +13,7 @@
pub fn main() { pub fn main() {
// Mixed types. // Mixed types.
let _ = 0u32..10i32; let _ = 0u32..10i32;
//~^ ERROR start and end of range have incompatible types //~^ ERROR mismatched types
// Bool => does not implement iterator. // Bool => does not implement iterator.
for i in false..true {} for i in false..true {}

View file

@ -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 // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -12,8 +12,10 @@
pub fn main() { pub fn main() {
let r = { let r = {
&42..&42 let a = 42;
//~^ ERROR borrowed value does not live long enough let b = 42;
//~^^ ERROR borrowed value does not live long enough &a..&b
//~^ ERROR `a` does not live long enough
//~^^ ERROR `b` does not live long enough
}; };
} }

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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
}

View file

@ -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 // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -44,6 +44,7 @@ pub fn main() {
let _ = 0_usize..4+4-3; let _ = 0_usize..4+4-3;
let _ = 0..foo(); let _ = 0..foo();
let _ = { &42..&100 }; // references to literals are OK
let _ = ..42_usize; let _ = ..42_usize;
// Test we can use two different types with a common supertype. // Test we can use two different types with a common supertype.

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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<i32> { 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::<Vec<_>>();
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]");
}

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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);
}