1 // SPDX-License-Identifier: Apache-2.0 OR MIT
3 use core::num::{Saturating, Wrapping};
7 #[rustc_specialization_trait]
8 pub(super) unsafe trait IsZero {
9 /// Whether this value's representation is all zeros,
10 /// or can be represented with all zeroes.
11 fn is_zero(&self) -> bool;
14 macro_rules! impl_is_zero {
15 ($t:ty, $is_zero:expr) => {
16 unsafe impl IsZero for $t {
18 fn is_zero(&self) -> bool {
25 impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
26 impl_is_zero!(i16, |x| x == 0);
27 impl_is_zero!(i32, |x| x == 0);
28 impl_is_zero!(i64, |x| x == 0);
29 impl_is_zero!(i128, |x| x == 0);
30 impl_is_zero!(isize, |x| x == 0);
32 impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
33 impl_is_zero!(u16, |x| x == 0);
34 impl_is_zero!(u32, |x| x == 0);
35 impl_is_zero!(u64, |x| x == 0);
36 impl_is_zero!(u128, |x| x == 0);
37 impl_is_zero!(usize, |x| x == 0);
39 impl_is_zero!(bool, |x| x == false);
40 impl_is_zero!(char, |x| x == '\0');
42 impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
43 impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
45 unsafe impl<T> IsZero for *const T {
47 fn is_zero(&self) -> bool {
52 unsafe impl<T> IsZero for *mut T {
54 fn is_zero(&self) -> bool {
59 unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
61 fn is_zero(&self) -> bool {
62 // Because this is generated as a runtime check, it's not obvious that
63 // it's worth doing if the array is really long. The threshold here
64 // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
65 // fails to const-fold the check in `vec![[1; 32]; n]`
66 // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
67 // Feel free to tweak if you have better evidence.
69 N <= 16 && self.iter().all(IsZero::is_zero)
73 // This is recursive macro.
74 macro_rules! impl_for_tuples {
77 // No use for implementing for empty tuple because it is ZST.
79 ($first_arg:ident $(,$rest:ident)*) => {
80 unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
82 fn is_zero(&self) -> bool{
83 // Destructure tuple to N references
84 // Rust allows to hide generic params by local variable names.
85 #[allow(non_snake_case)]
86 let ($first_arg, $($rest,)*) = self;
89 $( && $rest.is_zero() )*
93 impl_for_tuples!($($rest),*);
97 impl_for_tuples!(A, B, C, D, E, F, G, H);
99 // `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
100 // For fat pointers, the bytes that would be the pointer metadata in the `Some`
101 // variant are padding in the `None` variant, so ignoring them and
102 // zero-initializing instead is ok.
103 // `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
106 unsafe impl<T: ?Sized> IsZero for Option<&T> {
108 fn is_zero(&self) -> bool {
113 unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
115 fn is_zero(&self) -> bool {
120 // `Option<num::NonZeroU32>` and similar have a representation guarantee that
121 // they're the same size as the corresponding `u32` type, as well as a guarantee
122 // that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
123 // While the documentation officially makes it UB to transmute from `None`,
124 // we're the standard library so we can make extra inferences, and we know that
125 // the only niche available to represent `None` is the one that's all zeros.
127 macro_rules! impl_is_zero_option_of_nonzero {
128 ($($t:ident,)+) => {$(
129 unsafe impl IsZero for Option<core::num::$t> {
131 fn is_zero(&self) -> bool {
138 impl_is_zero_option_of_nonzero!(
153 macro_rules! impl_is_zero_option_of_num {
155 unsafe impl IsZero for Option<$t> {
157 fn is_zero(&self) -> bool {
159 let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
160 assert!(none.is_none());
168 impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,);
170 unsafe impl<T: IsZero> IsZero for Wrapping<T> {
172 fn is_zero(&self) -> bool {
177 unsafe impl<T: IsZero> IsZero for Saturating<T> {
179 fn is_zero(&self) -> bool {
184 macro_rules! impl_for_optional_bool {
186 unsafe impl IsZero for $t {
188 fn is_zero(&self) -> bool {
189 // SAFETY: This is *not* a stable layout guarantee, but
190 // inside `core` we're allowed to rely on the current rustc
191 // behaviour that options of bools will be one byte with
192 // no padding, so long as they're nested less than 254 deep.
193 let raw: u8 = unsafe { core::mem::transmute(*self) };
199 impl_for_optional_bool! {
201 Option<Option<bool>>,
202 Option<Option<Option<bool>>>,
203 // Could go further, but not worth the metadata overhead