Rollup merge of #148703 - pitaj:rangefrom-overflow_checks, r=Mark-Simulacrum

Use `overflow_checks` intrinsic so `IterRangeFrom` yields MAX before panicking in debug

Based on rust-lang/rust#128666. For your convenience, here is the [diff from that PR](https://github.com/pitaj/rust/compare/intrinsic-overflow_checks...pitaj:rust:rangefrom-overflow_checks).

When `overflow_checks` are enabled, the following code will output as shown
```rust
for n in std::range::RangeFrom::from(253_u8..) {
    println!("{n}");
}
// 253
// 254
// 255
// panic
```

Which is a change from the current behavior, where it will panic after printing 254.

This behavior was [requested by the libs team](https://github.com/rust-lang/rust/issues/125687#issuecomment-2151118208)

r? `@Mark-Simulacrum`
This commit is contained in:
Matthias Krüger 2025-11-16 20:40:22 +01:00 committed by GitHub
commit 79d765ed38
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 177 additions and 9 deletions

View file

@ -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<A: Step> IterRangeInclusive<A> {
}
}
#[unstable(feature = "trusted_random_access", issue = "none")]
#[unstable(feature = "new_range_api", issue = "125687")]
impl<A: Step> Iterator for IterRangeInclusive<A> {
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<A>(legacy::RangeFrom<A>);
pub struct IterRangeFrom<A> {
start: A,
/// Whether the first element of the iterator has yielded.
/// Only used when overflow checks are enabled.
first: bool,
}
impl<A> IterRangeFrom<A> {
impl<A: Step> IterRangeFrom<A> {
/// Returns the remainder of the range being iterated over.
#[inline]
#[rustc_inherit_overflow_checks]
pub fn remainder(self) -> RangeFrom<A> {
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<A: Step> Iterator for IterRangeFrom<A> {
type Item = A;
#[inline]
#[rustc_inherit_overflow_checks]
fn next(&mut self) -> Option<A> {
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<usize>) {
self.0.size_hint()
(usize::MAX, None)
}
#[inline]
#[rustc_inherit_overflow_checks]
fn nth(&mut self, n: usize) -> Option<A> {
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<A: Step> IntoIterator for RangeFrom<A> {
type IntoIter = IterRangeFrom<A>;
fn into_iter(self) -> Self::IntoIter {
IterRangeFrom(self.into())
IterRangeFrom { start: self.start, first: true }
}
}

View file

@ -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<i32>) -> RangeFrom<i32> {
// 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()
}

View file

@ -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());
}

View file

@ -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());
}

View file

@ -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);
}

View file

@ -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());
}