diff --git a/library/core/src/range/iter.rs b/library/core/src/range/iter.rs index 24efd4a204a5f..9a8824baefe4e 100644 --- a/library/core/src/range/iter.rs +++ b/library/core/src/range/iter.rs @@ -3,6 +3,7 @@ use crate::iter::{ }; use crate::num::NonZero; use crate::range::{Range, RangeFrom, RangeInclusive, legacy}; +use crate::{intrinsics, mem}; /// By-value [`Range`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] @@ -168,7 +169,7 @@ impl IterRangeInclusive { } } -#[unstable(feature = "trusted_random_access", issue = "none")] +#[unstable(feature = "new_range_api", issue = "125687")] impl Iterator for IterRangeInclusive { type Item = A; @@ -293,32 +294,74 @@ range_incl_exact_iter_impl! { /// By-value [`RangeFrom`] iterator. #[unstable(feature = "new_range_api", issue = "125687")] #[derive(Debug, Clone)] -pub struct IterRangeFrom(legacy::RangeFrom); +pub struct IterRangeFrom { + start: A, + /// Whether the first element of the iterator has yielded. + /// Only used when overflow checks are enabled. + first: bool, +} -impl IterRangeFrom { +impl IterRangeFrom { /// Returns the remainder of the range being iterated over. + #[inline] + #[rustc_inherit_overflow_checks] pub fn remainder(self) -> RangeFrom { - RangeFrom { start: self.0.start } + if intrinsics::overflow_checks() { + if !self.first { + return RangeFrom { start: Step::forward(self.start, 1) }; + } + } + + RangeFrom { start: self.start } } } -#[unstable(feature = "trusted_random_access", issue = "none")] +#[unstable(feature = "new_range_api", issue = "125687")] impl Iterator for IterRangeFrom { type Item = A; #[inline] + #[rustc_inherit_overflow_checks] fn next(&mut self) -> Option { - self.0.next() + if intrinsics::overflow_checks() { + if self.first { + self.first = false; + return Some(self.start.clone()); + } + + self.start = Step::forward(self.start.clone(), 1); + return Some(self.start.clone()); + } + + let n = Step::forward(self.start.clone(), 1); + Some(mem::replace(&mut self.start, n)) } #[inline] fn size_hint(&self) -> (usize, Option) { - self.0.size_hint() + (usize::MAX, None) } #[inline] + #[rustc_inherit_overflow_checks] fn nth(&mut self, n: usize) -> Option { - self.0.nth(n) + if intrinsics::overflow_checks() { + if self.first { + self.first = false; + + let plus_n = Step::forward(self.start.clone(), n); + self.start = plus_n.clone(); + return Some(plus_n); + } + + let plus_n = Step::forward(self.start.clone(), n); + self.start = Step::forward(plus_n.clone(), 1); + return Some(self.start.clone()); + } + + let plus_n = Step::forward(self.start.clone(), n); + self.start = Step::forward(plus_n.clone(), 1); + Some(plus_n) } } @@ -334,6 +377,6 @@ impl IntoIterator for RangeFrom { type IntoIter = IterRangeFrom; fn into_iter(self) -> Self::IntoIter { - IterRangeFrom(self.into()) + IterRangeFrom { start: self.start, first: true } } } diff --git a/tests/codegen-llvm/iterrangefrom-overflow-checks.rs b/tests/codegen-llvm/iterrangefrom-overflow-checks.rs new file mode 100644 index 0000000000000..88ff5a8508c8e --- /dev/null +++ b/tests/codegen-llvm/iterrangefrom-overflow-checks.rs @@ -0,0 +1,27 @@ +// With -Coverflow-checks=yes (enabled by default by -Cdebug-assertions=yes) we will produce a +// runtime check that panics after yielding the maximum value of the range bound type. That is +// tested for by tests/ui/iterators/rangefrom-overflow-overflow-checks.rs +// +// This test ensures that such a runtime check is *not* emitted when debug-assertions are +// enabled, but overflow-checks are explicitly disabled. + +//@ revisions: DEBUG NOCHECKS +//@ compile-flags: -O -Cdebug-assertions=yes +//@ [NOCHECKS] compile-flags: -Coverflow-checks=no + +#![crate_type = "lib"] +#![feature(new_range_api)] +use std::range::{IterRangeFrom, RangeFrom}; + +// CHECK-LABEL: @iterrangefrom_remainder( +#[no_mangle] +pub unsafe fn iterrangefrom_remainder(x: IterRangeFrom) -> RangeFrom { + // DEBUG: i32 noundef %x + // NOCHECKS: i32 noundef returned %x + // DEBUG: br i1 + // DEBUG: call core::panicking::panic_const::panic_const_add_overflow + // DEBUG: unreachable + // NOCHECKS-NOT: unreachable + // NOCHECKS: ret i32 %x + x.remainder() +} diff --git a/tests/ui/iterators/iterrangefrom.rs b/tests/ui/iterators/iterrangefrom.rs new file mode 100644 index 0000000000000..54d8f522a2c88 --- /dev/null +++ b/tests/ui/iterators/iterrangefrom.rs @@ -0,0 +1,23 @@ +//@ run-pass +//@ compile-flags: -C overflow-checks=yes + +#![feature(new_range_api)] + +use std::{iter, range}; + +fn main() { + for (a, b) in iter::zip(0_u32..256, range::RangeFrom::from(0_u8..)) { + assert_eq!(a, u32::from(b)); + } + + let mut a = range::RangeFrom::from(0_u8..).into_iter(); + let mut b = 0_u8..; + assert_eq!(a.next(), b.next()); + assert_eq!(a.nth(5), b.nth(5)); + assert_eq!(a.nth(0), b.next()); + + let mut a = range::RangeFrom::from(0_u8..).into_iter(); + let mut b = 0_u8..; + assert_eq!(a.nth(5), b.nth(5)); + assert_eq!(a.nth(0), b.next()); +} diff --git a/tests/ui/iterators/rangefrom-overflow-debug.rs b/tests/ui/iterators/rangefrom-overflow-debug.rs new file mode 100644 index 0000000000000..9a1bc6910a044 --- /dev/null +++ b/tests/ui/iterators/rangefrom-overflow-debug.rs @@ -0,0 +1,17 @@ +//@ run-pass +//@ needs-unwind +//@ compile-flags: -O -C debug_assertions=yes + +#![feature(new_range_api)] + +use std::panic; + +fn main() { + let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter(); + assert_eq!(it.next().unwrap(), 255); + + let r = panic::catch_unwind(|| { + let _ = it.remainder(); + }); + assert!(r.is_err()); +} diff --git a/tests/ui/iterators/rangefrom-overflow-ndebug.rs b/tests/ui/iterators/rangefrom-overflow-ndebug.rs new file mode 100644 index 0000000000000..4ce9b0636383c --- /dev/null +++ b/tests/ui/iterators/rangefrom-overflow-ndebug.rs @@ -0,0 +1,10 @@ +//@ run-pass +//@ compile-flags: -O -C debug_assertions=no + +#![feature(new_range_api)] + +fn main() { + let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter(); + assert_eq!(it.next().unwrap(), 255); + assert_eq!(it.remainder().start, u8::MIN); +} diff --git a/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs b/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs new file mode 100644 index 0000000000000..7e3b0fc308405 --- /dev/null +++ b/tests/ui/iterators/rangefrom-overflow-overflow-checks.rs @@ -0,0 +1,48 @@ +//@ run-pass +//@ needs-unwind +//@ compile-flags: -O -C overflow-checks=yes + +#![feature(new_range_api)] + +use std::panic; + +fn main() { + let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter(); + assert_eq!(it.next().unwrap(), 255); + + let r = panic::catch_unwind(move || { + let _ = it.remainder(); + }); + assert!(r.is_err()); + + let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter(); + assert_eq!(it.next().unwrap(), 255); + + let r = panic::catch_unwind(move || { + let _ = it.next(); + }); + assert!(r.is_err()); + + let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter(); + assert_eq!(it.next().unwrap(), 255); + + let r = panic::catch_unwind(move || { + let _ = it.nth(0); + }); + assert!(r.is_err()); + + let mut it = core::range::RangeFrom::from(u8::MAX-1..).into_iter(); + assert_eq!(it.nth(1).unwrap(), 255); + + let r = panic::catch_unwind(move || { + let _ = it.next(); + }); + assert!(r.is_err()); + + let mut it = core::range::RangeFrom::from(u8::MAX-1..).into_iter(); + + let r = panic::catch_unwind(move || { + let _ = it.nth(2); + }); + assert!(r.is_err()); +}