diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 2ecf9ce5b81b3..055ead117ea24 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -183,8 +183,25 @@ pub trait Step: Clone + PartialOrd + Sized { } } -// These are still macro-generated because the integer literals resolve to different types. -macro_rules! step_identical_methods { +// 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 { () => { #[inline] unsafe fn forward_unchecked(start: Self, n: usize) -> Self { @@ -197,7 +214,12 @@ macro_rules! step_identical_methods { // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. unsafe { start.unchecked_sub(n as Self) } } + }; +} +// These are still macro-generated because the integer literals resolve to different types. +macro_rules! step_identical_methods { + () => { #[inline] #[allow(arithmetic_overflow)] #[rustc_inherit_overflow_checks] @@ -238,6 +260,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_narrower { step_identical_methods!(); + step_unsigned_methods!(); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -270,6 +293,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_narrower { step_identical_methods!(); + step_signed_methods!($u_narrower); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -334,6 +358,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_wider { step_identical_methods!(); + step_unsigned_methods!(); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -359,6 +384,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_wider { step_identical_methods!(); + step_signed_methods!($u_wider); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index 9af07119a89a2..e31db0732e094 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -325,6 +325,11 @@ fn test_range_advance_by() { 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)); + + // 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)); } #[test] diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout b/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout index c68b40b744bf2..dce51a7fdbe0f 100644 --- a/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout +++ b/src/tools/miri/tests/pass/shims/time-with-isolation2.stdout @@ -1 +1 @@ -The loop took around 7s +The loop took around 12s