162306a36Sopenharmony_ci// SPDX-License-Identifier: Apache-2.0 OR MIT
262306a36Sopenharmony_ci
362306a36Sopenharmony_ciuse core::num::{Saturating, Wrapping};
462306a36Sopenharmony_ci
562306a36Sopenharmony_ciuse crate::boxed::Box;
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#[rustc_specialization_trait]
862306a36Sopenharmony_cipub(super) unsafe trait IsZero {
962306a36Sopenharmony_ci    /// Whether this value's representation is all zeros,
1062306a36Sopenharmony_ci    /// or can be represented with all zeroes.
1162306a36Sopenharmony_ci    fn is_zero(&self) -> bool;
1262306a36Sopenharmony_ci}
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cimacro_rules! impl_is_zero {
1562306a36Sopenharmony_ci    ($t:ty, $is_zero:expr) => {
1662306a36Sopenharmony_ci        unsafe impl IsZero for $t {
1762306a36Sopenharmony_ci            #[inline]
1862306a36Sopenharmony_ci            fn is_zero(&self) -> bool {
1962306a36Sopenharmony_ci                $is_zero(*self)
2062306a36Sopenharmony_ci            }
2162306a36Sopenharmony_ci        }
2262306a36Sopenharmony_ci    };
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ciimpl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
2662306a36Sopenharmony_ciimpl_is_zero!(i16, |x| x == 0);
2762306a36Sopenharmony_ciimpl_is_zero!(i32, |x| x == 0);
2862306a36Sopenharmony_ciimpl_is_zero!(i64, |x| x == 0);
2962306a36Sopenharmony_ciimpl_is_zero!(i128, |x| x == 0);
3062306a36Sopenharmony_ciimpl_is_zero!(isize, |x| x == 0);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciimpl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
3362306a36Sopenharmony_ciimpl_is_zero!(u16, |x| x == 0);
3462306a36Sopenharmony_ciimpl_is_zero!(u32, |x| x == 0);
3562306a36Sopenharmony_ciimpl_is_zero!(u64, |x| x == 0);
3662306a36Sopenharmony_ciimpl_is_zero!(u128, |x| x == 0);
3762306a36Sopenharmony_ciimpl_is_zero!(usize, |x| x == 0);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ciimpl_is_zero!(bool, |x| x == false);
4062306a36Sopenharmony_ciimpl_is_zero!(char, |x| x == '\0');
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciimpl_is_zero!(f32, |x: f32| x.to_bits() == 0);
4362306a36Sopenharmony_ciimpl_is_zero!(f64, |x: f64| x.to_bits() == 0);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciunsafe impl<T> IsZero for *const T {
4662306a36Sopenharmony_ci    #[inline]
4762306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
4862306a36Sopenharmony_ci        (*self).is_null()
4962306a36Sopenharmony_ci    }
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciunsafe impl<T> IsZero for *mut T {
5362306a36Sopenharmony_ci    #[inline]
5462306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
5562306a36Sopenharmony_ci        (*self).is_null()
5662306a36Sopenharmony_ci    }
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ciunsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
6062306a36Sopenharmony_ci    #[inline]
6162306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
6262306a36Sopenharmony_ci        // Because this is generated as a runtime check, it's not obvious that
6362306a36Sopenharmony_ci        // it's worth doing if the array is really long. The threshold here
6462306a36Sopenharmony_ci        // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
6562306a36Sopenharmony_ci        // fails to const-fold the check in `vec![[1; 32]; n]`
6662306a36Sopenharmony_ci        // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
6762306a36Sopenharmony_ci        // Feel free to tweak if you have better evidence.
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci        N <= 16 && self.iter().all(IsZero::is_zero)
7062306a36Sopenharmony_ci    }
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci// This is recursive macro.
7462306a36Sopenharmony_cimacro_rules! impl_for_tuples {
7562306a36Sopenharmony_ci    // Stopper
7662306a36Sopenharmony_ci    () => {
7762306a36Sopenharmony_ci        // No use for implementing for empty tuple because it is ZST.
7862306a36Sopenharmony_ci    };
7962306a36Sopenharmony_ci    ($first_arg:ident $(,$rest:ident)*) => {
8062306a36Sopenharmony_ci        unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
8162306a36Sopenharmony_ci            #[inline]
8262306a36Sopenharmony_ci            fn is_zero(&self) -> bool{
8362306a36Sopenharmony_ci                // Destructure tuple to N references
8462306a36Sopenharmony_ci                // Rust allows to hide generic params by local variable names.
8562306a36Sopenharmony_ci                #[allow(non_snake_case)]
8662306a36Sopenharmony_ci                let ($first_arg, $($rest,)*) = self;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci                $first_arg.is_zero()
8962306a36Sopenharmony_ci                    $( && $rest.is_zero() )*
9062306a36Sopenharmony_ci            }
9162306a36Sopenharmony_ci        }
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci        impl_for_tuples!($($rest),*);
9462306a36Sopenharmony_ci    }
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciimpl_for_tuples!(A, B, C, D, E, F, G, H);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci// `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
10062306a36Sopenharmony_ci// For fat pointers, the bytes that would be the pointer metadata in the `Some`
10162306a36Sopenharmony_ci// variant are padding in the `None` variant, so ignoring them and
10262306a36Sopenharmony_ci// zero-initializing instead is ok.
10362306a36Sopenharmony_ci// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
10462306a36Sopenharmony_ci// `SpecFromElem`.
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciunsafe impl<T: ?Sized> IsZero for Option<&T> {
10762306a36Sopenharmony_ci    #[inline]
10862306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
10962306a36Sopenharmony_ci        self.is_none()
11062306a36Sopenharmony_ci    }
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciunsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
11462306a36Sopenharmony_ci    #[inline]
11562306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
11662306a36Sopenharmony_ci        self.is_none()
11762306a36Sopenharmony_ci    }
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci// `Option<num::NonZeroU32>` and similar have a representation guarantee that
12162306a36Sopenharmony_ci// they're the same size as the corresponding `u32` type, as well as a guarantee
12262306a36Sopenharmony_ci// that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
12362306a36Sopenharmony_ci// While the documentation officially makes it UB to transmute from `None`,
12462306a36Sopenharmony_ci// we're the standard library so we can make extra inferences, and we know that
12562306a36Sopenharmony_ci// the only niche available to represent `None` is the one that's all zeros.
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cimacro_rules! impl_is_zero_option_of_nonzero {
12862306a36Sopenharmony_ci    ($($t:ident,)+) => {$(
12962306a36Sopenharmony_ci        unsafe impl IsZero for Option<core::num::$t> {
13062306a36Sopenharmony_ci            #[inline]
13162306a36Sopenharmony_ci            fn is_zero(&self) -> bool {
13262306a36Sopenharmony_ci                self.is_none()
13362306a36Sopenharmony_ci            }
13462306a36Sopenharmony_ci        }
13562306a36Sopenharmony_ci    )+};
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciimpl_is_zero_option_of_nonzero!(
13962306a36Sopenharmony_ci    NonZeroU8,
14062306a36Sopenharmony_ci    NonZeroU16,
14162306a36Sopenharmony_ci    NonZeroU32,
14262306a36Sopenharmony_ci    NonZeroU64,
14362306a36Sopenharmony_ci    NonZeroU128,
14462306a36Sopenharmony_ci    NonZeroI8,
14562306a36Sopenharmony_ci    NonZeroI16,
14662306a36Sopenharmony_ci    NonZeroI32,
14762306a36Sopenharmony_ci    NonZeroI64,
14862306a36Sopenharmony_ci    NonZeroI128,
14962306a36Sopenharmony_ci    NonZeroUsize,
15062306a36Sopenharmony_ci    NonZeroIsize,
15162306a36Sopenharmony_ci);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cimacro_rules! impl_is_zero_option_of_num {
15462306a36Sopenharmony_ci    ($($t:ty,)+) => {$(
15562306a36Sopenharmony_ci        unsafe impl IsZero for Option<$t> {
15662306a36Sopenharmony_ci            #[inline]
15762306a36Sopenharmony_ci            fn is_zero(&self) -> bool {
15862306a36Sopenharmony_ci                const {
15962306a36Sopenharmony_ci                    let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
16062306a36Sopenharmony_ci                    assert!(none.is_none());
16162306a36Sopenharmony_ci                }
16262306a36Sopenharmony_ci                self.is_none()
16362306a36Sopenharmony_ci            }
16462306a36Sopenharmony_ci        }
16562306a36Sopenharmony_ci    )+};
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciimpl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciunsafe impl<T: IsZero> IsZero for Wrapping<T> {
17162306a36Sopenharmony_ci    #[inline]
17262306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
17362306a36Sopenharmony_ci        self.0.is_zero()
17462306a36Sopenharmony_ci    }
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciunsafe impl<T: IsZero> IsZero for Saturating<T> {
17862306a36Sopenharmony_ci    #[inline]
17962306a36Sopenharmony_ci    fn is_zero(&self) -> bool {
18062306a36Sopenharmony_ci        self.0.is_zero()
18162306a36Sopenharmony_ci    }
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cimacro_rules! impl_for_optional_bool {
18562306a36Sopenharmony_ci    ($($t:ty,)+) => {$(
18662306a36Sopenharmony_ci        unsafe impl IsZero for $t {
18762306a36Sopenharmony_ci            #[inline]
18862306a36Sopenharmony_ci            fn is_zero(&self) -> bool {
18962306a36Sopenharmony_ci                // SAFETY: This is *not* a stable layout guarantee, but
19062306a36Sopenharmony_ci                // inside `core` we're allowed to rely on the current rustc
19162306a36Sopenharmony_ci                // behaviour that options of bools will be one byte with
19262306a36Sopenharmony_ci                // no padding, so long as they're nested less than 254 deep.
19362306a36Sopenharmony_ci                let raw: u8 = unsafe { core::mem::transmute(*self) };
19462306a36Sopenharmony_ci                raw == 0
19562306a36Sopenharmony_ci            }
19662306a36Sopenharmony_ci        }
19762306a36Sopenharmony_ci    )+};
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ciimpl_for_optional_bool! {
20062306a36Sopenharmony_ci    Option<bool>,
20162306a36Sopenharmony_ci    Option<Option<bool>>,
20262306a36Sopenharmony_ci    Option<Option<Option<bool>>>,
20362306a36Sopenharmony_ci    // Could go further, but not worth the metadata overhead
20462306a36Sopenharmony_ci}
205