Skip to content

Commit fa8b3be

Browse files
committed
new RangeFrom overflow check
1 parent a47d6eb commit fa8b3be

File tree

6 files changed

+144
-7
lines changed

6 files changed

+144
-7
lines changed

library/core/src/range/iter.rs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::iter::{
33
};
44
use crate::num::NonZero;
55
use crate::range::{Range, RangeFrom, RangeInclusive, legacy};
6+
use crate::{intrinsics, mem};
67

78
/// By-value [`Range`] iterator.
89
#[unstable(feature = "new_range_api", issue = "125687")]
@@ -293,12 +294,25 @@ range_incl_exact_iter_impl! {
293294
/// By-value [`RangeFrom`] iterator.
294295
#[unstable(feature = "new_range_api", issue = "125687")]
295296
#[derive(Debug, Clone)]
296-
pub struct IterRangeFrom<A>(legacy::RangeFrom<A>);
297+
pub struct IterRangeFrom<A> {
298+
start: A,
299+
/// Whether the first element of the iterator has yielded.
300+
/// Only used when overflow checks are enabled.
301+
first: bool,
302+
}
297303

298-
impl<A> IterRangeFrom<A> {
304+
impl<A: Step> IterRangeFrom<A> {
299305
/// Returns the remainder of the range being iterated over.
306+
#[inline]
307+
#[rustc_inherit_overflow_checks]
300308
pub fn remainder(self) -> RangeFrom<A> {
301-
RangeFrom { start: self.0.start }
309+
if intrinsics::overflow_checks() {
310+
if !self.first {
311+
return RangeFrom { start: Step::forward(self.start, 1) };
312+
}
313+
}
314+
315+
RangeFrom { start: self.start }
302316
}
303317
}
304318

@@ -307,18 +321,47 @@ impl<A: Step> Iterator for IterRangeFrom<A> {
307321
type Item = A;
308322

309323
#[inline]
324+
#[rustc_inherit_overflow_checks]
310325
fn next(&mut self) -> Option<A> {
311-
self.0.next()
326+
if intrinsics::overflow_checks() {
327+
if self.first {
328+
self.first = false;
329+
return Some(self.start.clone());
330+
}
331+
332+
self.start = Step::forward(self.start.clone(), 1);
333+
return Some(self.start.clone());
334+
}
335+
336+
let n = Step::forward(self.start.clone(), 1);
337+
Some(mem::replace(&mut self.start, n))
312338
}
313339

314340
#[inline]
315341
fn size_hint(&self) -> (usize, Option<usize>) {
316-
self.0.size_hint()
342+
(usize::MAX, None)
317343
}
318344

319345
#[inline]
346+
#[rustc_inherit_overflow_checks]
320347
fn nth(&mut self, n: usize) -> Option<A> {
321-
self.0.nth(n)
348+
if intrinsics::overflow_checks() {
349+
if self.first {
350+
self.first = false;
351+
352+
let plus_n = Step::forward(self.start.clone(), n);
353+
self.start = plus_n.clone();
354+
return Some(plus_n);
355+
}
356+
357+
let plus_n = Step::forward(self.start.clone(), n);
358+
self.start = Step::forward(plus_n.clone(), 1);
359+
return Some(self.start.clone());
360+
}
361+
362+
let plus_n = Step::forward(self.start.clone(), n);
363+
self.start = Step::forward(plus_n.clone(), 1);
364+
Some(plus_n)
322365
}
323366
}
324367

@@ -334,6 +377,6 @@ impl<A: Step> IntoIterator for RangeFrom<A> {
334377
type IntoIter = IterRangeFrom<A>;
335378

336379
fn into_iter(self) -> Self::IntoIter {
337-
IterRangeFrom(self.into())
380+
IterRangeFrom { start: self.start, first: true }
338381
}
339382
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// With -Coverflow-checks=yes (enabled by default by -Cdebug-assertions=yes) we will produce a
2+
// runtime check that panics after yielding the maximum value of the range bound type. That is
3+
// tested for by tests/ui/iterators/rangefrom-overflow-overflow-checks.rs
4+
//
5+
// This test ensures that such a runtime check is *not* emitted when debug-assertions are
6+
// enabled, but overflow-checks are explicitly disabled.
7+
8+
//@ revisions: DEBUG NOCHECKS
9+
//@ compile-flags: -O -Cdebug-assertions=yes
10+
//@ [NOCHECKS] compile-flags: -Coverflow-checks=no
11+
12+
#![crate_type = "lib"]
13+
#![feature(new_range_api)]
14+
use std::range::{IterRangeFrom, RangeFrom};
15+
16+
// CHECK-LABEL: @iterrangefrom_remainder(
17+
#[no_mangle]
18+
pub unsafe fn iterrangefrom_remainder(x: IterRangeFrom<i32>) -> RangeFrom<i32> {
19+
// DEBUG: i32 noundef %x.0
20+
// NOCHECKS: i32 noundef returned %x.0
21+
// CHECK: i1 noundef zeroext %x.1
22+
// DEBUG: br i1
23+
// DEBUG: call core::panicking::panic_const::panic_const_add_overflow
24+
// DEBUG: unreachable
25+
// NOCHECKS: ret i32 %x.0
26+
x.remainder()
27+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//@ run-pass
2+
//@ compile-flags: -C overflow-checks=yes
3+
4+
#![feature(new_range_api)]
5+
6+
use std::{iter, range};
7+
8+
fn main() {
9+
for (a, b) in iter::zip(0_u32..256, range::RangeFrom::from(0_u8..)) {
10+
assert_eq!(a, u32::from(b));
11+
}
12+
13+
let mut a = range::RangeFrom::from(0_u8..).into_iter();
14+
let mut b = 0_u8..;
15+
assert_eq!(a.next(), b.next());
16+
assert_eq!(a.nth(5), b.nth(5));
17+
assert_eq!(a.nth(0), b.next());
18+
19+
let mut a = range::RangeFrom::from(0_u8..).into_iter();
20+
let mut b = 0_u8..;
21+
assert_eq!(a.nth(5), b.nth(5));
22+
assert_eq!(a.nth(0), b.next());
23+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ run-pass
2+
//@ needs-unwind
3+
//@ compile-flags: -O -C debug_assertions=yes
4+
5+
#![feature(new_range_api)]
6+
7+
use std::panic;
8+
9+
fn main() {
10+
let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter();
11+
assert_eq!(it.next().unwrap(), 255);
12+
13+
let r = panic::catch_unwind(|| {
14+
let _ = it.remainder();
15+
});
16+
assert!(r.is_err());
17+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//@ run-pass
2+
//@ compile-flags: -O -C debug_assertions=no
3+
4+
#![feature(new_range_api)]
5+
6+
fn main() {
7+
let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter();
8+
assert_eq!(it.next().unwrap(), 255);
9+
assert_eq!(it.remainder().start, u8::MIN);
10+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//@ run-pass
2+
//@ needs-unwind
3+
//@ compile-flags: -O -C overflow-checks=yes
4+
5+
#![feature(new_range_api)]
6+
7+
use std::panic;
8+
9+
fn main() {
10+
let mut it = core::range::RangeFrom::from(u8::MAX..).into_iter();
11+
assert_eq!(it.next().unwrap(), 255);
12+
13+
let r = panic::catch_unwind(|| {
14+
let _ = it.remainder();
15+
});
16+
assert!(r.is_err());
17+
}

0 commit comments

Comments
 (0)