1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#![no_std]

#[doc(hidden)]
pub use core;

/// A marco that acts similarly to [`core::mem::zeroed()`], only is const
///
/// Example usage:
/// ```rust
/// use const_zero::const_zero;
/// struct OpaqueStruct {
///     nothing: core::ffi::c_void,
/// };
/// static mut zeroed_opaque: OpaqueStruct = unsafe {const_zero!(OpaqueStruct)};
/// ```
///
/// Ideally const_zero would be a generic function, but const generics need
/// more development first (`const_fn_transmute`, `const_generics`,
/// `const_evaluatable_checked`)
///
/// ## Safety
/// Similar to `core::mem::zeroed()`, except this zeroes padding bits. Zeroed
/// padding usually isn't relevant to safety, but might be if a C union is used.
/// To repeat `core::mem::zeroed()`'s safety, an all zero byte pattern might not
/// be a valid value for a type; for example, references &T, &mut T.
///
/// const_zero does not work on unsized types
/// ```rust compile_fail
/// use const_zero::const_zero;
/// // error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
/// const BYTES: [u8] = unsafe{const_zero!([u8])};
/// ```
/// reference types trigger a (denied by default) lint and cause immediate
/// undefined behavior if the lint is ignored
/// ```rust compile_fail
/// use const_zero::const_zero;
/// // error: any use of this value will cause an error
/// // note: `#[deny(const_err)]` on by default
/// const STR: &str = unsafe{const_zero!(&'static str)};
/// ```
///
/// ## Differences with `core::mem::zeroed`
/// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't
#[macro_export]
macro_rules! const_zero {
    ($type_:ty) => {{
        const TYPE_SIZE: $crate::core::primitive::usize = $crate::core::mem::size_of::<$type_>();
        union TypeAsBytes {
            bytes: [$crate::core::primitive::u8; TYPE_SIZE],
            inner: $crate::core::mem::ManuallyDrop<$type_>,
        }
        const ZERO: TypeAsBytes = TypeAsBytes {
            bytes: [0; TYPE_SIZE],
        };
        $crate::core::mem::ManuallyDrop::<$type_>::into_inner(ZERO.inner)
    }};
}

#[cfg(test)]
mod tests {
    use core::num::NonZeroU8;
    use core::num::Wrapping;

    // Ensure macros are hygienic, don't create name conflicts
    #[test]
    fn multiple() {
        const ZERO_1: i32 = unsafe { const_zero!(i32) };
        const ZERO_2: i32 = unsafe { const_zero!(i32) };
        const ZERO_3: i64 = unsafe { const_zero!(i64) };
        assert_eq!(ZERO_1, 0);
        assert_eq!(ZERO_2, 0);
        assert_eq!(ZERO_3, 0);
    }

    // All integers can be constructed
    #[test]
    fn zeroed_int() {
        macro_rules! test_int {
            ($type_:ty, $zero:expr) => {{
                const ZERO: $type_ = unsafe { const_zero!($type_) };
                assert_eq!(ZERO, $zero);
            }};
        }
        test_int!(i8, 0);
        test_int!(i16, 0);
        test_int!(i32, 0);
        test_int!(i64, 0);
        test_int!(i128, 0);
        test_int!(isize, 0);
        test_int!(Wrapping<i8>, Wrapping(0));
        test_int!(Wrapping<i16>, Wrapping(0));
        test_int!(Wrapping<i32>, Wrapping(0));
        test_int!(Wrapping<i64>, Wrapping(0));
        test_int!(Wrapping<i128>, Wrapping(0));
        test_int!(Wrapping<isize>, Wrapping(0));

        test_int!(u8, 0);
        test_int!(u16, 0);
        test_int!(u32, 0);
        test_int!(u64, 0);
        test_int!(u128, 0);
        test_int!(usize, 0);
        test_int!(Wrapping<u8>, Wrapping(0));
        test_int!(Wrapping<u16>, Wrapping(0));
        test_int!(Wrapping<u32>, Wrapping(0));
        test_int!(Wrapping<u64>, Wrapping(0));
        test_int!(Wrapping<u128>, Wrapping(0));
        test_int!(Wrapping<usize>, Wrapping(0));

        test_int!(f32, 0.);
        test_int!(f64, 0.);
    }

    #[test]
    fn zeroed_ptr() {
        const NULL: *const () = unsafe { const_zero!(*const ()) };
        assert_eq!(NULL, core::ptr::null());
        const NULL_MUT: *mut () = unsafe { const_zero!(*mut ()) };
        assert_eq!(NULL_MUT, core::ptr::null_mut());
    }

    // sentinel value Option optimization works
    #[test]
    fn zeroed_option() {
        const NONE: Option<NonZeroU8> = unsafe { const_zero!(Option<NonZeroU8>) };
        assert_eq!(NONE, None);
    }

    // a type with a drop implementation works
    #[test]
    fn drop_type() {
        #[derive(Clone, Debug)]
        struct Droppable {}
        impl Drop for Droppable {
            fn drop(&mut self) {
                // no-op
            }
        }
        #[allow(unused)]
        const DROPPABLE: Droppable = unsafe { const_zero!(Droppable) };
    }

    #[test]
    fn zeroed_unit() {
        const UNIT: () = unsafe { const_zero!(()) };
        assert_eq!((), UNIT);
    }
}

#[cfg(test)]
mod test_no_implicit_prelude {
    #![no_implicit_prelude]

    #[test]
    fn zeroed_unit() {
        const UNIT: () = unsafe { const_zero!(()) };
        ::core::assert_eq!((), UNIT);
    }
}