diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 2963e5fa87..21daca4be5 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -332,20 +332,8 @@ pub fn derive_unaligned(ts: proc_macro::TokenStream) -> proc_macro::TokenStream // A struct is `TryFromBytes` if: // - all fields are `TryFromBytes` -// - the struct is NOT `repr(packed)` or `repr(packed(N))` fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { - let reprs = try_or_print!(repr::reprs::(&ast.attrs)); - - for (meta, repr) in reprs { - if matches!(repr, Repr::Packed | Repr::PackedN(_)) { - try_or_print!(Err(vec![Error::new_spanned( - meta, - "cannot derive TryFromBytes with repr(packed)", - )])); - } - } - let extras = Some({ let fields = strct.fields(); let field_names = fields.iter().map(|(name, _ty)| name); diff --git a/zerocopy-derive/tests/struct_try_from_bytes.rs b/zerocopy-derive/tests/struct_try_from_bytes.rs index 2a01b38010..18ae218d96 100644 --- a/zerocopy-derive/tests/struct_try_from_bytes.rs +++ b/zerocopy-derive/tests/struct_try_from_bytes.rs @@ -14,7 +14,7 @@ use std::{marker::PhantomData, option::IntoIter}; use { static_assertions::assert_impl_all, - zerocopy::{FromBytes, FromZeros, TryFromBytes}, + zerocopy::{FromBytes, FromZeros, KnownLayout, TryFromBytes}, }; use crate::util::AU16; @@ -145,3 +145,74 @@ where T: 'a + 'b + TryFromBytes; assert_impl_all!(WithParams<'static, 'static, 42, u8>: TryFromBytes); + +#[derive(Debug, PartialEq, Eq, TryFromBytes, KnownLayout)] +#[repr(C, packed)] +struct CPacked { + a: u8, + // NOTE: The `u32` type is not guaranteed to have alignment 4, although it + // does on many platforms. However, to fix this would require a custom type + // with a `#[repr(align(4))]` attribute, and `#[repr(packed)]` types are not + // allowed to transitively contain `#[repr(align(...))]` types. Thus, we + // have no choice but to use `u32` here. Luckily, these tests run in CI on + // platforms on which `u32` has alignment 4, so this isn't that big of a + // deal. + b: u32, +} + +#[test] +fn c_packed() { + let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF]; + let converted = CPacked::try_from_ref(candidate); + assert_eq!(converted, Some(&CPacked { a: 42, b: u32::MAX })); +} + +#[derive(TryFromBytes, KnownLayout)] +#[repr(C, packed)] +struct CPackedUnsized { + a: u8, + // NOTE: The `u32` type is not guaranteed to have alignment 4, although it + // does on many platforms. However, to fix this would require a custom type + // with a `#[repr(align(4))]` attribute, and `#[repr(packed)]` types are not + // allowed to transitively contain `#[repr(align(...))]` types. Thus, we + // have no choice but to use `u32` here. Luckily, these tests run in CI on + // platforms on which `u32` has alignment 4, so this isn't that big of a + // deal. + b: [u32], +} + +#[test] +fn c_packed_unsized() { + let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF]; + let converted = CPackedUnsized::try_from_ref(candidate); + assert!(converted.is_some()); +} + +#[derive(TryFromBytes)] +#[repr(packed)] +struct PackedUnsized { + a: u8, + // NOTE: The `u32` type is not guaranteed to have alignment 4, although it + // does on many platforms. However, to fix this would require a custom type + // with a `#[repr(align(4))]` attribute, and `#[repr(packed)]` types are not + // allowed to transitively contain `#[repr(align(...))]` types. Thus, we + // have no choice but to use `u32` here. Luckily, these tests run in CI on + // platforms on which `u32` has alignment 4, so this isn't that big of a + // deal. + b: [u32], +} + +#[test] +fn packed_unsized() { + let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF]; + let converted = CPackedUnsized::try_from_ref(candidate); + assert!(converted.is_some()); + + let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + let converted = CPackedUnsized::try_from_ref(candidate); + assert!(converted.is_none()); + + let candidate = &[42u8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]; + let converted = CPackedUnsized::try_from_ref(candidate); + assert!(converted.is_some()); +}