From 414ee9a2541d1a866864641c250367f0c28978be Mon Sep 17 00:00:00 2001 From: Jimmy Cuadra Date: Wed, 30 Aug 2017 04:43:04 -0700 Subject: Remove test case that assumes FromStr provides TryFrom<&'a str>. See https://travis-ci.org/rust-lang/rust/jobs/269861252 --- src/libcore/tests/char.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/libcore/tests') diff --git a/src/libcore/tests/char.rs b/src/libcore/tests/char.rs index 7c3b90c8153..4e10ceac878 100644 --- a/src/libcore/tests/char.rs +++ b/src/libcore/tests/char.rs @@ -32,7 +32,6 @@ fn test_convert() { #[test] fn test_from_str() { assert_eq!(char::from_str("a").unwrap(), 'a'); - assert_eq!(char::try_from("a").unwrap(), 'a'); assert_eq!(char::from_str("\0").unwrap(), '\0'); assert_eq!(char::from_str("\u{D7FF}").unwrap(), '\u{d7FF}'); assert!(char::from_str("").is_err()); -- cgit 1.4.1-3-g733a5 From 93a56cdacd29e8b29bae45c3abca229f6011a492 Mon Sep 17 00:00:00 2001 From: Jimmy Cuadra Date: Fri, 1 Sep 2017 01:57:05 -0700 Subject: impl From for TryFromIntError. --- src/libcore/num/mod.rs | 9 ++++++++- src/libcore/tests/num/mod.rs | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'src/libcore/tests') diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 4372646a569..9f87e2bd831 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -12,7 +12,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use convert::TryFrom; +use convert::{Infallible, TryFrom}; use fmt; use intrinsics; use str::FromStr; @@ -2503,6 +2503,13 @@ impl fmt::Display for TryFromIntError { } } +#[unstable(feature = "try_from", issue = "33417")] +impl From for TryFromIntError { + fn from(_: Infallible) -> TryFromIntError { + TryFromIntError(()) + } +} + // no possible bounds violation macro_rules! try_from_unbounded { ($source:ty, $($target:ty),*) => {$( diff --git a/src/libcore/tests/num/mod.rs b/src/libcore/tests/num/mod.rs index 400d53ce51a..7eb5ff98857 100644 --- a/src/libcore/tests/num/mod.rs +++ b/src/libcore/tests/num/mod.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::convert::TryFrom; +use core::convert::{TryFrom, TryInto}; use core::cmp::PartialEq; use core::fmt::Debug; use core::marker::Copy; +use core::num::TryFromIntError; use core::ops::{Add, Sub, Mul, Div, Rem}; use core::option::Option; use core::option::Option::{Some, None}; @@ -134,6 +135,13 @@ fn test_empty() { assert_eq!("".parse::().ok(), None); } +#[test] +fn test_infallible_try_from_int_error() { + let func = |x: i8| -> Result { Ok(x.try_into()?) }; + + assert!(func(0).is_ok()); +} + macro_rules! test_impl_from { ($fn_name: ident, $Small: ty, $Large: ty) => { #[test] -- cgit 1.4.1-3-g733a5 From e64efc91f49affb265328e354c8c8f0544daa462 Mon Sep 17 00:00:00 2001 From: Alex Burka Date: Tue, 19 Sep 2017 05:40:04 +0000 Subject: Add support for `..=` syntax Add ..= to the parser Add ..= to libproc_macro Add ..= to ICH Highlight ..= in rustdoc Update impl Debug for RangeInclusive to ..= Replace `...` to `..=` in range docs Make the dotdoteq warning point to the ... Add warning for ... in expressions Updated more tests to the ..= syntax Updated even more tests to the ..= syntax Updated the inclusive_range entry in unstable book --- .../language-features/inclusive-range-syntax.md | 4 +- src/liballoc/tests/btree/map.rs | 28 ++++----- src/liballoc/tests/str.rs | 16 ++--- src/liballoc/tests/string.rs | 6 +- src/liballoc/tests/vec.rs | 28 ++++----- src/libcore/ops/range.rs | 50 ++++++++-------- src/libcore/slice/mod.rs | 4 ++ src/libcore/tests/iter.rs | 32 +++++----- src/libproc_macro/lib.rs | 2 + src/libproc_macro/quote.rs | 4 +- src/librustc/ich/impls_syntax.rs | 2 + src/librustdoc/html/highlight.rs | 6 +- src/libsyntax/diagnostic_list.rs | 4 +- src/libsyntax/ext/quote.rs | 2 + src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/parse/lexer/mod.rs | 3 + src/libsyntax/parse/parser.rs | 50 +++++++++++----- src/libsyntax/parse/token.rs | 35 ++++++----- src/libsyntax/print/pprust.rs | 2 + src/libsyntax/util/parser.rs | 17 +++--- src/test/compile-fail/E0586.rs | 2 +- src/test/compile-fail/impossible_range.rs | 8 +-- src/test/compile-fail/range_inclusive_gate.rs | 2 +- .../incremental/hashes/indexing_expressions.rs | 2 +- src/test/parse-fail/pat-ranges-1.rs | 2 +- src/test/parse-fail/pat-ranges-2.rs | 2 +- src/test/parse-fail/pat-ranges-3.rs | 2 +- src/test/parse-fail/pat-ranges-4.rs | 3 +- src/test/parse-fail/range_inclusive.rs | 2 +- src/test/parse-fail/range_inclusive_gate.rs | 14 ++--- src/test/run-pass/inc-range-pat.rs | 20 +++++++ src/test/run-pass/range_inclusive.rs | 68 +++++++++++----------- src/test/run-pass/range_inclusive_gate.rs | 2 +- 33 files changed, 244 insertions(+), 182 deletions(-) create mode 100644 src/test/run-pass/inc-range-pat.rs (limited to 'src/libcore/tests') diff --git a/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md b/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md index 255445c318d..56f58803150 100644 --- a/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md +++ b/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md @@ -7,13 +7,13 @@ The tracking issue for this feature is: [#28237] ------------------------ To get a range that goes from 0 to 10 and includes the value 10, you -can write `0...10`: +can write `0..=10`: ```rust #![feature(inclusive_range_syntax)] fn main() { - for i in 0...10 { + for i in 0..=10 { println!("{}", i); } } diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs index 2c899d96940..2393101040d 100644 --- a/src/liballoc/tests/btree/map.rs +++ b/src/liballoc/tests/btree/map.rs @@ -182,7 +182,7 @@ fn test_range_small() { fn test_range_inclusive() { let size = 500; - let map: BTreeMap<_, _> = (0...size).map(|i| (i, i)).collect(); + let map: BTreeMap<_, _> = (0..=size).map(|i| (i, i)).collect(); fn check<'a, L, R>(lhs: L, rhs: R) where L: IntoIterator, @@ -193,18 +193,18 @@ fn test_range_inclusive() { assert_eq!(lhs, rhs); } - check(map.range(size + 1...size + 1), vec![]); - check(map.range(size...size), vec![(&size, &size)]); - check(map.range(size...size + 1), vec![(&size, &size)]); - check(map.range(0...0), vec![(&0, &0)]); - check(map.range(0...size - 1), map.range(..size)); - check(map.range(-1...-1), vec![]); - check(map.range(-1...size), map.range(..)); - check(map.range(...size), map.range(..)); - check(map.range(...200), map.range(..201)); - check(map.range(5...8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]); - check(map.range(-1...0), vec![(&0, &0)]); - check(map.range(-1...2), vec![(&0, &0), (&1, &1), (&2, &2)]); + check(map.range(size + 1..=size + 1), vec![]); + check(map.range(size..=size), vec![(&size, &size)]); + check(map.range(size..=size + 1), vec![(&size, &size)]); + check(map.range(0..=0), vec![(&0, &0)]); + check(map.range(0..=size - 1), map.range(..size)); + check(map.range(-1..=-1), vec![]); + check(map.range(-1..=size), map.range(..)); + check(map.range(..=size), map.range(..)); + check(map.range(..=200), map.range(..201)); + check(map.range(5..=8), vec![(&5, &5), (&6, &6), (&7, &7), (&8, &8)]); + check(map.range(-1..=0), vec![(&0, &0)]); + check(map.range(-1..=2), vec![(&0, &0), (&1, &1), (&2, &2)]); } #[test] @@ -212,7 +212,7 @@ fn test_range_inclusive_max_value() { let max = ::std::usize::MAX; let map: BTreeMap<_, _> = vec![(max, 0)].into_iter().collect(); - assert_eq!(map.range(max...max).collect::>(), &[(&max, &0)]); + assert_eq!(map.range(max..=max).collect::>(), &[(&max, &0)]); } #[test] diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 9d8ca38b20e..b3178064505 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -361,13 +361,13 @@ fn test_slice_fail() { #[test] #[should_panic] fn test_str_slice_rangetoinclusive_max_panics() { - &"hello"[...usize::max_value()]; + &"hello"[..=usize::max_value()]; } #[test] #[should_panic] fn test_str_slice_rangeinclusive_max_panics() { - &"hello"[1...usize::max_value()]; + &"hello"[1..=usize::max_value()]; } #[test] @@ -375,7 +375,7 @@ fn test_str_slice_rangeinclusive_max_panics() { fn test_str_slicemut_rangetoinclusive_max_panics() { let mut s = "hello".to_owned(); let s: &mut str = &mut s; - &mut s[...usize::max_value()]; + &mut s[..=usize::max_value()]; } #[test] @@ -383,7 +383,7 @@ fn test_str_slicemut_rangetoinclusive_max_panics() { fn test_str_slicemut_rangeinclusive_max_panics() { let mut s = "hello".to_owned(); let s: &mut str = &mut s; - &mut s[1...usize::max_value()]; + &mut s[1..=usize::max_value()]; } #[test] @@ -391,13 +391,13 @@ fn test_str_get_maxinclusive() { let mut s = "hello".to_owned(); { let s: &str = &s; - assert_eq!(s.get(...usize::max_value()), None); - assert_eq!(s.get(1...usize::max_value()), None); + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); } { let s: &mut str = &mut s; - assert_eq!(s.get(...usize::max_value()), None); - assert_eq!(s.get(1...usize::max_value()), None); + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); } } diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 6aba18ddf49..ef6f5e10a72 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -456,9 +456,9 @@ fn test_splice_char_boundary() { #[test] fn test_splice_inclusive_range() { let mut v = String::from("12345"); - v.splice(2...3, "789"); + v.splice(2..=3, "789"); assert_eq!(v, "127895"); - v.splice(1...2, "A"); + v.splice(1..=2, "A"); assert_eq!(v, "1A895"); } @@ -473,7 +473,7 @@ fn test_splice_out_of_bounds() { #[should_panic] fn test_splice_inclusive_out_of_bounds() { let mut s = String::from("12345"); - s.splice(5...5, "789"); + s.splice(5..=5, "789"); } #[test] diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 670ea8089fc..0e25da5bd30 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -537,27 +537,27 @@ fn test_drain_range() { #[test] fn test_drain_inclusive_range() { let mut v = vec!['a', 'b', 'c', 'd', 'e']; - for _ in v.drain(1...3) { + for _ in v.drain(1..=3) { } assert_eq!(v, &['a', 'e']); - let mut v: Vec<_> = (0...5).map(|x| x.to_string()).collect(); - for _ in v.drain(1...5) { + let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect(); + for _ in v.drain(1..=5) { } assert_eq!(v, &["0".to_string()]); - let mut v: Vec = (0...5).map(|x| x.to_string()).collect(); - for _ in v.drain(0...5) { + let mut v: Vec = (0..=5).map(|x| x.to_string()).collect(); + for _ in v.drain(0..=5) { } assert_eq!(v, Vec::::new()); - let mut v: Vec<_> = (0...5).map(|x| x.to_string()).collect(); - for _ in v.drain(0...3) { + let mut v: Vec<_> = (0..=5).map(|x| x.to_string()).collect(); + for _ in v.drain(0..=3) { } assert_eq!(v, &["4".to_string(), "5".to_string()]); - let mut v: Vec<_> = (0...1).map(|x| x.to_string()).collect(); - for _ in v.drain(...0) { + let mut v: Vec<_> = (0..=1).map(|x| x.to_string()).collect(); + for _ in v.drain(..=0) { } assert_eq!(v, &["1".to_string()]); } @@ -572,7 +572,7 @@ fn test_drain_max_vec_size() { let mut v = Vec::<()>::with_capacity(usize::max_value()); unsafe { v.set_len(usize::max_value()); } - for _ in v.drain(usize::max_value() - 1...usize::max_value() - 1) { + for _ in v.drain(usize::max_value() - 1..=usize::max_value() - 1) { } assert_eq!(v.len(), usize::max_value() - 1); } @@ -581,7 +581,7 @@ fn test_drain_max_vec_size() { #[should_panic] fn test_drain_inclusive_out_of_bounds() { let mut v = vec![1, 2, 3, 4, 5]; - v.drain(5...5); + v.drain(5..=5); } #[test] @@ -598,10 +598,10 @@ fn test_splice() { fn test_splice_inclusive_range() { let mut v = vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; - let t1: Vec<_> = v.splice(2...3, a.iter().cloned()).collect(); + let t1: Vec<_> = v.splice(2..=3, a.iter().cloned()).collect(); assert_eq!(v, &[1, 2, 10, 11, 12, 5]); assert_eq!(t1, &[3, 4]); - let t2: Vec<_> = v.splice(1...2, Some(20)).collect(); + let t2: Vec<_> = v.splice(1..=2, Some(20)).collect(); assert_eq!(v, &[1, 20, 11, 12, 5]); assert_eq!(t2, &[2, 10]); } @@ -619,7 +619,7 @@ fn test_splice_out_of_bounds() { fn test_splice_inclusive_out_of_bounds() { let mut v = vec![1, 2, 3, 4, 5]; let a = [10, 11, 12]; - v.splice(5...5, a.iter().cloned()); + v.splice(5..=5, a.iter().cloned()); } #[test] diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 463a50491a8..3f573f7c7eb 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -241,9 +241,9 @@ impl> RangeTo { } } -/// An range bounded inclusively below and above (`start...end`). +/// An range bounded inclusively below and above (`start..=end`). /// -/// The `RangeInclusive` `start...end` contains all values with `x >= start` +/// The `RangeInclusive` `start..=end` contains all values with `x >= start` /// and `x <= end`. /// /// # Examples @@ -251,12 +251,12 @@ impl> RangeTo { /// ``` /// #![feature(inclusive_range,inclusive_range_syntax)] /// -/// assert_eq!((3...5), std::ops::RangeInclusive { start: 3, end: 5 }); -/// assert_eq!(3 + 4 + 5, (3...5).sum()); +/// assert_eq!((3..=5), std::ops::RangeInclusive { start: 3, end: 5 }); +/// assert_eq!(3 + 4 + 5, (3..=5).sum()); /// /// let arr = [0, 1, 2, 3]; -/// assert_eq!(arr[ ...2], [0,1,2 ]); -/// assert_eq!(arr[1...2], [ 1,2 ]); // RangeInclusive +/// assert_eq!(arr[ ..=2], [0,1,2 ]); +/// assert_eq!(arr[1..=2], [ 1,2 ]); // RangeInclusive /// ``` #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] @@ -276,7 +276,7 @@ pub struct RangeInclusive { #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{:?}...{:?}", self.start, self.end) + write!(fmt, "{:?}..={:?}", self.start, self.end) } } @@ -289,32 +289,32 @@ impl> RangeInclusive { /// ``` /// #![feature(range_contains,inclusive_range_syntax)] /// - /// assert!(!(3...5).contains(2)); - /// assert!( (3...5).contains(3)); - /// assert!( (3...5).contains(4)); - /// assert!( (3...5).contains(5)); - /// assert!(!(3...5).contains(6)); + /// assert!(!(3..=5).contains(2)); + /// assert!( (3..=5).contains(3)); + /// assert!( (3..=5).contains(4)); + /// assert!( (3..=5).contains(5)); + /// assert!(!(3..=5).contains(6)); /// - /// assert!( (3...3).contains(3)); - /// assert!(!(3...2).contains(3)); + /// assert!( (3..=3).contains(3)); + /// assert!(!(3..=2).contains(3)); /// ``` pub fn contains(&self, item: Idx) -> bool { self.start <= item && item <= self.end } } -/// A range only bounded inclusively above (`...end`). +/// A range only bounded inclusively above (`..=end`). /// -/// The `RangeToInclusive` `...end` contains all values with `x <= end`. +/// The `RangeToInclusive` `..=end` contains all values with `x <= end`. /// It cannot serve as an [`Iterator`] because it doesn't have a starting point. /// /// # Examples /// -/// The `...end` syntax is a `RangeToInclusive`: +/// The `..=end` syntax is a `RangeToInclusive`: /// /// ``` /// #![feature(inclusive_range,inclusive_range_syntax)] -/// assert_eq!((...5), std::ops::RangeToInclusive{ end: 5 }); +/// assert_eq!((..=5), std::ops::RangeToInclusive{ end: 5 }); /// ``` /// /// It does not have an [`IntoIterator`] implementation, so you can't use it in a @@ -325,7 +325,7 @@ impl> RangeInclusive { /// /// // error[E0277]: the trait bound `std::ops::RangeToInclusive<{integer}>: /// // std::iter::Iterator` is not satisfied -/// for i in ...5 { +/// for i in ..=5 { /// // ... /// } /// ``` @@ -337,8 +337,8 @@ impl> RangeInclusive { /// #![feature(inclusive_range_syntax)] /// /// let arr = [0, 1, 2, 3]; -/// assert_eq!(arr[ ...2], [0,1,2 ]); // RangeToInclusive -/// assert_eq!(arr[1...2], [ 1,2 ]); +/// assert_eq!(arr[ ..=2], [0,1,2 ]); // RangeToInclusive +/// assert_eq!(arr[1..=2], [ 1,2 ]); /// ``` /// /// [`IntoIterator`]: ../iter/trait.Iterator.html @@ -357,7 +357,7 @@ pub struct RangeToInclusive { #[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] impl fmt::Debug for RangeToInclusive { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "...{:?}", self.end) + write!(fmt, "..={:?}", self.end) } } @@ -370,9 +370,9 @@ impl> RangeToInclusive { /// ``` /// #![feature(range_contains,inclusive_range_syntax)] /// - /// assert!( (...5).contains(-1_000_000_000)); - /// assert!( (...5).contains(5)); - /// assert!(!(...5).contains(6)); + /// assert!( (..=5).contains(-1_000_000_000)); + /// assert!( (..=5).contains(5)); + /// assert!(!(..=5).contains(6)); /// ``` pub fn contains(&self, item: Idx) -> bool { (item <= self.end) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index dacc014955a..c52e88170ae 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -16,6 +16,10 @@ #![stable(feature = "rust1", since = "1.0.0")] +// FIXME: replace remaining ... by ..= after next stage0 +// Silence warning: "... is being replaced by ..=" +#![cfg_attr(not(stage0), allow(warnings))] + // How this module is organized. // // The library infrastructure for slices is fairly messy. There's diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 59ae30de452..3c4ea974fc9 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -1094,21 +1094,21 @@ fn test_range() { #[test] fn test_range_inclusive_exhaustion() { - let mut r = 10...10; + let mut r = 10..=10; assert_eq!(r.next(), Some(10)); - assert_eq!(r, 1...0); + assert_eq!(r, 1..=0); - let mut r = 10...10; + let mut r = 10..=10; assert_eq!(r.next_back(), Some(10)); - assert_eq!(r, 1...0); + assert_eq!(r, 1..=0); - let mut r = 10...12; + let mut r = 10..=12; assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 1...0); + assert_eq!(r, 1..=0); - let mut r = 10...12; + let mut r = 10..=12; assert_eq!(r.nth(5), None); - assert_eq!(r, 1...0); + assert_eq!(r, 1..=0); } @@ -1145,20 +1145,20 @@ fn test_range_from_nth() { #[test] fn test_range_inclusive_nth() { - assert_eq!((10...15).nth(0), Some(10)); - assert_eq!((10...15).nth(1), Some(11)); - assert_eq!((10...15).nth(5), Some(15)); - assert_eq!((10...15).nth(6), None); + assert_eq!((10..=15).nth(0), Some(10)); + assert_eq!((10..=15).nth(1), Some(11)); + assert_eq!((10..=15).nth(5), Some(15)); + assert_eq!((10..=15).nth(6), None); - let mut r = 10_u8...20; + let mut r = 10_u8..=20; assert_eq!(r.nth(2), Some(12)); - assert_eq!(r, 13...20); + assert_eq!(r, 13..=20); assert_eq!(r.nth(2), Some(15)); - assert_eq!(r, 16...20); + assert_eq!(r, 16..=20); assert_eq!(r.is_empty(), false); assert_eq!(r.nth(10), None); assert_eq!(r.is_empty(), true); - assert_eq!(r, 1...0); // We may not want to document/promise this detail + assert_eq!(r, 1..=0); // We may not want to document/promise this detail } #[test] diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 07e933985a0..2c540c8de8f 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -509,6 +509,7 @@ impl TokenTree { Dot => op!('.'), DotDot => joint!('.', Dot), DotDotDot => joint!('.', DotDot), + DotDotEq => joint!('.', DotEq), Comma => op!(','), Semi => op!(';'), Colon => op!(':'), @@ -531,6 +532,7 @@ impl TokenTree { }) } + DotEq => unreachable!(), OpenDelim(..) | CloseDelim(..) => unreachable!(), Whitespace | Comment | Shebang(..) | Eof => unreachable!(), }; diff --git a/src/libproc_macro/quote.rs b/src/libproc_macro/quote.rs index 0db2b86b15f..8c1f6bfc11a 100644 --- a/src/libproc_macro/quote.rs +++ b/src/libproc_macro/quote.rs @@ -202,8 +202,8 @@ impl Quote for Token { gen_match! { Eq, Lt, Le, EqEq, Ne, Ge, Gt, AndAnd, OrOr, Not, Tilde, At, Dot, DotDot, DotDotDot, - Comma, Semi, Colon, ModSep, RArrow, LArrow, FatArrow, Pound, Dollar, Question, - Underscore; + DotDotEq, Comma, Semi, Colon, ModSep, RArrow, LArrow, FatArrow, Pound, Dollar, + Question, Underscore; Token::OpenDelim(delim) => quote!(rt::token::OpenDelim((quote delim))), Token::CloseDelim(delim) => quote!(rt::token::CloseDelim((quote delim))), diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 56ec6a65eb6..669e1ba773e 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -272,6 +272,8 @@ fn hash_token<'gcx, W: StableHasherResult>(token: &token::Token, token::Token::Dot | token::Token::DotDot | token::Token::DotDotDot | + token::Token::DotDotEq | + token::Token::DotEq | token::Token::Comma | token::Token::Semi | token::Token::Colon | diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 081f950e40d..98863b229b5 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -249,8 +249,8 @@ impl<'a> Classifier<'a> { token::BinOpEq(..) | token::FatArrow => Class::Op, // Miscellaneous, no highlighting. - token::Dot | token::DotDot | token::DotDotDot | token::Comma | token::Semi | - token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) | + token::Dot | token::DotDot | token::DotDotDot | token::DotDotEq | token::Comma | + token::Semi | token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) | token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) | token::CloseDelim(token::NoDelim) => Class::None, @@ -353,7 +353,7 @@ impl<'a> Classifier<'a> { token::Lifetime(..) => Class::Lifetime, token::Underscore | token::Eof | token::Interpolated(..) | - token::Tilde | token::At => Class::None, + token::Tilde | token::At | token::DotEq => Class::None, }; // Anything that didn't return above is the simple case where we the diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index b29883670bd..c3cf4747835 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -291,7 +291,7 @@ Erroneous code example: fn main() { let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1]; - let x = &tmp[1...]; // error: inclusive range was used with no end + let x = &tmp[1..=]; // error: inclusive range was used with no end } ``` @@ -312,7 +312,7 @@ Or put an end to your inclusive range: fn main() { let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1]; - let x = &tmp[1...3]; // ok! + let x = &tmp[1..=3]; // ok! } ``` "##, diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index c3f3a59c302..bd8c9a0ed40 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -686,7 +686,9 @@ fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P { token::At => "At", token::Dot => "Dot", token::DotDot => "DotDot", + token::DotEq => "DotEq", token::DotDotDot => "DotDotDot", + token::DotDotEq => "DotDotEq", token::Comma => "Comma", token::Semi => "Semi", token::Colon => "Colon", diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1fef382c83a..2fc451d5d00 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -261,7 +261,7 @@ declare_features! ( // rustc internal (active, abi_vectorcall, "1.7.0", None), - // a...b and ...b + // a..=b and ..=b (active, inclusive_range_syntax, "1.7.0", Some(28237)), // X..Y patterns diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index ce3f16d2ba1..1cb7b0eca58 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1131,6 +1131,9 @@ impl<'a> StringReader<'a> { if self.ch_is('.') { self.bump(); Ok(token::DotDotDot) + } else if self.ch_is('=') { + self.bump(); + Ok(token::DotDotEq) } else { Ok(token::DotDot) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a2514a04254..80c976abd19 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -432,7 +432,7 @@ impl Error { Error::InclusiveRangeWithNoEnd => { let mut err = struct_span_err!(handler, sp, E0586, "inclusive range with no end"); - err.help("inclusive ranges must be bounded at the end (`...b` or `a...b`)"); + err.help("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)"); err } } @@ -2710,7 +2710,7 @@ impl<'a> Parser<'a> { LhsExpr::AttributesParsed(attrs) => Some(attrs), _ => None, }; - if self.token == token::DotDot || self.token == token::DotDotDot { + if [token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token) { return self.parse_prefix_range_expr(attrs); } else { self.parse_prefix_expr(attrs)? @@ -2744,6 +2744,10 @@ impl<'a> Parser<'a> { if op.precedence() < min_prec { break; } + // Warn about deprecated ... syntax (until SNAP) + if self.token == token::DotDotDot { + self.warn_dotdoteq(self.span); + } self.bump(); if op.is_comparison() { self.check_no_chained_comparison(&lhs, &op); @@ -2770,12 +2774,13 @@ impl<'a> Parser<'a> { } }; continue - } else if op == AssocOp::DotDot || op == AssocOp::DotDotDot { - // If we didn’t have to handle `x..`/`x...`, it would be pretty easy to + } else if op == AssocOp::DotDot || op == AssocOp::DotDotEq { + // If we didn’t have to handle `x..`/`x..=`, it would be pretty easy to // generalise it to the Fixity::None code. // - // We have 2 alternatives here: `x..y`/`x...y` and `x..`/`x...` The other + // We have 2 alternatives here: `x..y`/`x..=y` and `x..`/`x..=` The other // two variants are handled with `parse_prefix_range_expr` call above. + // (and `x...y`/`x...` until SNAP) let rhs = if self.is_at_start_of_range_notation_rhs() { Some(self.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed)?) @@ -2852,8 +2857,8 @@ impl<'a> Parser<'a> { let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); self.mk_expr(span, aopexpr, ThinVec::new()) } - AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotDot => { - self.bug("As, Colon, DotDot or DotDotDot branch reached") + AssocOp::As | AssocOp::Colon | AssocOp::DotDot | AssocOp::DotDotEq => { + self.bug("AssocOp should have been handled by special case") } }; @@ -2949,17 +2954,22 @@ impl<'a> Parser<'a> { } } - /// Parse prefix-forms of range notation: `..expr`, `..`, `...expr` + /// Parse prefix-forms of range notation: `..expr`, `..`, `..=expr` (and `...expr` until SNAP) fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option>) -> PResult<'a, P> { - debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot, - "parse_prefix_range_expr: token {:?} is not DotDot or DotDotDot", + // SNAP remove DotDotDot + debug_assert!([token::DotDot, token::DotDotDot, token::DotDotEq].contains(&self.token), + "parse_prefix_range_expr: token {:?} is not DotDot/DotDotDot/DotDotEq", self.token); let tok = self.token.clone(); let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; let lo = self.span; let mut hi = self.span; + // Warn about deprecated ... syntax (until SNAP) + if tok == token::DotDotDot { + self.warn_dotdoteq(self.span); + } self.bump(); let opt_end = if self.is_at_start_of_range_notation_rhs() { // RHS must be parsed with more associativity than the dots. @@ -3450,7 +3460,7 @@ impl<'a> Parser<'a> { fn parse_as_ident(&mut self) -> bool { self.look_ahead(1, |t| match *t { token::OpenDelim(token::Paren) | token::OpenDelim(token::Brace) | - token::DotDotDot | token::ModSep | token::Not => Some(false), + token::DotDotDot | token::DotDotEq | token::ModSep | token::Not => Some(false), // ensure slice patterns [a, b.., c] and [a, b, c..] don't go into the // range pattern branch token::DotDot => None, @@ -3544,11 +3554,12 @@ impl<'a> Parser<'a> { let mac = respan(lo.to(self.prev_span), Mac_ { path: path, tts: tts }); pat = PatKind::Mac(mac); } - token::DotDotDot | token::DotDot => { + token::DotDotDot | token::DotDotEq | token::DotDot => { let end_kind = match self.token { token::DotDot => RangeEnd::Excluded, - token::DotDotDot => RangeEnd::Included, - _ => panic!("can only parse `..` or `...` for ranges (checked above)"), + token::DotDotDot | token::DotDotEq => RangeEnd::Included, + _ => panic!("can only parse `..`/`...`/`..=` for ranges \ + (checked above)"), }; // Parse range let span = lo.to(self.prev_span); @@ -3590,6 +3601,9 @@ impl<'a> Parser<'a> { if self.eat(&token::DotDotDot) { let end = self.parse_pat_range_end()?; pat = PatKind::Range(begin, end, RangeEnd::Included); + } else if self.eat(&token::DotDotEq) { + let end = self.parse_pat_range_end()?; + pat = PatKind::Range(begin, end, RangeEnd::Included); } else if self.eat(&token::DotDot) { let end = self.parse_pat_range_end()?; pat = PatKind::Range(begin, end, RangeEnd::Excluded); @@ -3973,7 +3987,7 @@ impl<'a> Parser<'a> { token::BinOp(token::Minus) | token::BinOp(token::Star) | token::BinOp(token::And) | token::BinOp(token::Or) | token::AndAnd | token::OrOr | - token::DotDot | token::DotDotDot => false, + token::DotDot | token::DotDotDot | token::DotDotEq => false, _ => true, } { self.warn_missing_semicolon(); @@ -4195,6 +4209,12 @@ impl<'a> Parser<'a> { }).emit(); } + fn warn_dotdoteq(&self, span: Span) { + self.diagnostic().struct_span_warn(span, { + "`...` is being replaced by `..=`" + }).emit(); + } + // Parse bounds of a type parameter `BOUND + BOUND + BOUND`, possibly with trailing `+`. // BOUND = TY_BOUND | LT_BOUND // LT_BOUND = LIFETIME (e.g. `'a`) diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index a316733bdb5..4888654fac9 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -152,6 +152,8 @@ pub enum Token { Dot, DotDot, DotDotDot, + DotDotEq, + DotEq, // HACK(durka42) never produced by the parser, only used for libproc_macro Comma, Semi, Colon, @@ -212,18 +214,19 @@ impl Token { pub fn can_begin_expr(&self) -> bool { match *self { Ident(ident) => ident_can_begin_expr(ident), // value name or keyword - OpenDelim(..) | // tuple, array or block - Literal(..) | // literal - Not | // operator not - BinOp(Minus) | // unary minus - BinOp(Star) | // dereference - BinOp(Or) | OrOr | // closure - BinOp(And) | // reference - AndAnd | // double reference - DotDot | DotDotDot | // range notation - Lt | BinOp(Shl) | // associated path - ModSep | // global path - Pound => true, // expression attributes + OpenDelim(..) | // tuple, array or block + Literal(..) | // literal + Not | // operator not + BinOp(Minus) | // unary minus + BinOp(Star) | // dereference + BinOp(Or) | OrOr | // closure + BinOp(And) | // reference + AndAnd | // double reference + DotDot | DotDotDot | DotDotEq | // range notation + // SNAP remove DotDotDot + Lt | BinOp(Shl) | // associated path + ModSep | // global path + Pound => true, // expression attributes Interpolated(ref nt) => match nt.0 { NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) => true, _ => false, @@ -402,10 +405,12 @@ impl Token { Dot => match joint { Dot => DotDot, DotDot => DotDotDot, + DotEq => DotDotEq, _ => return None, }, DotDot => match joint { Dot => DotDotDot, + Eq => DotDotEq, _ => return None, }, Colon => match joint { @@ -413,9 +418,9 @@ impl Token { _ => return None, }, - Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | Comma | - Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | Question | - OpenDelim(..) | CloseDelim(..) | Underscore => return None, + Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq | + DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar | + Question | OpenDelim(..) | CloseDelim(..) | Underscore => return None, Literal(..) | Ident(..) | Lifetime(..) | Interpolated(..) | DocComment(..) | Whitespace | Comment | Shebang(..) | Eof => return None, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 9903dc50f36..cc4b34854fc 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -203,6 +203,8 @@ pub fn token_to_string(tok: &Token) -> String { token::Dot => ".".to_string(), token::DotDot => "..".to_string(), token::DotDotDot => "...".to_string(), + token::DotDotEq => "..=".to_string(), + token::DotEq => ".=".to_string(), token::Comma => ",".to_string(), token::Semi => ";".to_string(), token::Colon => ":".to_string(), diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index a4f06cb1b45..590874806d7 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -62,8 +62,8 @@ pub enum AssocOp { As, /// `..` range DotDot, - /// `...` range - DotDotDot, + /// `..=` range + DotDotEq, /// `:` Colon, } @@ -105,7 +105,8 @@ impl AssocOp { Token::AndAnd => Some(LAnd), Token::OrOr => Some(LOr), Token::DotDot => Some(DotDot), - Token::DotDotDot => Some(DotDotDot), + Token::DotDotEq => Some(DotDotEq), + Token::DotDotDot => Some(DotDotEq), // remove this after SNAP Token::Colon => Some(Colon), _ if t.is_keyword(keywords::As) => Some(As), _ => None @@ -151,7 +152,7 @@ impl AssocOp { Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, LAnd => 6, LOr => 5, - DotDot | DotDotDot => 4, + DotDot | DotDotEq => 4, Inplace => 3, Assign | AssignOp(_) => 2, } @@ -166,7 +167,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 | DotDotDot => Fixity::None + DotDot | DotDotEq => Fixity::None } } @@ -176,7 +177,7 @@ impl AssocOp { Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | - DotDot | DotDotDot | Colon => false + DotDot | DotDotEq | Colon => false } } @@ -186,7 +187,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 | DotDotDot | Colon => false + LOr | DotDot | DotDotEq | Colon => false } } @@ -211,7 +212,7 @@ impl AssocOp { BitOr => Some(BinOpKind::BitOr), LAnd => Some(BinOpKind::And), LOr => Some(BinOpKind::Or), - Inplace | Assign | AssignOp(_) | As | DotDot | DotDotDot | Colon => None + Inplace | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None } } } diff --git a/src/test/compile-fail/E0586.rs b/src/test/compile-fail/E0586.rs index 0b063569abc..c1bfc5c73a1 100644 --- a/src/test/compile-fail/E0586.rs +++ b/src/test/compile-fail/E0586.rs @@ -10,5 +10,5 @@ fn main() { let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1]; - let x = &tmp[1...]; //~ ERROR E0586 + let x = &tmp[1..=]; //~ ERROR E0586 } diff --git a/src/test/compile-fail/impossible_range.rs b/src/test/compile-fail/impossible_range.rs index 94e048fed65..e4465e9f6b6 100644 --- a/src/test/compile-fail/impossible_range.rs +++ b/src/test/compile-fail/impossible_range.rs @@ -18,12 +18,12 @@ pub fn main() { ..1; 0..1; - ...; //~ERROR inclusive range with no end + ..=; //~ERROR inclusive range with no end //~^HELP bounded at the end - 0...; //~ERROR inclusive range with no end + 0..=; //~ERROR inclusive range with no end //~^HELP bounded at the end - ...1; - 0...1; + ..=1; + 0..=1; } diff --git a/src/test/compile-fail/range_inclusive_gate.rs b/src/test/compile-fail/range_inclusive_gate.rs index 1d1153e951b..69b9a4c67ad 100644 --- a/src/test/compile-fail/range_inclusive_gate.rs +++ b/src/test/compile-fail/range_inclusive_gate.rs @@ -14,7 +14,7 @@ // #![feature(inclusive_range)] pub fn main() { - let _: std::ops::RangeInclusive<_> = { use std::intrinsics; 1 } ... { use std::intrinsics; 2 }; + 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 diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs index bb31982d93f..a12624d0832 100644 --- a/src/test/incremental/hashes/indexing_expressions.rs +++ b/src/test/incremental/hashes/indexing_expressions.rs @@ -153,5 +153,5 @@ fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { #[rustc_metadata_clean(cfg="cfail2")] #[rustc_metadata_clean(cfg="cfail3")] fn exclusive_to_inclusive_range(slice: &[u32]) -> &[u32] { - &slice[3...7] + &slice[3..=7] } diff --git a/src/test/parse-fail/pat-ranges-1.rs b/src/test/parse-fail/pat-ranges-1.rs index e1cbb961b1b..857a3924aec 100644 --- a/src/test/parse-fail/pat-ranges-1.rs +++ b/src/test/parse-fail/pat-ranges-1.rs @@ -11,5 +11,5 @@ // Parsing of range patterns fn main() { - let macropus!() ... 11 = 12; //~ error: expected one of `:`, `;`, or `=`, found `...` + let macropus!() ..= 11 = 12; //~ error: expected one of `:`, `;`, or `=`, found `..=` } diff --git a/src/test/parse-fail/pat-ranges-2.rs b/src/test/parse-fail/pat-ranges-2.rs index 04ad5ff083b..64c749333cf 100644 --- a/src/test/parse-fail/pat-ranges-2.rs +++ b/src/test/parse-fail/pat-ranges-2.rs @@ -11,5 +11,5 @@ // Parsing of range patterns fn main() { - let 10 ... makropulos!() = 12; //~ error: expected one of `::`, `:`, `;`, or `=`, found `!` + let 10 ..= makropulos!() = 12; //~ error: expected one of `::`, `:`, `;`, or `=`, found `!` } diff --git a/src/test/parse-fail/pat-ranges-3.rs b/src/test/parse-fail/pat-ranges-3.rs index 5f7aac71d29..1327a9fab36 100644 --- a/src/test/parse-fail/pat-ranges-3.rs +++ b/src/test/parse-fail/pat-ranges-3.rs @@ -11,5 +11,5 @@ // Parsing of range patterns fn main() { - let 10 ... 10 + 3 = 12; //~ expected one of `:`, `;`, or `=`, found `+` + let 10 ..= 10 + 3 = 12; //~ expected one of `:`, `;`, or `=`, found `+` } diff --git a/src/test/parse-fail/pat-ranges-4.rs b/src/test/parse-fail/pat-ranges-4.rs index 4bbf387d1c0..c159c770250 100644 --- a/src/test/parse-fail/pat-ranges-4.rs +++ b/src/test/parse-fail/pat-ranges-4.rs @@ -11,5 +11,6 @@ // Parsing of range patterns fn main() { - let 10 - 3 ... 10 = 8; //~ error: expected one of `...`, `..`, `:`, `;`, or `=`, found `-` + let 10 - 3 ..= 10 = 8; + //~^ error: expected one of `...`, `..=`, `..`, `:`, `;`, or `=`, found `-` } diff --git a/src/test/parse-fail/range_inclusive.rs b/src/test/parse-fail/range_inclusive.rs index ce97372c668..cc32b9903b5 100644 --- a/src/test/parse-fail/range_inclusive.rs +++ b/src/test/parse-fail/range_inclusive.rs @@ -13,7 +13,7 @@ #![feature(inclusive_range_syntax, inclusive_range)] pub fn main() { - for _ in 1... {} //~ERROR inclusive range with no end + for _ in 1..= {} //~ERROR inclusive range with no end //~^HELP bounded at the end } diff --git a/src/test/parse-fail/range_inclusive_gate.rs b/src/test/parse-fail/range_inclusive_gate.rs index 30dc6fc5b20..de690c3fea3 100644 --- a/src/test/parse-fail/range_inclusive_gate.rs +++ b/src/test/parse-fail/range_inclusive_gate.rs @@ -15,21 +15,21 @@ // #![feature(inclusive_range_syntax, inclusive_range)] macro_rules! m { - () => { for _ in 1...10 {} } //~ ERROR inclusive range syntax is experimental + () => { 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 + 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 + () => { for _ in 1..=10 {} } //~ ERROR inclusive range syntax is experimental } macro_rules! o { @@ -38,7 +38,7 @@ macro_rules! o { fn g() {} #[cfg(not(nope))] fn g() { - for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental + for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental } g(); @@ -54,7 +54,7 @@ macro_rules! p { fn h() {} #[cfg(not(nope))] fn h() { - for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental + for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental } h(); @@ -62,8 +62,8 @@ macro_rules! p { } pub fn main() { - for _ in 1...10 {} //~ ERROR inclusive range syntax is experimental - for _ in ...10 {} //~ ERROR inclusive range syntax is experimental + 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 diff --git a/src/test/run-pass/inc-range-pat.rs b/src/test/run-pass/inc-range-pat.rs new file mode 100644 index 00000000000..237b41b6128 --- /dev/null +++ b/src/test/run-pass/inc-range-pat.rs @@ -0,0 +1,20 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test old and new syntax for inclusive range patterns. + +fn main() { + assert!(match 42 { 0 ... 100 => true, _ => false }); + assert!(match 42 { 0 ..= 100 => true, _ => false }); + + assert!(match 'x' { 'a' ... 'z' => true, _ => false }); + assert!(match 'x' { 'a' ..= 'z' => true, _ => false }); +} + diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index f6119e70999..71e11804052 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -17,18 +17,18 @@ use std::ops::{RangeInclusive, RangeToInclusive}; fn foo() -> isize { 42 } // Test that range syntax works in return statements -fn return_range_to() -> RangeToInclusive { return ...1; } +fn return_range_to() -> RangeToInclusive { return ..=1; } pub fn main() { let mut count = 0; - for i in 0_usize...10 { + 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; + let mut range = 0_usize..=10; for i in range { assert!(i >= 0 && i <= 10); count += i; @@ -36,53 +36,53 @@ pub fn main() { assert_eq!(count, 55); let mut count = 0; - for i in (0_usize...10).step_by(2) { + 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 _ = 0_usize..=4+4-3; + let _ = 0..=foo(); - let _ = { &42...&100 }; // references to literals are OK - let _ = ...42_usize; + 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; + let _ = x..=&y; } // test collection indexing - let vec = (0...10).collect::>(); + let vec = (0..=10).collect::>(); let slice: &[_] = &*vec; let string = String::from("hello world"); let stir = "hello world"; - assert_eq!(&vec[3...6], &[3, 4, 5, 6]); - assert_eq!(&vec[ ...6], &[0, 1, 2, 3, 4, 5, 6]); + assert_eq!(&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!(&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!(&string[3..=6], "lo w"); + assert_eq!(&string[ ..=6], "hello w"); - assert_eq!(&stir[3...6], "lo w"); - assert_eq!(&stir[ ...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...42u8; + let mut long = 0..=255u8; + let mut short = 42..=42u8; 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, 1...0); + assert_eq!(short, 1..=0); assert_eq!(long.len(), 255); assert_eq!(short.len(), 0); @@ -94,31 +94,31 @@ pub fn main() { assert_eq!(long.next(), Some(1)); assert_eq!(long.next(), Some(2)); assert_eq!(long.next_back(), Some(252)); - for i in 3...251 { + for i in 3..=251 { assert_eq!(long.next(), Some(i)); } - assert_eq!(long, 1...0); + assert_eq!(long, 1..=0); // check underflow - let mut narrow = 1...0; + let mut narrow = 1..=0; assert_eq!(narrow.next_back(), None); - assert_eq!(narrow, 1...0); - let mut zero = 0u8...0; + assert_eq!(narrow, 1..=0); + let mut zero = 0u8..=0; assert_eq!(zero.next_back(), Some(0)); assert_eq!(zero.next_back(), None); - assert_eq!(zero, 1...0); - let mut high = 255u8...255; + assert_eq!(zero, 1..=0); + let mut high = 255u8..=255; assert_eq!(high.next_back(), Some(255)); assert_eq!(high.next_back(), None); - assert_eq!(high, 1...0); + assert_eq!(high, 1..=0); // what happens if you have a nonsense range? - let mut nonsense = 10...5; + let mut nonsense = 10..=5; assert_eq!(nonsense.next(), None); - assert_eq!(nonsense, 10...5); + assert_eq!(nonsense, 10..=5); // output - assert_eq!(format!("{:?}", 0...10), "0...10"); - assert_eq!(format!("{:?}", ...10), "...10"); - assert_eq!(format!("{:?}", long), "1...0"); + assert_eq!(format!("{:?}", 0..=10), "0..=10"); + assert_eq!(format!("{:?}", ..=10), "..=10"); + assert_eq!(format!("{:?}", long), "1..=0"); } diff --git a/src/test/run-pass/range_inclusive_gate.rs b/src/test/run-pass/range_inclusive_gate.rs index 5e0ec19d6b3..570087aedbb 100644 --- a/src/test/run-pass/range_inclusive_gate.rs +++ b/src/test/run-pass/range_inclusive_gate.rs @@ -14,7 +14,7 @@ fn main() { let mut count = 0; - for i in 0_usize...10 { + for i in 0_usize..=10 { assert!(i >= 0 && i <= 10); count += i; } -- cgit 1.4.1-3-g733a5 From 13724fafdc62e3e7e75822554cadb15ff5ce3035 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 25 Sep 2017 20:53:08 -0700 Subject: Add more custom folding to `core::iter` adaptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many of the iterator adaptors will perform faster folds if they forward to their inner iterator's folds, especially for inner types like `Chain` which are optimized too. The following types are newly specialized: | Type | `fold` | `rfold` | | ----------- | ------ | ------- | | `Enumerate` | ✓ | ✓ | | `Filter` | ✓ | ✓ | | `FilterMap` | ✓ | ✓ | | `FlatMap` | exists | ✓ | | `Fuse` | ✓ | ✓ | | `Inspect` | ✓ | ✓ | | `Peekable` | ✓ | N/A¹ | | `Skip` | ✓ | N/A² | | `SkipWhile` | ✓ | N/A¹ | ¹ not a `DoubleEndedIterator` ² `Skip::next_back` doesn't pull skipped items at all, but this couldn't be avoided if `Skip::rfold` were to call its inner iterator's `rfold`. Benchmarks ---------- In the following results, plain `_sum` computes the sum of a million integers -- note that `sum()` is implemented with `fold()`. The `_ref_sum` variants do the same on a `by_ref()` iterator, which is limited to calling `next()` one by one, without specialized `fold`. The `chain` variants perform the same tests on two iterators chained together, to show a greater benefit of forwarding `fold` internally. test iter::bench_enumerate_chain_ref_sum ... bench: 2,216,264 ns/iter (+/- 29,228) test iter::bench_enumerate_chain_sum ... bench: 922,380 ns/iter (+/- 2,676) test iter::bench_enumerate_ref_sum ... bench: 476,094 ns/iter (+/- 7,110) test iter::bench_enumerate_sum ... bench: 476,438 ns/iter (+/- 3,334) test iter::bench_filter_chain_ref_sum ... bench: 2,266,095 ns/iter (+/- 6,051) test iter::bench_filter_chain_sum ... bench: 745,594 ns/iter (+/- 2,013) test iter::bench_filter_ref_sum ... bench: 889,696 ns/iter (+/- 1,188) test iter::bench_filter_sum ... bench: 667,325 ns/iter (+/- 1,894) test iter::bench_filter_map_chain_ref_sum ... bench: 2,259,195 ns/iter (+/- 353,440) test iter::bench_filter_map_chain_sum ... bench: 1,223,280 ns/iter (+/- 1,972) test iter::bench_filter_map_ref_sum ... bench: 611,607 ns/iter (+/- 2,507) test iter::bench_filter_map_sum ... bench: 611,610 ns/iter (+/- 472) test iter::bench_fuse_chain_ref_sum ... bench: 2,246,106 ns/iter (+/- 22,395) test iter::bench_fuse_chain_sum ... bench: 634,887 ns/iter (+/- 1,341) test iter::bench_fuse_ref_sum ... bench: 444,816 ns/iter (+/- 1,748) test iter::bench_fuse_sum ... bench: 316,954 ns/iter (+/- 2,616) test iter::bench_inspect_chain_ref_sum ... bench: 2,245,431 ns/iter (+/- 21,371) test iter::bench_inspect_chain_sum ... bench: 631,645 ns/iter (+/- 4,928) test iter::bench_inspect_ref_sum ... bench: 317,437 ns/iter (+/- 702) test iter::bench_inspect_sum ... bench: 315,942 ns/iter (+/- 4,320) test iter::bench_peekable_chain_ref_sum ... bench: 2,243,585 ns/iter (+/- 12,186) test iter::bench_peekable_chain_sum ... bench: 634,848 ns/iter (+/- 1,712) test iter::bench_peekable_ref_sum ... bench: 444,808 ns/iter (+/- 480) test iter::bench_peekable_sum ... bench: 317,133 ns/iter (+/- 3,309) test iter::bench_skip_chain_ref_sum ... bench: 1,778,734 ns/iter (+/- 2,198) test iter::bench_skip_chain_sum ... bench: 761,850 ns/iter (+/- 1,645) test iter::bench_skip_ref_sum ... bench: 478,207 ns/iter (+/- 119,252) test iter::bench_skip_sum ... bench: 315,614 ns/iter (+/- 3,054) test iter::bench_skip_while_chain_ref_sum ... bench: 2,486,370 ns/iter (+/- 4,845) test iter::bench_skip_while_chain_sum ... bench: 633,915 ns/iter (+/- 5,892) test iter::bench_skip_while_ref_sum ... bench: 666,926 ns/iter (+/- 804) test iter::bench_skip_while_sum ... bench: 444,405 ns/iter (+/- 571) --- src/libcore/benches/iter.rs | 151 +++++++++++++++++++++++++++++-------- src/libcore/iter/mod.rs | 172 ++++++++++++++++++++++++++++++++++++++++++ src/libcore/tests/iter.rs | 180 +++++++++++++++++++++++++++++++++++++++++++- src/libcore/tests/lib.rs | 1 + 4 files changed, 471 insertions(+), 33 deletions(-) (limited to 'src/libcore/tests') diff --git a/src/libcore/benches/iter.rs b/src/libcore/benches/iter.rs index 827c6354c60..1f16f5b1df3 100644 --- a/src/libcore/benches/iter.rs +++ b/src/libcore/benches/iter.rs @@ -147,40 +147,131 @@ fn bench_for_each_chain_ref_fold(b: &mut Bencher) { }); } -#[bench] -fn bench_flat_map_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000).flat_map(|x| x..x+1000) - .map(black_box) - .sum() - }); + +/// Helper to benchmark `sum` for iterators taken by value which +/// can optimize `fold`, and by reference which cannot. +macro_rules! bench_sums { + ($bench_sum:ident, $bench_ref_sum:ident, $iter:expr) => { + #[bench] + fn $bench_sum(b: &mut Bencher) { + b.iter(|| -> i64 { + $iter.map(black_box).sum() + }); + } + + #[bench] + fn $bench_ref_sum(b: &mut Bencher) { + b.iter(|| -> i64 { + $iter.map(black_box).by_ref().sum() + }); + } + } } -#[bench] -fn bench_flat_map_ref_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000).flat_map(|x| x..x+1000) - .map(black_box) - .by_ref() - .sum() - }); +bench_sums! { + bench_flat_map_sum, + bench_flat_map_ref_sum, + (0i64..1000).flat_map(|x| x..x+1000) } -#[bench] -fn bench_flat_map_chain_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000000).flat_map(|x| once(x).chain(once(x))) - .map(black_box) - .sum() - }); +bench_sums! { + bench_flat_map_chain_sum, + bench_flat_map_chain_ref_sum, + (0i64..1000000).flat_map(|x| once(x).chain(once(x))) } -#[bench] -fn bench_flat_map_chain_ref_sum(b: &mut Bencher) { - b.iter(|| -> i64 { - (0i64..1000000).flat_map(|x| once(x).chain(once(x))) - .map(black_box) - .by_ref() - .sum() - }); +bench_sums! { + bench_enumerate_sum, + bench_enumerate_ref_sum, + (0i64..1000000).enumerate().map(|(i, x)| x * i as i64) +} + +bench_sums! { + bench_enumerate_chain_sum, + bench_enumerate_chain_ref_sum, + (0i64..1000000).chain(0..1000000).enumerate().map(|(i, x)| x * i as i64) +} + +bench_sums! { + bench_filter_sum, + bench_filter_ref_sum, + (0i64..1000000).filter(|x| x % 2 == 0) +} + +bench_sums! { + bench_filter_chain_sum, + bench_filter_chain_ref_sum, + (0i64..1000000).chain(0..1000000).filter(|x| x % 2 == 0) +} + +bench_sums! { + bench_filter_map_sum, + bench_filter_map_ref_sum, + (0i64..1000000).filter_map(|x| x.checked_mul(x)) +} + +bench_sums! { + bench_filter_map_chain_sum, + bench_filter_map_chain_ref_sum, + (0i64..1000000).chain(0..1000000).filter_map(|x| x.checked_mul(x)) +} + +bench_sums! { + bench_fuse_sum, + bench_fuse_ref_sum, + (0i64..1000000).fuse() +} + +bench_sums! { + bench_fuse_chain_sum, + bench_fuse_chain_ref_sum, + (0i64..1000000).chain(0..1000000).fuse() +} + +bench_sums! { + bench_inspect_sum, + bench_inspect_ref_sum, + (0i64..1000000).inspect(|_| {}) +} + +bench_sums! { + bench_inspect_chain_sum, + bench_inspect_chain_ref_sum, + (0i64..1000000).chain(0..1000000).inspect(|_| {}) +} + +bench_sums! { + bench_peekable_sum, + bench_peekable_ref_sum, + (0i64..1000000).peekable() +} + +bench_sums! { + bench_peekable_chain_sum, + bench_peekable_chain_ref_sum, + (0i64..1000000).chain(0..1000000).peekable() +} + +bench_sums! { + bench_skip_sum, + bench_skip_ref_sum, + (0i64..1000000).skip(1000) +} + +bench_sums! { + bench_skip_chain_sum, + bench_skip_chain_ref_sum, + (0i64..1000000).chain(0..1000000).skip(1000) +} + +bench_sums! { + bench_skip_while_sum, + bench_skip_while_ref_sum, + (0i64..1000000).skip_while(|&x| x < 1000) +} + +bench_sums! { + bench_skip_while_chain_sum, + bench_skip_while_chain_ref_sum, + (0i64..1000000).chain(0..1000000).skip_while(|&x| x < 1000) } diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 7907f2fd661..05a5f63680c 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -1238,6 +1238,18 @@ impl Iterator for Filter where P: FnMut(&I::Item) -> bool } count } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut predicate = self.predicate; + self.iter.fold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + acc + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1253,6 +1265,18 @@ impl DoubleEndedIterator for Filter } None } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut predicate = self.predicate; + self.iter.rfold(init, move |acc, item| if predicate(&item) { + fold(acc, item) + } else { + acc + }) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1304,6 +1328,17 @@ impl Iterator for FilterMap let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1319,6 +1354,17 @@ impl DoubleEndedIterator for FilterMap } None } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, item| match f(item) { + Some(x) => fold(acc, x), + None => acc, + }) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1383,6 +1429,19 @@ impl Iterator for Enumerate where I: Iterator { fn count(self) -> usize { self.iter.count() } + + #[inline] + #[rustc_inherit_overflow_checks] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut count = self.count; + self.iter.fold(init, move |acc, item| { + let acc = fold(acc, (count, item)); + count += 1; + acc + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1398,6 +1457,19 @@ impl DoubleEndedIterator for Enumerate where (self.count + len, a) }) } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + // Can safely add and subtract the count, as `ExactSizeIterator` promises + // that the number of elements fits into a `usize`. + let mut count = self.count + self.iter.len(); + self.iter.rfold(init, move |acc, item| { + count -= 1; + fold(acc, (count, item)) + }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1509,6 +1581,18 @@ impl Iterator for Peekable { let hi = hi.and_then(|x| x.checked_add(peek_len)); (lo, hi) } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let acc = match self.peeked { + Some(None) => return init, + Some(Some(v)) => fold(init, v), + None => init, + }; + self.iter.fold(acc, fold) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1617,6 +1701,19 @@ impl Iterator for SkipWhile let (_, upper) = self.iter.size_hint(); (0, upper) // can't know a lower bound, due to the predicate } + + #[inline] + fn fold(mut self, mut init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if !self.flag { + match self.next() { + Some(v) => init = fold(init, v), + None => return init, + } + } + self.iter.fold(init, fold) + } } #[unstable(feature = "fused", issue = "35602")] @@ -1757,6 +1854,19 @@ impl Iterator for Skip where I: Iterator { (lower, upper) } + + #[inline] + fn fold(mut self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.n > 0 { + // nth(n) skips n+1 + if self.iter.nth(self.n - 1).is_none() { + return init; + } + } + self.iter.fold(init, fold) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1979,6 +2089,16 @@ impl DoubleEndedIterator for FlatMap wher } } } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.frontiter.into_iter() + .chain(self.iter.map(self.f).map(U::into_iter)) + .chain(self.backiter) + .rfold(init, |acc, iter| iter.rfold(acc, &mut fold)) + } } #[unstable(feature = "fused", issue = "35602")] @@ -2056,6 +2176,17 @@ impl Iterator for Fuse where I: Iterator { self.iter.size_hint() } } + + #[inline] + default fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + init + } else { + self.iter.fold(init, fold) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2070,6 +2201,17 @@ impl DoubleEndedIterator for Fuse where I: DoubleEndedIterator { next } } + + #[inline] + default fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + if self.done { + init + } else { + self.iter.rfold(init, fold) + } + } } unsafe impl TrustedRandomAccess for Fuse @@ -2110,6 +2252,13 @@ impl Iterator for Fuse where I: FusedIterator { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn fold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.fold(init, fold) + } } #[unstable(feature = "fused", reason = "recently added", issue = "35602")] @@ -2120,6 +2269,13 @@ impl DoubleEndedIterator for Fuse fn next_back(&mut self) -> Option<::Item> { self.iter.next_back() } + + #[inline] + fn rfold(self, init: Acc, fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + self.iter.rfold(init, fold) + } } @@ -2184,6 +2340,14 @@ impl Iterator for Inspect where F: FnMut(&I::Item) { fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } + + #[inline] + fn fold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, item| { f(&item); fold(acc, item) }) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2195,6 +2359,14 @@ impl DoubleEndedIterator for Inspect let next = self.iter.next_back(); self.do_inspect(next) } + + #[inline] + fn rfold(self, init: Acc, mut fold: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.rfold(init, move |acc, item| { f(&item); fold(acc, item) }) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/tests/iter.rs b/src/libcore/tests/iter.rs index 59ae30de452..89454c52432 100644 --- a/src/libcore/tests/iter.rs +++ b/src/libcore/tests/iter.rs @@ -248,6 +248,25 @@ fn test_filter_map() { assert_eq!(it.collect::>(), [0*0, 2*2, 4*4, 6*6, 8*8]); } +#[test] +fn test_filter_map_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0*0, 2*2, 4*4, 6*6, 8*8]; + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x*x) } else { None }); + let i = it.fold(0, |i, x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter_map(|&x| if x % 2 == 0 { Some(x*x) } else { None }); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_iterator_enumerate() { let xs = [0, 1, 2, 3, 4, 5]; @@ -282,7 +301,31 @@ fn test_iterator_enumerate_nth() { #[test] fn test_iterator_enumerate_count() { let xs = [0, 1, 2, 3, 4, 5]; - assert_eq!(xs.iter().count(), 6); + assert_eq!(xs.iter().enumerate().count(), 6); +} + +#[test] +fn test_iterator_enumerate_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().enumerate(); + // steal a couple to get an interesting offset + assert_eq!(it.next(), Some((0, &0))); + assert_eq!(it.next(), Some((1, &1))); + let i = it.fold(2, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let mut it = xs.iter().enumerate(); + assert_eq!(it.next(), Some((0, &0))); + let i = it.rfold(xs.len() - 1, |i, (j, &x)| { + assert_eq!(i, j); + assert_eq!(x, xs[j]); + i - 1 + }); + assert_eq!(i, 0); } #[test] @@ -291,6 +334,25 @@ fn test_iterator_filter_count() { assert_eq!(xs.iter().filter(|&&x| x % 2 == 0).count(), 5); } +#[test] +fn test_iterator_filter_fold() { + let xs = [0, 1, 2, 3, 4, 5, 6, 7, 8]; + let ys = [0, 2, 4, 6, 8]; + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let it = xs.iter().filter(|&&x| x % 2 == 0); + let i = it.rfold(ys.len(), |i, &x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); +} + #[test] fn test_iterator_peekable() { let xs = vec![0, 1, 2, 3, 4, 5]; @@ -381,6 +443,18 @@ fn test_iterator_peekable_last() { assert_eq!(it.last(), None); } +#[test] +fn test_iterator_peekable_fold() { + let xs = [0, 1, 2, 3, 4, 5]; + let mut it = xs.iter().peekable(); + assert_eq!(it.peek(), Some(&&0)); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + /// This is an iterator that follows the Iterator contract, /// but it is not fused. After having returned None once, it will start /// producing elements if .next() is called again. @@ -470,6 +544,26 @@ fn test_iterator_skip_while() { assert_eq!(i, ys.len()); } +#[test] +fn test_iterator_skip_while_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; + let ys = [15, 16, 17, 19]; + let it = xs.iter().skip_while(|&x| *x < 15); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip_while(|&x| *x < 15); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + #[test] fn test_iterator_skip() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; @@ -566,6 +660,26 @@ fn test_iterator_skip_last() { assert_eq!(it.last(), Some(&30)); } +#[test] +fn test_iterator_skip_fold() { + let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19, 20, 30]; + let ys = [13, 15, 16, 17, 19, 20, 30]; + let it = xs.iter().skip(5); + let i = it.fold(0, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); + + let mut it = xs.iter().skip(5); + assert_eq!(it.next(), Some(&ys[0])); // process skips before folding + let i = it.fold(1, |i, &x| { + assert_eq!(x, ys[i]); + i + 1 + }); + assert_eq!(i, ys.len()); +} + #[test] fn test_iterator_take() { let xs = [0, 1, 2, 3, 5, 13, 15, 16, 17, 19]; @@ -661,13 +775,22 @@ fn test_iterator_flat_map_fold() { let xs = [0, 3, 6]; let ys = [1, 2, 3, 4, 5, 6, 7]; let mut it = xs.iter().flat_map(|&x| x..x+3); - it.next(); - it.next_back(); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); let i = it.fold(0, |i, x| { assert_eq!(x, ys[i]); i + 1 }); assert_eq!(i, ys.len()); + + let mut it = xs.iter().flat_map(|&x| x..x+3); + assert_eq!(it.next(), Some(0)); + assert_eq!(it.next_back(), Some(8)); + let i = it.rfold(ys.len(), |i, x| { + assert_eq!(x, ys[i - 1]); + i - 1 + }); + assert_eq!(i, 0); } #[test] @@ -684,6 +807,32 @@ fn test_inspect() { assert_eq!(&xs[..], &ys[..]); } +#[test] +fn test_inspect_fold() { + let xs = [1, 2, 3, 4]; + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + } + assert_eq!(n, xs.len()); + + let mut n = 0; + { + let it = xs.iter().inspect(|_| n += 1); + let i = it.rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + } + assert_eq!(n, xs.len()); +} + #[test] fn test_cycle() { let cycle_len = 3; @@ -1241,6 +1390,31 @@ fn test_fuse_count() { // Can't check len now because count consumes. } +#[test] +fn test_fuse_fold() { + let xs = [0, 1, 2]; + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().fold(0, |i, &x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); + + let it = xs.iter(); // `FusedIterator` + let i = it.fuse().rfold(xs.len(), |i, &x| { + assert_eq!(x, xs[i - 1]); + i - 1 + }); + assert_eq!(i, 0); + + let it = xs.iter().scan((), |_, &x| Some(x)); // `!FusedIterator` + let i = it.fuse().fold(0, |i, x| { + assert_eq!(x, xs[i]); + i + 1 + }); + assert_eq!(i, xs.len()); +} + #[test] fn test_once() { let mut it = once(42); diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 47995597a0a..d928276ad2f 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -25,6 +25,7 @@ #![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![feature(iter_rfind)] +#![feature(iter_rfold)] #![feature(nonzero)] #![feature(rand)] #![feature(raw)] -- cgit 1.4.1-3-g733a5 From f098d7be2978d8df3d180b6afae435468fc050de Mon Sep 17 00:00:00 2001 From: Hunter Praska Date: Wed, 7 Jun 2017 22:52:13 -0500 Subject: Add tests for Option and Result Try impl --- src/libcore/tests/lib.rs | 1 + src/libcore/tests/option.rs | 27 +++++++++++++++++++++++++++ src/libcore/tests/result.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) (limited to 'src/libcore/tests') diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 47995597a0a..938bc355665 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -38,6 +38,7 @@ #![feature(test)] #![feature(trusted_len)] #![feature(try_from)] +#![feature(try_trait)] #![feature(unique)] #![feature(const_atomic_bool_new)] diff --git a/src/libcore/tests/option.rs b/src/libcore/tests/option.rs index 6bac55575fb..00700b36b69 100644 --- a/src/libcore/tests/option.rs +++ b/src/libcore/tests/option.rs @@ -270,3 +270,30 @@ fn test_cloned() { assert_eq!(opt_ref_ref.clone().cloned(), Some(&val)); assert_eq!(opt_ref_ref.cloned().cloned(), Some(1)); } + +#[test] +fn test_try() { + fn try_option_some() -> Option { + let val = Some(1)?; + Some(val) + } + assert_eq!(try_option_some(), Some(1)); + + fn try_option_none() -> Option { + let val = None?; + Some(val) + } + assert_eq!(try_option_none(), None); + + fn try_option_ok() -> Result { + let val = Ok(1)?; + Ok(val) + } + assert_eq!(try_option_ok(), Ok(1)); + + fn try_option_err() -> Result { + let val = Err(Missing)?; + Ok(val) + } + assert_eq!(try_option_err(), Err(Missing)); +} diff --git a/src/libcore/tests/result.rs b/src/libcore/tests/result.rs index 4c5f19dee12..37264980dc7 100644 --- a/src/libcore/tests/result.rs +++ b/src/libcore/tests/result.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use core::option::*; + fn op1() -> Result { Ok(666) } fn op2() -> Result { Err("sadface") } @@ -202,3 +204,30 @@ pub fn test_unwrap_or_default() { assert_eq!(op1().unwrap_or_default(), 666); assert_eq!(op2().unwrap_or_default(), 0); } + +#[test] +fn test_try() { + fn try_result_some() -> Option { + let val = Ok(1)?; + Some(val) + } + assert_eq!(try_result_some(), Some(1)); + + fn try_result_none() -> Option { + let val = Err(Missing)?; + Some(val) + } + assert_eq!(try_result_none(), None); + + fn try_result_ok() -> Result { + let val = Ok(1)?; + Ok(val) + } + assert_eq!(try_result_ok(), Ok(1)); + + fn try_result_err() -> Result { + let val = Err(1)?; + Ok(val) + } + assert_eq!(try_result_err(), Err(1)); +} -- cgit 1.4.1-3-g733a5 From 8f63e8de464155e570ed81905ff203557dd02ac9 Mon Sep 17 00:00:00 2001 From: Hunter Praska Date: Thu, 8 Jun 2017 14:02:04 -0500 Subject: Add docs for Missing, correct Option's Try test --- src/libcore/option.rs | 2 +- src/libcore/tests/option.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/libcore/tests') diff --git a/src/libcore/option.rs b/src/libcore/option.rs index fa6e3095479..0c3339590db 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -1124,7 +1124,7 @@ impl> FromIterator> for Option { } } -/// The `Option` type. See [the module level documentation](index.html) for more. +/// The equivalent of `Option::None` for a `Result::Err`. #[unstable(feature = "try_trait", issue = "42327")] #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub struct Missing; diff --git a/src/libcore/tests/option.rs b/src/libcore/tests/option.rs index 00700b36b69..e2907e1dd89 100644 --- a/src/libcore/tests/option.rs +++ b/src/libcore/tests/option.rs @@ -286,13 +286,13 @@ fn test_try() { assert_eq!(try_option_none(), None); fn try_option_ok() -> Result { - let val = Ok(1)?; + let val = Some(1)?; Ok(val) } assert_eq!(try_option_ok(), Ok(1)); fn try_option_err() -> Result { - let val = Err(Missing)?; + let val = None?; Ok(val) } assert_eq!(try_option_err(), Err(Missing)); -- cgit 1.4.1-3-g733a5 From 28996db803b958b235f5f3e95bc1762955de8a05 Mon Sep 17 00:00:00 2001 From: Hunter Praska Date: Sun, 18 Jun 2017 13:07:09 -0500 Subject: Rename option::Missing to NoneError --- src/libcore/option.rs | 15 +++++++++------ src/libcore/tests/option.rs | 6 +++--- src/libcore/tests/result.rs | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src/libcore/tests') diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 0c3339590db..980ea551f08 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -1124,25 +1124,28 @@ impl> FromIterator> for Option { } } -/// The equivalent of `Option::None` for a `Result::Err`. +/// The error type that results from applying the try operator (`?`) to a `None` value. If you wish +/// to allow `x?` (where `x` is an `Option`) to be converted into your error type, you can +/// implement `impl From` for `YourErrorType`. In that case, `x?` within a function that +/// returns `Result<_, YourErrorType>` will translate a `None` value into an `Err` result. #[unstable(feature = "try_trait", issue = "42327")] #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -pub struct Missing; +pub struct NoneError; #[unstable(feature = "try_trait", issue = "42327")] impl ops::Try for Option { type Ok = T; - type Error = Missing; + type Error = NoneError; - fn into_result(self) -> Result { - self.ok_or(Missing) + fn into_result(self) -> Result { + self.ok_or(NoneError) } fn from_ok(v: T) -> Self { Some(v) } - fn from_error(_: Missing) -> Self { + fn from_error(_: NoneError) -> Self { None } } diff --git a/src/libcore/tests/option.rs b/src/libcore/tests/option.rs index e2907e1dd89..22109e28edd 100644 --- a/src/libcore/tests/option.rs +++ b/src/libcore/tests/option.rs @@ -285,15 +285,15 @@ fn test_try() { } assert_eq!(try_option_none(), None); - fn try_option_ok() -> Result { + fn try_option_ok() -> Result { let val = Some(1)?; Ok(val) } assert_eq!(try_option_ok(), Ok(1)); - fn try_option_err() -> Result { + fn try_option_err() -> Result { let val = None?; Ok(val) } - assert_eq!(try_option_err(), Err(Missing)); + assert_eq!(try_option_err(), Err(NoneError)); } diff --git a/src/libcore/tests/result.rs b/src/libcore/tests/result.rs index 37264980dc7..ce41bde8342 100644 --- a/src/libcore/tests/result.rs +++ b/src/libcore/tests/result.rs @@ -214,7 +214,7 @@ fn test_try() { assert_eq!(try_result_some(), Some(1)); fn try_result_none() -> Option { - let val = Err(Missing)?; + let val = Err(NoneError)?; Some(val) } assert_eq!(try_result_none(), None); -- cgit 1.4.1-3-g733a5