1use core::mem::SizedTypeProperties;
2use core::num::{NonZero, Saturating, Wrapping};
34use crate::boxed::Box;
56#[rustc_specialization_trait]
7pub(super) unsafe trait IsZero {
8/// Whether this value's representation is all zeros,
9 /// or can be represented with all zeroes.
10fn is_zero(&self) -> bool;
11}
1213macro_rules!impl_is_zero {
14 ($t:ty, $is_zero:expr) => {
15unsafe impl IsZero for $t {
16#[inline]
17fn is_zero(&self) -> bool {
18$is_zero(*self)
19 }
20 }
21 };
22}
2324unsafe impl IsZero for () {
#[inline]
fn is_zero(&self) -> bool { (|_: ()| true)(*self) }
}impl_is_zero!((), |_: ()| true); // It is needed to impl for arrays and tuples of ().
2526unsafe impl IsZero for i8 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(i8, |x| x == 0); // It is needed to impl for arrays and tuples of i8.
27unsafe impl IsZero for i16 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(i16, |x| x == 0);
28unsafe impl IsZero for i32 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(i32, |x| x == 0);
29unsafe impl IsZero for i64 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(i64, |x| x == 0);
30unsafe impl IsZero for i128 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(i128, |x| x == 0);
31unsafe impl IsZero for isize {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(isize, |x| x == 0);
3233unsafe impl IsZero for u8 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(u8, |x| x == 0); // It is needed to impl for arrays and tuples of u8.
34unsafe impl IsZero for u16 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(u16, |x| x == 0);
35unsafe impl IsZero for u32 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(u32, |x| x == 0);
36unsafe impl IsZero for u64 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(u64, |x| x == 0);
37unsafe impl IsZero for u128 {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(u128, |x| x == 0);
38unsafe impl IsZero for usize {
#[inline]
fn is_zero(&self) -> bool { (|x| x == 0)(*self) }
}impl_is_zero!(usize, |x| x == 0);
3940unsafe impl IsZero for bool {
#[inline]
fn is_zero(&self) -> bool { (|x| x == false)(*self) }
}impl_is_zero!(bool, |x| x == false);
41unsafe impl IsZero for char {
#[inline]
fn is_zero(&self) -> bool { (|x| x == '\0')(*self) }
}impl_is_zero!(char, |x| x == '\0');
4243unsafe impl IsZero for f32 {
#[inline]
fn is_zero(&self) -> bool { (|x: f32| x.to_bits() == 0)(*self) }
}impl_is_zero!(f32, |x: f32| x.to_bits() == 0);
44unsafe impl IsZero for f64 {
#[inline]
fn is_zero(&self) -> bool { (|x: f64| x.to_bits() == 0)(*self) }
}impl_is_zero!(f64, |x: f64| x.to_bits() == 0);
4546// `IsZero` cannot be soundly implemented for pointers because of provenance
47// (see #135338).
4849unsafe impl<T, const N: usize> IsZerofor [T; N] {
50#[inline]
51default fn is_zero(&self) -> bool {
52// If the array is of length zero,
53 // then it doesn't actually contain any `T`s,
54 // so `T::clone` doesn't need to be called,
55 // and we can "zero-initialize" all zero bytes of the array.
56N == 0
57}
58}
5960unsafe impl<T: IsZero, const N: usize> IsZerofor [T; N] {
61#[inline]
62fn is_zero(&self) -> bool {
63if T::IS_ZST {
64// If T is a ZST, then there is at most one possible value of `T`,
65 // so we only need to check one element for zeroness.
66 // We can't unconditionally return `true` here, since, e.g.
67 // `T = [NonTrivialCloneZst; 5]` is a ZST that implements `IsZero`
68 // due to the generic array impl, but `T::is_zero` returns `false`
69 // since the length is not 0.
70self.get(0).is_none_or(IsZero::is_zero)
71 } else {
72// Because this is generated as a runtime check, it's not obvious that
73 // it's worth doing if the array is really long. The threshold here
74 // is largely arbitrary, but was picked because as of 2022-07-01 LLVM
75 // fails to const-fold the check in `vec![[1; 32]; n]`
76 // See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
77 // Feel free to tweak if you have better evidence.
7879N <= 16 && self.iter().all(IsZero::is_zero)
80 }
81 }
82}
8384// This is recursive macro.
85macro_rules!impl_is_zero_tuples {
86// Stopper
87() => {
88// We already have an impl for () above.
89};
90 ($first_arg:ident $(,$rest:ident)*) => {
91unsafe impl <$first_arg: IsZero, $($rest: IsZero,)*> IsZero for ($first_arg, $($rest,)*){
92#[inline]
93fn is_zero(&self) -> bool{
94// Destructure tuple to N references
95 // Rust allows to hide generic params by local variable names.
96#[allow(non_snake_case)]
97let ($first_arg, $($rest,)*) = self;
9899$first_arg.is_zero()
100 $( && $rest.is_zero() )*
101 }
102 }
103104impl_is_zero_tuples!($($rest),*);
105 }
106}
107108unsafe impl<H: IsZero> IsZero for (H,) {
#[inline]
fn is_zero(&self) -> bool {
#[allow(non_snake_case)]
let (H,) = self;
H.is_zero()
}
}impl_is_zero_tuples!(A, B, C, D, E, F, G, H);
109110// `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
111// For fat pointers, the bytes that would be the pointer metadata in the `Some`
112// variant are padding in the `None` variant, so ignoring them and
113// zero-initializing instead is ok.
114// `Option<&mut T>` never implements `Clone`, so there's no need for an impl of
115// `SpecFromElem`.
116117unsafe impl<T: ?Sized> IsZerofor Option<&T> {
118#[inline]
119fn is_zero(&self) -> bool {
120self.is_none()
121 }
122}
123124unsafe impl<T: ?Sized> IsZerofor Option<Box<T>> {
125#[inline]
126fn is_zero(&self) -> bool {
127self.is_none()
128 }
129}
130131// `Option<NonZero<u32>>` and similar have a representation guarantee that
132// they're the same size as the corresponding `u32` type, as well as a guarantee
133// that transmuting between `NonZero<u32>` and `Option<NonZero<u32>>` works.
134// While the documentation officially makes it UB to transmute from `None`,
135// we're the standard library so we can make extra inferences, and we know that
136// the only niche available to represent `None` is the one that's all zeros.
137macro_rules!impl_is_zero_option_of_nonzero_int {
138 ($($t:ty),+ $(,)?) => {$(
139unsafe impl IsZero for Option<NonZero<$t>> {
140#[inline]
141fn is_zero(&self) -> bool {
142self.is_none()
143 }
144 }
145 )+};
146}
147148unsafe impl IsZero for Option<NonZero<isize>> {
#[inline]
fn is_zero(&self) -> bool { self.is_none() }
}impl_is_zero_option_of_nonzero_int!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
149150macro_rules!impl_is_zero_option_of_int {
151 ($($t:ty),+ $(,)?) => {$(
152unsafe impl IsZero for Option<$t> {
153#[inline]
154fn is_zero(&self) -> bool {
155const {
156let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
157assert!(none.is_none());
158 }
159self.is_none()
160 }
161 }
162 )+};
163}
164165unsafe impl IsZero for Option<isize> {
#[inline]
fn is_zero(&self) -> bool {
const {
let none: Self =
unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
if !none.is_none() {
::core::panicking::panic("assertion failed: none.is_none()")
};
}
self.is_none()
}
}impl_is_zero_option_of_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize);
166167unsafe impl<T: IsZero> IsZerofor Wrapping<T> {
168#[inline]
169fn is_zero(&self) -> bool {
170self.0.is_zero()
171 }
172}
173174unsafe impl<T: IsZero> IsZerofor Saturating<T> {
175#[inline]
176fn is_zero(&self) -> bool {
177self.0.is_zero()
178 }
179}
180181macro_rules!impl_is_zero_option_of_bool {
182 ($($t:ty),+ $(,)?) => {$(
183unsafe impl IsZero for $t {
184#[inline]
185fn is_zero(&self) -> bool {
186// SAFETY: This is *not* a stable layout guarantee, but
187 // inside `core` we're allowed to rely on the current rustc
188 // behavior that options of bools will be one byte with
189 // no padding, so long as they're nested less than 254 deep.
190let raw: u8 = unsafe { core::mem::transmute(*self) };
191 raw == 0
192}
193 }
194 )+};
195}
196197unsafe impl IsZero for Option<Option<Option<bool>>> {
#[inline]
fn is_zero(&self) -> bool {
let raw: u8 = unsafe { core::mem::transmute(*self) };
raw == 0
}
}impl_is_zero_option_of_bool! {
198Option<bool>,
199Option<Option<bool>>,
200Option<Option<Option<bool>>>,
201// Could go further, but not worth the metadata overhead.
202}