devela/sys/mem/size/expr/
mod.rs

1// devela::sys::mem::size::expr
2//
3// Original source code by Joshua Nelson, licensed as BSD-3,
4// https://crates.io/crates/size-of-trait/1.1.3
5
6/// A helper macro used to bridge the gap between compile-time and runtime.
7#[doc(hidden)]
8pub const fn __size_of_expr<T>(_zero_len_fn_ptr_array: [impl FnOnce() -> [T; 0]; 0]) -> usize {
9    crate::Mem::size_of::<T>()
10}
11
12/// Returns the size of an expression in bytes.
13///
14/// - The expression will not be evaluated.
15/// - This can be used in `const` contexts.
16///
17/// # Example
18/// ```
19/// # use devela::size_of_expr;
20/// async fn f() {
21///     let x = 1;
22///     core::future::ready(()).await;
23/// }
24/// const SIZE: usize = size_of_expr!(f());
25/// assert_eq!(SIZE, 2);
26/// ```
27/// # Features
28/// Makes use of [`unreachable_unchecked`][core::hint::unreachable_unchecked]
29/// if the `unsafe_hint` feature is enabled.
30#[doc = crate::doc_!(vendor: "size_of_trait")]
31#[macro_export]
32#[cfg_attr(cargo_primary_package, doc(hidden))]
33macro_rules! size_of_expr {
34    ($expr: expr) => {
35        $crate::sys::mem::__size_of_expr(
36            // The array of function pointers is created in an unreachable branch
37            // of an if-else block to avoid evaluating it.
38            if true {
39                []
40            } else {
41                #[cfg(any(feature = "safe_mem", not(feature = "unsafe_hint")))]
42                loop {}
43                #[cfg(all(not(feature = "safe_mem"), feature = "unsafe_hint"))]
44                unsafe {
45                    $crate::unreachable_unchecked()
46                }
47
48                #[expect(unreachable_code, reason = "avoid evaluating this branch")]
49                {
50                    [|| [$expr; 0]; 0]
51                }
52            },
53        )
54    };
55}
56#[doc(inline)]
57pub use size_of_expr;
58
59/* tests */
60
61// has to be a separate file because of experimental syntax
62#[cfg(all(test, feature = "nightly_coro", feature = "alloc"))]
63mod test_coro;
64
65#[cfg(test)]
66mod tests {
67    use super::size_of_expr;
68
69    async fn f() {
70        let _x = 1;
71        core::future::ready(()).await;
72        let _y = 2;
73    }
74
75    #[test]
76    fn api() {
77        const C: usize = size_of_expr!(f());
78        const D: usize = size_of_expr!(0_u8);
79        const E: usize = size_of_expr!(());
80        const F: usize = size_of_expr!(0_usize);
81
82        assert_eq!(C, 2);
83        assert_eq!(D, 1);
84        assert_eq!(E, 0);
85        assert_eq!(F, size_of::<usize>());
86    }
87
88    #[test]
89    fn edge_cases() {
90        // 1. works with references:
91        const _: [(); size_of_expr!(&Some(42))] = [(); size_of::<*const ()>()];
92        let _ = size_of_expr!(&Some(42));
93
94        // 2. works with temporaries:
95        #[allow(dropping_copy_types)]
96        const _: [(); size_of_expr!(Some(drop(())).as_ref())] = [(); size_of::<*const ()>()];
97        #[allow(dropping_copy_types)]
98        let _ = size_of_expr!(Some(drop(())).as_ref());
99
100        // 3. Does not move the named stuff
101        struct NotCopy {}
102        let it = NotCopy {};
103        assert_eq!(size_of_expr!(it), 0);
104        drop(it);
105
106        // 4. Does not even borrow the named stuff
107        let mut it = ();
108        let r = &mut it;
109        assert_eq!(size_of_expr!(it), 0);
110        #[allow(dropping_references)]
111        drop(r);
112    }
113}