Change:
I tried this code: I expected to see this happen: No UB. Instead, this happened: UB, confirmed by Miri. is called, which does , or . The implementation of is correct here, and should be safe to call. The implementation that does a wrapping cast from to before doing the unchecked math is the incorrect code here. Signed integers should switch to just using wrapping or unsigned math, whichever gets better codegen out of LLVM. We do have and methods now, but no . <!-- If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. --:
Note that this is reachable on stable since the default impl of uses and not all adapters forward both.
did you find this with Miri or just use Miri to confirm the bug? (I'm asking because I may want to add this to our "bugs found by Miri" list :)
Only confirmed/diagnosed with Miri, spotted manually when looking at the code.

Discussion:
} } <del> // These are still macro-generated because the integer literals resolve to different types. macro_rules! step_identical_methods { </del> <ins> // Separate impls for signed ranges because the distance within a signed range can be larger // than the signed::MAX value. Therefore `as` casting to the signed type would be incorrect. macro_rules! step_signed_methods { ($unsigned: ty) => { #[inline] unsafe fn forward_unchecked(start: Self, n: usize) -> Self { // SAFETY: the caller has to guarantee that `start + n` doesn't overflow. unsafe { start.checked_add_unsigned(n as $unsigned).unwrap_unchecked() } } #[inline] unsafe fn backward_unchecked(start: Self, n: usize) -> Self { // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. unsafe { start.checked_sub_unsigned(n as $unsigned).unwrap_unchecked() } } }; } macro_rules! step_unsigned_methods { </ins> () => { #[inline] unsafe fn forward_unchecked(start: Self, n: usize) -> Self {

// SAFETY: the caller has to guarantee that `start - n` doesn't overflow. unsafe { start.unchecked_sub(n as Self) } } <ins> }; } // These are still macro-generated because the integer literals resolve to different types. macro_rules! step_identical_methods { () => { </ins> #[inline] #[allow(arithmetic_overflow)] #[rustc_inherit_overflow_checks]

#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_narrower { step_identical_methods!(); <ins> step_unsigned_methods!(); </ins> #[inline] fn steps_between(start: &Self, end: &Self) -> Option<usize> {

#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_narrower { step_identical_methods!(); <ins> step_signed_methods!($u_narrower); </ins> #[inline] fn steps_between(start: &Self, end: &Self) -> Option<usize> {

#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_wider { step_identical_methods!(); <ins> step_unsigned_methods!(); </ins> #[inline] fn steps_between(start: &Self, end: &Self) -> Option<usize> {

#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_wider { step_identical_methods!(); <ins> step_signed_methods!($u_wider); </ins> #[inline] fn steps_between(start: &Self, end: &Self) -> Option<usize> {

assert_eq!(Ok(()), r.advance_back_by(usize::MAX)); assert_eq!((r.start, r.end), (0u128 + usize::MAX as u128, u128::MAX - usize::MAX as u128)); <ins> // issue 122420, Step::forward_unchecked was unsound for signed integers let mut r = -128i8..127; assert_eq!(Ok(()), r.advance_by(200)); assert_eq!(r.next(), Some(72)); </ins> } #[test]

<del> The loop took around 7s </del> <ins> The loop took around 12s </ins>