devela/sys/mem/size/expr/mod.rs
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
// devela::sys::mem::size::expr
//
// Original source code by Joshua Nelson, licensed as BSD-3,
// https://crates.io/crates/size-of-trait/1.1.3
/// A helper macro used to bridge the gap between compile-time and runtime.
#[doc(hidden)]
pub const fn __size_of_expr<T>(_zero_len_fn_ptr_array: [impl FnOnce() -> [T; 0]; 0]) -> usize {
crate::Mem::size_of::<T>()
}
/// Returns the size of an expression in bytes.
///
/// - The expression will not be evaluated.
/// - This can be used in `const` contexts.
///
/// # Example
/// ```
/// # use devela::size_of_expr;
/// async fn f() {
/// let x = 1;
/// core::future::ready(()).await;
/// }
/// const SIZE: usize = size_of_expr!(f());
/// assert_eq!(SIZE, 2);
/// ```
///
/// # Features
/// Makes use of [`unreachable_unchecked`][core::hint::unreachable_unchecked]
/// if the `unsafe_hint` feature is enabled.
#[macro_export]
#[cfg_attr(cargo_primary_package, doc(hidden))]
macro_rules! size_of_expr {
($expr: expr) => {
$crate::sys::mem::__size_of_expr(
// The array of function pointers is created in an unreachable branch
// of an if-else block to avoid evaluating it.
if true {
[]
} else {
#[cfg(any(feature = "safe_mem", not(feature = "unsafe_hint")))]
loop {}
#[cfg(all(not(feature = "safe_mem"), feature = "unsafe_hint"))]
unsafe {
$crate::unreachable_unchecked()
}
#[expect(unreachable_code, reason = "avoid evaluating this branch")]
{
[|| [$expr; 0]; 0]
}
},
)
};
}
#[doc(inline)]
pub use size_of_expr;
/* tests */
// has to be a separate file because of experimental syntax
#[cfg(all(test, feature = "nightly_coro", feature = "alloc"))]
mod test_coro;
#[cfg(test)]
mod tests {
use super::size_of_expr;
async fn f() {
let _x = 1;
core::future::ready(()).await;
let _y = 2;
}
#[test]
fn api() {
const C: usize = size_of_expr!(f());
const D: usize = size_of_expr!(0_u8);
const E: usize = size_of_expr!(());
const F: usize = size_of_expr!(0_usize);
assert_eq!(C, 2);
assert_eq!(D, 1);
assert_eq!(E, 0);
assert_eq!(F, size_of::<usize>());
}
#[test]
fn edge_cases() {
// 1. works with references:
const _: [(); size_of_expr!(&Some(42))] = [(); size_of::<*const ()>()];
let _ = size_of_expr!(&Some(42));
// 2. works with temporaries:
#[allow(dropping_copy_types)]
const _: [(); size_of_expr!(Some(drop(())).as_ref())] = [(); size_of::<*const ()>()];
#[allow(dropping_copy_types)]
let _ = size_of_expr!(Some(drop(())).as_ref());
// 3. Does not move the named stuff
struct NotCopy {}
let it = NotCopy {};
assert_eq!(size_of_expr!(it), 0);
drop(it);
// 4. Does not even borrow the named stuff
let mut it = ();
let r = &mut it;
assert_eq!(size_of_expr!(it), 0);
#[allow(dropping_references)]
drop(r);
}
}