Skip to content

Commit dbe801d

Browse files
authored
Rollup merge of rust-lang#127534 - jalil-salame:nonzero-uint-step, r=programmerjake,tgross35
feat(core): impl Step for NonZero<u*> Implement Step for NonZero unsigned integers as discussed in [libs-team#130][1]. [1]: rust-lang/libs-team#130 `step_nonzero_impls` was adapted from `step_integer_impls` and the tests were adapted from the step tests. It seems to compile and pass the tests c: I would like to test the edge cases, specially around overflows. To ensure safe code cannot generate a 0 `NonZero`, but I don't know how to go about it. I would love some feedback on that (and the PR overall). The impls are tagges with `#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]`. I assumed this was appropriate, but I am not sure.
2 parents 23acb17 + d6b2806 commit dbe801d

2 files changed

Lines changed: 275 additions & 10 deletions

File tree

library/core/src/iter/range.rs

Lines changed: 136 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ macro_rules! unsafe_impl_trusted_step {
1515
)*};
1616
}
1717
unsafe_impl_trusted_step![AsciiChar char i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize Ipv4Addr Ipv6Addr];
18+
unsafe_impl_trusted_step![NonZero<u8> NonZero<u16> NonZero<u32> NonZero<u64> NonZero<u128> NonZero<usize>];
1819

