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:
commit
8484831d29
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
@ -35,6 +35,7 @@ pub trait RangeArgument<T> {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME add inclusive ranges to RangeArgument
|
||||
|
||||
impl<T> RangeArgument<T> for RangeFull {}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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> {
|
||||
|
@ -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.
|
||||
|
@ -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`.
|
||||
///
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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(..) |
|
||||
|
@ -2008,7 +2008,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
hir::ExprCall(..) |
|
||||
hir::ExprMethodCall(..) |
|
||||
hir::ExprStruct(..) |
|
||||
hir::ExprRange(..) |
|
||||
hir::ExprTup(..) |
|
||||
hir::ExprIf(..) |
|
||||
hir::ExprMatch(..) |
|
||||
|
@ -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())),
|
||||
|
@ -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 {
|
||||
|
@ -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>.
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(..) |
|
||||
|
@ -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 {
|
||||
|
@ -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(..) |
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
@ -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>.
|
||||
|
@ -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,
|
||||
|
@ -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 }| {
|
||||
|
@ -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 didn’t have to handle `x..`, it would be pretty easy to generalise
|
||||
// it to the Fixity::None code.
|
||||
//
|
||||
// We have 2 alternatives here: `x..y` and `x..` The other two variants are
|
||||
// handled with `parse_prefix_range_expr` call above.
|
||||
let rhs = if self.is_at_start_of_range_notation_rhs() {
|
||||
let rhs = self.parse_assoc_expr_with(op.precedence() + 1,
|
||||
LhsExpr::NotYetParsed);
|
||||
match rhs {
|
||||
Ok(e) => Some(e),
|
||||
Err(mut e) => {
|
||||
e.cancel();
|
||||
None
|
||||
}
|
||||
// If we didn’t have to handle `x..`, it would be pretty easy to generalise
|
||||
// it to the Fixity::None code.
|
||||
//
|
||||
// We have 2 alternatives here: `x..y` and `x..` The other two variants are
|
||||
// handled with `parse_prefix_range_expr` call above.
|
||||
let rhs = if self.is_at_start_of_range_notation_rhs() {
|
||||
let rhs = self.parse_assoc_expr_with(op.precedence() + 1,
|
||||
LhsExpr::NotYetParsed);
|
||||
match rhs {
|
||||
Ok(e) => Some(e),
|
||||
Err(mut e) => {
|
||||
e.cancel();
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (lhs_span, rhs_span) = (lhs_span, if let Some(ref x) = rhs {
|
||||
x.span
|
||||
} else {
|
||||
cur_op_span
|
||||
});
|
||||
let r = self.mk_range(Some(lhs), rhs);
|
||||
lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
|
||||
break
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs {
|
||||
x.span
|
||||
} else {
|
||||
cur_op_span
|
||||
});
|
||||
let r = self.mk_range(Some(lhs), rhs, RangeLimits::HalfOpen);
|
||||
lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
|
||||
break
|
||||
}
|
||||
|
||||
let rhs = try!(match op.fixity() {
|
||||
@ -2986,8 +2987,8 @@ impl<'a> Parser<'a> {
|
||||
this.parse_assoc_expr_with(op.precedence() + 1,
|
||||
LhsExpr::NotYetParsed)
|
||||
}),
|
||||
// We currently have no non-associative operators that are not handled above by
|
||||
// the special cases. The code is here only for future convenience.
|
||||
// the only operator handled here is `...` (the other non-associative operators are
|
||||
// special-cased above)
|
||||
Fixity::None => self.with_res(
|
||||
restrictions - Restrictions::RESTRICTION_STMT_EXPR,
|
||||
|this| {
|
||||
@ -3028,6 +3029,11 @@ impl<'a> Parser<'a> {
|
||||
let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
|
||||
self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
|
||||
}
|
||||
AssocOp::DotDotDot => {
|
||||
let (lhs_span, rhs_span) = (lhs.span, rhs.span);
|
||||
let r = self.mk_range(Some(lhs), Some(rhs), RangeLimits::Closed);
|
||||
self.mk_expr(lhs_span.lo, rhs_span.hi, r, None)
|
||||
}
|
||||
AssocOp::As | AssocOp::Colon | AssocOp::DotDot => {
|
||||
self.bug("As, Colon or DotDot branch reached")
|
||||
}
|
||||
@ -3059,18 +3065,19 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse prefix-forms of range notation: `..expr` and `..`
|
||||
/// Parse prefix-forms of range notation: `..expr`, `..`, `...expr`
|
||||
fn parse_prefix_range_expr(&mut self,
|
||||
already_parsed_attrs: Option<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))
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
25
src/test/compile-fail/range_inclusive_gate.rs
Normal file
25
src/test/compile-fail/range_inclusive_gate.rs
Normal 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
|
||||
}
|
||||
|
||||
|
18
src/test/parse-fail/range_inclusive.rs
Normal file
18
src/test/parse-fail/range_inclusive.rs
Normal 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
|
||||
|
74
src/test/parse-fail/range_inclusive_gate.rs
Normal file
74
src/test/parse-fail/range_inclusive_gate.rs
Normal 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
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
129
src/test/run-pass/range_inclusive.rs
Normal file
129
src/test/run-pass/range_inclusive.rs
Normal 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]");
|
||||
}
|
23
src/test/run-pass/range_inclusive_gate.rs
Normal file
23
src/test/run-pass/range_inclusive_gate.rs
Normal 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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user