1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 use core::num::{Saturating, Wrapping}; 4 5 use crate::boxed::Box; 6 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; 12 } 13 14 macro_rules! impl_is_zero { 15 ($t:ty, $is_zero:expr) => { 16 unsafe impl IsZero for $t { 17 #[inline] 18 fn is_zero(&self) -> bool { 19 $is_zero(*self) 20 } 21 } 22 }; 23 } 24 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); 31 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); 38 39 impl_is_zero!(bool, |x| x == false); 40 impl_is_zero!(char, |x| x == '\0'); 41 42 impl_is_zero!(f32, |x: f32| x.to_bits() == 0); 43 impl_is_zero!(f64, |x: f64| x.to_bits() == 0); 44 45 unsafe impl<T> IsZero for *const T { 46 #[inline] 47 fn is_zero(&self) -> bool { 48 (*self).is_null() 49 } 50 } 51 52 unsafe impl<T> IsZero for *mut T { 53 #[inline] 54 fn is_zero(&self) -> bool { 55 (*self).is_null() 56 } 57 } 58 59 unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] { 60 #[inline] 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. 68 69 N <= 16 && self.iter().all(IsZero::is_zero) 70 } 71 } 72 73 // This is recursive macro. 74 macro_rules! impl_for_tuples { 75 // Stopper 76 () => { 77 // No use for implementing for empty tuple because it is ZST. 78 }; 79 ($first_arg:ident $(,$rest:ident)*) => { 80 unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){ 81 #[inline] 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; 87 88 $first_arg.is_zero() 89 $( && $rest.is_zero() )* 90 } 91 } 92 93 impl_for_tuples!($($rest),*); 94 } 95 } 96 97 impl_for_tuples!(A, B, C, D, E, F, G, H); 98 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 104 // `SpecFromElem`. 105 106 unsafe impl<T: ?Sized> IsZero for Option<&T> { 107 #[inline] 108 fn is_zero(&self) -> bool { 109 self.is_none() 110 } 111 } 112 113 unsafe impl<T: ?Sized> IsZero for Option<Box<T>> { 114 #[inline] 115 fn is_zero(&self) -> bool { 116 self.is_none() 117 } 118 } 119 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. 126 127 macro_rules! impl_is_zero_option_of_nonzero { 128 ($($t:ident,)+) => {$( 129 unsafe impl IsZero for Option<core::num::$t> { 130 #[inline] 131 fn is_zero(&self) -> bool { 132 self.is_none() 133 } 134 } 135 )+}; 136 } 137 138 impl_is_zero_option_of_nonzero!( 139 NonZeroU8, 140 NonZeroU16, 141 NonZeroU32, 142 NonZeroU64, 143 NonZeroU128, 144 NonZeroI8, 145 NonZeroI16, 146 NonZeroI32, 147 NonZeroI64, 148 NonZeroI128, 149 NonZeroUsize, 150 NonZeroIsize, 151 ); 152 153 macro_rules! impl_is_zero_option_of_num { 154 ($($t:ty,)+) => {$( 155 unsafe impl IsZero for Option<$t> { 156 #[inline] 157 fn is_zero(&self) -> bool { 158 const { 159 let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; 160 assert!(none.is_none()); 161 } 162 self.is_none() 163 } 164 } 165 )+}; 166 } 167 168 impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,); 169 170 unsafe impl<T: IsZero> IsZero for Wrapping<T> { 171 #[inline] 172 fn is_zero(&self) -> bool { 173 self.0.is_zero() 174 } 175 } 176 177 unsafe impl<T: IsZero> IsZero for Saturating<T> { 178 #[inline] 179 fn is_zero(&self) -> bool { 180 self.0.is_zero() 181 } 182 } 183 184 macro_rules! impl_for_optional_bool { 185 ($($t:ty,)+) => {$( 186 unsafe impl IsZero for $t { 187 #[inline] 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) }; 194 raw == 0 195 } 196 } 197 )+}; 198 } 199 impl_for_optional_bool! { 200 Option<bool>, 201 Option<Option<bool>>, 202 Option<Option<Option<bool>>>, 203 // Could go further, but not worth the metadata overhead 204 } 205