From d6f8386ddf93d576cc51f57cf9e244e2a210852e 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 The primary feature of this change is adding support for deriving zerocopy's traits on data-carrying enums. Along with that, it has a few other features: - Adds support for enums with repr(C, int) - Improves zero discriminant detection for enums with negative discriminants - For development, adds a VSCode config so we can prevent rust-analyzer from choking on our tests with enums that have thousands of variants --- .vscode/settings.json | 5 + src/macro_util.rs | 16 +- zerocopy-derive/src/enums.rs | 491 +++++++++++++++++++ zerocopy-derive/src/ext.rs | 58 ++- zerocopy-derive/src/lib.rs | 316 ++++++------ zerocopy-derive/src/repr.rs | 30 ++ zerocopy-derive/tests/enum_from_bytes.rs | 263 ++++++++++ zerocopy-derive/tests/enum_from_zeros.rs | 49 +- zerocopy-derive/tests/enum_to_bytes.rs | 20 + zerocopy-derive/tests/enum_try_from_bytes.rs | 55 +++ zerocopy-derive/tests/ui-msrv/enum.stderr | 257 +++++++--- zerocopy-derive/tests/ui-nightly/enum.rs | 332 ++++++++++++- zerocopy-derive/tests/ui-nightly/enum.stderr | 377 +++++++++++--- zerocopy-derive/tests/ui-stable/enum.stderr | 345 ++++++++++--- 14 files changed, 2213 insertions(+), 401 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 zerocopy-derive/src/enums.rs diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..c1b9d6bd11 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "rust-analyzer.check.extraEnv": { + "RUSTFLAGS": "--cfg rust_analyzer", + } +} diff --git a/src/macro_util.rs b/src/macro_util.rs index c475dff889..c8c7f140d8 100644 --- a/src/macro_util.rs +++ b/src/macro_util.rs @@ -274,7 +274,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>())* }; } @@ -294,11 +294,19 @@ 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>())* }; } +#[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!`. @@ -728,7 +736,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); @@ -759,7 +767,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); diff --git a/zerocopy-derive/src/enums.rs b/zerocopy-derive/src/enums.rs new file mode 100644 index 0000000000..6d89b02980 --- /dev/null +++ b/zerocopy-derive/src/enums.rs @@ -0,0 +1,491 @@ +// 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, Variant}; +use syn::Index; + +use crate::{EnumRepr, Trait}; + +struct RawIdents { + r#enum: Ident, + discriminant: Ident, + fields: Ident, + variant_prefix: String, +} + +impl RawIdents { + const DISCRIMINANT_VALUE_PREFIX: &'static str = "RAW_DISCRIMINANT_"; + + fn for_enum(ident: &Ident) -> Self { + Self { + r#enum: Ident::new(&format!("Raw{}Enum", ident), ident.span()), + discriminant: Ident::new(&format!("Raw{}Discriminant", ident), ident.span()), + fields: Ident::new(&format!("Raw{}Fields", ident), ident.span()), + variant_prefix: format!("Raw{}Variant", ident), + } + } + + fn discriminant_const(&self, variant: &Ident) -> Ident { + Ident::new(&format!("{}{}", Self::DISCRIMINANT_VALUE_PREFIX, variant), variant.span()) + } + + fn variant_struct(&self, variant: &Ident) -> Ident { + Ident::new(&format!("{}{}", self.variant_prefix, variant), variant.span()) + } +} + +/// Generates an implementation of `is_bit_valid` for the given enum input. +pub(crate) fn derive_is_bit_valid( + reprs: &[EnumRepr], + ident: &Ident, + generics: &Generics, + data: &DataEnum, +) -> TokenStream { + let idents = RawIdents::for_enum(ident); + + if reprs.contains(&EnumRepr::C) { + derive_is_bit_valid_c(data, generics, &idents) + } else { + derive_is_bit_valid_primitive(data, generics, discriminant_repr(reprs), &idents) + } +} + +pub(crate) fn discriminant_repr(reprs: &[EnumRepr]) -> &EnumRepr { + if reprs.contains(&EnumRepr::C) { + reprs.iter().find(|&r| r != &EnumRepr::C).unwrap_or(&EnumRepr::C) + } else { + reprs.first().unwrap() + } +} + +pub(crate) fn generate_discriminant_type( + data: &DataEnum, + repr: &EnumRepr, + name: &Ident, +) -> 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 #name { + #(#variants),* + } + } +} + +fn generate_discriminant_consts(data: &DataEnum, idents: &RawIdents) -> TokenStream { + let discriminant = &idents.discriminant; + + let consts = data.variants.iter().map(|v| { + let ident = &v.ident; + let const_ident = idents.discriminant_const(ident); + quote! { + // SAFETY: The discriminant type is a C-style enum, and all C-style + // enums are represented as simple integers. All integers can be + // transmuted to their constituent bytes. + const #const_ident: [u8; ::core::mem::size_of::<#discriminant>()] = unsafe { + ::core::mem::transmute(#discriminant::#ident) + }; + } + }); + + quote! { + #(#consts)* + } +} + +fn generate_variant_struct( + variant: &Variant, + generics: &Generics, + idents: &RawIdents, + with_tag: bool, +) -> TokenStream { + let discriminant_ident = &idents.discriminant; + let variant_struct_ident = idents.variant_struct(&variant.ident); + + let type_params = generics.type_params().map(|p| &p.ident); + let phantom_ty = quote! { + ::core::marker::PhantomData<(#(#type_params,)*)> + }; + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + let trait_path = Trait::TryFromBytes.path(); + + match &variant.fields { + Fields::Named(fields) => { + let tag_field = + if with_tag { Some(quote! { __tag: #discriminant_ident, }) } else { None }; + + let struct_fields = fields.named.iter().map(|f| { + let ident = &f.ident; + let ty = &f.ty; + quote! { + pub #ident: #ty + } + }); + + let field_checks = fields.named.iter().map(|f| { + let ident = &f.ident; + let ty = &f.ty; + quote! { + // 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. + <#ty as #trait_path>::is_bit_valid(unsafe { + this.project(|this| ::core::ptr::addr_of_mut!((*this).#ident)) + }) + } + }); + + quote! { + #[repr(C)] + #[allow(non_snake_case)] + struct #variant_struct_ident #impl_generics #where_clause { + #tag_field + #(#struct_fields,)* + _phantom: #phantom_ty, + } + + impl #variant_struct_ident #impl_generics #where_clause { + fn is_bit_valid(this: ::zerocopy::Maybe<'_, Self, A>) -> bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { + true #(&& #field_checks)* + } + } + } + } + Fields::Unnamed(fields) => { + let tag_field = if with_tag { Some(quote! { #discriminant_ident, }) } else { None }; + + let struct_fields = fields.unnamed.iter().map(|f| { + let ty = &f.ty; + quote! { + pub #ty + } + }); + + let field_checks = fields.unnamed.iter().enumerate().map(|(i, f)| { + let i = Index::from(i + tag_field.is_some() as usize); + let ty = &f.ty; + quote! { + // 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. + <#ty as #trait_path>::is_bit_valid(unsafe { + this.project(|this| ::core::ptr::addr_of_mut!((*this).#i)) + }) + } + }); + + quote! { + #[repr(C)] + #[allow(non_snake_case)] + struct #variant_struct_ident #impl_generics ( + #tag_field + #(#struct_fields,)* + #phantom_ty, + ) #where_clause; + + impl #variant_struct_ident #impl_generics #where_clause { + fn is_bit_valid(this: ::zerocopy::Maybe<'_, Self, A>) -> bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { + true #(&& #field_checks)* + } + } + } + } + Fields::Unit => { + let tag_field = if with_tag { Some(quote! { #discriminant_ident, }) } else { None }; + + quote! { + #[repr(C)] + #[allow(non_snake_case)] + struct #variant_struct_ident #impl_generics ( + #tag_field + #phantom_ty, + ) #where_clause; + + impl #variant_struct_ident #impl_generics #where_clause { + fn is_bit_valid(this: ::zerocopy::Maybe<'_, Self, A>) -> bool + where + A: ::zerocopy::pointer::invariant::Aliasing + + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>, + { true } + } + } + } + } +} + +fn derive_is_bit_valid_c(data: &DataEnum, generics: &Generics, idents: &RawIdents) -> TokenStream { + let discriminant_type = generate_discriminant_type(data, &EnumRepr::C, &idents.discriminant); + let discriminant_consts = generate_discriminant_consts(data, idents); + + let non_unit_variants = + data.variants.iter().filter(|variant| !matches!(variant.fields, Fields::Unit)); + let variant_structs = non_unit_variants + .clone() + .map(|variant| generate_variant_struct(variant, generics, idents, false)); + + let raw_ident = &idents.r#enum; + let discriminant_ident = &idents.discriminant; + let fields_ident = &idents.fields; + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + let variant_idents = non_unit_variants.clone().map(|v| &v.ident); + let variant_struct_idents = non_unit_variants.clone().map(|v| idents.variant_struct(&v.ident)); + + let match_arms = data.variants.iter().map(|variant| { + let value_ident = idents.discriminant_const(&variant.ident); + + if matches!(variant.fields, Fields::Unit) { + quote! { + #value_ident => true + } + } else { + let variant_struct = idents.variant_struct(&variant.ident); + + quote! { + #value_ident => { + // SAFETY: + // - `cast_unsized`: + // - 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 discriminant of the enum matched + // the value 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. + // - `assume_initialized`: `cast_unsized` removes the + // initialization invariant from `p`, so we re-assert that + // all of the bytes are initialized. + let candidate = unsafe { + fields + .cast_unsized(|p: *mut #fields_ident| p as *mut #variant_struct) + .assume_initialized() + }; + #variant_struct::is_bit_valid(candidate) + } + } + } + }); + + quote! { + // SAFETY: We use `is_bit_valid` to validate that the bit pattern + // corresponds to one of the enum's variant discriminants. Then, we + // check the bit validity of each field of that 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; + + let candidate = candidate.reborrow().forget_exclusive(); + + #discriminant_type + #discriminant_consts + #(#variant_structs)* + + #[repr(C)] + #[allow(non_snake_case)] + union #fields_ident #impl_generics #where_clause { + #( + pub #variant_idents: ::core::mem::ManuallyDrop< + #variant_struct_idents #ty_generics, + >, + )* + __nonempty: (), + } + + #[repr(C)] + struct #raw_ident #impl_generics #where_clause { + pub __tag: #discriminant_ident, + pub fields: #fields_ident #ty_generics, + } + + // SAFETY: + // - `cast` is implemented as required. + // - By definition, `*mut Self` and `*mut [u8; size_of::()]` + // are types of the same size. + let discriminant = unsafe { candidate.cast_unsized(|p: *mut Self| p as *mut [core_reexport::primitive::u8; core_reexport::mem::size_of::<#discriminant_ident>()]) }; + // SAFETY: Since `candidate` has the invariant `Initialized`, we + // know that `candidate`'s referent (and thus `discriminant`'s + // referent) is as-initialized as `Self`. 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(); + + // SAFETY: + // - `cast_unsized`: + // - 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. + // - `assume_initialized`: `cast_unsized` removes the + // initialization invariant from `p`, so we re-assert that + // all of the bytes are initialized. + // - `project`: + // - 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 fields = unsafe { + candidate + .cast_unsized(|p: *mut Self| p as *mut #raw_ident) + .assume_initialized() + .project(|p: *mut #raw_ident| ::core::ptr::addr_of_mut!((*p).fields)) + }; + + match discriminant { + #(#match_arms,)* + _ => false, + } + } + } +} + +fn derive_is_bit_valid_primitive( + data: &DataEnum, + generics: &Generics, + int: &EnumRepr, + idents: &RawIdents, +) -> TokenStream { + let discriminant_type = generate_discriminant_type(data, int, &idents.discriminant); + let discriminant_consts = generate_discriminant_consts(data, idents); + + let non_unit_variants = + data.variants.iter().filter(|variant| !matches!(variant.fields, Fields::Unit)); + let variant_structs = non_unit_variants + .clone() + .map(|variant| generate_variant_struct(variant, generics, idents, false)); + + let discriminant_ident = &idents.discriminant; + + let match_arms = data.variants.iter().map(|variant| { + let value_ident = idents.discriminant_const(&variant.ident); + + if matches!(variant.fields, Fields::Unit) { + quote! { + #value_ident => true + } + } else { + let variant_struct = idents.variant_struct(&variant.ident); + + quote! { + #value_ident => { + // SAFETY: + // - `cast_unsized`: + // - 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 discriminant of the enum matched + // the value 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. + // - `assume_initialized`: `cast_unsized` removes the + // initialization invariant from `p`, so we re-assert that + // all of the bytes are initialized. + let candidate = unsafe { + candidate + .cast_unsized(|p: *mut Self| p as *mut #variant_struct) + .assume_initialized() + }; + #variant_struct::is_bit_valid(candidate) + } + } + } + }); + + quote! { + // SAFETY: We use `is_bit_valid` to validate that the bit pattern + // corresponds to one of the enum's variant discriminants. Then, we + // check the bit validity of each field of that 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; + + let candidate = candidate.reborrow().forget_exclusive(); + + #discriminant_type + #discriminant_consts + #(#variant_structs)* + + // SAFETY: + // - `cast` is implemented as required. + // - By definition, `*mut Self` and `*mut [u8; size_of::()]` + // are types of the same size. + let discriminant = unsafe { candidate.cast_unsized(|p: *mut Self| p as *mut [core_reexport::primitive::u8; core_reexport::mem::size_of::<#discriminant_ident>()]) }; + // SAFETY: Since `candidate` has the invariant `Initialized`, we + // know that `candidate`'s referent (and thus `discriminant`'s + // referent) is as-initialized as `Self`. 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(); + + match discriminant { + #(#match_arms,)* + _ => false, + } + } + } +} diff --git a/zerocopy-derive/src/ext.rs b/zerocopy-derive/src/ext.rs index ba6438c38e..7a008f06fe 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 discriminant(&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 discriminant(&self) -> Option { + match self { + Data::Struct(strc) => strc.discriminant(), + Data::Enum(enm) => enm.discriminant(), + Data::Union(un) => un.discriminant(), + } + } } impl DataExt for DataStruct { fn fields(&self) -> Vec<(TokenStream, &Type)> { map_fields(&self.fields) } + + fn variants(&self) -> Vec> { + vec![self.fields()] + } + + fn discriminant(&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 discriminant(&self) -> Option { + Some(Ident::new("Discriminant", 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 discriminant(&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 2d2515613a..4b0c775ee7 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -28,10 +28,12 @@ )] #![recursion_limit = "128"] +mod enums; mod ext; mod repr; use quote::quote_spanned; +use syn::{punctuated::Punctuated, token::Comma, ExprUnary, UnOp, Variant}; use { proc_macro2::Span, @@ -509,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.ident, &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], @@ -618,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: &[], } @@ -631,33 +560,57 @@ 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` - -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(); +// - that discriminant's fields all implement `FromZeros` + +fn find_zero_variant(variants: &Punctuated) -> Option { + let mut current_implicit_discriminant = Some(0isize); + for (i, v) in variants.iter().enumerate() { + // Whenever we encounter a discriminant that we don't understand, we + // stop tracking the current implicit discriminant. Those could have any + // value, even zero or a negative number. + match v.discriminant.as_ref() { + // Implicit discriminant + None => { + if current_implicit_discriminant == Some(0) { + return Some(i); + } + } + // Explicit discriminant + Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => { + match int.base10_parse::().ok() { + Some(0) => return Some(i), + Some(x) => current_implicit_discriminant = Some(x as isize), + None => current_implicit_discriminant = None, + } + } + // Explicit negative discriminant + Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr { + Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => { + match int.base10_parse::().ok() { + Some(0) => return Some(i), + Some(x) => current_implicit_discriminant = Some(-(x as isize)), + None => current_implicit_discriminant = None, + } + } + _ => current_implicit_discriminant = None, + }, + _ => current_implicit_discriminant = None, + } + if let Some(n) = current_implicit_discriminant.as_mut() { + *n += 1; + } } + None +} +fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { // 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)); - let has_explicit_zero_discriminant = - enm.variants.iter().filter_map(|v| v.discriminant.as_ref()).any(|(_, e)| { - if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = e { - i.base10_parse::().ok() == Some(0) - } else { - false - } - }); - // If the first variant of an enum does not specify its discriminant, it is set to zero: - // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations - let has_implicit_zero_discriminant = - enm.variants.iter().next().map(|v| v.discriminant.is_none()) == Some(true); - - if !has_explicit_zero_discriminant && !has_implicit_zero_discriminant { + let zero_variant_index = find_zero_variant(&enm.variants); + if zero_variant_index.is_none() { return Error::new_spanned( ast, "FromZeros only supported on enums with a variant that has a discriminant of `0`", @@ -665,7 +618,25 @@ fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok .to_compile_error(); } - impl_block(ast, enm, Trait::FromZeros, FieldBounds::ALL_SELF, SelfBounds::None, None, None) + let zero_variant = enm.variants.iter().nth(zero_variant_index.unwrap()).unwrap(); + 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::Explicit(explicit_bounds), + SelfBounds::None, + None, + None, + ) } // Unions are `FromZeros` if @@ -701,11 +672,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 @@ -831,15 +797,23 @@ 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 discriminant = Ident::new("Discriminant", Span::call_site()); + let context = + enums::generate_discriminant_type(enm, enums::discriminant_repr(&reprs), &discriminant); + impl_block_with_context( + ast, + enm, + Trait::IntoBytes, + FieldBounds::ALL_SELF, + SelfBounds::None, + Some(PaddingCheck::Enum), + None, + Some(context), + ) } #[rustfmt::skip] @@ -848,20 +822,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: &[], } @@ -923,11 +907,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. @@ -950,9 +929,10 @@ const ENUM_UNALIGNED_CFG: Config = { allowed_combinations: &[ &[U8], &[I8], + &[C, U8], + &[C, I8], ], disallowed_but_legal_combinations: &[ - &[C], &[U16], &[U32], &[U64], @@ -961,6 +941,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], ], } }; @@ -989,6 +978,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 { @@ -998,6 +989,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()) @@ -1035,11 +1027,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> { @@ -1073,6 +1065,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: // @@ -1135,6 +1150,8 @@ fn impl_block( let type_ident = &input.ident; let trait_path = trt.path(); let fields = data.fields(); + let variants = data.variants(); + let discriminant = data.discriminant(); fn bound_tt(ty: &Type, traits: impl Iterator) -> WherePredicate { let traits = traits.map(|t| t.path()); @@ -1148,16 +1165,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 ) }); @@ -1205,15 +1227,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 10479d87e1..9ecec4c05d 100644 --- a/zerocopy-derive/tests/enum_from_bytes.rs +++ b/zerocopy-derive/tests/enum_from_bytes.rs @@ -10,6 +10,8 @@ #![no_implicit_prelude] #![allow(warnings)] #![deny(deprecated)] +// The giant enums in this file cause rust-analyzer to hang during analysis. +#![cfg(not(rust_analyzer))] include!("include.rs"); @@ -293,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 0e2ead1590..99c61c2680 100644 --- a/zerocopy-derive/tests/enum_from_zeros.rs +++ b/zerocopy-derive/tests/enum_from_zeros.rs @@ -30,9 +30,54 @@ 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(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..ee31a971d7 100644 --- a/zerocopy-derive/tests/enum_try_from_bytes.rs +++ b/zerocopy-derive/tests/enum_try_from_bytes.rs @@ -177,3 +177,58 @@ 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() }), + ); +} diff --git a/zerocopy-derive/tests/ui-msrv/enum.stderr b/zerocopy-derive/tests/ui-msrv/enum.stderr index 4c623b7ff8..b5c6e59998 100644 --- a/zerocopy-derive/tests/ui-msrv/enum.stderr +++ b/zerocopy-derive/tests/ui-msrv/enum.stderr @@ -38,177 +38,198 @@ 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:110:1 + | +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` + --> tests/ui-msrv/enum.rs:125:1 + | +125 | / #[repr(u8)] +126 | | enum FromZeros6 { +127 | | A = 1, +128 | | B(NotFromZeros), +129 | | } + | |_^ + +error: FromZeros only supported on enums with a variant that has a discriminant of `0` + --> tests/ui-msrv/enum.rs:134:1 + | +134 | / #[repr(i8)] +135 | | enum FromZeros7 { +136 | | A = NEGATIVE_ONE, +137 | | B, +138 | | } + | |_^ + error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:106:8 + --> tests/ui-msrv/enum.rs:145:8 | -106 | #[repr(C)] +145 | #[repr(C)] | ^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:112:8 + --> tests/ui-msrv/enum.rs:151:8 | -112 | #[repr(usize)] +151 | #[repr(usize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:118:8 + --> tests/ui-msrv/enum.rs:157:8 | -118 | #[repr(isize)] +157 | #[repr(isize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:124:8 + --> tests/ui-msrv/enum.rs:163:8 | -124 | #[repr(u32)] +163 | #[repr(u32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:130:8 + --> tests/ui-msrv/enum.rs:169:8 | -130 | #[repr(i32)] +169 | #[repr(i32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:136:8 + --> tests/ui-msrv/enum.rs:175:8 | -136 | #[repr(u64)] +175 | #[repr(u64)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:142:8 + --> tests/ui-msrv/enum.rs:181:8 | -142 | #[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:152:8 + --> tests/ui-msrv/enum.rs:452:8 | -152 | #[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:158:8 + --> tests/ui-msrv/enum.rs:458:8 | -158 | #[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:164:8 + --> tests/ui-msrv/enum.rs:464:8 | -164 | #[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:170:8 + --> tests/ui-msrv/enum.rs:470:8 | -170 | #[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:176:8 + --> tests/ui-msrv/enum.rs:476:8 | -176 | #[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:182:8 + --> tests/ui-msrv/enum.rs:482:8 | -182 | #[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:188:8 + --> tests/ui-msrv/enum.rs:488:8 | -188 | #[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:194:8 + --> tests/ui-msrv/enum.rs:494:8 | -194 | #[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:200:8 + --> tests/ui-msrv/enum.rs:500:8 | -200 | #[repr(isize)] +500 | #[repr(isize)] | ^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:206:12 + --> tests/ui-msrv/enum.rs:506:12 | -206 | #[repr(u8, align(2))] +506 | #[repr(u8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:212:12 + --> tests/ui-msrv/enum.rs:512:12 | -212 | #[repr(i8, align(2))] +512 | #[repr(i8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:218:18 + --> tests/ui-msrv/enum.rs:518:18 | -218 | #[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:224:8 + --> tests/ui-msrv/enum.rs:524:8 | -224 | #[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:127:9 + | +127 | A = 1, + | ^ disallowed custom discriminant +128 | 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 | @@ -248,3 +269,81 @@ 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 `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-msrv/enum.rs:118:10 + | +118 | #[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:118:10 + | +118 | #[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:124:10 + | +124 | #[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 `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 c87a9008b2..09b1488a1c 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), } // @@ -98,6 +106,37 @@ enum FromZeros3 { B, } +#[derive(FromZeros)] +#[repr(u8)] +enum FromZeros4 { + A = 1, + B = 2, +} + +struct NotFromZeros; + +#[derive(FromZeros)] +#[repr(u8)] +enum FromZeros5 { + A(NotFromZeros), +} + +#[derive(FromZeros)] +#[repr(u8)] +enum FromZeros6 { + A = 1, + B(NotFromZeros), +} + +const NEGATIVE_ONE: i8 = -1; + +#[derive(FromZeros)] +#[repr(i8)] +enum FromZeros7 { + A = NEGATIVE_ONE, + B, +} + // // FromBytes errors // @@ -144,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 // @@ -225,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 75f03ec2c7..ae744156f8 100644 --- a/zerocopy-derive/tests/ui-nightly/enum.stderr +++ b/zerocopy-derive/tests/ui-nightly/enum.stderr @@ -38,175 +38,186 @@ 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:110:1 + | +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` + --> tests/ui-nightly/enum.rs:125:1 + | +125 | / #[repr(u8)] +126 | | enum FromZeros6 { +127 | | A = 1, +128 | | B(NotFromZeros), +129 | | } + | |_^ + +error: FromZeros only supported on enums with a variant that has a discriminant of `0` + --> tests/ui-nightly/enum.rs:134:1 + | +134 | / #[repr(i8)] +135 | | enum FromZeros7 { +136 | | A = NEGATIVE_ONE, +137 | | B, +138 | | } + | |_^ + error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:106:8 + --> tests/ui-nightly/enum.rs:145:8 | -106 | #[repr(C)] +145 | #[repr(C)] | ^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:112:8 + --> tests/ui-nightly/enum.rs:151:8 | -112 | #[repr(usize)] +151 | #[repr(usize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:118:8 + --> tests/ui-nightly/enum.rs:157:8 | -118 | #[repr(isize)] +157 | #[repr(isize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:124:8 + --> tests/ui-nightly/enum.rs:163:8 | -124 | #[repr(u32)] +163 | #[repr(u32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:130:8 + --> tests/ui-nightly/enum.rs:169:8 | -130 | #[repr(i32)] +169 | #[repr(i32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:136:8 + --> tests/ui-nightly/enum.rs:175:8 | -136 | #[repr(u64)] +175 | #[repr(u64)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:142:8 + --> tests/ui-nightly/enum.rs:181:8 | -142 | #[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:152:8 + --> tests/ui-nightly/enum.rs:452:8 | -152 | #[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:158:8 + --> tests/ui-nightly/enum.rs:458:8 | -158 | #[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:164:8 + --> tests/ui-nightly/enum.rs:464:8 | -164 | #[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:170:8 + --> tests/ui-nightly/enum.rs:470:8 | -170 | #[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:176:8 + --> tests/ui-nightly/enum.rs:476:8 | -176 | #[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:182:8 + --> tests/ui-nightly/enum.rs:482:8 | -182 | #[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:188:8 + --> tests/ui-nightly/enum.rs:488:8 | -188 | #[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:194:8 + --> tests/ui-nightly/enum.rs:494:8 | -194 | #[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:200:8 + --> tests/ui-nightly/enum.rs:500:8 | -200 | #[repr(isize)] +500 | #[repr(isize)] | ^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:206:12 + --> tests/ui-nightly/enum.rs:506:12 | -206 | #[repr(u8, align(2))] +506 | #[repr(u8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:212:12 + --> tests/ui-nightly/enum.rs:512:12 | -212 | #[repr(i8, align(2))] +512 | #[repr(i8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:218:18 + --> tests/ui-nightly/enum.rs:518:18 | -218 | #[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:224:8 + --> tests/ui-nightly/enum.rs:524:8 | -224 | #[repr(align(2), align(4))] +524 | #[repr(align(2), align(4))] | ^^^^^^^^ error[E0565]: meta item in `repr` must be an identifier @@ -278,3 +289,211 @@ 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:118:10 + | +118 | #[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:118:10 + | +118 | #[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:124:10 + | +124 | #[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)] + | + +error[E0277]: the trait bound `NotTryFromBytes: TryFromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:85:7 + | +85 | A(NotTryFromBytes), + | ^^^^^^^^^^^^^^^ 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 + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:121:7 + | +121 | A(NotFromZeros), + | ^^^^^^^^^^^^ 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 + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-nightly/enum.rs:128:7 + | +128 | B(NotFromZeros), + | ^^^^^^^^^^^^ 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 diff --git a/zerocopy-derive/tests/ui-stable/enum.stderr b/zerocopy-derive/tests/ui-stable/enum.stderr index 9abfebfa10..f925f5b42f 100644 --- a/zerocopy-derive/tests/ui-stable/enum.stderr +++ b/zerocopy-derive/tests/ui-stable/enum.stderr @@ -38,175 +38,186 @@ 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:110:1 + | +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` + --> tests/ui-stable/enum.rs:125:1 + | +125 | / #[repr(u8)] +126 | | enum FromZeros6 { +127 | | A = 1, +128 | | B(NotFromZeros), +129 | | } + | |_^ + +error: FromZeros only supported on enums with a variant that has a discriminant of `0` + --> tests/ui-stable/enum.rs:134:1 + | +134 | / #[repr(i8)] +135 | | enum FromZeros7 { +136 | | A = NEGATIVE_ONE, +137 | | B, +138 | | } + | |_^ + error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:106:8 + --> tests/ui-stable/enum.rs:145:8 | -106 | #[repr(C)] +145 | #[repr(C)] | ^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:112:8 + --> tests/ui-stable/enum.rs:151:8 | -112 | #[repr(usize)] +151 | #[repr(usize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:118:8 + --> tests/ui-stable/enum.rs:157:8 | -118 | #[repr(isize)] +157 | #[repr(isize)] | ^^^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:124:8 + --> tests/ui-stable/enum.rs:163:8 | -124 | #[repr(u32)] +163 | #[repr(u32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:130:8 + --> tests/ui-stable/enum.rs:169:8 | -130 | #[repr(i32)] +169 | #[repr(i32)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:136:8 + --> tests/ui-stable/enum.rs:175:8 | -136 | #[repr(u64)] +175 | #[repr(u64)] | ^^^ error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:142:8 + --> tests/ui-stable/enum.rs:181:8 | -142 | #[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:152:8 + --> tests/ui-stable/enum.rs:452:8 | -152 | #[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:158:8 + --> tests/ui-stable/enum.rs:458:8 | -158 | #[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:164:8 + --> tests/ui-stable/enum.rs:464:8 | -164 | #[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:170:8 + --> tests/ui-stable/enum.rs:470:8 | -170 | #[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:176:8 + --> tests/ui-stable/enum.rs:476:8 | -176 | #[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:182:8 + --> tests/ui-stable/enum.rs:482:8 | -182 | #[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:188:8 + --> tests/ui-stable/enum.rs:488:8 | -188 | #[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:194:8 + --> tests/ui-stable/enum.rs:494:8 | -194 | #[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:200:8 + --> tests/ui-stable/enum.rs:500:8 | -200 | #[repr(isize)] +500 | #[repr(isize)] | ^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:206:12 + --> tests/ui-stable/enum.rs:506:12 | -206 | #[repr(u8, align(2))] +506 | #[repr(u8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:212:12 + --> tests/ui-stable/enum.rs:512:12 | -212 | #[repr(i8, align(2))] +512 | #[repr(i8, align(2))] | ^^^^^^^^ error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:218:18 + --> tests/ui-stable/enum.rs:518:18 | -218 | #[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:224:8 + --> tests/ui-stable/enum.rs:524:8 | -224 | #[repr(align(2), align(4))] +524 | #[repr(align(2), align(4))] | ^^^^^^^^ error[E0565]: meta item in `repr` must be an identifier @@ -270,3 +281,179 @@ 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:118:10 + | +118 | #[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:118:10 + | +118 | #[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:124:10 + | +124 | #[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) + +error[E0277]: the trait bound `NotTryFromBytes: TryFromBytes` is not satisfied + --> tests/ui-stable/enum.rs:85:7 + | +85 | A(NotTryFromBytes), + | ^^^^^^^^^^^^^^^ 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 + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-stable/enum.rs:121:7 + | +121 | A(NotFromZeros), + | ^^^^^^^^^^^^ 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 + +error[E0277]: the trait bound `NotFromZeros: TryFromBytes` is not satisfied + --> tests/ui-stable/enum.rs:128:7 + | +128 | B(NotFromZeros), + | ^^^^^^^^^^^^ 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