1use core::num::{Saturating, Wrapping};
2
3use crate::boxed::Box;
4
5#[rustc_specialization_trait]
6pub(super) unsafe trait IsZero {
7 /// Whether this value's representation is all zeros,
8 /// or can be represented with all zeroes.
9 fn is_zero(&self) -> bool;
10}
11
12macro_rules! impl_is_zero {
13 ($t:ty, $is_zero:expr) => {
14 unsafe impl IsZero for $t {
15 #[inline]
16 fn is_zero(&self) -> bool {
17 $is_zero(*self)
18 }
19 }
20 };
21}
22
23impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
24impl_is_zero!(i16, |x| x == 0);
25impl_is_zero!(i32, |x| x == 0);
26impl_is_zero!(i64, |x| x == 0);
27impl_is_zero!(i128, |x| x == 0);
28impl_is_zero!(isize, |x| x == 0);
29
30impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
31impl_is_zero!(u16, |x| x == 0);
32impl_is_zero!(u32, |x| x == 0);
33impl_is_zero!(u64, |x| x == 0);
34impl_is_zero!(u128, |x| x == 0);
35impl_is_zero!(usize, |x| x == 0);
36
37impl_is_zero!(bool, |x| x == false);
38impl_is_zero!(char, |x| x == '\0');
39
40impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
41impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
42
43unsafe impl<T> IsZero for *const T {
44 #[inline]
45 fn is_zero(&self) -> bool {
46 (*self).is_null()
47 }
48}
49
50unsafe impl<T> IsZero for *mut T {
51 #[inline]
52 fn is_zero(&self) -> bool {
53 (*self).is_null()
54 }
55}
56
57unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
58 #[inline]
59 fn is_zero(&self) -> bool {
60 // Because this is generated as a runtime check, it's not obvious that
61 // it's worth doing if the array is really long. The threshold here
62 // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
63 // fails to const-fold the check in `vec![[1; 32]; n]`
64 // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
65 // Feel free to tweak if you have better evidence.
66
67 N <= 16 && self.iter().all(IsZero::is_zero)
68 }
69}
70
71// This is recursive macro.
72macro_rules! impl_for_tuples {
73 // Stopper
74 () => {
75 // No use for implementing for empty tuple because it is ZST.
76 };
77 ($first_arg:ident $(,$rest:ident)*) => {
78 unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
79 #[inline]
80 fn is_zero(&self) -> bool{
81 // Destructure tuple to N references
82 // Rust allows to hide generic params by local variable names.
83 #[allow(non_snake_case)]
84 let ($first_arg, $($rest,)*) = self;
85
86 $first_arg.is_zero()
87 $( && $rest.is_zero() )*
88 }
89 }
90
91 impl_for_tuples!($($rest),*);
92 }
93}
94
95impl_for_tuples!(A, B, C, D, E, F, G, H);
96
97// `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
98// For fat pointers, the bytes that would be the pointer metadata in the `Some`
99// variant are padding in the `None` variant, so ignoring them and
100// zero-initializing instead is ok.
101// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
102// `SpecFromElem`.
103
104unsafe impl<T: ?Sized> IsZero for Option<&T> {
105 #[inline]
106 fn is_zero(&self) -> bool {
107 self.is_none()
108 }
109}
110
111unsafe impl<T: ?Sized> IsZero for Option<Box<T>> {
112 #[inline]
113 fn is_zero(&self) -> bool {
114 self.is_none()
115 }
116}
117
118// `Option<num::NonZeroU32>` and similar have a representation guarantee that
119// they're the same size as the corresponding `u32` type, as well as a guarantee
120// that transmuting between `NonZeroU32` and `Option<num::NonZeroU32>` works.
121// While the documentation officially makes it UB to transmute from `None`,
122// we're the standard library so we can make extra inferences, and we know that
123// the only niche available to represent `None` is the one that's all zeros.
124
125macro_rules! impl_is_zero_option_of_nonzero {
126 ($($t:ident,)+) => {$(
127 unsafe impl IsZero for Option<core::num::$t> {
128 #[inline]
129 fn is_zero(&self) -> bool {
130 self.is_none()
131 }
132 }
133 )+};
134}
135
136impl_is_zero_option_of_nonzero!(
137 NonZeroU8,
138 NonZeroU16,
139 NonZeroU32,
140 NonZeroU64,
141 NonZeroU128,
142 NonZeroI8,
143 NonZeroI16,
144 NonZeroI32,
145 NonZeroI64,
146 NonZeroI128,
147 NonZeroUsize,
148 NonZeroIsize,
149);
150
151macro_rules! impl_is_zero_option_of_num {
152 ($($t:ty,)+) => {$(
153 unsafe impl IsZero for Option<$t> {
154 #[inline]
155 fn is_zero(&self) -> bool {
156 const {
157 let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
158 assert!(none.is_none());
159 }
160 self.is_none()
161 }
162 }
163 )+};
164}
165
166impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,);
167
168unsafe impl<T: IsZero> IsZero for Wrapping<T> {
169 #[inline]
170 fn is_zero(&self) -> bool {
171 self.0.is_zero()
172 }
173}
174
175unsafe impl<T: IsZero> IsZero for Saturating<T> {
176 #[inline]
177 fn is_zero(&self) -> bool {
178 self.0.is_zero()
179 }
180}
181
182macro_rules! impl_for_optional_bool {
183 ($($t:ty,)+) => {$(
184 unsafe impl IsZero for $t {
185 #[inline]
186 fn is_zero(&self) -> bool {
187 // SAFETY: This is *not* a stable layout guarantee, but
188 // inside `core` we're allowed to rely on the current rustc
189 // behaviour that options of bools will be one byte with
190 // no padding, so long as they're nested less than 254 deep.
191 let raw: u8 = unsafe { core::mem::transmute(*self) };
192 raw == 0
193 }
194 }
195 )+};
196}
197impl_for_optional_bool! {
198 Option<bool>,
199 Option<Option<bool>>,
200 Option<Option<Option<bool>>>,
201 // Could go further, but not worth the metadata overhead
202}
203