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
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

View File

@ -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

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
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,

View File

@ -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)]

View File

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

View File

@ -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<ops::RangeFull> for String {
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")]
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) }
}
}
#[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")]
impl ops::Deref for String {

View File

@ -1226,6 +1226,24 @@ impl<T> ops::Index<ops::RangeFull> for Vec<T> {
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")]
impl<T> ops::IndexMut<ops::Range<usize>> for Vec<T> {
@ -1255,6 +1273,20 @@ impl<T> ops::IndexMut<ops::RangeFull> for Vec<T> {
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")]
impl<T> ops::Deref for Vec<T> {

View File

@ -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<A, 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
/// 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")]
impl<A> Iterator for StepBy<A, RangeFrom<A>> where
impl<A> Iterator for StepBy<A, ops::RangeFrom<A>> where
A: Clone,
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")]
impl<A: Step + Zero + Clone> Iterator for StepBy<A, ops::Range<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 {
($($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<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.
///
/// 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")]
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<Idx: ?Sized>: Index<Idx> {
/// 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<Idx> {
/// 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.
#[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<Idx> {
/// 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.
#[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<Idx> {
/// 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
/// 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);
}
// FIXME implement indexing with inclusive ranges
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> ops::Index<ops::Range<usize>> for [T] {
type Output = [T];
@ -558,7 +560,7 @@ impl<T> ops::Index<ops::RangeTo<usize>> for [T] {
#[inline]
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")]
@ -567,7 +569,7 @@ impl<T> ops::Index<ops::RangeFrom<usize>> for [T] {
#[inline]
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")]
@ -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")]
impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
#[inline]
@ -601,7 +629,7 @@ impl<T> ops::IndexMut<ops::Range<usize>> for [T] {
impl<T> ops::IndexMut<ops::RangeTo<usize>> for [T] {
#[inline]
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")]
@ -609,7 +637,7 @@ impl<T> ops::IndexMut<ops::RangeFrom<usize>> for [T] {
#[inline]
fn index_mut(&mut self, index: ops::RangeFrom<usize>) -> &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<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

View File

@ -1460,6 +1460,62 @@ mod traits {
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

View File

@ -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::<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)
self.walk_callee(expr, &callee);
self.consume_exprs(args);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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(..) |

View File

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

View File

@ -243,7 +243,6 @@ mod svh_visitor {
SawExprAssign,
SawExprAssignOp(hir::BinOp_),
SawExprIndex,
SawExprRange,
SawExprPath(Option<usize>),
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())),

View File

@ -1090,10 +1090,6 @@ pub fn noop_fold_expr<T: Folder>(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 {

View File

@ -776,8 +776,6 @@ pub enum Expr_ {
ExprTupField(P<Expr>, Spanned<usize>),
/// An indexing operation (`foo[2]`)
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
/// 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(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);

View File

@ -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<hir::Expr> {
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<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) => {
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,
attrs: ThinAttributes) -> P<hir::Expr> {
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)
}
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_,
attrs: ThinAttributes) -> P<hir::Expr> {
P(hir::Expr {

View File

@ -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))
}

View File

@ -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<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(..) => {
convert_path_expr(cx, self)
}

View File

@ -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(..) |

View File

@ -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 {

View File

@ -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::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) => {
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(..) |

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,

View File

@ -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,

View File

@ -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)]

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)]
pub enum ExprKind {
/// A `box x` expression.
@ -974,8 +983,8 @@ pub enum ExprKind {
TupField(P<Expr>, Spanned<usize>),
/// An indexing operation (`foo[2]`)
Index(P<Expr>, P<Expr>),
/// A range (`1..2`, `1..`, or `..2`)
Range(Option<P<Expr>>, Option<P<Expr>>),
/// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`)
Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
/// Variable reference, possibly containing `::` and/or type
/// 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),
// 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<F>(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,

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(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 }| {

View File

@ -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<P<Expr>>,
end: Option<P<Expr>>)
end: Option<P<Expr>>,
limits: RangeLimits)
-> 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 {
@ -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 didnt 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 didnt 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<ThinAttributes>)
-> 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 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))
}

View File

@ -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,

View File

@ -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));
}

View File

@ -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
}
}
}

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(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);
}

View File

@ -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 {}

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
// 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
};
}

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
// 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.

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);
}