From 34f90918e542bbc58be5420664c7d4c994c1c1a8 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 15 Sep 2024 17:22:02 -0700 Subject: [PATCH] [derive] Test TryFromBytes output on dataful enums Makes progress on #367, #1634 --- zerocopy-derive/src/enum.rs | 8 +- zerocopy-derive/src/lib.rs | 2 +- zerocopy-derive/src/output_tests.rs | 387 +++++++++++++++++++++++++++- 3 files changed, 389 insertions(+), 8 deletions(-) diff --git a/zerocopy-derive/src/enum.rs b/zerocopy-derive/src/enum.rs index 0f9907fb1e..dd0cdd1234 100644 --- a/zerocopy-derive/src/enum.rs +++ b/zerocopy-derive/src/enum.rs @@ -59,7 +59,7 @@ pub(crate) fn generate_tag_enum(repr: &EnumRepr, data: &DataEnum) -> TokenStream #[repr(#repr)] #[allow(dead_code)] enum ___ZerocopyTag { - #(#variants),* + #(#variants,)* } } } @@ -305,9 +305,9 @@ pub(crate) fn derive_is_bit_valid( #tag_enum - type ___ZerocopyTagPrimitive = ::zerocopy::util::macro_util::SizeToTag<{ - core_reexport::mem::size_of::<___ZerocopyTag>() - }>; + type ___ZerocopyTagPrimitive = ::zerocopy::util::macro_util::SizeToTag< + { core_reexport::mem::size_of::<___ZerocopyTag>() }, + >; #tag_consts diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index c52c9cb829..23ab3f15b2 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -422,7 +422,7 @@ fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_m // validities of its fields, so this is a sound implementation of // `is_bit_valid`. fn is_bit_valid >( - mut candidate: ::zerocopy::Maybe + mut candidate: ::zerocopy::Maybe, ) -> bool { true #(&& { // SAFETY: diff --git a/zerocopy-derive/src/output_tests.rs b/zerocopy-derive/src/output_tests.rs index 8ca9bd2728..7285601417 100644 --- a/zerocopy-derive/src/output_tests.rs +++ b/zerocopy-derive/src/output_tests.rs @@ -92,7 +92,7 @@ fn test_try_from_bytes() { A: ::zerocopy::pointer::invariant::Aliasing + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared> >( - mut candidate: ::zerocopy::Maybe + mut candidate: ::zerocopy::Maybe, ) -> bool { true } @@ -115,7 +115,7 @@ fn test_from_zeros() { A: ::zerocopy::pointer::invariant::Aliasing + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared> >( - mut candidate: ::zerocopy::Maybe + mut candidate: ::zerocopy::Maybe, ) -> bool { true } @@ -143,7 +143,7 @@ fn test_from_bytes() { A: ::zerocopy::pointer::invariant::Aliasing + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared> >( - mut candidate: ::zerocopy::Maybe + mut candidate: ::zerocopy::Maybe, ) -> bool { true } @@ -191,3 +191,384 @@ fn test_unaligned() { } no_build } } + +#[test] +fn test_try_from_bytes_enum() { + test_derive! { + TryFromBytes { + #[repr(u8)] + enum ComplexWithGenerics { + UnitLike, + StructLike { a: u8, b: X }, + TupleLike(bool, Y), + } + } expands to { + #[allow(deprecated)] + unsafe impl ::zerocopy::TryFromBytes for ComplexWithGenerics + where + u8: ::zerocopy::TryFromBytes, + X: ::zerocopy::TryFromBytes, + bool: ::zerocopy::TryFromBytes, + Y: ::zerocopy::TryFromBytes, + { + fn only_derive_is_allowed_to_implement_this_trait() {} + fn is_bit_valid( + mut candidate: ::zerocopy::Maybe<'_, Self, A>, + ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { + use ::zerocopy::util::macro_util::core_reexport; + #[repr(u8)] + #[allow(dead_code)] + enum ___ZerocopyTag { + UnitLike, + StructLike, + TupleLike, + } + type ___ZerocopyTagPrimitive = ::zerocopy::util::macro_util::SizeToTag< + { core_reexport::mem::size_of::<___ZerocopyTag>() }, + >; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_UnitLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::UnitLike as ___ZerocopyTagPrimitive; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_StructLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::StructLike as ___ZerocopyTagPrimitive; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_TupleLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::TupleLike as ___ZerocopyTagPrimitive; + type ___ZerocopyOuterTag = (); + type ___ZerocopyInnerTag = ___ZerocopyTag; + #[repr(C)] + #[allow(non_snake_case)] + #[derive(:: zerocopy_derive :: TryFromBytes)] + struct ___ZerocopyVariantStruct_StructLike( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + u8, + X, + core_reexport::marker::PhantomData >, + ); + #[repr(C)] + #[allow(non_snake_case)] + #[derive(:: zerocopy_derive :: TryFromBytes)] + struct ___ZerocopyVariantStruct_TupleLike( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + bool, + Y, + core_reexport::marker::PhantomData >, + ); + #[repr(C)] + #[allow(non_snake_case)] + union ___ZerocopyVariants { + __field_StructLike: + core_reexport::mem::ManuallyDrop<___ZerocopyVariantStruct_StructLike >, + __field_TupleLike: + core_reexport::mem::ManuallyDrop<___ZerocopyVariantStruct_TupleLike >, + __nonempty: (), + } + #[repr(C)] + struct ___ZerocopyRawEnum { + tag: ___ZerocopyOuterTag, + variants: ___ZerocopyVariants, + } + let tag = { + let tag_ptr = unsafe { + candidate.reborrow().cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyTagPrimitive }) + }; + let tag_ptr = unsafe { tag_ptr.assume_initialized() }; + tag_ptr.bikeshed_recall_valid().read_unaligned() + }; + let raw_enum = + unsafe { candidate.cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyRawEnum }) }; + let raw_enum = unsafe { raw_enum.assume_initialized() }; + let variants = unsafe { + raw_enum.project(|p: *mut ___ZerocopyRawEnum| { + core_reexport::ptr::addr_of_mut!((*p).variants) + }) + }; + #[allow(non_upper_case_globals)] + match tag { + ___ZEROCOPY_TAG_UnitLike => true, + ___ZEROCOPY_TAG_StructLike => { + let variant = unsafe { + variants.cast_unsized(|p: *mut ___ZerocopyVariants| { + p as *mut ___ZerocopyVariantStruct_StructLike + }) + }; + let variant = unsafe { variant.assume_initialized() }; + < ___ZerocopyVariantStruct_StructLike < X , Y > as :: zerocopy :: TryFromBytes > :: is_bit_valid ( + variant + ) + }, + ___ZEROCOPY_TAG_TupleLike => { + let variant = unsafe { + variants.cast_unsized(|p: *mut ___ZerocopyVariants| { + p as *mut ___ZerocopyVariantStruct_TupleLike + }) + }; + let variant = unsafe { variant.assume_initialized() }; + <___ZerocopyVariantStruct_TupleLike as ::zerocopy::TryFromBytes>::is_bit_valid( + variant + ) + }, + _ => false, + } + } + } + } no_build + } + + test_derive! { + TryFromBytes { + #[repr(u32)] + enum ComplexWithGenerics { + UnitLike, + StructLike { a: u8, b: X }, + TupleLike(bool, Y), + } + } expands to { + #[allow(deprecated)] + unsafe impl ::zerocopy::TryFromBytes for ComplexWithGenerics + where + u8: ::zerocopy::TryFromBytes, + X: ::zerocopy::TryFromBytes, + bool: ::zerocopy::TryFromBytes, + Y: ::zerocopy::TryFromBytes, + { + fn only_derive_is_allowed_to_implement_this_trait() {} + fn is_bit_valid( + mut candidate: ::zerocopy::Maybe<'_, Self, A>, + ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { + use ::zerocopy::util::macro_util::core_reexport; + #[repr(u32)] + #[allow(dead_code)] + enum ___ZerocopyTag { + UnitLike, + StructLike, + TupleLike, + } + type ___ZerocopyTagPrimitive = ::zerocopy::util::macro_util::SizeToTag< + { core_reexport::mem::size_of::<___ZerocopyTag>() }, + >; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_UnitLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::UnitLike as ___ZerocopyTagPrimitive; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_StructLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::StructLike as ___ZerocopyTagPrimitive; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_TupleLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::TupleLike as ___ZerocopyTagPrimitive; + type ___ZerocopyOuterTag = (); + type ___ZerocopyInnerTag = ___ZerocopyTag; + #[repr(C)] + #[allow(non_snake_case)] + #[derive(:: zerocopy_derive :: TryFromBytes)] + struct ___ZerocopyVariantStruct_StructLike( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + u8, + X, + core_reexport::marker::PhantomData >, + ); + #[repr(C)] + #[allow(non_snake_case)] + #[derive(:: zerocopy_derive :: TryFromBytes)] + struct ___ZerocopyVariantStruct_TupleLike( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + bool, + Y, + core_reexport::marker::PhantomData >, + ); + #[repr(C)] + #[allow(non_snake_case)] + union ___ZerocopyVariants { + __field_StructLike: + core_reexport::mem::ManuallyDrop<___ZerocopyVariantStruct_StructLike >, + __field_TupleLike: + core_reexport::mem::ManuallyDrop<___ZerocopyVariantStruct_TupleLike >, + __nonempty: (), + } + #[repr(C)] + struct ___ZerocopyRawEnum { + tag: ___ZerocopyOuterTag, + variants: ___ZerocopyVariants, + } + let tag = { + let tag_ptr = unsafe { + candidate.reborrow().cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyTagPrimitive }) + }; + let tag_ptr = unsafe { tag_ptr.assume_initialized() }; + tag_ptr.bikeshed_recall_valid().read_unaligned() + }; + let raw_enum = + unsafe { candidate.cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyRawEnum }) }; + let raw_enum = unsafe { raw_enum.assume_initialized() }; + let variants = unsafe { + raw_enum.project(|p: *mut ___ZerocopyRawEnum| { + core_reexport::ptr::addr_of_mut!((*p).variants) + }) + }; + #[allow(non_upper_case_globals)] + match tag { + ___ZEROCOPY_TAG_UnitLike => true, + ___ZEROCOPY_TAG_StructLike => { + let variant = unsafe { + variants.cast_unsized(|p: *mut ___ZerocopyVariants| { + p as *mut ___ZerocopyVariantStruct_StructLike + }) + }; + let variant = unsafe { variant.assume_initialized() }; + < ___ZerocopyVariantStruct_StructLike < X , Y > as :: zerocopy :: TryFromBytes > :: is_bit_valid ( + variant + ) + }, + ___ZEROCOPY_TAG_TupleLike => { + let variant = unsafe { + variants.cast_unsized(|p: *mut ___ZerocopyVariants| { + p as *mut ___ZerocopyVariantStruct_TupleLike + }) + }; + let variant = unsafe { variant.assume_initialized() }; + <___ZerocopyVariantStruct_TupleLike as ::zerocopy::TryFromBytes>::is_bit_valid( + variant + ) + }, + _ => false, + } + } + } + } no_build + } + + test_derive! { + TryFromBytes { + #[repr(C)] + enum ComplexWithGenerics { + UnitLike, + StructLike { a: u8, b: X }, + TupleLike(bool, Y), + } + } expands to { + #[allow(deprecated)] + unsafe impl ::zerocopy::TryFromBytes for ComplexWithGenerics + where + u8: ::zerocopy::TryFromBytes, + X: ::zerocopy::TryFromBytes, + bool: ::zerocopy::TryFromBytes, + Y: ::zerocopy::TryFromBytes, + { + fn only_derive_is_allowed_to_implement_this_trait() {} + fn is_bit_valid( + mut candidate: ::zerocopy::Maybe<'_, Self, A>, + ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { + use ::zerocopy::util::macro_util::core_reexport; + #[repr(C)] + #[allow(dead_code)] + enum ___ZerocopyTag { + UnitLike, + StructLike, + TupleLike, + } + type ___ZerocopyTagPrimitive = ::zerocopy::util::macro_util::SizeToTag< + { core_reexport::mem::size_of::<___ZerocopyTag>() }, + >; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_UnitLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::UnitLike as ___ZerocopyTagPrimitive; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_StructLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::StructLike as ___ZerocopyTagPrimitive; + #[allow(non_upper_case_globals)] + const ___ZEROCOPY_TAG_TupleLike: ___ZerocopyTagPrimitive = + ___ZerocopyTag::TupleLike as ___ZerocopyTagPrimitive; + type ___ZerocopyOuterTag = ___ZerocopyTag; + type ___ZerocopyInnerTag = (); + #[repr(C)] + #[allow(non_snake_case)] + #[derive(:: zerocopy_derive :: TryFromBytes)] + struct ___ZerocopyVariantStruct_StructLike( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + u8, + X, + core_reexport::marker::PhantomData >, + ); + #[repr(C)] + #[allow(non_snake_case)] + #[derive(:: zerocopy_derive :: TryFromBytes)] + struct ___ZerocopyVariantStruct_TupleLike( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + bool, + Y, + core_reexport::marker::PhantomData >, + ); + #[repr(C)] + #[allow(non_snake_case)] + union ___ZerocopyVariants { + __field_StructLike: + core_reexport::mem::ManuallyDrop<___ZerocopyVariantStruct_StructLike >, + __field_TupleLike: + core_reexport::mem::ManuallyDrop<___ZerocopyVariantStruct_TupleLike >, + __nonempty: (), + } + #[repr(C)] + struct ___ZerocopyRawEnum { + tag: ___ZerocopyOuterTag, + variants: ___ZerocopyVariants, + } + let tag = { + let tag_ptr = unsafe { + candidate.reborrow().cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyTagPrimitive }) + }; + let tag_ptr = unsafe { tag_ptr.assume_initialized() }; + tag_ptr.bikeshed_recall_valid().read_unaligned() + }; + let raw_enum = + unsafe { candidate.cast_unsized(|p: *mut Self| { p as *mut ___ZerocopyRawEnum }) }; + let raw_enum = unsafe { raw_enum.assume_initialized() }; + let variants = unsafe { + raw_enum.project(|p: *mut ___ZerocopyRawEnum| { + core_reexport::ptr::addr_of_mut!((*p).variants) + }) + }; + #[allow(non_upper_case_globals)] + match tag { + ___ZEROCOPY_TAG_UnitLike => true, + ___ZEROCOPY_TAG_StructLike => { + let variant = unsafe { + variants.cast_unsized(|p: *mut ___ZerocopyVariants| { + p as *mut ___ZerocopyVariantStruct_StructLike + }) + }; + let variant = unsafe { variant.assume_initialized() }; + < ___ZerocopyVariantStruct_StructLike < X , Y > as :: zerocopy :: TryFromBytes > :: is_bit_valid ( + variant + ) + }, + ___ZEROCOPY_TAG_TupleLike => { + let variant = unsafe { + variants.cast_unsized(|p: *mut ___ZerocopyVariants| { + p as *mut ___ZerocopyVariantStruct_TupleLike + }) + }; + let variant = unsafe { variant.assume_initialized() }; + <___ZerocopyVariantStruct_TupleLike as ::zerocopy::TryFromBytes>::is_bit_valid( + variant + ) + }, + _ => false, + } + } + } + } no_build + } +}