diff --git a/src/byteorder.rs b/src/byteorder.rs index bfbc7e567a..f433775c36 100644 --- a/src/byteorder.rs +++ b/src/byteorder.rs @@ -35,10 +35,10 @@ //! //! ```rust,edition2021 //! # #[cfg(feature = "derive")] { // This example uses derives, and won't compile without them -//! use zerocopy::{IntoBytes, ByteSlice, FromBytes, FromZeros, NoCell, Ref, Unaligned}; +//! use zerocopy::{IntoBytes, ByteSlice, FromBytes, NoCell, Ref, Unaligned}; //! use zerocopy::byteorder::network_endian::U16; //! -//! #[derive(FromZeros, FromBytes, IntoBytes, NoCell, Unaligned)] +//! #[derive(FromBytes, IntoBytes, NoCell, Unaligned)] //! #[repr(C)] //! struct UdpHeader { //! src_port: U16, @@ -357,7 +357,7 @@ example of how it can be used for parsing UDP packets. [`IntoBytes`]: crate::IntoBytes [`Unaligned`]: crate::Unaligned"), #[derive(Copy, Clone, Eq, PartialEq, Hash)] - #[cfg_attr(any(feature = "derive", test), derive(KnownLayout, NoCell, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned))] + #[cfg_attr(any(feature = "derive", test), derive(KnownLayout, NoCell, FromBytes, IntoBytes, Unaligned))] #[repr(transparent)] pub struct $name([u8; $bytes], PhantomData); } diff --git a/src/lib.rs b/src/lib.rs index e981c77c79..9a298b7394 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5766,9 +5766,7 @@ mod tests { // // This is used to test the custom derives of our traits. The `[u8]` type // gets a hand-rolled impl, so it doesn't exercise our custom derives. - #[derive( - Debug, Eq, PartialEq, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, NoCell, - )] + #[derive(Debug, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, NoCell)] #[repr(transparent)] struct Unsized([u8]); @@ -7896,7 +7894,7 @@ mod tests { assert_eq!(too_many_bytes[0], 123); } - #[derive(Debug, Eq, PartialEq, TryFromBytes, FromZeros, FromBytes, IntoBytes, NoCell)] + #[derive(Debug, Eq, PartialEq, FromBytes, IntoBytes, NoCell)] #[repr(C)] struct Foo { a: u32, @@ -7925,7 +7923,7 @@ mod tests { #[test] fn test_array() { - #[derive(TryFromBytes, FromZeros, FromBytes, IntoBytes, NoCell)] + #[derive(FromBytes, IntoBytes, NoCell)] #[repr(C)] struct Foo { a: [u16; 33], @@ -7989,7 +7987,7 @@ mod tests { #[test] fn test_transparent_packed_generic_struct() { - #[derive(IntoBytes, TryFromBytes, FromZeros, FromBytes, Unaligned)] + #[derive(IntoBytes, FromBytes, Unaligned)] #[repr(transparent)] struct Foo { _t: T, @@ -7999,7 +7997,7 @@ mod tests { assert_impl_all!(Foo: FromZeros, FromBytes, IntoBytes); assert_impl_all!(Foo: Unaligned); - #[derive(IntoBytes, TryFromBytes, FromZeros, FromBytes, Unaligned)] + #[derive(IntoBytes, FromBytes, Unaligned)] #[repr(packed)] struct Bar { _t: T, diff --git a/src/util.rs b/src/util.rs index a471c535da..b3a5796c4e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -220,8 +220,6 @@ pub(crate) mod testutil { #[derive( KnownLayout, NoCell, - TryFromBytes, - FromZeros, FromBytes, IntoBytes, Eq, @@ -249,9 +247,7 @@ pub(crate) mod testutil { } } - #[derive( - NoCell, FromZeros, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone, - )] + #[derive(NoCell, FromBytes, Eq, PartialEq, Ord, PartialOrd, Default, Debug, Copy, Clone)] #[repr(C)] pub(crate) struct Nested { _t: T, diff --git a/src/wrappers.rs b/src/wrappers.rs index 37e8becd99..978183cf57 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -53,7 +53,7 @@ use super::*; #[derive(Default, Copy)] #[cfg_attr( any(feature = "derive", test), - derive(NoCell, KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned) + derive(NoCell, KnownLayout, FromBytes, IntoBytes, Unaligned) )] #[repr(C, packed)] pub struct Unalign(T); diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 37a4050813..679fa0dbba 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -295,13 +295,16 @@ pub fn derive_try_from_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenSt #[proc_macro_derive(FromZeros)] pub fn derive_from_zeros(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { + let try_from_bytes = derive_try_from_bytes(ts.clone()); + let ast = syn::parse_macro_input!(ts as DeriveInput); - match &ast.data { + let from_zeros = match &ast.data { Data::Struct(strct) => derive_from_zeros_struct(&ast, strct), Data::Enum(enm) => derive_from_zeros_enum(&ast, enm), Data::Union(unn) => derive_from_zeros_union(&ast, unn), } - .into() + .into(); + IntoIterator::into_iter([try_from_bytes, from_zeros]).collect() } /// Deprecated: prefer [`FromZeros`] instead. @@ -314,13 +317,17 @@ pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStrea #[proc_macro_derive(FromBytes)] pub fn derive_from_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { + let from_zeros = derive_from_zeros(ts.clone()); + let ast = syn::parse_macro_input!(ts as DeriveInput); - match &ast.data { + let from_bytes = match &ast.data { Data::Struct(strct) => derive_from_bytes_struct(&ast, strct), Data::Enum(enm) => derive_from_bytes_enum(&ast, enm), Data::Union(unn) => derive_from_bytes_union(&ast, unn), } - .into() + .into(); + + IntoIterator::into_iter([from_zeros, from_bytes]).collect() } #[proc_macro_derive(IntoBytes)] @@ -447,25 +454,33 @@ fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2: .to_compile_error(); } - // We don't actually care what the repr is; we just care that it's one of - // the allowed ones. - try_or_print!(ENUM_TRY_FROM_BYTES_CFG.validate_reprs(ast)); + let reprs = try_or_print!(ENUM_TRY_FROM_BYTES_CFG.validate_reprs(ast)); + + // Figure out whether the enum could in theory implement `FromBytes`. + let from_bytes = enum_size_from_repr(reprs.as_slice()) + .map(|size| { + // As of this writing, `enm.is_fieldless()` is redundant since we've + // already checked for it and returned if the check failed. However, if + // we ever remove that check, then without a similar check here, this + // code would become unsound. + enm.is_fieldless() && enm.variants.len() == 1usize << size + }) + .unwrap_or(false); + let variant_names = enm.variants.iter().map(|v| &v.ident); - let extras = Some(quote!( - // SAFETY: We use `is_bit_valid` to validate that the bit pattern - // corresponds to one of the field-less enum's variant discriminants. - // Thus, this is a sound implementation of `is_bit_valid`. - fn is_bit_valid( - candidate: ::zerocopy::Ptr< - '_, - Self, - ( - ::zerocopy::pointer::invariant::Shared, - ::zerocopy::pointer::invariant::AnyAlignment, - ::zerocopy::pointer::invariant::Initialized, - ), - >, - ) -> ::zerocopy::macro_util::core_reexport::primitive::bool { + let is_bit_valid_body = if from_bytes { + // If the enum could implement `FromBytes`, we can avoid emitting a + // match statement. This is faster to compile, and generates code which + // performs better. + quote!({ + // Prevent an "unused" warning. + let _ = candidate; + // SAFETY: If the enum could implement `FromBytes`, then all bit + // patterns are valid. Thus, this is a sound implementation. + true + }) + } else { + quote!( use ::zerocopy::macro_util::core_reexport; // SAFETY: // - `cast` is implemented as required. @@ -499,6 +514,25 @@ fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2: // `candidate` refers to a bit-valid `Self`. discriminant == d })* + ) + }; + + let extras = Some(quote!( + // SAFETY: We use `is_bit_valid` to validate that the bit pattern + // corresponds to one of the field-less enum's variant discriminants. + // Thus, this is a sound implementation of `is_bit_valid`. + fn is_bit_valid( + candidate: ::zerocopy::Ptr< + '_, + Self, + ( + ::zerocopy::pointer::invariant::Shared, + ::zerocopy::pointer::invariant::AnyAlignment, + ::zerocopy::pointer::invariant::Initialized, + ), + >, + ) -> ::zerocopy::macro_util::core_reexport::primitive::bool { + #is_bit_valid_body } )); impl_block(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, extras) @@ -608,13 +642,9 @@ fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok let reprs = try_or_print!(ENUM_FROM_BYTES_CFG.validate_reprs(ast)); - let variants_required = match reprs.as_slice() { - [EnumRepr::U8] | [EnumRepr::I8] => 1usize << 8, - [EnumRepr::U16] | [EnumRepr::I16] => 1usize << 16, - // `validate_reprs` has already validated that it's one of the preceding - // patterns. - _ => unreachable!(), - }; + let variants_required = 1usize + << enum_size_from_repr(reprs.as_slice()) + .expect("internal error: `validate_reprs` has already validated that the reprs guarantee the enum's size"); if enm.variants.len() != variants_required { return Error::new_spanned( ast, @@ -629,6 +659,15 @@ fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok impl_block(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, None) } +// Returns `None` if the enum's size is not guaranteed by the repr. +fn enum_size_from_repr(reprs: &[EnumRepr]) -> Option { + match reprs { + [EnumRepr::U8] | [EnumRepr::I8] => Some(8), + [EnumRepr::U16] | [EnumRepr::I16] => Some(16), + _ => None, + } +} + #[rustfmt::skip] const ENUM_FROM_BYTES_CFG: Config = { use EnumRepr::*; diff --git a/zerocopy-derive/tests/enum_from_bytes.rs b/zerocopy-derive/tests/enum_from_bytes.rs index 48278b51c5..2fd33d5fcf 100644 --- a/zerocopy-derive/tests/enum_from_bytes.rs +++ b/zerocopy-derive/tests/enum_from_bytes.rs @@ -29,7 +29,7 @@ include!("include.rs"); // `Variant128` has a discriminant of -128) since Rust won't automatically wrap // a signed discriminant around without you explicitly telling it to. -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(u8)] enum FooU8 { Variant0, @@ -292,7 +292,7 @@ enum FooU8 { util_assert_impl_all!(FooU8: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(i8)] enum FooI8 { Variant0, @@ -555,7 +555,7 @@ enum FooI8 { util_assert_impl_all!(FooI8: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(u8, align(2))] enum FooU8Align { Variant0, @@ -818,7 +818,7 @@ enum FooU8Align { util_assert_impl_all!(FooU8Align: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(i8, align(2))] enum FooI8Align { Variant0, @@ -1081,7 +1081,7 @@ enum FooI8Align { util_assert_impl_all!(FooI8Align: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(u16)] enum FooU16 { Variant0, @@ -66624,7 +66624,7 @@ enum FooU16 { util_assert_impl_all!(FooU16: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(i16)] enum FooI16 { Variant0, diff --git a/zerocopy-derive/tests/hygiene.rs b/zerocopy-derive/tests/hygiene.rs index 344be994ee..9c3671d8f0 100644 --- a/zerocopy-derive/tests/hygiene.rs +++ b/zerocopy-derive/tests/hygiene.rs @@ -18,14 +18,7 @@ include!("include.rs"); extern crate zerocopy as _zerocopy; -// #[macro_use] -// mod util; - -// use std::{marker::PhantomData, option::IntoIter}; - -#[derive( - _zerocopy::KnownLayout, _zerocopy::FromZeros, _zerocopy::FromBytes, _zerocopy::Unaligned, -)] +#[derive(_zerocopy::KnownLayout, _zerocopy::FromBytes, _zerocopy::Unaligned)] #[repr(C)] struct TypeParams<'a, T, I: imp::Iterator> { a: T, diff --git a/zerocopy-derive/tests/include.rs b/zerocopy-derive/tests/include.rs index c732f72312..265aee1a27 100644 --- a/zerocopy-derive/tests/include.rs +++ b/zerocopy-derive/tests/include.rs @@ -47,8 +47,6 @@ pub mod util { #[derive( super::imp::KnownLayout, super::imp::NoCell, - super::imp::TryFromBytes, - super::imp::FromZeros, super::imp::FromBytes, super::imp::IntoBytes, Copy, diff --git a/zerocopy-derive/tests/paths_and_modules.rs b/zerocopy-derive/tests/paths_and_modules.rs index 34f4067603..74f28558d6 100644 --- a/zerocopy-derive/tests/paths_and_modules.rs +++ b/zerocopy-derive/tests/paths_and_modules.rs @@ -17,13 +17,13 @@ include!("include.rs"); mod foo { use super::*; - #[derive(imp::FromZeros, imp::FromBytes, imp::IntoBytes, imp::Unaligned)] + #[derive(imp::FromBytes, imp::IntoBytes, imp::Unaligned)] #[repr(C)] pub struct Foo { foo: u8, } - #[derive(imp::FromZeros, imp::FromBytes, imp::IntoBytes, imp::Unaligned)] + #[derive(imp::FromBytes, imp::IntoBytes, imp::Unaligned)] #[repr(C)] pub struct Bar { bar: u8, @@ -32,7 +32,7 @@ mod foo { use foo::Foo; -#[derive(imp::FromZeros, imp::FromBytes, imp::IntoBytes, imp::Unaligned)] +#[derive(imp::FromBytes, imp::IntoBytes, imp::Unaligned)] #[repr(C)] struct Baz { foo: Foo, diff --git a/zerocopy-derive/tests/struct_from_bytes.rs b/zerocopy-derive/tests/struct_from_bytes.rs index 4a27752cc3..680b1c9e3c 100644 --- a/zerocopy-derive/tests/struct_from_bytes.rs +++ b/zerocopy-derive/tests/struct_from_bytes.rs @@ -15,19 +15,19 @@ include!("include.rs"); // A struct is `FromBytes` if: // - all fields are `FromBytes` -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] struct Zst; util_assert_impl_all!(Zst: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] struct One { a: u8, } util_assert_impl_all!(One: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] struct Two { a: u8, b: Zst, @@ -35,14 +35,14 @@ struct Two { util_assert_impl_all!(Two: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] struct Unsized { a: [u8], } util_assert_impl_all!(Unsized: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] struct TypeParams<'a, T: ?imp::Sized, I: imp::Iterator> { a: I::Item, b: u8, @@ -58,7 +58,7 @@ util_assert_impl_all!(TypeParams<'static, [util::AU16], imp::IntoIter<()>>: imp: // Deriving `FromBytes` should work if the struct has bounded parameters. -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(transparent)] struct WithParams<'a: 'b, 'b: 'a, T: 'a + 'b + imp::FromBytes, const N: usize>( [T; N], diff --git a/zerocopy-derive/tests/struct_try_from_bytes.rs b/zerocopy-derive/tests/struct_try_from_bytes.rs index 72a348af33..3b05c8f3fd 100644 --- a/zerocopy-derive/tests/struct_try_from_bytes.rs +++ b/zerocopy-derive/tests/struct_try_from_bytes.rs @@ -26,7 +26,7 @@ fn zst() { imp::assert!(is_bit_valid); } -#[derive(imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(C)] struct One { a: u8, @@ -45,7 +45,7 @@ fn one() { imp::assert!(is_bit_valid); } -#[derive(imp::TryFromBytes, imp::FromZeros)] +#[derive(imp::FromZeros)] #[repr(C)] struct Two { a: bool, @@ -88,7 +88,7 @@ fn two_bad() { imp::assert!(!is_bit_valid); } -#[derive(imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(C)] struct Unsized { a: [u8], @@ -119,7 +119,7 @@ fn un_sized() { imp::assert!(is_bit_valid); } -#[derive(imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(C)] struct TypeParams<'a, T: ?imp::Sized, I: imp::Iterator> { a: I::Item, @@ -136,7 +136,7 @@ util_assert_impl_all!(TypeParams<'static, [util::AU16], imp::IntoIter<()>>: imp: // Deriving `imp::TryFromBytes` should work if the struct has bounded parameters. -#[derive(imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::FromBytes)] #[repr(transparent)] struct WithParams<'a: 'b, 'b: 'a, T: 'a + 'b + imp::TryFromBytes, const N: usize>( imp::PhantomData<&'a &'b ()>, diff --git a/zerocopy-derive/tests/union_from_bytes.rs b/zerocopy-derive/tests/union_from_bytes.rs index 0bafc5d0e6..cc868bcd39 100644 --- a/zerocopy-derive/tests/union_from_bytes.rs +++ b/zerocopy-derive/tests/union_from_bytes.rs @@ -15,21 +15,21 @@ include!("include.rs"); // A union is `imp::FromBytes` if: // - all fields are `imp::FromBytes` -#[derive(Clone, Copy, imp::FromZeros, imp::FromBytes)] +#[derive(Clone, Copy, imp::NoCell, imp::FromBytes)] union Zst { a: (), } util_assert_impl_all!(Zst: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] union One { a: u8, } util_assert_impl_all!(One: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] union Two { a: u8, b: Zst, @@ -37,7 +37,7 @@ union Two { util_assert_impl_all!(Two: imp::FromBytes); -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] union TypeParams<'a, T: imp::Copy, I: imp::Iterator> where I::Item: imp::Copy, @@ -54,7 +54,7 @@ util_assert_impl_all!(TypeParams<'static, (), imp::IntoIter<()>>: imp::FromBytes // Deriving `imp::FromBytes` should work if the union has bounded parameters. -#[derive(imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] #[repr(C)] union WithParams<'a: 'b, 'b: 'a, T: 'a + 'b + imp::FromBytes, const N: usize> where diff --git a/zerocopy-derive/tests/union_from_zeros.rs b/zerocopy-derive/tests/union_from_zeros.rs index 12234d5ccf..872b0a524b 100644 --- a/zerocopy-derive/tests/union_from_zeros.rs +++ b/zerocopy-derive/tests/union_from_zeros.rs @@ -15,21 +15,21 @@ include!("include.rs"); // A union is `imp::FromZeros` if: // - all fields are `imp::FromZeros` -#[derive(Clone, Copy, imp::FromZeros)] +#[derive(Clone, Copy, imp::NoCell, imp::FromZeros)] union Zst { a: (), } util_assert_impl_all!(Zst: imp::FromZeros); -#[derive(imp::FromZeros)] +#[derive(imp::NoCell, imp::FromZeros)] union One { a: bool, } util_assert_impl_all!(One: imp::FromZeros); -#[derive(imp::FromZeros)] +#[derive(imp::NoCell, imp::FromZeros)] union Two { a: bool, b: Zst, @@ -37,7 +37,7 @@ union Two { util_assert_impl_all!(Two: imp::FromZeros); -#[derive(imp::FromZeros)] +#[derive(imp::NoCell, imp::FromZeros)] union TypeParams<'a, T: imp::Copy, I: imp::Iterator> where I::Item: imp::Copy, @@ -54,7 +54,7 @@ util_assert_impl_all!(TypeParams<'static, (), imp::IntoIter<()>>: imp::FromZeros // Deriving `imp::FromZeros` should work if the union has bounded parameters. -#[derive(imp::FromZeros)] +#[derive(imp::NoCell, imp::FromZeros)] #[repr(C)] union WithParams<'a: 'b, 'b: 'a, T: 'a + 'b + imp::FromZeros, const N: usize> where diff --git a/zerocopy-derive/tests/union_try_from_bytes.rs b/zerocopy-derive/tests/union_try_from_bytes.rs index a0dd29f3ff..f1a3b53a7f 100644 --- a/zerocopy-derive/tests/union_try_from_bytes.rs +++ b/zerocopy-derive/tests/union_try_from_bytes.rs @@ -15,7 +15,7 @@ include!("include.rs"); // A struct is `imp::TryFromBytes` if: // - any of its fields are `imp::TryFromBytes` -#[derive(imp::NoCell, imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] union One { a: u8, } @@ -33,7 +33,7 @@ fn one() { assert!(is_bit_valid); } -#[derive(imp::NoCell, imp::TryFromBytes, imp::FromZeros)] +#[derive(imp::NoCell, imp::FromZeros)] #[repr(C)] union Two { a: bool, @@ -83,7 +83,7 @@ fn two_bad() { assert!(!is_bit_valid); } -#[derive(imp::NoCell, imp::TryFromBytes, imp::FromZeros)] +#[derive(imp::NoCell, imp::FromZeros)] #[repr(C)] union BoolAndZst { a: bool, @@ -113,7 +113,7 @@ fn bool_and_zst() { assert!(is_bit_valid); } -#[derive(imp::NoCell, imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] #[repr(C)] union TypeParams<'a, T: imp::Copy, I: imp::Iterator> where @@ -133,7 +133,7 @@ util_assert_impl_all!(TypeParams<'static, [util::AU16; 2], imp::IntoIter<()>>: i // Deriving `imp::TryFromBytes` should work if the union has bounded parameters. -#[derive(imp::NoCell, imp::TryFromBytes, imp::FromZeros, imp::FromBytes)] +#[derive(imp::NoCell, imp::FromBytes)] #[repr(C)] union WithParams<'a: 'b, 'b: 'a, T: 'a + 'b + imp::TryFromBytes, const N: usize> where