1920
/// Objects that have a notion of *successor* and *predecessor* operations.
2021
///
@@ -255,10 +256,8 @@ macro_rules! step_identical_methods {
255256

256257
macro_rules! step_integer_impls {
257258
{
258-
narrower than or same width as usize:
259-
$( [ $u_narrower:ident $i_narrower:ident ] ),+;
260-
wider than usize:
261-
$( [ $u_wider:ident $i_wider:ident ] ),+;
259+
[ $( [ $u_narrower:ident $i_narrower:ident ] ),+ ] <= usize <
260+
[ $( [ $u_wider:ident $i_wider:ident ] ),+ ]
262261
} => {
263262
$(
264263
#[allow(unreachable_patterns)]
@@ -437,20 +436,138 @@ macro_rules! step_integer_impls {
437436

438437
#[cfg(target_pointer_width = "64")]
439438
step_integer_impls! {
440-
narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize];
441-
wider than usize: [u128 i128];
439+
[ [u8 i8], [u16 i16], [u32 i32], [u64 i64], [usize isize] ] <= usize < [ [u128 i128] ]
442440
}
443441

444442
#[cfg(target_pointer_width = "32")]
445443
step_integer_impls! {
446-
narrower than or same width as usize: [u8 i8], [u16 i16], [u32 i32], [usize isize];
447-
wider than usize: [u64 i64], [u128 i128];
444+
[ [u8 i8], [u16 i16], [u32 i32], [usize isize] ] <= usize < [ [u64 i64], [u128 i128] ]
448445
}
449446

450447
#[cfg(target_pointer_width = "16")]
451448
step_integer_impls! {
452-
narrower than or same width as usize: [u8 i8], [u16 i16], [usize isize];
453-
wider than usize: [u32 i32], [u64 i64], [u128 i128];
449+
[ [u8 i8], [u16 i16], [usize isize] ] <= usize < [ [u32 i32], [u64 i64], [u128 i128] ]
450+
}
451+
452+
// These are still macro-generated because the integer literals resolve to different types.
453+
macro_rules! step_nonzero_identical_methods {
454+
($int:ident) => {
455+
#[inline]
456+
unsafe fn forward_unchecked(start: Self, n: usize) -> Self {
457+
// SAFETY: the caller has to guarantee that `start + n` doesn't overflow.
458+
unsafe { Self::new_unchecked(start.get().unchecked_add(n as $int)) }
459+
}
460+
461+
#[inline]
462+
unsafe fn backward_unchecked(start: Self, n: usize) -> Self {
463+
// SAFETY: the caller has to guarantee that `start - n` doesn't overflow or hit zero.
464+
unsafe { Self::new_unchecked(start.get().unchecked_sub(n as $int)) }
465+
}
466+
467+
#[inline]
468+
#[allow(arithmetic_overflow)]
469+
#[rustc_inherit_overflow_checks]
470+
fn forward(start: Self, n: usize) -> Self {
471+
// In debug builds, trigger a panic on overflow.
472+
// This should optimize completely out in release builds.
473+
if Self::forward_checked(start, n).is_none() {
474+
let _ = $int::MAX + 1;
475+
}
476+
// Do saturating math (wrapping math causes UB if it wraps to Zero)
477+
start.saturating_add(n as $int)
478+
}
479+
480+
#[inline]
481+
#[allow(arithmetic_overflow)]
482+
#[rustc_inherit_overflow_checks]
483+
fn backward(start: Self, n: usize) -> Self {
484+
// In debug builds, trigger a panic on overflow.
485+
// This should optimize completely out in release builds.
486+
if Self::backward_checked(start, n).is_none() {
487+
let _ = $int::MIN - 1;
488+
}
489+
// Do saturating math (wrapping math causes UB if it wraps to Zero)
490+
Self::new(start.get().saturating_sub(n as $int)).unwrap_or(Self::MIN)
491+
}
492+
493+
#[inline]
494+
fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
495+
if *start <= *end {
496+
#[allow(irrefutable_let_patterns, reason = "happens on usize or narrower")]
497+
if let Ok(steps) = usize::try_from(end.get() - start.get()) {
498+
(steps, Some(steps))
499+
} else {
500+
(usize::MAX, None)
501+
}
502+
} else {
503+
(0, None)
504+
}
505+
}
506+
};
507+
}
508+
509+
macro_rules! step_nonzero_impls {
510+
{
511+
[$( $narrower:ident ),+] <= usize < [$( $wider:ident ),+]
512+
} => {
513+
$(
514+
#[allow(unreachable_patterns)]
515+
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
516+
impl Step for NonZero<$narrower> {
517+
step_nonzero_identical_methods!($narrower);
518+
519+
#[inline]
520+
fn forward_checked(start: Self, n: usize) -> Option<Self> {
521+
match $narrower::try_from(n) {
522+
Ok(n) => start.checked_add(n),
523+
Err(_) => None, // if n is out of range, `unsigned_start + n` is too
524+
}
525+
}
526+
527+
#[inline]
528+
fn backward_checked(start: Self, n: usize) -> Option<Self> {
529+
match $narrower::try_from(n) {
530+
// *_sub() is not implemented on NonZero<T>
531+
Ok(n) => start.get().checked_sub(n).and_then(Self::new),
532+
Err(_) => None, // if n is out of range, `unsigned_start - n` is too
533+
}
534+
}
535+
}
536+
)+
537+
538+
$(
539+
#[allow(unreachable_patterns)]
540+
#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")]
541+
impl Step for NonZero<$wider> {
542+
step_nonzero_identical_methods!($wider);
543+
544+
#[inline]
545+
fn forward_checked(start: Self, n: usize) -> Option<Self> {
546+
start.checked_add(n as $wider)
547+
}
548+
549+
#[inline]
550+
fn backward_checked(start: Self, n: usize) -> Option<Self> {
551+
start.get().checked_sub(n as $wider).and_then(Self::new)
552+
}
553+
}
554+
)+
555+
};
556+
}
557+
558+
#[cfg(target_pointer_width = "64")]
559+
step_nonzero_impls! {
560+
[u8, u16, u32, u64, usize] <= usize < [u128]
561+
}
562+
563+
#[cfg(target_pointer_width = "32")]
564+
step_nonzero_impls! {
565+
[u8, u16, u32, usize] <= usize < [u64, u128]
566+
}
567+
568+
#[cfg(target_pointer_width = "16")]
569+
step_nonzero_impls! {
570+
[u8, u16, usize] <= usize < [u32, u64, u128]
454571
}
455572

456573
#[unstable(feature = "step_trait", issue = "42168")]
@@ -944,6 +1061,7 @@ impl<A: Step> Iterator for ops::Range<A> {
9441061
range_exact_iter_impl! {
9451062
usize u8 u16
9461063
isize i8 i16
1064+
NonZero<usize> NonZero<u8> NonZero<u16>
9471065

9481066
// These are incorrect per the reasoning above,
9491067
// but removing them would be a breaking change as they were stabilized in Rust 1.0.0.
@@ -956,22 +1074,30 @@ range_exact_iter_impl! {
9561074
unsafe_range_trusted_random_access_impl! {
9571075
usize u8 u16
9581076
isize i8 i16
1077+
NonZero<usize> NonZero<u8> NonZero<u16>
9591078
}
9601079

9611080
#[cfg(target_pointer_width = "32")]
9621081
unsafe_range_trusted_random_access_impl! {
9631082
u32 i32
1083+
NonZero<u32>
9641084
}
9651085

9661086
#[cfg(target_pointer_width = "64")]
9671087
unsafe_range_trusted_random_access_impl! {
9681088
u32 i32
9691089
u64 i64
1090+
NonZero<u32>
1091+
NonZero<u64>
9701092
}
9711093

9721094
range_incl_exact_iter_impl! {
9731095
u8
9741096
i8
1097+
NonZero<u8>
1098+
// Since RangeInclusive<NonZero<uN>> can only be 1..=uN::MAX the length of this range is always
1099+
// <= uN::MAX, so they are always valid ExactSizeIterator unlike the ranges that include zero.
1100+
NonZero<u16> NonZero<usize>
9751101

9761102
// These are incorrect per the reasoning above,
9771103
// but removing them would be a breaking change as they were stabilized in Rust 1.26.0.

library/coretests/tests/iter/range.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,3 +502,142 @@ fn test_double_ended_range() {
502502
panic!("unreachable");
503503
}
504504
}
505+
506+
macro_rules! nz {
507+
(NonZero<$type:ident>($val:literal)) => {
508+
::core::num::NonZero::<$type>::new($val).unwrap()
509+
};
510+
(NonZero<$type:ident>::MAX) => {
511+
::core::num::NonZero::<$type>::new($type::MAX).unwrap()
512+
};
513+
}
514+
515+
macro_rules! nonzero_array {
516+
(NonZero<$type:ident>[$($val:literal),*]) => {
517+
[$(nz!(NonZero<$type>($val))),*]
518+
}
519+
}
520+
521+
macro_rules! nonzero_range {
522+
(NonZero<$type:ident>($($left:literal)?..$($right:literal)?)) => {
523+
nz!(NonZero<$type>($($left)?))..nz!(NonZero<$type>($($right)?))
524+
};
525+
(NonZero<$type:ident>($($left:literal)?..MAX)) => {
526+
nz!(NonZero<$type>($($left)?))..nz!(NonZero<$type>::MAX)
527+
};
528+
(NonZero<$type:ident>($($left:literal)?..=$right:literal)) => {
529+
nz!(NonZero<$type>($($left)?))..=nz!(NonZero<$type>($right))
530+
};
531+
(NonZero<$type:ident>($($left:literal)?..=MAX)) => {
532+
nz!(NonZero<$type>($($left)?))..=nz!(NonZero<$type>::MAX)
533+
};
534+
}
535+
536+
#[test]
537+
fn test_nonzero_range() {
538+
assert_eq!(
539+
nonzero_range!(NonZero<usize>(1..=21)).step_by(5).collect::<Vec<_>>(),
540+
nonzero_array!(NonZero<usize>[1, 6, 11, 16, 21])
541+
);
542+
assert_eq!(
543+
nonzero_range!(NonZero<usize>(1..=20)).step_by(5).collect::<Vec<_>>(),
544+
nonzero_array!(NonZero<usize>[1, 6, 11, 16])
545+
);
546+
assert_eq!(
547+
nonzero_range!(NonZero<usize>(1..20)).step_by(5).collect::<Vec<_>>(),
548+
nonzero_array!(NonZero<usize>[1, 6, 11, 16])
549+
);
550+
assert_eq!(
551+
nonzero_range!(NonZero<usize>(1..21)).rev().step_by(5).collect::<Vec<_>>(),
552+
nonzero_array!(NonZero<usize>[20, 15, 10, 5])
553+
);
554+
assert_eq!(
555+
nonzero_range!(NonZero<usize>(1..21)).rev().step_by(6).collect::<Vec<_>>(),
556+
nonzero_array!(NonZero<usize>[20, 14, 8, 2])
557+
);
558+
assert_eq!(
559+
nonzero_range!(NonZero<u8>(200..255)).step_by(50).collect::<Vec<_>>(),
560+
nonzero_array!(NonZero<u8>[200, 250])
561+
);
562+
assert_eq!(
563+
nonzero_range!(NonZero<usize>(200..5)).step_by(1).collect::<Vec<_>>(),
564+
nonzero_array!(NonZero<usize>[])
565+
);
566+
assert_eq!(
567+
nonzero_range!(NonZero<usize>(200..200)).step_by(1).collect::<Vec<_>>(),
568+
nonzero_array!(NonZero<usize>[])
569+
);
570+
571+
assert_eq!(nonzero_range!(NonZero<u32>(10..20)).step_by(1).size_hint(), (10, Some(10)));
572+
assert_eq!(nonzero_range!(NonZero<u32>(10..20)).step_by(5).size_hint(), (2, Some(2)));
573+
assert_eq!(nonzero_range!(NonZero<u32>(1..21)).rev().step_by(5).size_hint(), (4, Some(4)));
574+
assert_eq!(nonzero_range!(NonZero<u32>(1..21)).rev().step_by(6).size_hint(), (4, Some(4)));
575+
assert_eq!(nonzero_range!(NonZero<u32>(20..1)).step_by(1).size_hint(), (0, Some(0)));
576+
assert_eq!(nonzero_range!(NonZero<u32>(20..20)).step_by(1).size_hint(), (0, Some(0)));
577+
578+
// ExactSizeIterator
579+
assert_eq!(nonzero_range!(NonZero<u8>(1..=MAX)).step_by(1).len(), usize::from(u8::MAX));
580+
assert_eq!(nonzero_range!(NonZero<u16>(1..=MAX)).step_by(1).len(), usize::from(u16::MAX));
581+
assert_eq!(nonzero_range!(NonZero<usize>(1..=MAX)).step_by(1).len(), usize::MAX);
582+
583+
// Limits (next)
584+
let mut range = nonzero_range!(NonZero<u8>(254..=MAX));
585+
assert_eq!(range.next(), Some(nz!(NonZero<u8>(254))));
586+
assert_eq!(range.next(), Some(nz!(NonZero<u8>(255))));
587+
assert_eq!(range.next(), None);
588+
589+
let mut range = nonzero_range!(NonZero<u16>(65534..=MAX));
590+
assert_eq!(range.next(), Some(nz!(NonZero<u16>(65534))));
591+
assert_eq!(range.next(), Some(nz!(NonZero<u16>(65535))));
592+
assert_eq!(range.next(), None);
593+
594+
// Limits (size_hint, exclusive range)
595+
assert_eq!(
596+
nonzero_range!(NonZero<u8>(1..MAX)).step_by(1).size_hint(),
597+
(u8::MAX as usize - 1, Some(u8::MAX as usize - 1))
598+
);
599+
assert_eq!(
600+
nonzero_range!(NonZero<u16>(1..MAX)).step_by(1).size_hint(),
601+
(u16::MAX as usize - 1, Some(u16::MAX as usize - 1))
602+
);
603+
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
604+
assert_eq!(
605+
nonzero_range!(NonZero<u32>(1..MAX)).step_by(1).size_hint(),
606+
(u32::MAX as usize - 1, Some(u32::MAX as usize - 1))
607+
);
608+
#[cfg(target_pointer_width = "64")]
609+
assert_eq!(
610+
nonzero_range!(NonZero<u64>(1..MAX)).step_by(1).size_hint(),
611+
(u64::MAX as usize - 1, Some(u64::MAX as usize - 1))
612+
);
613+
assert_eq!(nonzero_range!(NonZero<u128>(1..MAX)).step_by(1).size_hint(), (usize::MAX, None));
614+
assert_eq!(
615+
nonzero_range!(NonZero<usize>(1..MAX)).step_by(1).size_hint(),
616+
(usize::MAX - 1, Some(usize::MAX - 1))
617+
);
618+
619+
// Limits (size_hint, inclusive range)
620+
assert_eq!(
621+
nonzero_range!(NonZero<u8>(1..=MAX)).step_by(1).size_hint(),
622+
(u8::MAX as usize, Some(u8::MAX as usize))
623+
);
624+
assert_eq!(
625+
nonzero_range!(NonZero<u16>(1..=MAX)).step_by(1).size_hint(),
626+
(u16::MAX as usize, Some(u16::MAX as usize))
627+
);
628+
#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))]
629+
assert_eq!(
630+
nonzero_range!(NonZero<u32>(1..=MAX)).step_by(1).size_hint(),
631+
(u32::MAX as usize, Some(u32::MAX as usize))
632+
);
633+
#[cfg(target_pointer_width = "64")]
634+
assert_eq!(
635+
nonzero_range!(NonZero<u64>(1..=MAX)).step_by(1).size_hint(),
636+
(u64::MAX as usize, Some(u64::MAX as usize))
637+
);
638+
assert_eq!(nonzero_range!(NonZero<u128>(1..=MAX)).step_by(1).size_hint(), (usize::MAX, None));
639+
assert_eq!(
640+
nonzero_range!(NonZero<usize>(1..=MAX)).step_by(1).size_hint(),
641+
(usize::MAX, Some(usize::MAX))
642+
);
643+
}

0 commit comments

Comments
 (0)