Skip to content

Commit 94cb3c2

Browse files
committed
new RangeFrom overflow check
1 parent 5ffc73f commit 94cb3c2

File tree

6 files changed

+152
-7
lines changed

6 files changed

+152
-7
lines changed

library/core/src/range/iter.rs

+56-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
#[cfg(not(bootstrap))]
2+
use crate::intrinsics;
13
use crate::iter::{
24
FusedIterator, Step, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce, TrustedStep,
35
};
6+
use crate::mem;
47
use crate::num::NonZero;
58
use crate::range::{Range, RangeFrom, RangeInclusive, legacy};
69

@@ -293,12 +296,27 @@ range_incl_exact_iter_impl! {
293296
/// By-value [`RangeFrom`] iterator.
294297
#[unstable(feature = "new_range_api", issue = "125687")]
295298
#[derive(Debug, Clone)]
296-
pub struct IterRangeFrom<A>(legacy::RangeFrom<A>);
299+
pub struct IterRangeFrom<A> {
300+
start: A,
301+
#[allow(dead_code)]
302+
/// Whether the first element of the iterator has yielded.
303+
/// Only used when overflow checks are enabled.
304+
first: bool,
305+
}
297306

298-
impl<A> IterRangeFrom<A> {
307+
impl<A: Step> IterRangeFrom<A> {
299308
/// Returns the remainder of the range being iterated over.
309+
#[inline]
310+
#[rustc_inherit_overflow_checks]
300311
pub fn remainder(self) -> RangeFrom<A> {
301-
RangeFrom { start: self.0.start }
312+
#[cfg(not(bootstrap))]
313+
if intrinsics::overflow_checks() {
314+
if !self.first {
315+
return RangeFrom { start: Step::forward(self.start, 1) };
316+
}
317+
}
318+
319+
RangeFrom { start: self.start }
302320
}
303321
}
304322

@@ -307,18 +325,49 @@ impl<A: Step> Iterator for IterRangeFrom<A> {
307325
type Item = A;
308326

309327
#[inline]
328+
#[rustc_inherit_overflow_checks]
310329
fn next(&mut self) -> Option<A> {
311-
self.0.next()
330+
#[cfg(not(bootstrap))]
331+
if intrinsics::overflow_checks() {
332+
if self.first {
333+
self.first = false;
334+
return Some(self.start.clone());
335+
}
336+
337+
self.start = Step::forward(self.start.clone(), 1);
338+
return Some(self.start.clone());
339+
}
340+
341+
let n = Step::forward(self.start.clone(), 1);
342+
Some(mem::replace(&mut self.start, n))
312343
}
313344

314345
#[inline]
315346
fn size_hint(&self) -> (usize, Option<usize>) {
316-
self.0.size_hint()
347+
(usize::MAX, None)
317348
}
318349

319350
#[inline]
351+
#[rustc_inherit_overflow_checks]
320352
fn nth(&mut self, n: usize) -> Option<A> {
321-
self.0.nth(n)
353+
#[cfg(not(bootstrap))]
354+
if intrinsics::overflow_checks() {
355+
if self.first {
356+
self.first = false;
357+
358+
let plus_n = Step::forward(self.start.clone(), n);
359+
self.start = plus_n.clone();
360+
return Some(plus_n);
361+
}
362+
363+
let plus_n = Step::forward(self.start.clone(), n);
364+
self.start = Step::forward(plus_n.clone(), 1);
365+
return Some(self.start.clone());
366+
}
367+
368+
let plus_n = Step::forward(self.start.clone(), n);
369+
self.start = Step::forward(plus_n.clone(), 1);
370+
Some(plus_n)
322371
}
323372
}
324373

@@ -334,6 +383,6 @@ impl<A: Step> IntoIterator for RangeFrom<A> {
334383
type IntoIter = IterRangeFrom<A>;
335384

336385
fn into_iter(self) -> Self::IntoIter {
337-
IterRangeFrom(self.into())
386+
IterRangeFrom { start: self.start, first: true }
338387
}
339388
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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+
//@ [DEBUG] compile-flags:
10+
//@ [NOCHECKS] compile-flags: -Coverflow-checks=no
11+
//@ compile-flags: -O -Cdebug-assertions=yes
12+
13+
#![crate_type = "lib"]
14+
15+
#![feature(new_range_api)]
16+
use std::range::{RangeFrom, IterRangeFrom};
17+
18+
// CHECK-LABEL: @iterrangefrom_remainder(
19+
#[no_mangle]
20+
pub unsafe fn iterrangefrom_remainder(x: IterRangeFrom<i32>) -> RangeFrom<i32> {
21+
// DEBUG: i32 noundef %x.0
22+
// NOCHECKS: i32 noundef returned %x.0
23+
// CHECK: i1 noundef zeroext %x.1
24+
// DEBUG: br i1
25+
// DEBUG: call core::panicking::panic_const::panic_const_add_overflow
26+
// DEBUG: unreachable
27+
// NOCHECKS: ret i32 %x.0
28+
x.remainder()
29+
}

tests/ui/iterators/iterrangefrom.rs

+23
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+
}
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+
}
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+
}
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)