Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions library/alloc/src/vec/is_zero.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::mem::SizedTypeProperties;
use core::num::{NonZero, Saturating, Wrapping};

use crate::boxed::Box;
Expand All @@ -20,6 +21,8 @@ macro_rules! impl_is_zero {
};
}

impl_is_zero!((), |_: ()| true); // It is needed to impl for arrays and tuples of ().

impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
impl_is_zero!(i16, |x| x == 0);
impl_is_zero!(i32, |x| x == 0);
Expand All @@ -43,25 +46,46 @@ impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
// `IsZero` cannot be soundly implemented for pointers because of provenance
// (see #135338).

unsafe impl<T, const N: usize> IsZero for [T; N] {
#[inline]
default fn is_zero(&self) -> bool {
// If the array is of length zero,
// then it doesn't actually contain any `T`s,
// so `T::clone` doesn't need to be called,
// and we can "zero-initialize" all zero bytes of the array.
N == 0
}
}

unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
#[inline]
fn is_zero(&self) -> bool {
// Because this is generated as a runtime check, it's not obvious that
// it's worth doing if the array is really long. The threshold here
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
// fails to const-fold the check in `vec![[1; 32]; n]`
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
// Feel free to tweak if you have better evidence.

N <= 16 && self.iter().all(IsZero::is_zero)
if T::IS_ZST {
// If T is a ZST, then there is at most one possible value of `T`,
// so we only need to check one element for zeroness.
// We can't unconditionally return `true` here, since, e.g.
// `T = [NonTrivialCloneZst; 5]` is a ZST that implements `IsZero`
// due to the generic array impl, but `T::is_zero` returns `false`
// since the length is not 0.
self.get(0).is_none_or(IsZero::is_zero)
} else {
// Because this is generated as a runtime check, it's not obvious that
// it's worth doing if the array is really long. The threshold here
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
// fails to const-fold the check in `vec![[1; 32]; n]`
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
// Feel free to tweak if you have better evidence.

N <= 16 && self.iter().all(IsZero::is_zero)
}
}
}

// This is recursive macro.
macro_rules! impl_is_zero_tuples {
// Stopper
() => {
// No use for implementing for empty tuple because it is ZST.
// We already have an impl for () above.
};
($first_arg:ident $(,$rest:ident)*) => {
unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
Expand Down
Loading