From cecacd9a03bb682326e15f5c7a715659fba53dbf Mon Sep 17 00:00:00 2001 From: David Koloski Date: Wed, 14 Feb 2024 12:38:59 -0500 Subject: [PATCH] Add support for data-carrying enums --- .vscode/settings.json | 16 +- src/impls.rs | 10 +- src/macro_util.rs | 165 ++++++++- zerocopy-derive/src/enums.rs | 359 +++++++++++++++++++ zerocopy-derive/src/ext.rs | 58 ++- zerocopy-derive/src/lib.rs | 313 ++++++++-------- zerocopy-derive/src/repr.rs | 30 ++ zerocopy-derive/tests/enum_from_bytes.rs | 261 ++++++++++++++ zerocopy-derive/tests/enum_from_zeros.rs | 53 ++- zerocopy-derive/tests/enum_to_bytes.rs | 20 ++ zerocopy-derive/tests/enum_try_from_bytes.rs | 84 +++++ zerocopy-derive/tests/ui-msrv/enum.stderr | 288 ++++++++++----- zerocopy-derive/tests/ui-nightly/enum.rs | 316 +++++++++++++++- zerocopy-derive/tests/ui-nightly/enum.stderr | 330 ++++++++++++----- zerocopy-derive/tests/ui-stable/enum.stderr | 298 ++++++++++----- 15 files changed, 2150 insertions(+), 451 deletions(-) create mode 100644 zerocopy-derive/src/enums.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 2bbd0790ca..bb94ee84e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,8 @@ -{ - "rust-analyzer.cargo.cfgs": { - // This adds an extra cfg to rust-analyzer so we can prevent it from - // analyzing tests with a lot of generated code. These can cause it to - // hang or run slowly in general, which is bad for workflows. - "rust_analyzer": null, - } -} +{ + "rust-analyzer.cargo.cfgs": { + // This adds an extra cfg to rust-analyzer so we can prevent it from + // analyzing tests with a lot of generated code. These can cause it to + // hang or run slowly in general, which is bad for workflows. + "rust_analyzer": null, + } +} diff --git a/src/impls.rs b/src/impls.rs index 92ad671eda..e17d04a713 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1731,7 +1731,15 @@ mod tests { Unaligned, !FromBytes ); - assert_impls!([NotZerocopy]: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned); + assert_impls!( + [NotZerocopy]: KnownLayout, + !Immutable, + !TryFromBytes, + !FromZeros, + !FromBytes, + !IntoBytes, + !Unaligned + ); assert_impls!( [u8; 0]: KnownLayout, Immutable, diff --git a/src/macro_util.rs b/src/macro_util.rs index 15ccb5e261..84b5b03024 100644 --- a/src/macro_util.rs +++ b/src/macro_util.rs @@ -256,6 +256,32 @@ macro_rules! align_of { }}; } +mod size_to_tag { + pub trait SizeToTag { + type Tag; + } + + impl SizeToTag<1> for () { + type Tag = u8; + } + impl SizeToTag<2> for () { + type Tag = u16; + } + impl SizeToTag<4> for () { + type Tag = u32; + } + impl SizeToTag<8> for () { + type Tag = u64; + } + impl SizeToTag<16> for () { + type Tag = u128; + } +} + +/// An alias for the unsigned integer of the given size in bytes. +#[doc(hidden)] +pub type SizeToTag = <() as size_to_tag::SizeToTag>::Tag; + /// Does the struct type `$t` have padding? /// /// `$ts` is the list of the type of every field in `$t`. `$t` must be a @@ -271,7 +297,7 @@ macro_rules! align_of { #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! struct_has_padding { - ($t:ty, $($ts:ty),*) => { + ($t:ty, [$($ts:ty),*]) => { ::zerocopy::macro_util::core_reexport::mem::size_of::<$t>() > 0 $(+ ::zerocopy::macro_util::core_reexport::mem::size_of::<$ts>())* }; } @@ -291,11 +317,38 @@ macro_rules! struct_has_padding { #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! union_has_padding { - ($t:ty, $($ts:ty),*) => { + ($t:ty, [$($ts:ty),*]) => { false $(|| ::zerocopy::macro_util::core_reexport::mem::size_of::<$t>() != ::zerocopy::macro_util::core_reexport::mem::size_of::<$ts>())* }; } +/// Does the enum type `$t` have padding? +/// +/// `$disc` is the type of the discriminant for the enum, and `$ts` is a list of +/// fields in each square-bracket-deiminated variant. `$t` must be an enum, or +/// else `enum_has_padding!`'s result may be meaningless. An enum has padding if +/// any of its variant structs contain padding, and so all of the variants of an +/// enum must be "full" in order for the enum to not have padding. +/// +/// The results of `enum_has_padding!` require that the enum is not +/// `repr(Rust)`, as `repr(Rust)` enums may niche the enum's tag and reduce the +/// total number of bytes required to represent the enum as a result. As long as +/// the enum is `repr(C)`, `repr(int)`, or `repr(C, int)`, this will +/// consistently return whether the enum contains any padding bytes. +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! enum_has_padding { + ($t:ty, $disc:ty, $([$($ts:ty),*]),*) => { + false $( + || ::zerocopy::macro_util::core_reexport::mem::size_of::<$t>() + != ( + ::zerocopy::macro_util::core_reexport::mem::size_of::<$disc>() + $(+ ::zerocopy::macro_util::core_reexport::mem::size_of::<$ts>())* + ) + )* + } +} + /// Does `t` have alignment greater than or equal to `u`? If not, this macro /// produces a compile error. It must be invoked in a dead codepath. This is /// used in `transmute_ref!` and `transmute_mut!`. @@ -717,6 +770,36 @@ mod tests { */ } + #[test] + fn test_enum_casts() { + // Test that casting the variants of enums with signed integer reprs to + // unsigned integers obeys expected signed -> unsigned casting rules. + + #[repr(i8)] + enum ReprI8 { + MinusOne = -1, + Zero = 0, + Min = i8::MIN, + Max = i8::MAX, + } + + #[allow(clippy::as_conversions)] + let x = ReprI8::MinusOne as u8; + assert_eq!(x, u8::MAX); + + #[allow(clippy::as_conversions)] + let x = ReprI8::Zero as u8; + assert_eq!(x, 0); + + #[allow(clippy::as_conversions)] + let x = ReprI8::Min as u8; + assert_eq!(x, 128); + + #[allow(clippy::as_conversions)] + let x = ReprI8::Max as u8; + assert_eq!(x, 127); + } + #[test] fn test_struct_has_padding() { // Test that, for each provided repr, `struct_has_padding!` reports the @@ -725,7 +808,7 @@ mod tests { (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{ #[$cfg] struct Test($(#[allow(dead_code)] $ts),*); - assert_eq!(struct_has_padding!(Test, $($ts),*), $expect); + assert_eq!(struct_has_padding!(Test, [$($ts),*]), $expect); }}; (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => { test!(#[$cfg] ($($ts),*) => $expect); @@ -756,7 +839,7 @@ mod tests { #[$cfg] #[allow(unused)] // fields are never read union Test{ $($fs: $ts),* } - assert_eq!(union_has_padding!(Test, $($ts),*), $expect); + assert_eq!(union_has_padding!(Test, [$($ts),*]), $expect); }}; (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => { test!(#[$cfg] {$($fs: $ts),*} => $expect); @@ -774,4 +857,78 @@ mod tests { // anyway. test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true); } + + #[test] + fn test_enum_has_padding() { + // Test that, for each provided repr, `enum_has_padding!` reports the + // expected value. + macro_rules! test { + (#[repr($disc:ident $(, $c:ident)?)] { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => { + test!(@case #[repr($disc $(, $c)?)] { $($vs ($($ts),*),)* } => $expect); + }; + (#[repr($disc:ident $(, $c:ident)?)] #[$cfg:meta] $(#[$cfgs:meta])* { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => { + test!(@case #[repr($disc $(, $c)?)] #[$cfg] { $($vs ($($ts),*),)* } => $expect); + test!(#[repr($disc $(, $c)?)] $(#[$cfgs])* { $($vs ($($ts),*),)* } => $expect); + }; + (@case #[repr($disc:ident $(, $c:ident)?)] $(#[$cfg:meta])? { $($vs:ident ($($ts:ty),*),)* } => $expect:expr) => {{ + #[repr($disc $(, $c)?)] + $(#[$cfg])? + #[allow(unused)] // variants and fields are never used + enum Test { + $($vs ($($ts),*),)* + } + assert_eq!( + enum_has_padding!(Test, $disc, $([$($ts),*]),*), + $expect + ); + }}; + } + + #[allow(unused)] + #[repr(align(2))] + struct U16(u16); + + #[allow(unused)] + #[repr(align(4))] + struct U32(u32); + + test!(#[repr(u8)] #[repr(C)] { + A(u8), + } => false); + test!(#[repr(u16)] #[repr(C)] { + A(u8, u8), + B(U16), + } => false); + test!(#[repr(u32)] #[repr(C)] { + A(u8, u8, u8, u8), + B(U16, u8, u8), + C(u8, u8, U16), + D(U16, U16), + E(U32), + } => false); + + // `repr(int)` can pack the discriminant more efficiently + test!(#[repr(u8)] { + A(u8, U16), + } => false); + test!(#[repr(u8)] { + A(u8, U16, U32), + } => false); + + // `repr(C)` cannot + test!(#[repr(u8, C)] { + A(u8, U16), + } => true); + test!(#[repr(u8, C)] { + A(u8, u8, u8, U32), + } => true); + + // And field ordering can always cause problems + test!(#[repr(u8)] #[repr(C)] { + A(U16, u8), + } => true); + test!(#[repr(u8)] #[repr(C)] { + A(U32, u8, u8, u8), + } => true); + } } diff --git a/zerocopy-derive/src/enums.rs b/zerocopy-derive/src/enums.rs new file mode 100644 index 0000000000..3d1142b8f0 --- /dev/null +++ b/zerocopy-derive/src/enums.rs @@ -0,0 +1,359 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +use ::proc_macro2::TokenStream; +use ::quote::quote; +use ::syn::{DataEnum, Fields, Generics, Ident}; + +use crate::{EnumRepr, Trait}; + +/// Returns the repr for the tag enum, given the collection of reprs on the +/// enum. This function returns: +/// +/// - `Some(C)` for `repr(C)` +/// - `Some(int)` for `repr(int)` and `repr(C, int)` +/// - `None` for all other reprs +pub(crate) fn tag_repr(reprs: &[EnumRepr]) -> Option<&EnumRepr> { + let mut result = None; + for repr in reprs { + match repr { + EnumRepr::C => result = Some(repr), + EnumRepr::U8 + | EnumRepr::U16 + | EnumRepr::U32 + | EnumRepr::U64 + | EnumRepr::Usize + | EnumRepr::I8 + | EnumRepr::I16 + | EnumRepr::I32 + | EnumRepr::I64 + | EnumRepr::Isize => { + return Some(repr); + } + _ => (), + } + } + result +} + +/// Generates a tag enum for the given enum. This generates an enum with the +/// same `repr`s, variants, and corresponding discriminants, but none of the +/// fields. +pub(crate) fn generate_tag_enum(repr: &EnumRepr, data: &DataEnum) -> TokenStream { + let variants = data.variants.iter().map(|v| { + let ident = &v.ident; + if let Some((eq, discriminant)) = &v.discriminant { + quote! { #ident #eq #discriminant } + } else { + quote! { #ident } + } + }); + + quote! { + #[repr(#repr)] + #[allow(dead_code)] + enum ___ZerocopyTag { + #(#variants),* + } + } +} + +fn tag_ident(variant_ident: &Ident) -> Ident { + Ident::new(&format!("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span()) +} + +/// Generates a constant for each variant of the enum. When we match on the +/// enum's tag, each arm matches one of these constants. We have to use +/// constants here because: +/// +/// - The type that we're matching on is not the type of the tag, it's just a +/// bunch of bytes. +/// - We can't read the enum tag as an enum because the bytes may not represent +/// a valid variant. +/// - Patterns do not currently support const expressions +fn generate_tag_consts(data: &DataEnum) -> TokenStream { + let tags = data.variants.iter().map(|v| { + let variant_ident = &v.ident; + let tag_ident = tag_ident(variant_ident); + + quote! { + #[allow(non_upper_case_globals)] + const #tag_ident: ___ZerocopyTagPrimitive = + ___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive; + } + }); + + quote! { + #(#tags)* + } +} + +fn variant_struct_ident(variant_ident: &Ident) -> Ident { + Ident::new(&format!("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span()) +} + +/// Generates variant structs for the given enum variant. +/// +/// These are structs associated with each variant of an enum. They are +/// `repr(C)` tuple struct with the same fields as the variant after an +/// `InnerTag`. +fn generate_variant_structs(generics: &Generics, data: &DataEnum) -> TokenStream { + // All variant structs have a `PhantomData<(T0, T1, T2, ...)>` field because + // we don't know which type parameters each variant will use, and unused + // type parameters are a compiler error. + let type_params = generics.type_params().map(|p| &p.ident); + let phantom_ty = quote! { + core_reexport::marker::PhantomData<(#(#type_params,)*)> + }; + + let variant_structs = data.variants.iter().filter_map(|variant| { + // We don't generate variant structs for unit variants because we only + // need to check the tag. This helps cut down our generated code a bit. + if matches!(variant.fields, Fields::Unit) { + return None; + } + + let trait_path = Trait::TryFromBytes.derive_path(); + let variant_struct_ident = variant_struct_ident(&variant.ident); + let field_types = variant.fields.iter().map(|f| &f.ty); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Some(quote! { + #[repr(C)] + #[allow(non_snake_case)] + #[derive(#trait_path)] + struct #variant_struct_ident #impl_generics ( + core_reexport::mem::MaybeUninit<___ZerocopyInnerTag>, + #(#field_types,)* + #phantom_ty, + ) #where_clause; + }) + }); + + quote! { + #(#variant_structs)* + } +} + +fn generate_variants_union(generics: &Generics, data: &DataEnum) -> TokenStream { + let (_, ty_generics, _) = generics.split_for_impl(); + + let fields = data.variants.iter().filter_map(|variant| { + if matches!(variant.fields, Fields::Unit) { + return None; + } + + // Field names are prefixed with `field_` to prevent name collision with + // the `__nonempty` field. + let field_name = Ident::new(&format!("field_{}", &variant.ident), variant.ident.span()); + let variant_struct_ident = variant_struct_ident(&variant.ident); + + Some(quote! { + #field_name: core_reexport::mem::ManuallyDrop< + #variant_struct_ident #ty_generics + >, + }) + }); + + quote! { + #[repr(C)] + #[allow(non_snake_case)] + union ___ZerocopyVariants #generics { + #(#fields)* + // Enums can have variants with no fields, but unions must + // have at least one field. So we just add a trailing unit + // to ensure that this union always has at least one field. + // Because this union is `repr(C)`, this unit type does not + // affect the layout. + __nonempty: (), + } + } +} + +/// Generates an implementation of `is_bit_valid` for an arbitrary enum. +/// +/// The general process is: +/// +/// 1. Generate a tag enum. This is an enum with the same repr, variants, and +/// corresponding discriminants as the original enum, but without any fields +/// on the variants. This gives us access to an enum where the variants have +/// the same discriminants as the one we're writing `is_bit_valid` for. +/// 2. Make constants from the variants of the tag enum. We need these because +/// we can't put const exprs in match arms. +/// 3. Generate variant structs. These are structs which have the same fields as +/// each variant of the enum, and are `#[repr(C)]` with an optional "inner +/// tag". +/// 4. Generate a variants union, with one field for each variant struct type. +/// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and +/// the variants union. +/// +/// See these reference links for fully-worked example decompositions. +/// +/// - `repr(C)`: +/// - `repr(int)`: +/// - `repr(C, int)`: +pub(crate) fn derive_is_bit_valid( + reprs: &[EnumRepr], + generics: &Generics, + data: &DataEnum, +) -> TokenStream { + let repr = + tag_repr(reprs).expect("cannot derive is_bit_valid for enum without a well-defined repr"); + + let trait_path = Trait::TryFromBytes.crate_path(); + let tag_enum = generate_tag_enum(repr, data); + let tag_consts = generate_tag_consts(data); + + let (outer_tag_type, inner_tag_type) = if matches!(repr, EnumRepr::C) { + (quote! { ___ZerocopyTag }, quote! { () }) + } else { + (quote! { () }, quote! { ___ZerocopyTag }) + }; + + let variant_structs = generate_variant_structs(generics, data); + let variants_union = generate_variants_union(generics, data); + + let (_, ty_generics, _) = generics.split_for_impl(); + + let match_arms = data.variants.iter().map(|variant| { + let tag_ident = tag_ident(&variant.ident); + let variant_struct_ident = variant_struct_ident(&variant.ident); + + if matches!(variant.fields, Fields::Unit) { + // Unit variants don't need any further validation beyond checking + // the tag. + quote! { + #tag_ident => true + } + } else { + quote! { + #tag_ident => { + // SAFETY: + // - The variant struct has the same size as the enum + // - The returned pointer is cast from `p`, and so has the + // same provenance as `p`. + // - We checked that the tag of the enum matched the + // constant for this variant, so this cast preserves + // types and locations of all fields. Therefore, any + // `UnsafeCell`s will have the same location as in the + // original type. + let variant = unsafe { + variants.cast_unsized( + |p: *mut ___ZerocopyVariants #ty_generics| { + p as *mut #variant_struct_ident #ty_generics + } + ) + }; + // SAFETY: `cast_unsized` removes the initialization + // invariant from `p`, so we re-assert that all of the bytes + // are initialized. + let variant = unsafe { variant.assume_initialized() }; + < + #variant_struct_ident #ty_generics as #trait_path + >::is_bit_valid(variant) + } + } + } + }); + + quote! { + // SAFETY: We use `is_bit_valid` to validate that the bit pattern of the + // enum's tag corresponds to one of the enum's discriminants. Then, we + // check the bit validity of each field of the corresponding variant. + // Thus, this is a sound implementation of `is_bit_valid`. + fn is_bit_valid( + mut candidate: ::zerocopy::Maybe<'_, Self, A>, + ) -> ::zerocopy::macro_util::core_reexport::primitive::bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { + use ::zerocopy::macro_util::core_reexport; + + #tag_enum + + type ___ZerocopyTagPrimitive = ::zerocopy::macro_util::SizeToTag<{ + core_reexport::mem::size_of::<___ZerocopyTag>() + }>; + + #tag_consts + + type ___ZerocopyOuterTag = #outer_tag_type; + type ___ZerocopyInnerTag = #inner_tag_type; + + #variant_structs + + #variants_union + + #[repr(C)] + struct ___ZerocopyRawEnum #generics { + tag: ___ZerocopyOuterTag, + variants: ___ZerocopyVariants #ty_generics, + } + + let candidate = candidate.reborrow().forget_exclusive(); + + // SAFETY: + // - `cast` is implemented as required. + // - By definition, `*mut Self` and `*mut __ZerocopyTagPrimitive` + // are types of the same size. + let tag = unsafe { + candidate.cast_unsized(|p: *mut Self| { + p as *mut ___ZerocopyTagPrimitive + }) + }; + // SAFETY: Since `candidate` has the invariant `Initialized`, we + // know that `candidate`'s referent (and thus `tag`'s referent) is + // as-initialized as `Self`. Since all of the possible tag types + // always have all of their bytes initialized, we know that + // `tag`'s referent has all of its bytes initialized. Since the + // validity invariant of `__ZerocopyTagPrimitive` is just that all + // of its bytes are initialized, we know that `tag`'s referent is + // bit-valid. + let tag = unsafe { tag.assume_valid() }; + let tag = tag.read_unaligned(); + + // SAFETY: + // - The raw enum has the same size as the input enum. + // - The returned pointer is cast from `p`, and so has the same + // provenance as `p`. + // - The raw enum has the same types at the same locations as the + // original enum, and so preserves the locations of any + // `UnsafeCell`s. + let raw_enum = unsafe { + candidate.cast_unsized(|p: *mut Self| { + p as *mut ___ZerocopyRawEnum #ty_generics + }) + }; + // SAFETY: `cast_unsized` removes the initialization invariant from + // `p`, so we re-assert that all of the bytes are initialized. + let raw_enum = unsafe { raw_enum.assume_initialized() }; + // SAFETY: + // - This projection returns a subfield of `this` using + // `addr_of_mut!`. + // - Because the subfield pointer is derived from `this`, it has the + // same provenance. + // - The locations of `UnsafeCell`s in the subfield match the + // locations of `UnsafeCell`s in `this`. This is because the + // subfield pointer just points to a smaller portion of the + // overall struct. + let variants = unsafe { + raw_enum.project(|p: *mut ___ZerocopyRawEnum #ty_generics| { + core_reexport::ptr::addr_of_mut!((*p).variants) + }) + }; + + #[allow(non_upper_case_globals)] + match tag { + #(#match_arms,)* + _ => false, + } + } + } +} diff --git a/zerocopy-derive/src/ext.rs b/zerocopy-derive/src/ext.rs index ba6438c38e..9b446f53a9 100644 --- a/zerocopy-derive/src/ext.rs +++ b/zerocopy-derive/src/ext.rs @@ -6,9 +6,9 @@ // This file may not be copied, modified, or distributed except according to // those terms. -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use quote::ToTokens; -use syn::{Data, DataEnum, DataStruct, DataUnion, Field, Index, Type}; +use syn::{Data, DataEnum, DataStruct, DataUnion, Field, Ident, Index, Type}; pub(crate) trait DataExt { /// Extracts the names and types of all fields. For enums, extracts the names @@ -20,6 +20,10 @@ pub(crate) trait DataExt { /// about transitive ownership. But for field names, we'd only use them when /// generating is_bit_valid, which cares about where they live. fn fields(&self) -> Vec<(TokenStream, &Type)>; + + fn variants(&self) -> Vec>; + + fn tag(&self) -> Option; } impl DataExt for Data { @@ -30,24 +34,64 @@ impl DataExt for Data { Data::Union(un) => un.fields(), } } + + fn variants(&self) -> Vec> { + match self { + Data::Struct(strc) => strc.variants(), + Data::Enum(enm) => enm.variants(), + Data::Union(un) => un.variants(), + } + } + + fn tag(&self) -> Option { + match self { + Data::Struct(strc) => strc.tag(), + Data::Enum(enm) => enm.tag(), + Data::Union(un) => un.tag(), + } + } } impl DataExt for DataStruct { fn fields(&self) -> Vec<(TokenStream, &Type)> { map_fields(&self.fields) } + + fn variants(&self) -> Vec> { + vec![self.fields()] + } + + fn tag(&self) -> Option { + None + } } impl DataExt for DataEnum { fn fields(&self) -> Vec<(TokenStream, &Type)> { map_fields(self.variants.iter().flat_map(|var| &var.fields)) } + + fn variants(&self) -> Vec> { + self.variants.iter().map(|var| map_fields(&var.fields)).collect() + } + + fn tag(&self) -> Option { + Some(Ident::new("___ZerocopyTag", Span::call_site())) + } } impl DataExt for DataUnion { fn fields(&self) -> Vec<(TokenStream, &Type)> { map_fields(&self.fields.named) } + + fn variants(&self) -> Vec> { + vec![self.fields()] + } + + fn tag(&self) -> Option { + None + } } fn map_fields<'a>( @@ -67,13 +111,3 @@ fn map_fields<'a>( }) .collect() } - -pub(crate) trait EnumExt { - fn is_fieldless(&self) -> bool; -} - -impl EnumExt for DataEnum { - fn is_fieldless(&self) -> bool { - self.fields().is_empty() - } -} diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 912cdbbdf6..4fbbe1ff62 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -28,18 +28,19 @@ )] #![recursion_limit = "128"] +mod enums; mod ext; mod repr; -use quote::quote_spanned; +use proc_macro2::{TokenStream, TokenTree}; +use quote::ToTokens; use { proc_macro2::Span, quote::quote, syn::{ - parse_quote, parse_quote_spanned, Data, DataEnum, DataStruct, DataUnion, DeriveInput, - Error, Expr, ExprLit, ExprUnary, GenericParam, Ident, Lit, Path, Type, UnOp, - WherePredicate, + parse_quote, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit, + ExprUnary, GenericParam, Ident, Lit, Path, Type, UnOp, WherePredicate, }, }; @@ -510,105 +511,21 @@ const STRUCT_UNION_ALLOWED_REPR_COMBINATIONS: &[&[StructRepr]] = &[ ]; fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - if !enm.is_fieldless() { - return Error::new_spanned(ast, "only field-less enums can implement TryFromBytes") - .to_compile_error(); - } - 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 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: - // - The closure is a pointer cast, and `Self` and `[u8; - // size_of::()]` have the same size, so the returned pointer - // addresses the same bytes as `p` subset of the bytes addressed - // by `slf` - // - ..., and so it preserves provenance - // - Since we validate that this type is a field-less enum, it - // cannot contain any `UnsafeCell`s. Neither does `[u8; N]`. - let discriminant = unsafe { candidate.cast_unsized(|p: *mut Self| p as *mut [core_reexport::primitive::u8; core_reexport::mem::size_of::()]) }; - // SAFETY: Since `candidate` has the invariant `Initialized`, we - // know that `candidate`'s referent (and thus `discriminant`'s - // referent) are fully initialized. Since all of the allowed `repr`s - // are types for which all bytes are always initialized, we know - // that `discriminant`'s referent has all of its bytes initialized. - // Since `[u8; N]`'s validity invariant is just that all of its - // bytes are initialized, we know that `discriminant`'s referent is - // bit-valid. - let discriminant = unsafe { discriminant.assume_valid() }; - let discriminant = discriminant.read_unaligned(); - - false #(|| { - let v = Self::#variant_names{}; - // SAFETY: All of the allowed `repr`s for `Self` guarantee that - // `Self`'s discriminant bytes are all initialized. Since we - // validate that `Self` has no fields, it has no bytes other - // than the discriminant. Thus, it is sound to transmute any - // instance of `Self` to `[u8; size_of::()]`. - let d: [core_reexport::primitive::u8; core_reexport::mem::size_of::()] = unsafe { core_reexport::mem::transmute(v) }; - // SAFETY: Here we check that the bits of the argument - // `candidate` are equal to the bits of a `Self` constructed - // using safe code. If this condition passes, then we know that - // `candidate` refers to a bit-valid `Self`. - discriminant == d - })* - ) - }; + // Generate the equivalent enum in terms of just structs and unions + let extra = Some(enums::derive_is_bit_valid(&reprs, &ast.generics, enm)); - 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, - ( - A, - ::zerocopy::pointer::invariant::Any, - ::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) + impl_block(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, extra) } #[rustfmt::skip] const ENUM_TRY_FROM_BYTES_CFG: Config = { use EnumRepr::*; Config { - allowed_combinations_message: r#"TryFromBytes requires repr of "C", "u8", "u16", "u32", "u64", "usize", "i8", or "i16", "i32", "i64", or "isize""#, + allowed_combinations_message: r#"TryFromBytes requires an enum repr of a primitive, "C", or "C" with a primitive"#, derive_unaligned: false, allowed_combinations: &[ - &[C], &[U8], &[U16], &[U32], @@ -619,6 +536,17 @@ const ENUM_TRY_FROM_BYTES_CFG: Config = { &[I32], &[I64], &[Isize], + &[C], + &[C, U8], + &[C, U16], + &[C, U32], + &[C, U64], + &[C, Usize], + &[C, I8], + &[C, I16], + &[C, I32], + &[C, I64], + &[C, Isize], ], disallowed_but_legal_combinations: &[], } @@ -632,7 +560,6 @@ fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro } // An enum is `FromZeros` if: -// - all of its variants are fieldless // - one of the variants has a discriminant of `0` // Returns `Ok(index)` if variant `index` of the enum has a discriminant of @@ -714,17 +641,14 @@ fn find_zero_variant(enm: &DataEnum) -> Result { } fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - if !enm.is_fieldless() { - return Error::new_spanned(ast, "only field-less enums can implement FromZeros") - .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_FROM_ZEROS_INTO_BYTES_CFG.validate_reprs(ast)); - if let Err(has_unknown_variants) = find_zero_variant(enm) { - if has_unknown_variants { + let zero_variant = match find_zero_variant(enm) { + Ok(index) => enm.variants.iter().nth(index).unwrap(), + // Has unknown variants + Err(true) => { return Error::new_spanned( ast, "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\ @@ -733,16 +657,35 @@ fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok define or imply the variant with a discriminant of zero.", ) .to_compile_error(); - } else { + } + // Does not have unknown variants + Err(false) => { return Error::new_spanned( ast, "FromZeros only supported on enums with a variant that has a discriminant of `0`", ) .to_compile_error(); } - } + }; + + let explicit_bounds = zero_variant + .fields + .iter() + .map(|field| { + let ty = &field.ty; + parse_quote! { #ty: ::zerocopy::FromZeros } + }) + .collect::>(); - impl_block(ast, enm, Trait::FromZeros, FieldBounds::ALL_SELF, SelfBounds::None, None, None) + impl_block( + ast, + enm, + Trait::FromZeros, + FieldBounds::Explicit(explicit_bounds), + SelfBounds::None, + None, + None, + ) } // Unions are `FromZeros` if @@ -778,11 +721,6 @@ fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro // this would require ~4 billion enum variants, which obviously isn't a thing. fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - if !enm.is_fieldless() { - return Error::new_spanned(ast, "only field-less enums can implement FromBytes") - .to_compile_error(); - } - let reprs = try_or_print!(ENUM_FROM_BYTES_CFG.validate_reprs(ast)); let variants_required = 1usize @@ -908,15 +846,28 @@ const STRUCT_UNION_INTO_BYTES_CFG: Config = Config { // An enum is `IntoBytes` if it is field-less and has a defined repr. fn derive_into_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - if !enm.is_fieldless() { - return Error::new_spanned(ast, "only field-less enums can implement IntoBytes") - .to_compile_error(); - } - // We don't care what the repr is; we only care that it is one of the // allowed ones. - try_or_print!(ENUM_FROM_ZEROS_INTO_BYTES_CFG.validate_reprs(ast)); - impl_block(ast, enm, Trait::IntoBytes, FieldBounds::None, SelfBounds::None, None, None) + let reprs = try_or_print!(ENUM_FROM_ZEROS_INTO_BYTES_CFG.validate_reprs(ast)); + let repr = enums::tag_repr(&reprs) + .expect("cannot derive IntoBytes for enum without a well-defined repr"); + + // In the derive for `IntoBytes`, we emit a padding check which makes sure + // that, for each variant, the sum of the sizes of the discriminat and each + // of the fields equals the overall size of the enum. In order to write that + // padding check, we need to emit a definition for the discriminant type so + // that we can use its size. + let context = enums::generate_tag_enum(repr, enm); + impl_block_with_context( + ast, + enm, + Trait::IntoBytes, + FieldBounds::ALL_SELF, + SelfBounds::None, + Some(PaddingCheck::Enum), + None, + Some(context), + ) } #[rustfmt::skip] @@ -925,20 +876,30 @@ const ENUM_FROM_ZEROS_INTO_BYTES_CFG: Config = { Config { // Since `disallowed_but_legal_combinations` is empty, this message will // never actually be emitted. - allowed_combinations_message: r#"Deriving this trait requires repr of "C", "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", or "isize""#, + allowed_combinations_message: r#"FromZeros requires an enum repr of a primitive, "C", or "C" with a primitive"#, derive_unaligned: false, allowed_combinations: &[ - &[C], &[U8], &[U16], + &[U32], + &[U64], + &[Usize], &[I8], &[I16], - &[U32], &[I32], - &[U64], &[I64], - &[Usize], &[Isize], + &[C], + &[C, U8], + &[C, U16], + &[C, U32], + &[C, U64], + &[C, Usize], + &[C, I8], + &[C, I16], + &[C, I32], + &[C, I64], + &[C, Isize], ], disallowed_but_legal_combinations: &[], } @@ -1000,11 +961,6 @@ const STRUCT_UNION_UNALIGNED_CFG: Config = Config { // - `repr(u8)` or `repr(i8)` fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - if !enm.is_fieldless() { - return Error::new_spanned(ast, "only field-less enums can implement Unaligned") - .to_compile_error(); - } - // The only valid reprs are `u8` and `i8`, and optionally `align(1)`. We // don't actually care what the reprs are so long as they satisfy that // requirement. @@ -1027,9 +983,10 @@ const ENUM_UNALIGNED_CFG: Config = { allowed_combinations: &[ &[U8], &[I8], + &[C, U8], + &[C, I8], ], disallowed_but_legal_combinations: &[ - &[C], &[U16], &[U32], &[U64], @@ -1038,6 +995,15 @@ const ENUM_UNALIGNED_CFG: Config = { &[I32], &[I64], &[Isize], + &[C], + &[C, U16], + &[C, U32], + &[C, U64], + &[C, Usize], + &[C, I16], + &[C, I32], + &[C, I64], + &[C, Isize], ], } }; @@ -1066,6 +1032,8 @@ enum PaddingCheck { Struct, // Check that the size of each field exactly equals the union's size. Union, + // Check that every variant of the enum contains no padding. + Enum, } impl PaddingCheck { @@ -1075,6 +1043,7 @@ impl PaddingCheck { let s = match self { PaddingCheck::Struct => "struct_has_padding", PaddingCheck::Union => "union_has_padding", + PaddingCheck::Enum => "enum_has_padding", }; Ident::new(s, Span::call_site()) @@ -1093,16 +1062,26 @@ enum Trait { Sized, } +impl ToTokens for Trait { + fn to_tokens(&self, tokens: &mut TokenStream) { + let ident = Ident::new(&format!("{:?}", self), Span::call_site()); + tokens.extend(core::iter::once(TokenTree::Ident(ident))); + } +} + impl Trait { - fn path(&self) -> Path { - let span = Span::call_site(); - let root = if *self == Self::Sized { - quote_spanned!(span=> ::zerocopy::macro_util::core_reexport::marker) - } else { - quote_spanned!(span=> ::zerocopy) - }; - let ident = Ident::new(&format!("{:?}", self), span); - parse_quote_spanned! {span=> #root::#ident} + fn crate_path(&self) -> Path { + match self { + Self::Sized => parse_quote!(::zerocopy::macro_util::core_reexport::marker::#self), + _ => parse_quote!(::zerocopy::#self), + } + } + + fn derive_path(&self) -> Path { + match self { + Self::Sized => panic!("no derive path for builtin types"), + _ => parse_quote!(::zerocopy_derive::#self), + } } } @@ -1112,11 +1091,11 @@ enum TraitBound { Other(Trait), } -#[derive(Debug, Eq, PartialEq)] enum FieldBounds<'a> { None, All(&'a [TraitBound]), Trailing(&'a [TraitBound]), + Explicit(Vec), } impl<'a> FieldBounds<'a> { @@ -1150,6 +1129,29 @@ fn impl_block( self_type_trait_bounds: SelfBounds, padding_check: Option, extras: Option, +) -> proc_macro2::TokenStream { + impl_block_with_context( + input, + data, + trt, + field_type_trait_bounds, + self_type_trait_bounds, + padding_check, + extras, + None, + ) +} + +#[allow(clippy::too_many_arguments)] +fn impl_block_with_context( + input: &DeriveInput, + data: &D, + trt: Trait, + field_type_trait_bounds: FieldBounds, + self_type_trait_bounds: SelfBounds, + padding_check: Option, + extras: Option, + context: Option, ) -> proc_macro2::TokenStream { // In this documentation, we will refer to this hypothetical struct: // @@ -1210,11 +1212,13 @@ fn impl_block( // = note: required by `zerocopy::Unaligned` let type_ident = &input.ident; - let trait_path = trt.path(); + let trait_path = trt.crate_path(); let fields = data.fields(); + let variants = data.variants(); + let discriminant = data.tag(); fn bound_tt(ty: &Type, traits: impl Iterator) -> WherePredicate { - let traits = traits.map(|t| t.path()); + let traits = traits.map(|t| t.crate_path()); parse_quote!(#ty: #(#traits)+*) } let field_type_bounds: Vec<_> = match (field_type_trait_bounds, &fields[..]) { @@ -1225,16 +1229,21 @@ fn impl_block( (FieldBounds::Trailing(traits), [.., last]) => { vec![bound_tt(last.1, normalize_bounds(trt, traits))] } + (FieldBounds::Explicit(bounds), _) => bounds, }; // Don't bother emitting a padding check if there are no fields. #[allow(unstable_name_collisions)] // See `BoolExt` below #[allow(clippy::incompatible_msrv)] // Work around https://github.com/rust-lang/rust-clippy/issues/12280 let padding_check_bound = padding_check.and_then(|check| (!fields.is_empty()).then_some(check)).map(|check| { - let fields = fields.iter().map(|(_name, ty)| ty); + let variant_types = variants.iter().map(|var| { + let types = var.iter().map(|(_name, ty)| ty); + quote!([#(#types),*]) + }); let validator_macro = check.validator_macro_ident(); + let d = discriminant.iter(); parse_quote!( - ::zerocopy::macro_util::HasPadding<#type_ident, {::zerocopy::#validator_macro!(#type_ident, #(#fields),*)}>: + ::zerocopy::macro_util::HasPadding<#type_ident, {::zerocopy::#validator_macro!(#type_ident, #(#d,)* #(#variant_types),*)}>: ::zerocopy::macro_util::ShouldBe ) }); @@ -1282,15 +1291,21 @@ fn impl_block( }); quote! { - #[allow(deprecated)] - unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* > - where - #(#bounds,)* - { - fn only_derive_is_allowed_to_implement_this_trait() {} - - #extras - } + const _: () = { + #context + + // TODO(#553): Add a test that generates a warning when + // `#[allow(deprecated)]` isn't present. + #[allow(deprecated)] + unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* > + where + #(#bounds,)* + { + fn only_derive_is_allowed_to_implement_this_trait() {} + + #extras + } + }; } } diff --git a/zerocopy-derive/src/repr.rs b/zerocopy-derive/src/repr.rs index fe74faf6b8..90007efcf2 100644 --- a/zerocopy-derive/src/repr.rs +++ b/zerocopy-derive/src/repr.rs @@ -8,6 +8,9 @@ use core::fmt::{self, Display, Formatter}; +use proc_macro2::Ident; +use quote::{ToTokens, TokenStreamExt as _}; + use { proc_macro2::Span, syn::punctuated::Punctuated, @@ -171,6 +174,33 @@ define_kind_specific_repr!( [Align] ); +impl EnumRepr { + fn as_str(&self) -> Option<&'static str> { + Some(match self { + EnumRepr::C => "C", + EnumRepr::U8 => "u8", + EnumRepr::U16 => "u16", + EnumRepr::U32 => "u32", + EnumRepr::U64 => "u64", + EnumRepr::Usize => "usize", + EnumRepr::I8 => "i8", + EnumRepr::I16 => "i16", + EnumRepr::I32 => "i32", + EnumRepr::I64 => "i64", + EnumRepr::Isize => "isize", + EnumRepr::Align(_) => return None, + }) + } +} + +impl ToTokens for EnumRepr { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + if let Some(s) = self.as_str() { + tokens.append(Ident::new(s, Span::call_site())); + } + } +} + // All representations known to Rust. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub(crate) enum Repr { diff --git a/zerocopy-derive/tests/enum_from_bytes.rs b/zerocopy-derive/tests/enum_from_bytes.rs index bb1d976694..9ecec4c05d 100644 --- a/zerocopy-derive/tests/enum_from_bytes.rs +++ b/zerocopy-derive/tests/enum_from_bytes.rs @@ -295,6 +295,267 @@ enum FooU8 { Variant255, } +#[derive(imp::FromBytes)] +#[repr(u8)] +enum FooU8WithData { + Variant0(u32), + Variant1(u8), + Variant2(i128), + Variant3, + Variant4, + Variant5, + Variant6, + Variant7, + Variant8, + Variant9, + Variant10, + Variant11, + Variant12, + Variant13, + Variant14, + Variant15, + Variant16, + Variant17, + Variant18, + Variant19, + Variant20, + Variant21, + Variant22, + Variant23, + Variant24, + Variant25, + Variant26, + Variant27, + Variant28, + Variant29, + Variant30, + Variant31, + Variant32, + Variant33, + Variant34, + Variant35, + Variant36, + Variant37, + Variant38, + Variant39, + Variant40, + Variant41, + Variant42, + Variant43, + Variant44, + Variant45, + Variant46, + Variant47, + Variant48, + Variant49, + Variant50, + Variant51, + Variant52, + Variant53, + Variant54, + Variant55, + Variant56, + Variant57, + Variant58, + Variant59, + Variant60, + Variant61, + Variant62, + Variant63, + Variant64, + Variant65, + Variant66, + Variant67, + Variant68, + Variant69, + Variant70, + Variant71, + Variant72, + Variant73, + Variant74, + Variant75, + Variant76, + Variant77, + Variant78, + Variant79, + Variant80, + Variant81, + Variant82, + Variant83, + Variant84, + Variant85, + Variant86, + Variant87, + Variant88, + Variant89, + Variant90, + Variant91, + Variant92, + Variant93, + Variant94, + Variant95, + Variant96, + Variant97, + Variant98, + Variant99, + Variant100, + Variant101, + Variant102, + Variant103, + Variant104, + Variant105, + Variant106, + Variant107, + Variant108, + Variant109, + Variant110, + Variant111, + Variant112, + Variant113, + Variant114, + Variant115, + Variant116, + Variant117, + Variant118, + Variant119, + Variant120, + Variant121, + Variant122, + Variant123, + Variant124, + Variant125, + Variant126, + Variant127, + Variant128, + Variant129, + Variant130, + Variant131, + Variant132, + Variant133, + Variant134, + Variant135, + Variant136, + Variant137, + Variant138, + Variant139, + Variant140, + Variant141, + Variant142, + Variant143, + Variant144, + Variant145, + Variant146, + Variant147, + Variant148, + Variant149, + Variant150, + Variant151, + Variant152, + Variant153, + Variant154, + Variant155, + Variant156, + Variant157, + Variant158, + Variant159, + Variant160, + Variant161, + Variant162, + Variant163, + Variant164, + Variant165, + Variant166, + Variant167, + Variant168, + Variant169, + Variant170, + Variant171, + Variant172, + Variant173, + Variant174, + Variant175, + Variant176, + Variant177, + Variant178, + Variant179, + Variant180, + Variant181, + Variant182, + Variant183, + Variant184, + Variant185, + Variant186, + Variant187, + Variant188, + Variant189, + Variant190, + Variant191, + Variant192, + Variant193, + Variant194, + Variant195, + Variant196, + Variant197, + Variant198, + Variant199, + Variant200, + Variant201, + Variant202, + Variant203, + Variant204, + Variant205, + Variant206, + Variant207, + Variant208, + Variant209, + Variant210, + Variant211, + Variant212, + Variant213, + Variant214, + Variant215, + Variant216, + Variant217, + Variant218, + Variant219, + Variant220, + Variant221, + Variant222, + Variant223, + Variant224, + Variant225, + Variant226, + Variant227, + Variant228, + Variant229, + Variant230, + Variant231, + Variant232, + Variant233, + Variant234, + Variant235, + Variant236, + Variant237, + Variant238, + Variant239, + Variant240, + Variant241, + Variant242, + Variant243, + Variant244, + Variant245, + Variant246, + Variant247, + Variant248, + Variant249, + Variant250, + Variant251, + Variant252, + Variant253, + Variant254, + Variant255, +} + #[allow(deprecated)] fn _allow_deprecated() { util_assert_impl_all!(FooU8: imp::FromBytes); diff --git a/zerocopy-derive/tests/enum_from_zeros.rs b/zerocopy-derive/tests/enum_from_zeros.rs index 95f03bcfe5..1011d688a7 100644 --- a/zerocopy-derive/tests/enum_from_zeros.rs +++ b/zerocopy-derive/tests/enum_from_zeros.rs @@ -30,12 +30,12 @@ util_assert_impl_all!(Bar: imp::FromZeros); #[derive(imp::FromZeros)] #[repr(C)] -enum Baz { +enum TwoVariantsHasExplicitZero { A = 1, B = 0, } -util_assert_impl_all!(Baz: imp::FromZeros); +util_assert_impl_all!(TwoVariantsHasExplicitZero: imp::FromZeros); #[derive(imp::FromZeros)] #[repr(i8)] @@ -44,9 +44,58 @@ enum ImplicitNonFirstVariantIsZero { B, } +util_assert_impl_all!(ImplicitNonFirstVariantIsZero: imp::FromZeros); + #[derive(imp::FromZeros)] #[repr(u64)] enum LargeDiscriminant { A = 0xFFFF_FFFF_FFFF_FFFF, B = 0x0000_0000_0000_0000, } + +util_assert_impl_all!(LargeDiscriminant: imp::FromZeros); + +#[derive(imp::FromZeros)] +#[repr(C)] +enum FirstVariantIsZeroable { + A(u32), + B { foo: u32 }, +} + +util_assert_impl_all!(FirstVariantIsZeroable: imp::FromZeros); + +#[derive(imp::FromZeros)] +#[repr(u8)] +enum FirstVariantIsZeroableSecondIsNot { + A(bool), + B(::core::num::NonZeroU8), +} + +util_assert_impl_all!(FirstVariantIsZeroableSecondIsNot: imp::FromZeros); + +// MSRV does not support data-carrying enum variants with explicit discriminants +#[cfg(not(__ZEROCOPY_TOOLCHAIN = "msrv"))] +#[derive(imp::FromZeros)] +#[repr(u8)] +enum ImplicitFirstVariantIsZeroable { + A(bool), + B(::core::num::NonZeroU8) = 1, +} + +// MSRV does not support data-carrying enum variants with explicit discriminants +#[cfg(not(__ZEROCOPY_TOOLCHAIN = "msrv"))] +util_assert_impl_all!(ImplicitFirstVariantIsZeroable: imp::FromZeros); + +// MSRV does not support data-carrying enum variants with explicit discriminants +#[cfg(not(__ZEROCOPY_TOOLCHAIN = "msrv"))] +#[derive(imp::FromZeros)] +#[repr(i8)] +enum ImplicitNonFirstVariantIsZeroable { + A(::core::num::NonZeroU8) = 1, + B = -1, + C(bool), +} + +// MSRV does not support data-carrying enum variants with explicit discriminants +#[cfg(not(__ZEROCOPY_TOOLCHAIN = "msrv"))] +util_assert_impl_all!(ImplicitNonFirstVariantIsZeroable: imp::FromZeros); diff --git a/zerocopy-derive/tests/enum_to_bytes.rs b/zerocopy-derive/tests/enum_to_bytes.rs index d455fcad79..fa42f66124 100644 --- a/zerocopy-derive/tests/enum_to_bytes.rs +++ b/zerocopy-derive/tests/enum_to_bytes.rs @@ -101,3 +101,23 @@ enum Isize { } util_assert_impl_all!(Isize: imp::IntoBytes); + +#[derive(imp::IntoBytes)] +#[repr(u8)] +enum HasData { + A(u8), + B(i8), +} + +util_assert_impl_all!(HasData: imp::IntoBytes); + +#[derive(imp::IntoBytes)] +#[repr(u32)] +enum HasData32 { + A(u32), + B(i32), + C([u8; 4]), + D([u16; 2]), +} + +util_assert_impl_all!(HasData: imp::IntoBytes); diff --git a/zerocopy-derive/tests/enum_try_from_bytes.rs b/zerocopy-derive/tests/enum_try_from_bytes.rs index b7b9471590..37a05c09e3 100644 --- a/zerocopy-derive/tests/enum_try_from_bytes.rs +++ b/zerocopy-derive/tests/enum_try_from_bytes.rs @@ -177,3 +177,87 @@ fn test_weird_discriminants() { ::try_read_from_bytes(&[0xFF; SIZE][..]).is_err() ); } + +#[derive( + Eq, PartialEq, Debug, imp::KnownLayout, imp::Immutable, imp::TryFromBytes, imp::IntoBytes, +)] +#[repr(C)] +enum HasFields { + A(u32), + B { foo: ::core::num::NonZeroU32 }, +} + +#[test] +fn test_has_fields() { + const SIZE: usize = ::core::mem::size_of::(); + + let bytes: [u8; SIZE] = ::zerocopy::transmute!(HasFields::A(10)); + imp::assert_eq!( + ::try_read_from_bytes(&bytes[..]), + imp::Ok(HasFields::A(10)), + ); + + let bytes: [u8; SIZE] = + ::zerocopy::transmute!(HasFields::B { foo: ::core::num::NonZeroU32::new(123456).unwrap() }); + imp::assert_eq!( + ::try_read_from_bytes(&bytes[..]), + imp::Ok(HasFields::B { foo: ::core::num::NonZeroU32::new(123456).unwrap() }), + ); +} + +#[derive( + Eq, PartialEq, Debug, imp::KnownLayout, imp::Immutable, imp::TryFromBytes, imp::IntoBytes, +)] +#[repr(u32)] +enum HasFieldsPrimitive { + A(u32), + B { foo: ::core::num::NonZeroU32 }, +} + +#[test] +fn test_has_fields_primitive() { + const SIZE: usize = ::core::mem::size_of::(); + + let bytes: [u8; SIZE] = ::zerocopy::transmute!(HasFieldsPrimitive::A(10)); + imp::assert_eq!( + ::try_read_from_bytes(&bytes[..]), + imp::Ok(HasFieldsPrimitive::A(10)), + ); + + let bytes: [u8; SIZE] = ::zerocopy::transmute!(HasFieldsPrimitive::B { + foo: ::core::num::NonZeroU32::new(123456).unwrap(), + }); + imp::assert_eq!( + ::try_read_from_bytes(&bytes[..]), + imp::Ok(HasFieldsPrimitive::B { foo: ::core::num::NonZeroU32::new(123456).unwrap() }), + ); +} + +#[derive(imp::TryFromBytes)] +#[repr(align(4), u32)] +enum HasReprAlignFirst { + A, + B, +} + +util_assert_impl_all!(HasReprAlignFirst: imp::TryFromBytes); + +#[derive(imp::KnownLayout, imp::TryFromBytes, imp::Immutable)] +#[repr(u8)] +enum Complex { + UnitLike, + StructLike { a: u8, b: u16 }, + TupleLike(bool, char), +} + +util_assert_impl_all!(Complex: imp::TryFromBytes); + +#[derive(imp::KnownLayout, imp::TryFromBytes, imp::Immutable)] +#[repr(u8)] +enum ComplexWithGenerics { + UnitLike, + StructLike { a: u8, b: X }, + TupleLike(bool, Y), +} + +util_assert_impl_all!(ComplexWithGenerics: imp::TryFromBytes); diff --git a/zerocopy-derive/tests/ui-msrv/enum.stderr b/zerocopy-derive/tests/ui-msrv/enum.stderr index c11e8db309..e9005e20f4 100644 --- a/zerocopy-derive/tests/ui-msrv/enum.stderr +++ b/zerocopy-derive/tests/ui-msrv/enum.stderr @@ -38,198 +38,199 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only field-less enums can implement TryFromBytes - --> tests/ui-msrv/enum.rs:75:1 - | -75 | / #[repr(u8)] -76 | | enum TryFromBytes2 { -77 | | A(u8), -78 | | } - | |_^ - -error: only field-less enums can implement TryFromBytes - --> tests/ui-msrv/enum.rs:85:1 +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-msrv/enum.rs:74:10 | -85 | / enum FromZeros1 { -86 | | A(u8), -87 | | } - | |_^ - -error: only field-less enums can implement FromZeros - --> tests/ui-msrv/enum.rs:85:1 +74 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ | -85 | / enum FromZeros1 { -86 | | A(u8), -87 | | } - | |_^ + = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only field-less enums can implement TryFromBytes - --> tests/ui-msrv/enum.rs:90:1 +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-msrv/enum.rs:92:10 | -90 | / enum FromZeros2 { -91 | | A, -92 | | B(u8), -93 | | } - | |_^ - -error: only field-less enums can implement FromZeros - --> tests/ui-msrv/enum.rs:90:1 +92 | #[derive(FromZeros)] + | ^^^^^^^^^ | -90 | / enum FromZeros2 { -91 | | A, -92 | | B(u8), -93 | | } - | |_^ + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout - --> tests/ui-msrv/enum.rs:95:10 + --> tests/ui-msrv/enum.rs:97:10 | -95 | #[derive(FromZeros)] +97 | #[derive(FromZeros)] | ^^^^^^^^^ | = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-msrv/enum.rs:103:10 + | +103 | #[derive(FromZeros)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + error: FromZeros only supported on enums with a variant that has a discriminant of `0` - --> tests/ui-msrv/enum.rs:102:1 + --> tests/ui-msrv/enum.rs:110:1 | -102 | / #[repr(u8)] -103 | | enum FromZeros4 { -104 | | A = 1, -105 | | B = 2, -106 | | } +110 | / #[repr(u8)] +111 | | enum FromZeros4 { +112 | | A = 1, +113 | | B = 2, +114 | | } | |_^ error: FromZeros only supported on enums with a variant that has a discriminant of `0` help: This enum has discriminants which are not literal integers. One of those may define or imply which variant has a discriminant of zero. Use a literal integer to define or imply the variant with a discriminant of zero. - --> tests/ui-msrv/enum.rs:111:1 + --> tests/ui-msrv/enum.rs:119:1 + | +119 | / #[repr(i8)] +120 | | enum FromZeros5 { +121 | | A = NEGATIVE_ONE, +122 | | B, +123 | | } + | |_^ + +error: FromZeros only supported on enums with a variant that has a discriminant of `0` + --> tests/ui-msrv/enum.rs:134:1 | -111 | / #[repr(i8)] -112 | | enum FromZeros5 { -113 | | A = NEGATIVE_ONE, -114 | | B, -115 | | } +134 | / #[repr(u8)] +135 | | enum FromZeros7 { +136 | | A = 1, +137 | | B(NotFromZeros), +138 | | } | |_^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:122:8 + --> tests/ui-msrv/enum.rs:145:8 | -122 | #[repr(C)] +145 | #[repr(C)] | ^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:128:8 + --> tests/ui-msrv/enum.rs:151:8 | -128 | #[repr(usize)] +151 | #[repr(usize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:134:8 + --> tests/ui-msrv/enum.rs:157:8 | -134 | #[repr(isize)] +157 | #[repr(isize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:140:8 + --> tests/ui-msrv/enum.rs:163:8 | -140 | #[repr(u32)] +163 | #[repr(u32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:146:8 + --> tests/ui-msrv/enum.rs:169:8 | -146 | #[repr(i32)] +169 | #[repr(i32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:152:8 + --> tests/ui-msrv/enum.rs:175:8 | -152 | #[repr(u64)] +175 | #[repr(u64)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:158:8 + --> tests/ui-msrv/enum.rs:181:8 | -158 | #[repr(i64)] +181 | #[repr(i64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:168:8 + --> tests/ui-msrv/enum.rs:452:8 | -168 | #[repr(C)] +452 | #[repr(C)] | ^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:174:8 + --> tests/ui-msrv/enum.rs:458:8 | -174 | #[repr(u16)] +458 | #[repr(u16)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:180:8 + --> tests/ui-msrv/enum.rs:464:8 | -180 | #[repr(i16)] +464 | #[repr(i16)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:186:8 + --> tests/ui-msrv/enum.rs:470:8 | -186 | #[repr(u32)] +470 | #[repr(u32)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:192:8 + --> tests/ui-msrv/enum.rs:476:8 | -192 | #[repr(i32)] +476 | #[repr(i32)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:198:8 + --> tests/ui-msrv/enum.rs:482:8 | -198 | #[repr(u64)] +482 | #[repr(u64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:204:8 + --> tests/ui-msrv/enum.rs:488:8 | -204 | #[repr(i64)] +488 | #[repr(i64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:210:8 + --> tests/ui-msrv/enum.rs:494:8 | -210 | #[repr(usize)] +494 | #[repr(usize)] | ^^^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:216:8 + --> tests/ui-msrv/enum.rs:500:8 | -216 | #[repr(isize)] +500 | #[repr(isize)] | ^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:222:12 + --> tests/ui-msrv/enum.rs:506:12 | -222 | #[repr(u8, align(2))] +506 | #[repr(u8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:228:12 + --> tests/ui-msrv/enum.rs:512:12 | -228 | #[repr(i8, align(2))] +512 | #[repr(i8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:234:18 + --> tests/ui-msrv/enum.rs:518:18 | -234 | #[repr(align(1), align(2))] +518 | #[repr(align(1), align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:240:8 + --> tests/ui-msrv/enum.rs:524:8 | -240 | #[repr(align(2), align(4))] +524 | #[repr(align(2), align(4))] | ^^^^^^^^ +error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants + --> tests/ui-msrv/enum.rs:136:9 + | +136 | A = 1, + | ^ disallowed custom discriminant +137 | B(NotFromZeros), + | --------------- tuple variant defined here + | + = note: see issue #60553 for more information + error[E0565]: meta item in `repr` must be an identifier --> tests/ui-msrv/enum.rs:19:8 | @@ -269,3 +270,108 @@ error[E0277]: the trait bound `UnsafeCell: Immutable` is not satisfied | = help: see issue #48214 = note: this error originates in the derive macro `Immutable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotTryFromBytes: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:82:10 + | +82 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotTryFromBytes` + | + = help: see issue #48214 + = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotTryFromBytes: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:82:10 + | +82 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotTryFromBytes` + | + = help: see issue #48214 + = note: this error originates in the derive macro `::zerocopy_derive::TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: FromZeros` is not satisfied + --> tests/ui-msrv/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `FromZeros` is not implemented for `NotFromZeros` + | + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: see issue #48214 + = note: this error originates in the derive macro `::zerocopy_derive::TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:133:10 + | +133 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:133:10 + | +133 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: see issue #48214 + = note: this error originates in the derive macro `::zerocopy_derive::TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `bool: FromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:186:10 + | +186 | #[derive(FromBytes)] + | ^^^^^^^^^ the trait `FromBytes` is not implemented for `bool` + | + = help: see issue #48214 + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-msrv/enum.rs:533:10 + | +533 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the following implementations were found: + as ShouldBe> + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-msrv/enum.rs:544:10 + | +544 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the following implementations were found: + as ShouldBe> + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-msrv/enum.rs:550:10 + | +550 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the following implementations were found: + as ShouldBe> + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/tests/ui-nightly/enum.rs b/zerocopy-derive/tests/ui-nightly/enum.rs index 329c757bcc..0b17423de8 100644 --- a/zerocopy-derive/tests/ui-nightly/enum.rs +++ b/zerocopy-derive/tests/ui-nightly/enum.rs @@ -72,9 +72,17 @@ enum TryFromBytes1 { } #[derive(TryFromBytes)] -#[repr(u8)] enum TryFromBytes2 { - A(u8), + A, + B(u8), +} + +struct NotTryFromBytes; + +#[derive(TryFromBytes)] +#[repr(u8)] +enum TryFromBytes3 { + A(NotTryFromBytes), } // @@ -114,6 +122,21 @@ enum FromZeros5 { B, } +struct NotFromZeros; + +#[derive(FromZeros)] +#[repr(u8)] +enum FromZeros6 { + A(NotFromZeros), +} + +#[derive(FromZeros)] +#[repr(u8)] +enum FromZeros7 { + A = 1, + B(NotFromZeros), +} + // // FromBytes errors // @@ -160,6 +183,267 @@ enum FromBytes7 { A, } +#[derive(FromBytes)] +#[repr(u8)] +enum FooU8 { + Variant0, + Variant1, + Variant2, + Variant3, + Variant4, + Variant5, + Variant6, + Variant7, + Variant8, + Variant9, + Variant10, + Variant11, + Variant12, + Variant13, + Variant14, + Variant15, + Variant16, + Variant17, + Variant18, + Variant19, + Variant20, + Variant21, + Variant22, + Variant23, + Variant24, + Variant25, + Variant26, + Variant27, + Variant28, + Variant29, + Variant30, + Variant31, + Variant32, + Variant33, + Variant34, + Variant35, + Variant36, + Variant37, + Variant38, + Variant39, + Variant40, + Variant41, + Variant42, + Variant43, + Variant44, + Variant45, + Variant46, + Variant47, + Variant48, + Variant49, + Variant50, + Variant51, + Variant52, + Variant53, + Variant54, + Variant55, + Variant56, + Variant57, + Variant58, + Variant59, + Variant60, + Variant61, + Variant62, + Variant63, + Variant64, + Variant65, + Variant66, + Variant67, + Variant68, + Variant69, + Variant70, + Variant71, + Variant72, + Variant73, + Variant74, + Variant75, + Variant76, + Variant77, + Variant78, + Variant79, + Variant80, + Variant81, + Variant82, + Variant83, + Variant84, + Variant85, + Variant86, + Variant87, + Variant88, + Variant89, + Variant90, + Variant91, + Variant92, + Variant93, + Variant94, + Variant95, + Variant96, + Variant97, + Variant98, + Variant99, + Variant100, + Variant101, + Variant102, + Variant103, + Variant104, + Variant105, + Variant106, + Variant107, + Variant108, + Variant109, + Variant110, + Variant111, + Variant112, + Variant113, + Variant114, + Variant115, + Variant116, + Variant117, + Variant118, + Variant119, + Variant120, + Variant121, + Variant122, + Variant123, + Variant124, + Variant125, + Variant126, + Variant127, + Variant128, + Variant129, + Variant130, + Variant131, + Variant132, + Variant133, + Variant134, + Variant135, + Variant136, + Variant137, + Variant138, + Variant139, + Variant140, + Variant141, + Variant142, + Variant143, + Variant144, + Variant145, + Variant146, + Variant147, + Variant148, + Variant149, + Variant150, + Variant151, + Variant152, + Variant153, + Variant154, + Variant155, + Variant156, + Variant157, + Variant158, + Variant159, + Variant160, + Variant161, + Variant162, + Variant163, + Variant164, + Variant165, + Variant166, + Variant167, + Variant168, + Variant169, + Variant170, + Variant171, + Variant172, + Variant173, + Variant174, + Variant175, + Variant176, + Variant177, + Variant178, + Variant179, + Variant180, + Variant181, + Variant182, + Variant183, + Variant184, + Variant185, + Variant186, + Variant187, + Variant188, + Variant189, + Variant190, + Variant191, + Variant192, + Variant193, + Variant194, + Variant195, + Variant196, + Variant197, + Variant198, + Variant199, + Variant200, + Variant201, + Variant202, + Variant203, + Variant204, + Variant205, + Variant206, + Variant207, + Variant208, + Variant209, + Variant210, + Variant211, + Variant212, + Variant213, + Variant214, + Variant215, + Variant216, + Variant217, + Variant218, + Variant219, + Variant220, + Variant221, + Variant222, + Variant223, + Variant224, + Variant225, + Variant226, + Variant227, + Variant228, + Variant229, + Variant230, + Variant231, + Variant232, + Variant233, + Variant234, + Variant235, + Variant236, + Variant237, + Variant238, + Variant239, + Variant240, + Variant241, + Variant242, + Variant243, + Variant244, + Variant245, + Variant246, + Variant247, + Variant248, + Variant249, + Variant250, + Variant251, + Variant252, + Variant253, + Variant254, + Variant255(bool), +} + // // Unaligned errors // @@ -241,3 +525,31 @@ enum Unaligned12 { enum Unaligned13 { A, } + +// +// IntoBytes errors +// + +#[derive(IntoBytes)] +#[repr(u8)] +enum IntoBytes1 { + A, + B(u8), +} + +#[derive(IntoBytes)] +#[repr(C, align(4))] +struct Align4IntoBytes(u32); + +#[derive(IntoBytes)] +#[repr(u8)] +enum IntoBytes2 { + A(Align4IntoBytes), +} + +#[derive(IntoBytes)] +#[repr(u32)] +enum IntoBytes3 { + A(u32), + B(u16), +} diff --git a/zerocopy-derive/tests/ui-nightly/enum.stderr b/zerocopy-derive/tests/ui-nightly/enum.stderr index e97d5ed218..1aee5b6ea9 100644 --- a/zerocopy-derive/tests/ui-nightly/enum.stderr +++ b/zerocopy-derive/tests/ui-nightly/enum.stderr @@ -38,196 +38,187 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only field-less enums can implement TryFromBytes - --> tests/ui-nightly/enum.rs:75:1 - | -75 | / #[repr(u8)] -76 | | enum TryFromBytes2 { -77 | | A(u8), -78 | | } - | |_^ - -error: only field-less enums can implement TryFromBytes - --> tests/ui-nightly/enum.rs:85:1 +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-nightly/enum.rs:74:10 | -85 | / enum FromZeros1 { -86 | | A(u8), -87 | | } - | |_^ - -error: only field-less enums can implement FromZeros - --> tests/ui-nightly/enum.rs:85:1 +74 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ | -85 | / enum FromZeros1 { -86 | | A(u8), -87 | | } - | |_^ + = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only field-less enums can implement TryFromBytes - --> tests/ui-nightly/enum.rs:90:1 +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-nightly/enum.rs:92:10 | -90 | / enum FromZeros2 { -91 | | A, -92 | | B(u8), -93 | | } - | |_^ - -error: only field-less enums can implement FromZeros - --> tests/ui-nightly/enum.rs:90:1 +92 | #[derive(FromZeros)] + | ^^^^^^^^^ | -90 | / enum FromZeros2 { -91 | | A, -92 | | B(u8), -93 | | } - | |_^ + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout - --> tests/ui-nightly/enum.rs:95:10 + --> tests/ui-nightly/enum.rs:97:10 | -95 | #[derive(FromZeros)] +97 | #[derive(FromZeros)] | ^^^^^^^^^ | = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-nightly/enum.rs:103:10 + | +103 | #[derive(FromZeros)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + error: FromZeros only supported on enums with a variant that has a discriminant of `0` - --> tests/ui-nightly/enum.rs:102:1 + --> tests/ui-nightly/enum.rs:110:1 | -102 | / #[repr(u8)] -103 | | enum FromZeros4 { -104 | | A = 1, -105 | | B = 2, -106 | | } +110 | / #[repr(u8)] +111 | | enum FromZeros4 { +112 | | A = 1, +113 | | B = 2, +114 | | } | |_^ error: FromZeros only supported on enums with a variant that has a discriminant of `0` help: This enum has discriminants which are not literal integers. One of those may define or imply which variant has a discriminant of zero. Use a literal integer to define or imply the variant with a discriminant of zero. - --> tests/ui-nightly/enum.rs:111:1 + --> tests/ui-nightly/enum.rs:119:1 | -111 | / #[repr(i8)] -112 | | enum FromZeros5 { -113 | | A = NEGATIVE_ONE, -114 | | B, -115 | | } +119 | / #[repr(i8)] +120 | | enum FromZeros5 { +121 | | A = NEGATIVE_ONE, +122 | | B, +123 | | } + | |_^ + +error: FromZeros only supported on enums with a variant that has a discriminant of `0` + --> tests/ui-nightly/enum.rs:134:1 + | +134 | / #[repr(u8)] +135 | | enum FromZeros7 { +136 | | A = 1, +137 | | B(NotFromZeros), +138 | | } | |_^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:122:8 + --> tests/ui-nightly/enum.rs:145:8 | -122 | #[repr(C)] +145 | #[repr(C)] | ^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:128:8 + --> tests/ui-nightly/enum.rs:151:8 | -128 | #[repr(usize)] +151 | #[repr(usize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:134:8 + --> tests/ui-nightly/enum.rs:157:8 | -134 | #[repr(isize)] +157 | #[repr(isize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:140:8 + --> tests/ui-nightly/enum.rs:163:8 | -140 | #[repr(u32)] +163 | #[repr(u32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:146:8 + --> tests/ui-nightly/enum.rs:169:8 | -146 | #[repr(i32)] +169 | #[repr(i32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:152:8 + --> tests/ui-nightly/enum.rs:175:8 | -152 | #[repr(u64)] +175 | #[repr(u64)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:158:8 + --> tests/ui-nightly/enum.rs:181:8 | -158 | #[repr(i64)] +181 | #[repr(i64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:168:8 + --> tests/ui-nightly/enum.rs:452:8 | -168 | #[repr(C)] +452 | #[repr(C)] | ^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:174:8 + --> tests/ui-nightly/enum.rs:458:8 | -174 | #[repr(u16)] +458 | #[repr(u16)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:180:8 + --> tests/ui-nightly/enum.rs:464:8 | -180 | #[repr(i16)] +464 | #[repr(i16)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:186:8 + --> tests/ui-nightly/enum.rs:470:8 | -186 | #[repr(u32)] +470 | #[repr(u32)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:192:8 + --> tests/ui-nightly/enum.rs:476:8 | -192 | #[repr(i32)] +476 | #[repr(i32)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:198:8 + --> tests/ui-nightly/enum.rs:482:8 | -198 | #[repr(u64)] +482 | #[repr(u64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:204:8 + --> tests/ui-nightly/enum.rs:488:8 | -204 | #[repr(i64)] +488 | #[repr(i64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:210:8 + --> tests/ui-nightly/enum.rs:494:8 | -210 | #[repr(usize)] +494 | #[repr(usize)] | ^^^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:216:8 + --> tests/ui-nightly/enum.rs:500:8 | -216 | #[repr(isize)] +500 | #[repr(isize)] | ^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:222:12 + --> tests/ui-nightly/enum.rs:506:12 | -222 | #[repr(u8, align(2))] +506 | #[repr(u8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:228:12 + --> tests/ui-nightly/enum.rs:512:12 | -228 | #[repr(i8, align(2))] +512 | #[repr(i8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:234:18 + --> tests/ui-nightly/enum.rs:518:18 | -234 | #[repr(align(1), align(2))] +518 | #[repr(align(1), align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:240:8 + --> tests/ui-nightly/enum.rs:524:8 | -240 | #[repr(align(2), align(4))] +524 | #[repr(align(2), align(4))] | ^^^^^^^^ error[E0565]: meta item in `repr` must be an identifier @@ -299,3 +290,160 @@ help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | 9 + #![feature(trivial_bounds)] | + +error[E0277]: the trait bound `NotTryFromBytes: TryFromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:82:10 + | +82 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotTryFromBytes` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `NotFromZeros: FromZeros` is not satisfied + --> tests/ui-nightly/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `FromZeros` is not implemented for `NotFromZeros` + | + = help: the following other types implement trait `FromZeros`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:133:10 + | +133 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `bool: FromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:186:10 + | +186 | #[derive(FromBytes)] + | ^^^^^^^^^ the trait `FromBytes` is not implemented for `bool` + | + = help: the following other types implement trait `FromBytes`: + () + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + AtomicU16 + AtomicU32 + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-nightly/enum.rs:533:10 + | +533 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the trait `ShouldBe` is implemented for `HasPadding` + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-nightly/enum.rs:544:10 + | +544 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the trait `ShouldBe` is implemented for `HasPadding` + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-nightly/enum.rs:550:10 + | +550 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the trait `ShouldBe` is implemented for `HasPadding` + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) +help: add `#![feature(trivial_bounds)]` to the crate attributes to enable + | +9 + #![feature(trivial_bounds)] + | diff --git a/zerocopy-derive/tests/ui-stable/enum.stderr b/zerocopy-derive/tests/ui-stable/enum.stderr index ec9b89f276..09b8b1b954 100644 --- a/zerocopy-derive/tests/ui-stable/enum.stderr +++ b/zerocopy-derive/tests/ui-stable/enum.stderr @@ -38,196 +38,187 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only field-less enums can implement TryFromBytes - --> tests/ui-stable/enum.rs:75:1 - | -75 | / #[repr(u8)] -76 | | enum TryFromBytes2 { -77 | | A(u8), -78 | | } - | |_^ - -error: only field-less enums can implement TryFromBytes - --> tests/ui-stable/enum.rs:85:1 +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-stable/enum.rs:74:10 | -85 | / enum FromZeros1 { -86 | | A(u8), -87 | | } - | |_^ - -error: only field-less enums can implement FromZeros - --> tests/ui-stable/enum.rs:85:1 +74 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ | -85 | / enum FromZeros1 { -86 | | A(u8), -87 | | } - | |_^ + = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: only field-less enums can implement TryFromBytes - --> tests/ui-stable/enum.rs:90:1 +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-stable/enum.rs:92:10 | -90 | / enum FromZeros2 { -91 | | A, -92 | | B(u8), -93 | | } - | |_^ - -error: only field-less enums can implement FromZeros - --> tests/ui-stable/enum.rs:90:1 +92 | #[derive(FromZeros)] + | ^^^^^^^^^ | -90 | / enum FromZeros2 { -91 | | A, -92 | | B(u8), -93 | | } - | |_^ + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout - --> tests/ui-stable/enum.rs:95:10 + --> tests/ui-stable/enum.rs:97:10 | -95 | #[derive(FromZeros)] +97 | #[derive(FromZeros)] | ^^^^^^^^^ | = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) +error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout + --> tests/ui-stable/enum.rs:103:10 + | +103 | #[derive(FromZeros)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + error: FromZeros only supported on enums with a variant that has a discriminant of `0` - --> tests/ui-stable/enum.rs:102:1 + --> tests/ui-stable/enum.rs:110:1 | -102 | / #[repr(u8)] -103 | | enum FromZeros4 { -104 | | A = 1, -105 | | B = 2, -106 | | } +110 | / #[repr(u8)] +111 | | enum FromZeros4 { +112 | | A = 1, +113 | | B = 2, +114 | | } | |_^ error: FromZeros only supported on enums with a variant that has a discriminant of `0` help: This enum has discriminants which are not literal integers. One of those may define or imply which variant has a discriminant of zero. Use a literal integer to define or imply the variant with a discriminant of zero. - --> tests/ui-stable/enum.rs:111:1 + --> tests/ui-stable/enum.rs:119:1 + | +119 | / #[repr(i8)] +120 | | enum FromZeros5 { +121 | | A = NEGATIVE_ONE, +122 | | B, +123 | | } + | |_^ + +error: FromZeros only supported on enums with a variant that has a discriminant of `0` + --> tests/ui-stable/enum.rs:134:1 | -111 | / #[repr(i8)] -112 | | enum FromZeros5 { -113 | | A = NEGATIVE_ONE, -114 | | B, -115 | | } +134 | / #[repr(u8)] +135 | | enum FromZeros7 { +136 | | A = 1, +137 | | B(NotFromZeros), +138 | | } | |_^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:122:8 + --> tests/ui-stable/enum.rs:145:8 | -122 | #[repr(C)] +145 | #[repr(C)] | ^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:128:8 + --> tests/ui-stable/enum.rs:151:8 | -128 | #[repr(usize)] +151 | #[repr(usize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:134:8 + --> tests/ui-stable/enum.rs:157:8 | -134 | #[repr(isize)] +157 | #[repr(isize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:140:8 + --> tests/ui-stable/enum.rs:163:8 | -140 | #[repr(u32)] +163 | #[repr(u32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:146:8 + --> tests/ui-stable/enum.rs:169:8 | -146 | #[repr(i32)] +169 | #[repr(i32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:152:8 + --> tests/ui-stable/enum.rs:175:8 | -152 | #[repr(u64)] +175 | #[repr(u64)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:158:8 + --> tests/ui-stable/enum.rs:181:8 | -158 | #[repr(i64)] +181 | #[repr(i64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:168:8 + --> tests/ui-stable/enum.rs:452:8 | -168 | #[repr(C)] +452 | #[repr(C)] | ^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:174:8 + --> tests/ui-stable/enum.rs:458:8 | -174 | #[repr(u16)] +458 | #[repr(u16)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:180:8 + --> tests/ui-stable/enum.rs:464:8 | -180 | #[repr(i16)] +464 | #[repr(i16)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:186:8 + --> tests/ui-stable/enum.rs:470:8 | -186 | #[repr(u32)] +470 | #[repr(u32)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:192:8 + --> tests/ui-stable/enum.rs:476:8 | -192 | #[repr(i32)] +476 | #[repr(i32)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:198:8 + --> tests/ui-stable/enum.rs:482:8 | -198 | #[repr(u64)] +482 | #[repr(u64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:204:8 + --> tests/ui-stable/enum.rs:488:8 | -204 | #[repr(i64)] +488 | #[repr(i64)] | ^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:210:8 + --> tests/ui-stable/enum.rs:494:8 | -210 | #[repr(usize)] +494 | #[repr(usize)] | ^^^^^ error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:216:8 + --> tests/ui-stable/enum.rs:500:8 | -216 | #[repr(isize)] +500 | #[repr(isize)] | ^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:222:12 + --> tests/ui-stable/enum.rs:506:12 | -222 | #[repr(u8, align(2))] +506 | #[repr(u8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:228:12 + --> tests/ui-stable/enum.rs:512:12 | -228 | #[repr(i8, align(2))] +512 | #[repr(i8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:234:18 + --> tests/ui-stable/enum.rs:518:18 | -234 | #[repr(align(1), align(2))] +518 | #[repr(align(1), align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:240:8 + --> tests/ui-stable/enum.rs:524:8 | -240 | #[repr(align(2), align(4))] +524 | #[repr(align(2), align(4))] | ^^^^^^^^ error[E0565]: meta item in `repr` must be an identifier @@ -291,3 +282,128 @@ error[E0277]: the trait bound `UnsafeCell: Immutable` is not satisfied and $N others = help: see issue #48214 = note: this error originates in the derive macro `Immutable` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotTryFromBytes: TryFromBytes` is not satisfied + --> tests/ui-stable/enum.rs:82:10 + | +82 | #[derive(TryFromBytes)] + | ^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotTryFromBytes` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `TryFromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-stable/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: FromZeros` is not satisfied + --> tests/ui-stable/enum.rs:127:10 + | +127 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `FromZeros` is not implemented for `NotFromZeros` + | + = help: the following other types implement trait `FromZeros`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-stable/enum.rs:133:10 + | +133 | #[derive(FromZeros)] + | ^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotFromZeros` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromZeros` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `bool: FromBytes` is not satisfied + --> tests/ui-stable/enum.rs:186:10 + | +186 | #[derive(FromBytes)] + | ^^^^^^^^^ the trait `FromBytes` is not implemented for `bool` + | + = help: the following other types implement trait `FromBytes`: + () + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + AtomicU16 + AtomicU32 + and $N others + = help: see issue #48214 + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-stable/enum.rs:533:10 + | +533 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the trait `ShouldBe` is implemented for `HasPadding` + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-stable/enum.rs:544:10 + | +544 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the trait `ShouldBe` is implemented for `HasPadding` + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `HasPadding: ShouldBe` is not satisfied + --> tests/ui-stable/enum.rs:550:10 + | +550 | #[derive(IntoBytes)] + | ^^^^^^^^^ the trait `ShouldBe` is not implemented for `HasPadding` + | + = help: the trait `ShouldBe` is implemented for `HasPadding` + = help: see issue #48214 + = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info)