From aa9d0149c8365e0667f276eca6b1c5029bfb5614 Mon Sep 17 00:00:00 2001 From: Matt Jan Date: Sun, 9 Jun 2024 17:01:38 +0800 Subject: [PATCH] Implement Inline Size Assertion Annotations Implem the procedural macro `inline_assert_size_eq` to check the size of a type at compile time. Additionally, add the test file `inline_assert_size_eq_failed.rs` to ensure that the macro tirggers a compile-time error when the size is not as expected Fixes #1329 --- .../inline_assert_size_eq_failed.rs | 9 +++++ .../inline_assert_size_eq_failed.stderr | 7 ++++ .../ui-stable/inline_assert_size_eq_failed.rs | 1 + .../inline_assert_size_eq_failed.stderr | 7 ++++ zerocopy-derive/src/lib.rs | 39 +++++++++++++++++++ 5 files changed, 63 insertions(+) create mode 100644 tests/ui-nightly/inline_assert_size_eq_failed.rs create mode 100644 tests/ui-nightly/inline_assert_size_eq_failed.stderr create mode 120000 tests/ui-stable/inline_assert_size_eq_failed.rs create mode 100644 tests/ui-stable/inline_assert_size_eq_failed.stderr diff --git a/tests/ui-nightly/inline_assert_size_eq_failed.rs b/tests/ui-nightly/inline_assert_size_eq_failed.rs new file mode 100644 index 0000000000..c31b05c640 --- /dev/null +++ b/tests/ui-nightly/inline_assert_size_eq_failed.rs @@ -0,0 +1,9 @@ +extern crate zerocopy_derive; +use zerocopy_derive::inline_assert_size_eq; + +#[inline_assert_size_eq(1)] +struct TestSizeNeqStruct { + i: i32, +} + +fn main() {} diff --git a/tests/ui-nightly/inline_assert_size_eq_failed.stderr b/tests/ui-nightly/inline_assert_size_eq_failed.stderr new file mode 100644 index 0000000000..ab2a94ef4b --- /dev/null +++ b/tests/ui-nightly/inline_assert_size_eq_failed.stderr @@ -0,0 +1,7 @@ +error[E0080]: evaluation of constant value failed + --> tests/ui-nightly/inline_assert_size_eq_failed.rs:4:1 + | +4 | #[inline_assert_size_eq(1)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow + | + = note: this error originates in the macro `static_assertions::const_assert` which comes from the expansion of the attribute macro `inline_assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/inline_assert_size_eq_failed.rs b/tests/ui-stable/inline_assert_size_eq_failed.rs new file mode 120000 index 0000000000..12ea739f7b --- /dev/null +++ b/tests/ui-stable/inline_assert_size_eq_failed.rs @@ -0,0 +1 @@ +../ui-nightly/inline_assert_size_eq_failed.rs \ No newline at end of file diff --git a/tests/ui-stable/inline_assert_size_eq_failed.stderr b/tests/ui-stable/inline_assert_size_eq_failed.stderr new file mode 100644 index 0000000000..c4acb505ad --- /dev/null +++ b/tests/ui-stable/inline_assert_size_eq_failed.stderr @@ -0,0 +1,7 @@ +error[E0080]: evaluation of constant value failed + --> tests/ui-stable/inline_assert_size_eq_failed.rs:4:1 + | +4 | #[inline_assert_size_eq(1)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow + | + = note: this error originates in the macro `static_assertions::const_assert` which comes from the expansion of the attribute macro `inline_assert_size_eq` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 2d2515613a..a53aedd0ec 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -55,6 +55,45 @@ macro_rules! try_or_print { }; } +/// Inline size assertion annotations +/// Any non-generic type can be check if the size is as expected or not in compile time +/// # Implementation +/// ``` +/// # use zerocopy_derive::inline_assert_size_eq; +/// # use static_assertions; +/// #[inline_assert_size_eq(4)] +/// struct MyStruct { +/// val: i32 +/// } +/// ``` +/// # Example that will fail +/// ```compile_fail +/// # use zero_derive::inline_assert_size_eq; +/// # use static_assertions; +/// #[inline_assert_size_eq(1)] +/// struct MyStruct { +/// val: i32 +/// } +/// ``` +#[proc_macro_attribute] +pub fn inline_assert_size_eq( + args: proc_macro::TokenStream, + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let ast: Result = syn::parse(input.clone()); + let expected_size: Result = args.to_string().trim().parse(); + let mut ret: proc_macro::TokenStream = input; + if let (Ok(ast), Ok(size)) = (ast, expected_size) { + let name = &ast.ident; + ret = quote! { + #ast + static_assertions::const_assert!(core::mem::size_of::<#name>() == #size); + } + .into() + } + ret +} + // TODO(https://github.com/rust-lang/rust/issues/54140): Some errors could be // made better if we could add multiple lines of error output like this: //