From 47374c38b72d0e6393f74d2873c8da6da6194939 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Tue, 24 Sep 2024 21:47:31 -0700 Subject: [PATCH] [derive] Overhaul repr parsing Represent the result of parsing all `#[repr(...)]` attributes on a type as a high-level type which is only capable of representing valid combinations of `#[repr(...)]` attributes and processes them into a concise representation that's easier for high-level code to work with. This prepares us to more easily fix #1748. While we're here, we make a number of other improvements. 1) Errors are now converted to `TokenStream`s as late as possible rather than as early as possible, which was the previous behavior. This allows us to bail early when deriving an implied trait fails (e.g., deriving `TryFromBytes` when the user wrote `#[derive(FromZeros)]`). 2) Avoid re-computing some repr information in `TryFromBytes` enum support. --- zerocopy-derive/src/enum.rs | 64 +- zerocopy-derive/src/lib.rs | 517 ++++------ zerocopy-derive/src/output_tests.rs | 3 +- zerocopy-derive/src/repr.rs | 974 +++++++++++++----- zerocopy-derive/tests/ui-msrv/enum.stderr | 225 ++-- zerocopy-derive/tests/ui-msrv/struct.stderr | 50 +- zerocopy-derive/tests/ui-msrv/union.stderr | 18 +- zerocopy-derive/tests/ui-nightly/enum.rs | 19 +- zerocopy-derive/tests/ui-nightly/enum.stderr | 234 ++--- zerocopy-derive/tests/ui-nightly/struct.rs | 6 + .../tests/ui-nightly/struct.stderr | 48 +- zerocopy-derive/tests/ui-nightly/union.stderr | 12 +- zerocopy-derive/tests/ui-stable/enum.stderr | 236 +++-- zerocopy-derive/tests/ui-stable/struct.stderr | 54 +- zerocopy-derive/tests/ui-stable/union.stderr | 18 +- 15 files changed, 1412 insertions(+), 1066 deletions(-) diff --git a/zerocopy-derive/src/enum.rs b/zerocopy-derive/src/enum.rs index cfe723e97b3..f0397ad12e2 100644 --- a/zerocopy-derive/src/enum.rs +++ b/zerocopy-derive/src/enum.rs @@ -6,42 +6,11 @@ // 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 syn::parse_quote; +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{parse_quote, DataEnum, Error, Fields, Generics, Ident}; -use crate::{derive_try_from_bytes_inner, 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 -} +use crate::{derive_try_from_bytes_inner, repr::EnumRepr, Trait}; /// 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 @@ -57,7 +26,7 @@ pub(crate) fn generate_tag_enum(repr: &EnumRepr, data: &DataEnum) -> TokenStream }); quote! { - #[repr(#repr)] + #repr #[allow(dead_code)] enum ___ZerocopyTag { #(#variants,)* @@ -157,7 +126,8 @@ fn generate_variant_structs( // We do this rather than emitting `#[derive(::zerocopy::TryFromBytes)]` // because that is not hygienic, and this is also more performant. - let try_from_bytes_impl = derive_try_from_bytes_inner(&variant_struct); + let try_from_bytes_impl = derive_try_from_bytes_inner(&variant_struct) + .expect("derive_try_from_bytes_inner should not fail on synthesized type"); Some(quote! { #variant_struct @@ -231,21 +201,23 @@ fn generate_variants_union(generics: &Generics, data: &DataEnum) -> TokenStream /// - `repr(C, int)`: pub(crate) fn derive_is_bit_valid( enum_ident: &Ident, - reprs: &[EnumRepr], + repr: &EnumRepr, generics: &Generics, data: &DataEnum, -) -> TokenStream { - let repr = - tag_repr(reprs).expect("cannot derive is_bit_valid for enum without a well-defined repr"); - +) -> Result { 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) { + let (outer_tag_type, inner_tag_type) = if repr.is_c() { (quote! { ___ZerocopyTag }, quote! { () }) - } else { + } else if repr.is_primitive() { (quote! { () }, quote! { ___ZerocopyTag }) + } else { + return Err(Error::new( + Span::call_site(), + "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout", + )); }; let variant_structs = generate_variant_structs(enum_ident, generics, data); @@ -297,7 +269,7 @@ pub(crate) fn derive_is_bit_valid( } }); - quote! { + Ok(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. @@ -392,5 +364,5 @@ pub(crate) fn derive_is_bit_valid( _ => false, } } - } + }) } diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 60e37696342..88463455910 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -48,17 +48,6 @@ use { use {crate::ext::*, crate::repr::*}; -/// Unwraps a `Result<_, Vec>`, converting any `Err` value into a -/// `TokenStream` and returning it. -macro_rules! try_or_print { - ($e:expr) => { - match $e { - Ok(x) => x, - Err(errors) => return print_all_errors(errors).into(), - } - }; -} - // TODO(https://github.com/rust-lang/rust/issues/54140): Some errors could be // made better if we could add multiple lines of error output like this: // @@ -87,11 +76,30 @@ macro_rules! derive { #[proc_macro_derive($trait)] pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse_macro_input!(ts as DeriveInput); - $inner(&ast).into() + $inner(&ast).into_ts().into() } }; } +trait IntoTokenStream { + fn into_ts(self) -> TokenStream; +} + +impl IntoTokenStream for TokenStream { + fn into_ts(self) -> TokenStream { + self + } +} + +impl IntoTokenStream for Result { + fn into_ts(self) -> TokenStream { + match self { + Ok(ts) => ts, + Err(err) => err.to_compile_error(), + } + } +} + derive!(KnownLayout => derive_known_layout => derive_known_layout_inner); derive!(Immutable => derive_no_cell => derive_no_cell_inner); derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner); @@ -116,12 +124,12 @@ pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { derive_into_bytes(ts) } -fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { +fn derive_known_layout_inner(ast: &DeriveInput) -> Result { let is_repr_c_struct = match &ast.data { Data::Struct(..) => { - let reprs = try_or_print!(repr::reprs::(&ast.attrs)); - if reprs.iter().any(|(_meta, repr)| repr == &Repr::C) { - Some(reprs) + let repr = StructUnionRepr::from_attrs(&ast.attrs)?; + if repr.is_c() { + Some(repr) } else { None } @@ -131,36 +139,27 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { let fields = ast.data.fields(); - let (self_bounds, extras) = if let (Some(reprs), Some((trailing_field, leading_fields))) = + let (self_bounds, extras) = if let (Some(repr), Some((trailing_field, leading_fields))) = (is_repr_c_struct, fields.split_last()) { let (_name, trailing_field_ty) = trailing_field; let leading_fields_tys = leading_fields.iter().map(|(_name, ty)| ty); let core_path = quote!(::zerocopy::util::macro_util::core_reexport); - let repr_align = reprs - .iter() - .find_map( - |(_meta, repr)| { - if let Repr::Align(repr_align) = repr { - Some(repr_align) - } else { - None - } - }, - ) - .map(|repr_align| quote!(#core_path::num::NonZeroUsize::new(#repr_align as usize))) - .unwrap_or(quote!(#core_path::option::Option::None)); - - let repr_packed = reprs - .iter() - .find_map(|(_meta, repr)| match repr { - Repr::Packed => Some(1), - Repr::PackedN(repr_packed) => Some(*repr_packed), - _ => None, + let repr_align = repr + .get_align() + .map(|align| { + let align = align.t.get(); + quote!(#core_path::num::NonZeroUsize::new(#align as usize)) }) - .map(|repr_packed| quote!(#core_path::num::NonZeroUsize::new(#repr_packed as usize))) - .unwrap_or(quote!(#core_path::option::Option::None)); + .unwrap_or_else(|| quote!(#core_path::option::Option::None)); + let repr_packed = repr + .get_packed() + .map(|packed| { + let packed = packed.get(); + quote!(#core_path::num::NonZeroUsize::new(#packed as usize)) + }) + .unwrap_or_else(|| quote!(#core_path::option::Option::None)); ( SelfBounds::None, @@ -279,7 +278,7 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { ) }; - match &ast.data { + Ok(match &ast.data { Data::Struct(strct) => { let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED { FieldBounds::None @@ -327,10 +326,10 @@ fn derive_known_layout_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { Some(extras), ) } - } + }) } -fn derive_no_cell_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { +fn derive_no_cell_inner(ast: &DeriveInput) -> TokenStream { match &ast.data { Data::Struct(strct) => impl_block( ast, @@ -362,36 +361,36 @@ fn derive_no_cell_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { } } -fn derive_try_from_bytes_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { +fn derive_try_from_bytes_inner(ast: &DeriveInput) -> Result { match &ast.data { Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct), Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm), - Data::Union(unn) => derive_try_from_bytes_union(ast, unn), + Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn)), } } -fn derive_from_zeros_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { - let try_from_bytes = derive_try_from_bytes_inner(ast); +fn derive_from_zeros_inner(ast: &DeriveInput) -> Result { + let try_from_bytes = derive_try_from_bytes_inner(ast)?; let from_zeros = match &ast.data { Data::Struct(strct) => derive_from_zeros_struct(ast, strct), - Data::Enum(enm) => derive_from_zeros_enum(ast, enm), + Data::Enum(enm) => derive_from_zeros_enum(ast, enm)?, Data::Union(unn) => derive_from_zeros_union(ast, unn), }; - IntoIterator::into_iter([try_from_bytes, from_zeros]).collect() + Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect()) } -fn derive_from_bytes_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { - let from_zeros = derive_from_zeros_inner(ast); +fn derive_from_bytes_inner(ast: &DeriveInput) -> Result { + let from_zeros = derive_from_zeros_inner(ast)?; let from_bytes = match &ast.data { Data::Struct(strct) => derive_from_bytes_struct(ast, strct), - Data::Enum(enm) => derive_from_bytes_enum(ast, enm), + Data::Enum(enm) => derive_from_bytes_enum(ast, enm)?, Data::Union(unn) => derive_from_bytes_union(ast, unn), }; - IntoIterator::into_iter([from_zeros, from_bytes]).collect() + Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect()) } -fn derive_into_bytes_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { +fn derive_into_bytes_inner(ast: &DeriveInput) -> Result { match &ast.data { Data::Struct(strct) => derive_into_bytes_struct(ast, strct), Data::Enum(enm) => derive_into_bytes_enum(ast, enm), @@ -399,7 +398,7 @@ fn derive_into_bytes_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { } } -fn derive_unaligned_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { +fn derive_unaligned_inner(ast: &DeriveInput) -> Result { match &ast.data { Data::Struct(strct) => derive_unaligned_struct(ast, strct), Data::Enum(enm) => derive_unaligned_enum(ast, enm), @@ -409,7 +408,10 @@ fn derive_unaligned_inner(ast: &DeriveInput) -> proc_macro2::TokenStream { /// A struct is `TryFromBytes` if: /// - all fields are `TryFromBytes` -fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { +fn derive_try_from_bytes_struct( + ast: &DeriveInput, + strct: &DataStruct, +) -> Result { let extras = Some({ let fields = strct.fields(); let field_names = fields.iter().map(|(name, _ty)| name); @@ -447,7 +449,7 @@ fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_m } ) }); - impl_block( + Ok(impl_block( ast, strct, Trait::TryFromBytes, @@ -455,12 +457,12 @@ fn derive_try_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_m SelfBounds::None, None, extras, - ) + )) } /// A union is `TryFromBytes` if: /// - all of its fields are `TryFromBytes` and `Immutable` -fn derive_try_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { +fn derive_try_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> TokenStream { // TODO(#5): Remove the `Immutable` bound. let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]); @@ -512,58 +514,26 @@ fn derive_try_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro ) } -const STRUCT_UNION_ALLOWED_REPR_COMBINATIONS: &[&[StructRepr]] = &[ - &[StructRepr::C], - &[StructRepr::Transparent], - &[StructRepr::Packed], - &[StructRepr::C, StructRepr::Packed], -]; - -fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - let reprs = try_or_print!(ENUM_TRY_FROM_BYTES_CFG.validate_reprs(ast)); +fn derive_try_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> Result { + let repr = EnumRepr::from_attrs(&ast.attrs)?; // The enum derive requires some extra scaffolding - let extra = Some(r#enum::derive_is_bit_valid(&ast.ident, &reprs, &ast.generics, enm)); + let extra = Some(r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm)?); - impl_block(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, extra) + Ok(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 an enum repr of a primitive, "C", or "C" with a primitive"#, - derive_unaligned: false, - allowed_combinations: &[ - &[U8], - &[U16], - &[U32], - &[U64], - &[Usize], - &[I8], - &[I16], - &[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: &[], - } -}; - /// A struct is `FromZeros` if: /// - all fields are `FromZeros` -fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { +fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> TokenStream { impl_block(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, SelfBounds::None, None, None) } @@ -648,31 +618,38 @@ fn find_zero_variant(enm: &DataEnum) -> Result { /// An enum is `FromZeros` if: /// - one of the variants has a discriminant of `0` /// - that variant's fields are all `FromZeros` -fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { +fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> Result { + let repr = EnumRepr::from_attrs(&ast.attrs)?; + // 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)); + match repr { + Repr::Transparent(_) + | Repr::Compound( + Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ }, + _, + ) => {} + Repr::Compound(Spanned { t: CompoundRepr::Rust, span: _ }, _) => return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout")), + } 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( + return Err(Error::new_spanned( ast, "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\ 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.", - ) - .to_compile_error(); + )); } // Does not have unknown variants Err(false) => { - return Error::new_spanned( + return Err(Error::new_spanned( ast, "FromZeros only supported on enums with a variant that has a discriminant of `0`", - ) - .to_compile_error(); + )); } }; @@ -685,7 +662,7 @@ fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok }) .collect::>(); - impl_block( + Ok(impl_block( ast, enm, Trait::FromZeros, @@ -693,12 +670,12 @@ fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok SelfBounds::None, None, None, - ) + )) } /// Unions are `FromZeros` if /// - all fields are `FromZeros` and `Immutable` -fn derive_from_zeros_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { +fn derive_from_zeros_union(ast: &DeriveInput, unn: &DataUnion) -> TokenStream { // TODO(#5): Remove the `Immutable` bound. It's only necessary for // compatibility with `derive(TryFromBytes)` on unions; not for soundness. let field_type_trait_bounds = @@ -708,7 +685,7 @@ fn derive_from_zeros_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::T /// A struct is `FromBytes` if: /// - all fields are `FromBytes` -fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { +fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> TokenStream { impl_block(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, None) } @@ -726,62 +703,41 @@ fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro /// platform-specific and, b) even on Rust's smallest bit width platform (32), /// this would require ~4 billion enum variants, which obviously isn't a thing. /// - All fields of all variants are `FromBytes`. -fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - let reprs = try_or_print!(ENUM_FROM_BYTES_CFG.validate_reprs(ast)); +fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> Result { + let repr = EnumRepr::from_attrs(&ast.attrs)?; - let variants_required = 1usize - << enum_size_from_repr(reprs.as_slice()) - .expect("internal error: `validate_reprs` has already validated that the reprs guarantee the enum's size"); + let variants_required = 1usize << enum_size_from_repr(&repr)?; if enm.variants.len() != variants_required { - return Error::new_spanned( + return Err(Error::new_spanned( ast, format!( "FromBytes only supported on {} enum with {} variants", - reprs[0], variants_required + repr.repr_type_name(), + variants_required ), - ) - .to_compile_error(); + )); } - impl_block(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, None) + Ok(impl_block(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, None)) } // Returns `None` if the enum's size is not guaranteed by the repr. -fn enum_size_from_repr(reprs: &[EnumRepr]) -> Option { - match reprs { - [EnumRepr::U8] | [EnumRepr::I8] => Some(8), - [EnumRepr::U16] | [EnumRepr::I16] => Some(16), - _ => None, +fn enum_size_from_repr(repr: &EnumRepr) -> Result { + use {CompoundRepr::*, PrimitiveRepr::*, Repr::*}; + match repr { + Transparent(span) + | Compound( + Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | Usize | Isize), span }, + _, + ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")), + Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8), + Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16), } } -#[rustfmt::skip] -const ENUM_FROM_BYTES_CFG: Config = { - use EnumRepr::*; - Config { - allowed_combinations_message: r#"FromBytes requires repr of "u8", "u16", "i8", or "i16""#, - derive_unaligned: false, - allowed_combinations: &[ - &[U8], - &[U16], - &[I8], - &[I16], - ], - disallowed_but_legal_combinations: &[ - &[C], - &[U32], - &[I32], - &[U64], - &[I64], - &[Usize], - &[Isize], - ], - } -}; - /// Unions are `FromBytes` if /// - all fields are `FromBytes` and `Immutable` -fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { +fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> TokenStream { // TODO(#5): Remove the `Immutable` bound. It's only necessary for // compatibility with `derive(TryFromBytes)` on unions; not for soundness. let field_type_trait_bounds = @@ -789,13 +745,15 @@ fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::T impl_block(ast, unn, Trait::FromBytes, field_type_trait_bounds, SelfBounds::None, None, None) } -fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { - let reprs = try_or_print!(STRUCT_UNION_INTO_BYTES_CFG.validate_reprs(ast)); - let is_transparent = reprs.contains(&StructRepr::Transparent); - let is_packed = reprs.contains(&StructRepr::Packed); +fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> Result { + let repr = StructUnionRepr::from_attrs(&ast.attrs)?; + + let is_transparent = repr.is_transparent(); + let is_c = repr.is_c(); + let is_packed_1 = repr.is_packed_1(); let num_fields = strct.fields().len(); - let (padding_check, require_unaligned_fields) = if is_transparent || is_packed { + let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 { // No padding check needed. // - repr(transparent): The layout and ABI of the whole struct is the // same as its only non-ZST field (meaning there's no padding outside @@ -806,7 +764,7 @@ fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro // which we require to be `IntoBytes` (meaning they don't have any // padding). (None, false) - } else if reprs.contains(&StructRepr::C) && num_fields <= 1 { + } else if is_c && num_fields <= 1 { // No padding check needed. A repr(C) struct with zero or one field has // no padding. (None, false) @@ -819,7 +777,7 @@ fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro // Based on the allowed reprs, we know that this type must be repr(C) by // the time we get here, but the soundness of this impl relies on it, so // let's double-check. - assert!(reprs.contains(&StructRepr::C)); + assert!(is_c); // We can't use a padding check since there are generic type arguments. // Instead, we require all field types to implement `Unaligned`. This // ensures that the `repr(C)` layout algorithm will not insert any @@ -836,32 +794,28 @@ fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro FieldBounds::ALL_SELF }; - impl_block(ast, strct, Trait::IntoBytes, field_bounds, SelfBounds::None, padding_check, None) + Ok(impl_block( + ast, + strct, + Trait::IntoBytes, + field_bounds, + SelfBounds::None, + padding_check, + None, + )) } -const STRUCT_UNION_INTO_BYTES_CFG: Config = Config { - // Since `disallowed_but_legal_combinations` is empty, this message will - // never actually be emitted. - allowed_combinations_message: r#"IntoBytes requires either a) repr "C" or "transparent" with all fields implementing IntoBytes or, b) repr "packed""#, - derive_unaligned: false, - allowed_combinations: STRUCT_UNION_ALLOWED_REPR_COMBINATIONS, - disallowed_but_legal_combinations: &[], -}; - /// If the type is an enum: /// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`, /// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`). /// - It must have no padding bytes. /// - Its fields must be `IntoBytes`. -fn derive_into_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { +fn derive_into_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> Result { // We don't care what the repr is; we only care that it is one of the // allowed ones. - let reprs = try_or_print!(ENUM_FROM_ZEROS_INTO_BYTES_CFG.validate_reprs(ast)); - let repr = r#enum::tag_repr(&reprs) - .expect("cannot derive IntoBytes for enum without a well-defined repr"); - let tag_type_definition = r#enum::generate_tag_enum(repr, enm); - - impl_block( + let repr = EnumRepr::from_attrs(&ast.attrs)?; + let tag_type_definition = r#enum::generate_tag_enum(&repr, enm); + Ok(impl_block( ast, enm, Trait::IntoBytes, @@ -869,58 +823,25 @@ fn derive_into_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok SelfBounds::None, Some(PaddingCheck::Enum { tag_type_definition }), None, - ) + )) } -#[rustfmt::skip] -const ENUM_FROM_ZEROS_INTO_BYTES_CFG: Config = { - use EnumRepr::*; - Config { - // Since `disallowed_but_legal_combinations` is empty, this message will - // never actually be emitted. - allowed_combinations_message: r#"FromZeros requires an enum repr of a primitive, "C", or "C" with a primitive"#, - derive_unaligned: false, - allowed_combinations: &[ - &[U8], - &[U16], - &[U32], - &[U64], - &[Usize], - &[I8], - &[I16], - &[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: &[], - } -}; - /// A union is `IntoBytes` if: /// - all fields are `IntoBytes` /// - `repr(C)`, `repr(transparent)`, or `repr(packed)` /// - no padding (size of union equals size of each field type) -fn derive_into_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { +fn derive_into_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> Result { // TODO(#10): Support type parameters. if !ast.generics.params.is_empty() { - return Error::new(Span::call_site(), "unsupported on types with type parameters") - .to_compile_error(); + return Err(Error::new(Span::call_site(), "unsupported on types with type parameters")); } - try_or_print!(STRUCT_UNION_INTO_BYTES_CFG.validate_reprs(ast)); + let repr = StructUnionRepr::from_attrs(&ast.attrs)?; + if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() { + return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout")); + } - impl_block( + Ok(impl_block( ast, unn, Trait::IntoBytes, @@ -928,7 +849,7 @@ fn derive_into_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::T SelfBounds::None, Some(PaddingCheck::Union), None, - ) + )) } /// A struct is `Unaligned` if: @@ -936,87 +857,61 @@ fn derive_into_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::T /// - `repr(C)` or `repr(transparent)` and /// - all fields `Unaligned` /// - `repr(packed)` -fn derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { - let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast)); - let field_bounds = if !reprs.contains(&StructRepr::Packed) { +fn derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> Result { + let repr = StructUnionRepr::from_attrs(&ast.attrs)?; + repr.unaligned_validate_no_align_gt_1()?; + + let field_bounds = if repr.is_packed_1() { + FieldBounds::None + } else if repr.is_c() || repr.is_transparent() { FieldBounds::ALL_SELF } else { - FieldBounds::None + return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment")); }; - impl_block(ast, strct, Trait::Unaligned, field_bounds, SelfBounds::None, None, None) + Ok(impl_block(ast, strct, Trait::Unaligned, field_bounds, SelfBounds::None, None, None)) } -const STRUCT_UNION_UNALIGNED_CFG: Config = Config { - // Since `disallowed_but_legal_combinations` is empty, this message will - // never actually be emitted. - allowed_combinations_message: r#"Unaligned requires either a) repr "C" or "transparent" with all fields implementing Unaligned or, b) repr "packed""#, - derive_unaligned: true, - allowed_combinations: STRUCT_UNION_ALLOWED_REPR_COMBINATIONS, - disallowed_but_legal_combinations: &[], -}; - /// An enum is `Unaligned` if: /// - No `repr(align(N > 1))` /// - `repr(u8)` or `repr(i8)` -fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream { - // 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. - let _: Vec = try_or_print!(ENUM_UNALIGNED_CFG.validate_reprs(ast)); +fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> Result { + let repr = EnumRepr::from_attrs(&ast.attrs)?; + repr.unaligned_validate_no_align_gt_1()?; - impl_block(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, SelfBounds::None, None, None) -} - -#[rustfmt::skip] -const ENUM_UNALIGNED_CFG: Config = { - use EnumRepr::*; - Config { - allowed_combinations_message: - r#"Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1)))"#, - derive_unaligned: true, - allowed_combinations: &[ - &[U8], - &[I8], - &[C, U8], - &[C, I8], - ], - disallowed_but_legal_combinations: &[ - &[U16], - &[U32], - &[U64], - &[Usize], - &[I16], - &[I32], - &[I64], - &[Isize], - &[C], - &[C, U16], - &[C, U32], - &[C, U64], - &[C, Usize], - &[C, I16], - &[C, I32], - &[C, I64], - &[C, Isize], - ], + if !repr.is_u8() || !repr.is_i8() { + return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment")); } -}; + + Ok(impl_block(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, SelfBounds::None, None, None)) +} /// Like structs, a union is `Unaligned` if: /// - `repr(align)` is no more than 1 and either /// - `repr(C)` or `repr(transparent)` and /// - all fields `Unaligned` /// - `repr(packed)` -fn derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { - let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast)); - let field_type_trait_bounds = if !reprs.contains(&StructRepr::Packed) { +fn derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> Result { + let repr = StructUnionRepr::from_attrs(&ast.attrs)?; + repr.unaligned_validate_no_align_gt_1()?; + + let field_type_trait_bounds = if repr.is_packed_1() { + FieldBounds::None + } else if repr.is_c() || repr.is_transparent() { FieldBounds::ALL_SELF } else { - FieldBounds::None + return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment")); }; - impl_block(ast, unn, Trait::Unaligned, field_type_trait_bounds, SelfBounds::None, None, None) + Ok(impl_block( + ast, + unn, + Trait::Unaligned, + field_type_trait_bounds, + SelfBounds::None, + None, + None, + )) } /// This enum describes what kind of padding check needs to be generated for the @@ -1128,8 +1023,8 @@ fn impl_block( field_type_trait_bounds: FieldBounds, self_type_trait_bounds: SelfBounds, padding_check: Option, - extras: Option, -) -> proc_macro2::TokenStream { + extras: Option, +) -> TokenStream { // In this documentation, we will refer to this hypothetical struct: // // #[derive(FromBytes)] @@ -1290,10 +1185,6 @@ fn impl_block( } } -fn print_all_errors(errors: Vec) -> proc_macro2::TokenStream { - errors.iter().map(Error::to_compile_error).collect() -} - // A polyfill for `Option::then_some`, which was added after our MSRV. // // The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain @@ -1315,55 +1206,3 @@ impl BoolExt for bool { } } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_config_repr_orderings() { - // Validate that the repr lists in the various configs are in the - // canonical order. If they aren't, then our algorithm to look up in - // those lists won't work. - - // TODO(https://github.com/rust-lang/rust/issues/53485): Remove once - // `Vec::is_sorted` is stabilized. - fn is_sorted_and_deduped(ts: &[T]) -> bool { - let mut sorted = ts.to_vec(); - sorted.sort(); - sorted.dedup(); - ts == sorted.as_slice() - } - - fn elements_are_sorted_and_deduped(lists: &[&[T]]) -> bool { - lists.iter().all(|list| is_sorted_and_deduped(list)) - } - - fn config_is_sorted(config: &Config) -> bool { - elements_are_sorted_and_deduped(config.allowed_combinations) - && elements_are_sorted_and_deduped(config.disallowed_but_legal_combinations) - } - - assert!(config_is_sorted(&STRUCT_UNION_UNALIGNED_CFG)); - assert!(config_is_sorted(&ENUM_FROM_BYTES_CFG)); - assert!(config_is_sorted(&ENUM_UNALIGNED_CFG)); - } - - #[test] - fn test_config_repr_no_overlap() { - // Validate that no set of reprs appears in both the - // `allowed_combinations` and `disallowed_but_legal_combinations` lists. - - fn overlap(a: &[T], b: &[T]) -> bool { - a.iter().any(|elem| b.contains(elem)) - } - - fn config_overlaps(config: &Config) -> bool { - overlap(config.allowed_combinations, config.disallowed_but_legal_combinations) - } - - assert!(!config_overlaps(&STRUCT_UNION_UNALIGNED_CFG)); - assert!(!config_overlaps(&ENUM_FROM_BYTES_CFG)); - assert!(!config_overlaps(&ENUM_UNALIGNED_CFG)); - } -} diff --git a/zerocopy-derive/src/output_tests.rs b/zerocopy-derive/src/output_tests.rs index 3707cca3f02..d52d81c0859 100644 --- a/zerocopy-derive/src/output_tests.rs +++ b/zerocopy-derive/src/output_tests.rs @@ -6,6 +6,7 @@ // This file may not be copied, modified, or distributed except according to // those terms. +use crate::IntoTokenStream; use dissimilar::Chunk; use proc_macro2::TokenStream; @@ -52,7 +53,7 @@ macro_rules! test { let ast = syn::parse2::(ts).unwrap(); let res = $name(&ast); let expected_toks = quote::quote!( $($o)* ); - assert_eq_streams(expected_toks.into(), res.into()); + assert_eq_streams(expected_toks.into(), res.into_ts().into()); } }; } diff --git a/zerocopy-derive/src/repr.rs b/zerocopy-derive/src/repr.rs index c1dc185801d..899aaf645f8 100644 --- a/zerocopy-derive/src/repr.rs +++ b/zerocopy-derive/src/repr.rs @@ -6,202 +6,273 @@ // This file may not be copied, modified, or distributed except according to // those terms. -use core::fmt::{self, Display, Formatter}; - -use proc_macro2::Ident; -use quote::{ToTokens, TokenStreamExt as _}; +use core::{ + convert::{Infallible, TryFrom}, + num::NonZeroU64, +}; -use { - proc_macro2::Span, - syn::punctuated::Punctuated, - syn::spanned::Spanned, - syn::token::Comma, - syn::{Attribute, DeriveInput, Error, LitInt, Meta}, +use proc_macro2::{Span, TokenStream}; +use quote::{quote_spanned, ToTokens, TokenStreamExt as _}; +use syn::{ + punctuated::Punctuated, spanned::Spanned as _, token::Comma, Attribute, Error, LitInt, Meta, + MetaList, }; -pub(crate) struct Config { - // A human-readable message describing what combinations of representations - // are allowed. This will be printed to the user if they use an invalid - // combination. - pub(crate) allowed_combinations_message: &'static str, - // Whether we're checking as part of `derive(Unaligned)`. If not, we can - // ignore `repr(align)`, which makes the code (and the list of valid repr - // combinations we have to enumerate) somewhat simpler. If we're checking - // for `Unaligned`, then in addition to checking against illegal - // combinations, we also check to see if there exists a `repr(align(N > 1))` - // attribute. - pub(crate) derive_unaligned: bool, - // Combinations which are valid for the trait. - pub(crate) allowed_combinations: &'static [&'static [Repr]], - // Combinations which are not valid for the trait, but are legal according - // to Rust. Any combination not in this or `allowed_combinations` is either - // illegal according to Rust or the behavior is unspecified. If the behavior - // is unspecified, it might become specified in the future, and that - // specification might not play nicely with our requirements. Thus, we - // reject combinations with unspecified behavior in addition to illegal - // combinations. - pub(crate) disallowed_but_legal_combinations: &'static [&'static [Repr]], -} - -impl Config { - /// Validate that `input`'s representation attributes conform to the - /// requirements specified by this `Config`. +/// The computed representation of a type. +/// +/// This is the result of processing all `#[repr(...)]` attributes on a type, if +/// any. A `Repr` is only capable of representing legal combinations of +/// `#[repr(...)]` attributes. +#[cfg_attr(test, derive(Copy, Clone, Debug))] +pub(crate) enum Repr { + /// `#[repr(transparent)]` + Transparent(Span), + /// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)` + /// optionally combined with `repr(packed(...))` or `repr(align(...))` + Compound(Spanned>, Option>>), +} + +/// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`. +#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] +pub(crate) enum CompoundRepr { + C, + Rust, + Primitive(Prim), +} + +/// `repr(Int)` +#[derive(Copy, Clone)] +#[cfg_attr(test, derive(Debug, Eq, PartialEq))] +pub(crate) enum PrimitiveRepr { + U8, + U16, + U32, + U64, + Usize, + I8, + I16, + I32, + I64, + Isize, +} + +/// `repr(packed(...))` or `repr(align(...))` +#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] +pub(crate) enum AlignRepr { + Packed(Packed), + Align(NonZeroU64), +} + +/// The representations which can legally appear on a struct or union type. +pub(crate) type StructUnionRepr = Repr; + +/// The representations which can legally appear on an enum type. +pub(crate) type EnumRepr = Repr; + +impl Repr { + /// Gets the name of this "repr type" - the non-align `repr(X)` that is used + /// in prose to refer to this type. /// - /// `validate_reprs` extracts the `repr` attributes, validates that they - /// conform to the requirements of `self`, and returns them. Regardless of - /// whether `align` attributes are considered during validation, they are - /// stripped out of the returned value since no callers care about them. - pub(crate) fn validate_reprs(&self, input: &DeriveInput) -> Result, Vec> { - let mut metas_reprs = reprs(&input.attrs)?; - metas_reprs.sort_by(|a: &(_, R), b| a.1.partial_cmp(&b.1).unwrap()); - - if self.derive_unaligned { - if let Some((meta, _)) = - metas_reprs.iter().find(|&repr: &&(_, R)| repr.1.is_align_gt_one()) - { - return Err(vec![Error::new_spanned( - meta, - "cannot derive Unaligned with repr(align(N > 1))", - )]); - } + /// For example, we would refer to `#[repr(C, align(4))] struct Foo { ... }` + /// as a "`repr(C)` struct". + pub(crate) fn repr_type_name(&self) -> &str + where + Prim: Copy + With, + { + use {CompoundRepr::*, PrimitiveRepr::*, Repr::*}; + match self { + Transparent(_span) => "repr(transparent)", + Compound(Spanned { t: repr, span: _ }, _align) => match repr { + C => "repr(C)", + Rust => "repr(Rust)", + Primitive(prim) => prim.with(|prim| match prim { + U8 => "repr(u8)", + U16 => "repr(u16)", + U32 => "repr(u32)", + U64 => "repr(u64)", + Usize => "repr(usize)", + I8 => "repr(i8)", + I16 => "repr(i16)", + I32 => "repr(i32)", + I64 => "repr(i64)", + Isize => "repr(isize)", + }), + }, } + } - let mut metas = Vec::new(); - let mut reprs = Vec::new(); - metas_reprs.into_iter().filter(|(_, repr)| !repr.is_align()).for_each(|(meta, repr)| { - metas.push(meta); - reprs.push(repr) - }); - - if reprs.is_empty() { - // Use `Span::call_site` to report this error on the - // `#[derive(...)]` itself. - return Err(vec![Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout")]); + pub(crate) fn is_transparent(&self) -> bool { + matches!(self, Repr::Transparent(_)) + } + + pub(crate) fn is_c(&self) -> bool { + use CompoundRepr::*; + matches!(self, Repr::Compound(Spanned { t: C, span: _ }, _align)) + } + + pub(crate) fn is_primitive(&self) -> bool { + use CompoundRepr::*; + matches!(self, Repr::Compound(Spanned { t: Primitive(_), span: _ }, _align)) + } + + pub(crate) fn get_packed(&self) -> Option<&Packed> { + use {AlignRepr::*, Repr::*}; + if let Compound(_, Some(Spanned { t: Packed(p), span: _ })) = self { + Some(p) + } else { + None } + } - let initial_sp = metas[0].span(); - let err_span = metas.iter().skip(1).try_fold(initial_sp, |sp, meta| sp.join(meta.span())); + pub(crate) fn get_align(&self) -> Option> { + use {AlignRepr::*, Repr::*}; + if let Compound(_, Some(Spanned { t: Align(n), span })) = self { + Some(Spanned::new(*n, *span)) + } else { + None + } + } +} - if self.allowed_combinations.contains(&reprs.as_slice()) { - Ok(reprs) - } else if self.disallowed_but_legal_combinations.contains(&reprs.as_slice()) { - Err(vec![Error::new( - err_span.unwrap_or_else(|| input.span()), - self.allowed_combinations_message, - )]) +impl Repr { + /// When deriving `Unaligned`, validate that the decorated type has no + /// `#[repr(align(N))]` attribute where `N > 1`. If no such attribute exists + /// (including if `N == 1`), this returns `Ok(())`, and otherwise it returns + /// a descriptive error. + pub(crate) fn unaligned_validate_no_align_gt_1(&self) -> Result<(), Error> { + if let Some(n) = self.get_align().filter(|n| n.t.get() > 1) { + Err(Error::new( + n.span, + "cannot derive `Unaligned` on type with alignment greater than 1", + )) } else { - Err(vec![Error::new( - err_span.unwrap_or_else(|| input.span()), - "conflicting representation hints", - )]) + Ok(()) } } } -// The type of valid reprs for a particular kind (enum, struct, union). -pub(crate) trait KindRepr: 'static + Sized + Ord { - fn is_align(&self) -> bool; - fn is_align_gt_one(&self) -> bool; - fn parse(meta: &Meta) -> syn::Result; +impl Repr { + /// Does `self` describe a `#[repr(packed)]` or `#[repr(packed(1))]` type? + pub(crate) fn is_packed_1(&self) -> bool { + self.get_packed().map(|n| n.get() == 1).unwrap_or(false) + } } -// Defines an enum for reprs which are valid for a given kind (structs, enums, -// etc), and provide implementations of `KindRepr`, `Ord`, and `Display`, and -// those traits' super-traits. -macro_rules! define_kind_specific_repr { - ($type_name:expr, $repr_name:ident, [ $($repr_variant:ident),* ] , [ $($repr_variant_aligned:ident),* ]) => { - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub(crate) enum $repr_name { - $($repr_variant,)* - $($repr_variant_aligned(u64),)* +impl Repr { + fn get_primitive(&self) -> Option<&PrimitiveRepr> { + use {CompoundRepr::*, Repr::*}; + if let Compound(Spanned { t: Primitive(p), span: _ }, _align) = self { + Some(p) + } else { + None } + } - impl KindRepr for $repr_name { - fn is_align(&self) -> bool { - match self { - $($repr_name::$repr_variant_aligned(_) => true,)* - _ => false, - } - } + /// Does `self` describe a `#[repr(u8)]` type? + pub(crate) fn is_u8(&self) -> bool { + matches!(self.get_primitive(), Some(PrimitiveRepr::U8)) + } - fn is_align_gt_one(&self) -> bool { - match self { - // `packed(n)` only lowers alignment - $repr_name::Align(n) => n > &1, - _ => false, - } - } + /// Does `self` describe a `#[repr(i8)]` type? + pub(crate) fn is_i8(&self) -> bool { + matches!(self.get_primitive(), Some(PrimitiveRepr::I8)) + } +} - fn parse(meta: &Meta) -> syn::Result<$repr_name> { - match Repr::from_meta(meta)? { - $(Repr::$repr_variant => Ok($repr_name::$repr_variant),)* - $(Repr::$repr_variant_aligned(u) => Ok($repr_name::$repr_variant_aligned(u)),)* - _ => Err(Error::new_spanned(meta, concat!("unsupported representation for deriving zerocopy trait(s) on ", $type_name))) +impl ToTokens for Repr +where + Prim: With + Copy, + Packed: With + Copy, +{ + fn to_tokens(&self, ts: &mut TokenStream) { + use Repr::*; + match self { + Transparent(span) => ts.append_all(quote_spanned! { *span=> #[repr(transparent)] }), + Compound(repr, align) => { + repr.to_tokens(ts); + if let Some(align) = align { + align.to_tokens(ts); } } } + } +} - // Define a stable ordering so we can canonicalize lists of reprs. The - // ordering itself doesn't matter so long as it's stable. - impl PartialOrd for $repr_name { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } +impl + Copy> ToTokens for Spanned> { + fn to_tokens(&self, ts: &mut TokenStream) { + use CompoundRepr::*; + match &self.t { + C => ts.append_all(quote_spanned! { self.span=> #[repr(C)] }), + Rust => ts.append_all(quote_spanned! { self.span=> #[repr(Rust)] }), + Primitive(prim) => prim.with(|prim| Spanned::new(prim, self.span).to_tokens(ts)), } + } +} - impl Ord for $repr_name { - fn cmp(&self, other: &Self) -> core::cmp::Ordering { - format!("{:?}", self).cmp(&format!("{:?}", other)) - } +impl ToTokens for Spanned { + fn to_tokens(&self, ts: &mut TokenStream) { + use PrimitiveRepr::*; + match self.t { + U8 => ts.append_all(quote_spanned! { self.span => #[repr(u8)] }), + U16 => ts.append_all(quote_spanned! { self.span => #[repr(u16)] }), + U32 => ts.append_all(quote_spanned! { self.span => #[repr(u32)] }), + U64 => ts.append_all(quote_spanned! { self.span => #[repr(u64)] }), + Usize => ts.append_all(quote_spanned! { self.span => #[repr(usize)] }), + I8 => ts.append_all(quote_spanned! { self.span => #[repr(i8)] }), + I16 => ts.append_all(quote_spanned! { self.span => #[repr(i16)] }), + I32 => ts.append_all(quote_spanned! { self.span => #[repr(i32)] }), + I64 => ts.append_all(quote_spanned! { self.span => #[repr(i64)] }), + Isize => ts.append_all(quote_spanned! { self.span => #[repr(isize)] }), } + } +} - impl core::fmt::Display for $repr_name { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - $($repr_name::$repr_variant => Repr::$repr_variant,)* - $($repr_name::$repr_variant_aligned(u) => Repr::$repr_variant_aligned(*u),)* - }.fmt(f) +impl + Copy> ToTokens for Spanned> { + fn to_tokens(&self, ts: &mut TokenStream) { + use AlignRepr::*; + match self.t { + Packed(n) => n.with(|n| { + let n = n.get(); + ts.append_all(quote_spanned! { self.span => #[repr(packed(#n))] }) + }), + Align(n) => { + let n = n.get(); + ts.append_all(quote_spanned! { self.span => #[repr(align(#n))] }) } } } } -define_kind_specific_repr!("a struct", StructRepr, [C, Transparent, Packed], [Align, PackedN]); -define_kind_specific_repr!( - "an enum", - EnumRepr, - [C, U8, U16, U32, U64, Usize, I8, I16, I32, I64, Isize], - [Align] -); +// Used to permit implementing `With for T: Inhabited` and for `Infallible` +// without a blanket impl conflict. +pub(crate) trait Inhabited {} +impl Inhabited for PrimitiveRepr {} +impl Inhabited for NonZeroU64 {} -impl EnumRepr { - fn as_str(&self) -> &'static str { - 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(_) => unimplemented!("repr not yet supported"), - } +pub(crate) trait With { + fn with O>(self, f: F) -> O; +} + +impl With for T { + fn with O>(self, f: F) -> O { + f(self) } } -impl ToTokens for EnumRepr { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - tokens.append(Ident::new(self.as_str(), Span::call_site())); +impl With for Infallible { + fn with O>(self, _f: F) -> O { + match self {} } } -// All representations known to Rust. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub(crate) enum Repr { +/// The result of parsing a single `#[repr(...)]` attribute or a single +/// directive inside a compound `#[repr(..., ...)]` attribute. +#[derive(Copy, Clone, PartialEq, Eq)] +#[cfg_attr(test, derive(Debug))] +pub(crate) enum RawRepr { + Transparent, + C, + Rust, U8, U16, U32, @@ -212,128 +283,531 @@ pub(crate) enum Repr { I32, I64, Isize, - C, - Transparent, + Align(NonZeroU64), + PackedN(NonZeroU64), Packed, - PackedN(u64), - Align(u64), } -impl Repr { - fn from_meta(meta: &Meta) -> Result { - let (path, list) = match meta { - Meta::Path(path) => (path, None), - Meta::List(list) => (&list.path, Some(list)), - _ => return Err(Error::new_spanned(meta, "unrecognized representation hint")), - }; +/// A value with an associated span. +#[derive(Copy, Clone)] +#[cfg_attr(test, derive(Debug))] +pub(crate) struct Spanned { + pub(crate) t: T, + pub(crate) span: Span, +} - let ident = path - .get_ident() - .ok_or_else(|| Error::new_spanned(meta, "unrecognized representation hint"))?; +impl Spanned { + fn new(t: T, span: Span) -> Spanned { + Spanned { t, span } + } - Ok(match (ident.to_string().as_str(), list) { - ("u8", None) => Repr::U8, - ("u16", None) => Repr::U16, - ("u32", None) => Repr::U32, - ("u64", None) => Repr::U64, - ("usize", None) => Repr::Usize, - ("i8", None) => Repr::I8, - ("i16", None) => Repr::I16, - ("i32", None) => Repr::I32, - ("i64", None) => Repr::I64, - ("isize", None) => Repr::Isize, - ("C", None) => Repr::C, - ("transparent", None) => Repr::Transparent, - ("packed", None) => Repr::Packed, - ("packed", Some(list)) => { - Repr::PackedN(list.parse_args::()?.base10_parse::()?) - } - ("align", Some(list)) => { - Repr::Align(list.parse_args::()?.base10_parse::()?) - } - _ => return Err(Error::new_spanned(meta, "unrecognized representation hint")), + fn from(s: Spanned) -> Spanned + where + T: From, + { + let Spanned { t: u, span } = s; + Spanned::new(u.into(), span) + } + + /// Delegates to `T: TryFrom`, preserving span information in both the + /// success and error cases. + fn try_from(u: Spanned) -> Result, FromRawReprError>> + where + T: TryFrom>, + { + let Spanned { t: u, span } = u; + T::try_from(u).map(|t| Spanned { t, span }).map_err(|err| match err { + FromRawReprError::None => FromRawReprError::None, + FromRawReprError::Err(e) => FromRawReprError::Err(Spanned::new(e, span)), }) } } -impl KindRepr for Repr { - fn is_align(&self) -> bool { - false +/// The error from converting from a `RawRepr`. +#[cfg_attr(test, derive(Debug, Eq, PartialEq))] +pub(crate) enum FromRawReprError { + /// The `RawRepr` doesn't affect the high-level repr we're parsing (e.g. + /// it's `align(...)` and we're parsing a `CompoundRepr`). + None, + /// The `RawRepr` is invalid for the high-level repr we're parsing (e.g. + /// it's `packed` repr and we're parsing an `AlignRepr` for an enum type). + Err(E), +} + +/// The representation hint is not supported for the decorated type. +#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] +pub(crate) struct UnsupportedReprError; + +impl TryFrom for PrimitiveRepr { + type Error = UnsupportedReprError; + fn try_from(raw: RawRepr) -> Result { + use RawRepr::*; + match raw { + U8 => Ok(PrimitiveRepr::U8), + U16 => Ok(PrimitiveRepr::U16), + U32 => Ok(PrimitiveRepr::U32), + U64 => Ok(PrimitiveRepr::U64), + Usize => Ok(PrimitiveRepr::Usize), + I8 => Ok(PrimitiveRepr::I8), + I16 => Ok(PrimitiveRepr::I16), + I32 => Ok(PrimitiveRepr::I32), + I64 => Ok(PrimitiveRepr::I64), + Isize => Ok(PrimitiveRepr::Isize), + Transparent | C | Rust | Align(_) | PackedN(_) | Packed => Err(UnsupportedReprError), + } } +} - fn is_align_gt_one(&self) -> bool { - false +impl TryFrom for Infallible { + type Error = UnsupportedReprError; + fn try_from(_raw: RawRepr) -> Result { + Err(UnsupportedReprError) } +} - fn parse(meta: &Meta) -> syn::Result { - Self::from_meta(meta) +impl> TryFrom for CompoundRepr { + type Error = FromRawReprError; + fn try_from( + raw: RawRepr, + ) -> Result, FromRawReprError> { + use RawRepr::*; + match raw { + C => Ok(CompoundRepr::C), + Rust => Ok(CompoundRepr::Rust), + raw @ (U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize) => { + Prim::try_from(raw).map(CompoundRepr::Primitive).map_err(FromRawReprError::Err) + } + Transparent | Align(_) | PackedN(_) | Packed => Err(FromRawReprError::None), + } } } -impl Display for Repr { - fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { - if let Repr::Align(n) = self { - return write!(f, "repr(align({}))", n); +impl TryFrom for NonZeroU64 { + type Error = UnsupportedReprError; + fn try_from(raw: RawRepr) -> Result { + use RawRepr::*; + match raw { + Packed => Ok(NonZeroU64::new(1).unwrap()), + PackedN(n) => Ok(n), + U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | Transparent | C + | Rust | Align(_) => Err(UnsupportedReprError), } - if let Repr::PackedN(n) = self { - return write!(f, "repr(packed({}))", n); + } +} + +impl> TryFrom for AlignRepr { + type Error = FromRawReprError; + fn try_from(raw: RawRepr) -> Result, FromRawReprError> { + use RawRepr::*; + match raw { + Packed | PackedN(_) => { + Pcked::try_from(raw).map(AlignRepr::Packed).map_err(FromRawReprError::Err) + } + Align(n) => Ok(AlignRepr::Align(n)), + U8 | U16 | U32 | U64 | Usize | I8 | I16 | I32 | I64 | Isize | Transparent | C + | Rust => Err(FromRawReprError::None), } - write!( - f, - "repr({})", - match self { - Repr::U8 => "u8", - Repr::U16 => "u16", - Repr::U32 => "u32", - Repr::U64 => "u64", - Repr::Usize => "usize", - Repr::I8 => "i8", - Repr::I16 => "i16", - Repr::I32 => "i32", - Repr::I64 => "i64", - Repr::Isize => "isize", - Repr::C => "C", - Repr::Transparent => "transparent", - Repr::Packed => "packed", - _ => unreachable!(), + } +} + +/// The error from extracting a high-level repr type from a list of `RawRepr`s. +#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] +enum FromRawReprsError { + /// One of the `RawRepr`s is invalid for the high-level repr we're parsing + /// (e.g. there's a `packed` repr and we're parsing an `AlignRepr` for an + /// enum type). + Single(E), + /// Two `RawRepr`s appear which both affect the high-level repr we're + /// parsing (e.g., the list is `#[repr(align(2), packed)]`). Note that we + /// conservatively treat redundant reprs as conflicting (e.g. + /// `#[repr(packed, packed)]`). + Conflict, +} + +/// Tries to extract a high-level repr from a list of `RawRepr`s. +fn try_from_raw_reprs<'a, E, R: TryFrom>>( + r: impl IntoIterator>, +) -> Result>, Spanned>> { + // Walk the list of `RawRepr`s and attempt to convert each to an `R`. Bail + // if we find any errors. If we find more than one which converts to an `R`, + // bail with a `Conflict` error. + r.into_iter().try_fold(None, |found, raw| { + let new = match Spanned::::try_from(*raw) { + Ok(r) => r, + // This `RawRepr` doesn't convert to an `R`, so keep the current + // found `R`, if any. + Err(FromRawReprError::None) => return Ok(found), + // This repr is unsupported for the decorated type (e.g. + // `repr(packed)` on an enum). + Err(FromRawReprError::Err(Spanned { t: err, span })) => { + return Err(Spanned::new(FromRawReprsError::Single(err), span)) + } + }; + + if found.is_some() { + // We already found an `R`, but this `RawRepr` also converts to an + // `R`, so that's a conflict. + Err(Spanned::new(FromRawReprsError::Conflict, new.span)) + } else { + Ok(Some(new)) + } + }) +} + +/// The error returned from [`Repr::from_attrs`]. +#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))] +enum FromAttrsError { + FromRawReprs(FromRawReprsError), + Unrecognized, +} + +impl From> for FromAttrsError { + fn from(err: FromRawReprsError) -> FromAttrsError { + FromAttrsError::FromRawReprs(err) + } +} + +impl From for FromAttrsError { + fn from(_err: UnrecognizedReprError) -> FromAttrsError { + FromAttrsError::Unrecognized + } +} + +impl From> for Error { + fn from(err: Spanned) -> Error { + let Spanned { t: err, span } = err; + match err { + FromAttrsError::FromRawReprs(FromRawReprsError::Single( + _err @ UnsupportedReprError, + )) => Error::new(span, "unsupported representation hint for the decorated type"), + FromAttrsError::FromRawReprs(FromRawReprsError::Conflict) => { + // NOTE: This says "another" rather than "a preceding" because + // when one of the reprs involved is `transparent`, we detect + // that condition in `Repr::from_attrs`, and at that point we + // can't tell which repr came first, so we might report this on + // the first involved repr rather than the second, third, etc. + Error::new(span, "this conflicts with another representation hint") } - ) + FromAttrsError::Unrecognized => Error::new(span, "unrecognized representation hint"), + } } } -pub(crate) fn reprs(attrs: &[Attribute]) -> Result, Vec> { - let mut reprs = Vec::new(); - let mut errors = Vec::new(); - for attr in attrs { - // Ignore documentation attributes. - if attr.path().is_ident("doc") { - continue; +impl Repr { + fn from_attrs_inner(attrs: &[Attribute]) -> Result, Spanned> + where + Prim: TryFrom, + Packed: TryFrom, + { + let raw_reprs = RawRepr::from_attrs(attrs).map_err(Spanned::from)?; + + let transparent = { + let mut transparents = raw_reprs.iter().filter_map(|Spanned { t, span }| match t { + RawRepr::Transparent => Some(span), + _ => None, + }); + let first = transparents.next(); + let second = transparents.next(); + match (first, second) { + (None, None) => None, + (Some(span), None) => Some(*span), + (Some(_), Some(second)) => { + return Err(Spanned::new( + FromAttrsError::FromRawReprs(FromRawReprsError::Conflict), + *second, + ) + .into()) + } + // An iterator can't produce a value only on the second call to + // `.next()`. + (None, Some(_)) => unreachable!(), + } + }; + + let compound: Option>> = + try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?; + let align: Option>> = + try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?; + + if let Some(span) = transparent { + if compound.is_some() || align.is_some() { + // Arbitrarily report the problem on the `transparent` span. Any + // span will do. + return Err(Spanned::new(FromRawReprsError::Conflict.into(), span)); + } + + Ok(Repr::Transparent(span)) + } else { + Ok(Repr::Compound( + compound.unwrap_or(Spanned::new(CompoundRepr::Rust, Span::call_site())), + align, + )) } - if let Meta::List(ref meta_list) = attr.meta { - if meta_list.path.is_ident("repr") { - let parsed: Punctuated = - match meta_list.parse_args_with(Punctuated::parse_terminated) { - Ok(parsed) => parsed, - Err(_) => { - errors.push(Error::new_spanned( - &meta_list.tokens, - "unrecognized representation hint", - )); - continue; - } - }; - for meta in parsed { - match R::parse(&meta) { - Ok(repr) => reprs.push((meta, repr)), - Err(err) => errors.push(err), + } +} + +impl Repr { + pub(crate) fn from_attrs(attrs: &[Attribute]) -> Result, Error> + where + Prim: TryFrom, + Packed: TryFrom, + { + Repr::from_attrs_inner(attrs).map_err(Into::into) + } +} + +/// The representation hint could not be parsed or was unrecognized. +struct UnrecognizedReprError; + +impl RawRepr { + fn from_attrs( + attrs: &[Attribute], + ) -> Result>, Spanned> { + let mut reprs = Vec::new(); + for attr in attrs { + // Ignore documentation attributes. + if attr.path().is_ident("doc") { + continue; + } + if let Meta::List(ref meta_list) = attr.meta { + if meta_list.path.is_ident("repr") { + let parsed: Punctuated = + match meta_list.parse_args_with(Punctuated::parse_terminated) { + Ok(parsed) => parsed, + Err(_) => { + return Err(Spanned::new( + UnrecognizedReprError, + meta_list.tokens.span(), + )) + } + }; + for meta in parsed { + let s = meta.span(); + reprs.push( + RawRepr::from_meta(&meta) + .map(|r| Spanned::new(r, s)) + .map_err(|e| Spanned::new(e, s))?, + ); } } } } + + Ok(reprs) + } + + fn from_meta(meta: &Meta) -> Result { + let (path, list) = match meta { + Meta::Path(path) => (path, None), + Meta::List(list) => (&list.path, Some(list)), + _ => return Err(UnrecognizedReprError), + }; + + let ident = path.get_ident().ok_or(UnrecognizedReprError)?; + + // Only returns `Ok` for non-zero power-of-two values. + let parse_nzu64 = |list: &MetaList| { + list.parse_args::() + .and_then(|int| int.base10_parse::()) + .map_err(|_| UnrecognizedReprError) + .and_then(|nz| { + if nz.get().is_power_of_two() { + Ok(nz) + } else { + Err(UnrecognizedReprError) + } + }) + }; + + use RawRepr::*; + Ok(match (ident.to_string().as_str(), list) { + ("u8", None) => U8, + ("u16", None) => U16, + ("u32", None) => U32, + ("u64", None) => U64, + ("usize", None) => Usize, + ("i8", None) => I8, + ("i16", None) => I16, + ("i32", None) => I32, + ("i64", None) => I64, + ("isize", None) => Isize, + ("C", None) => C, + ("transparent", None) => Transparent, + ("Rust", None) => Rust, + ("packed", None) => Packed, + ("packed", Some(list)) => PackedN(parse_nzu64(list)?), + ("align", Some(list)) => Align(parse_nzu64(list)?), + _ => return Err(UnrecognizedReprError), + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use syn::parse_quote; + + impl From for Spanned { + fn from(t: T) -> Spanned { + Spanned::new(t, Span::call_site()) + } + } + + // We ignore spans for equality in testing since real spans are hard to + // synthesize and don't implement `PartialEq`. + impl PartialEq for Spanned { + fn eq(&self, other: &Spanned) -> bool { + self.t.eq(&other.t) + } + } + + impl Eq for Spanned {} + + impl PartialEq for Repr { + fn eq(&self, other: &Repr) -> bool { + match (self, other) { + (Repr::Transparent(_), Repr::Transparent(_)) => true, + (Repr::Compound(sc, sa), Repr::Compound(oc, oa)) => (sc, sa) == (oc, oa), + _ => false, + } + } } - if !errors.is_empty() { - return Err(errors); + fn s() -> Span { + Span::call_site() + } + + #[test] + fn test() { + // Test that a given `#[repr(...)]` attribute parses and returns the + // given `Repr` or error. + macro_rules! test { + ($(#[$attr:meta])* => $repr:expr) => { + test!(@inner $(#[$attr])* => Repr => Ok($repr)); + }; + // In the error case, the caller must explicitly provide the name of + // the `Repr` type to assist in type inference. + (@error $(#[$attr:meta])* => $typ:ident => $repr:expr) => { + test!(@inner $(#[$attr])* => $typ => Err($repr)); + }; + (@inner $(#[$attr:meta])* => $typ:ident => $repr:expr) => { + let attr: Attribute = parse_quote!($(#[$attr])*); + let mut got = $typ::from_attrs_inner(&[attr]); + let expect: Result, _> = $repr; + if false { + // Force Rust to infer `got` as having the same type as + // `expect`. + got = expect; + } + assert_eq!(got, expect, stringify!($(#[$attr])*)); + }; + } + + use {AlignRepr::*, CompoundRepr::*, PrimitiveRepr::*}; + let nz = |n: u64| NonZeroU64::new(n).unwrap(); + + test!(#[repr(transparent)] => StructUnionRepr::Transparent(s())); + test!(#[repr()] => StructUnionRepr::Compound(Rust.into(), None)); + test!(#[repr(packed)] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(1)).into()))); + test!(#[repr(packed(2))] => StructUnionRepr::Compound(Rust.into(), Some(Packed(nz(2)).into()))); + test!(#[repr(align(1))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(1)).into()))); + test!(#[repr(align(2))] => StructUnionRepr::Compound(Rust.into(), Some(Align(nz(2)).into()))); + test!(#[repr(C)] => StructUnionRepr::Compound(C.into(), None)); + test!(#[repr(C, packed)] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(1)).into()))); + test!(#[repr(C, packed(2))] => StructUnionRepr::Compound(C.into(), Some(Packed(nz(2)).into()))); + test!(#[repr(C, align(1))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(1)).into()))); + test!(#[repr(C, align(2))] => StructUnionRepr::Compound(C.into(), Some(Align(nz(2)).into()))); + + test!(#[repr(transparent)] => EnumRepr::Transparent(s())); + test!(#[repr()] => EnumRepr::Compound(Rust.into(), None)); + test!(#[repr(align(1))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(1)).into()))); + test!(#[repr(align(2))] => EnumRepr::Compound(Rust.into(), Some(Align(nz(2)).into()))); + + macro_rules! for_each_compound_repr { + ($($r:tt => $var:expr),*) => { + $( + test!(#[repr($r)] => EnumRepr::Compound($var.into(), None)); + test!(#[repr($r, align(1))] => EnumRepr::Compound($var.into(), Some(Align(nz(1)).into()))); + test!(#[repr($r, align(2))] => EnumRepr::Compound($var.into(), Some(Align(nz(2)).into()))); + )* + } + } + + for_each_compound_repr!( + C => C, + u8 => Primitive(U8), + u16 => Primitive(U16), + u32 => Primitive(U32), + u64 => Primitive(U64), + usize => Primitive(Usize), + i8 => Primitive(I8), + i16 => Primitive(I16), + i32 => Primitive(I32), + i64 => Primitive(I64), + isize => Primitive(Isize) + ); + + use {FromAttrsError::*, FromRawReprsError::*}; + + // Run failure tests which are valid for both `StructUnionRepr` and + // `EnumRepr`. + macro_rules! for_each_repr_type { + ($($repr:ident),*) => { + $( + // Invalid packed or align attributes + test!(@error #[repr(packed(0))] => $repr => Unrecognized.into()); + test!(@error #[repr(packed(3))] => $repr => Unrecognized.into()); + test!(@error #[repr(align(0))] => $repr => Unrecognized.into()); + test!(@error #[repr(align(3))] => $repr => Unrecognized.into()); + + // Conflicts + test!(@error #[repr(transparent, transparent)] => $repr => FromRawReprs(Conflict).into()); + test!(@error #[repr(transparent, C)] => $repr => FromRawReprs(Conflict).into()); + test!(@error #[repr(transparent, Rust)] => $repr => FromRawReprs(Conflict).into()); + + test!(@error #[repr(C, transparent)] => $repr => FromRawReprs(Conflict).into()); + test!(@error #[repr(C, C)] => $repr => FromRawReprs(Conflict).into()); + test!(@error #[repr(C, Rust)] => $repr => FromRawReprs(Conflict).into()); + + test!(@error #[repr(Rust, transparent)] => $repr => FromRawReprs(Conflict).into()); + test!(@error #[repr(Rust, C)] => $repr => FromRawReprs(Conflict).into()); + test!(@error #[repr(Rust, Rust)] => $repr => FromRawReprs(Conflict).into()); + )* + } + } + + for_each_repr_type!(StructUnionRepr, EnumRepr); + + // Enum-specific conflicts. + // + // We don't bother to test every combination since that would be a huge + // number (enums can have primitive reprs u8, u16, u32, u64, usize, i8, + // i16, i32, i64, and isize). Instead, since the conflict logic doesn't + // care what specific value of `PrimitiveRepr` is present, we assume + // that testing against u8 alone is fine. + test!(@error #[repr(transparent, u8)] => EnumRepr => FromRawReprs(Conflict).into()); + test!(@error #[repr(u8, transparent)] => EnumRepr => FromRawReprs(Conflict).into()); + test!(@error #[repr(C, u8)] => EnumRepr => FromRawReprs(Conflict).into()); + test!(@error #[repr(u8, C)] => EnumRepr => FromRawReprs(Conflict).into()); + test!(@error #[repr(Rust, u8)] => EnumRepr => FromRawReprs(Conflict).into()); + test!(@error #[repr(u8, Rust)] => EnumRepr => FromRawReprs(Conflict).into()); + test!(@error #[repr(u8, u8)] => EnumRepr => FromRawReprs(Conflict).into()); + + // Illegal struct/union reprs + test!(@error #[repr(u8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(u16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(u32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(u64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(usize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(i8)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(i16)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(i32)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(i64)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(isize)] => StructUnionRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + + // Illegal enum reprs + test!(@error #[repr(packed)] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(packed(1))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into()); + test!(@error #[repr(packed(2))] => EnumRepr => FromRawReprs(Single(UnsupportedReprError)).into()); } - Ok(reprs) } diff --git a/zerocopy-derive/tests/ui-msrv/enum.stderr b/zerocopy-derive/tests/ui-msrv/enum.stderr index 84a0f9bc01e..ee853d1057f 100644 --- a/zerocopy-derive/tests/ui-msrv/enum.stderr +++ b/zerocopy-derive/tests/ui-msrv/enum.stderr @@ -10,19 +10,21 @@ error: unrecognized representation hint 25 | #[repr(foo)] | ^^^ -error: unsupported representation for deriving zerocopy trait(s) on an enum - --> tests/ui-msrv/enum.rs:31:8 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout + --> tests/ui-msrv/enum.rs:30:10 | -31 | #[repr(transparent)] - | ^^^^^^^^^^^ +30 | #[derive(FromBytes)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: conflicting representation hints - --> tests/ui-msrv/enum.rs:37:1 +error: this conflicts with another representation hint + --> tests/ui-msrv/enum.rs:37:12 | 37 | #[repr(u8, u16)] - | ^ + | ^^^ -error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-msrv/enum.rs:42:10 | 42 | #[derive(FromBytes)] @@ -30,7 +32,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = note: this error originates in the derive macro `FromBytes` (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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-msrv/enum.rs:69:10 | 69 | #[derive(TryFromBytes)] @@ -38,7 +40,7 @@ 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: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-msrv/enum.rs:74:10 | 74 | #[derive(TryFromBytes)] @@ -46,7 +48,7 @@ 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: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-msrv/enum.rs:92:10 | 92 | #[derive(FromZeros)] @@ -54,7 +56,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = 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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-msrv/enum.rs:97:10 | 97 | #[derive(FromZeros)] @@ -62,7 +64,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = 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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-msrv/enum.rs:103:10 | 103 | #[derive(FromZeros)] @@ -101,125 +103,151 @@ error: FromZeros only supported on enums with a variant that has a discriminant 138 | | } | |_^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:145:8 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout + --> tests/ui-msrv/enum.rs:144:10 | -145 | #[repr(C)] +144 | #[derive(FromBytes)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:150:8 + | +150 | #[repr(C)] | ^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:151:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:156:8 | -151 | #[repr(usize)] +156 | #[repr(usize)] | ^^^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:157:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:162:8 | -157 | #[repr(isize)] +162 | #[repr(isize)] | ^^^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:163:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:168:8 | -163 | #[repr(u32)] +168 | #[repr(u32)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:169:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:174:8 | -169 | #[repr(i32)] +174 | #[repr(i32)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:175:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:180:8 | -175 | #[repr(u64)] +180 | #[repr(u64)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-msrv/enum.rs:181:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-msrv/enum.rs:186:8 | -181 | #[repr(i64)] +186 | #[repr(i64)] | ^^^ -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:452:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:456:10 | -452 | #[repr(C)] - | ^ +456 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:458:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:462:10 | -458 | #[repr(u16)] - | ^^^ +462 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:464:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:468:10 | -464 | #[repr(i16)] - | ^^^ +468 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:470:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:474:10 | -470 | #[repr(u32)] - | ^^^ +474 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:476:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:480:10 | -476 | #[repr(i32)] - | ^^^ +480 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:482:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:486:10 | -482 | #[repr(u64)] - | ^^^ +486 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:488:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:492:10 | -488 | #[repr(i64)] - | ^^^ +492 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:494:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:498:10 | -494 | #[repr(usize)] - | ^^^^^ +498 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-msrv/enum.rs:500:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-msrv/enum.rs:504:10 | -500 | #[repr(isize)] - | ^^^^^ +504 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:506:12 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-msrv/enum.rs:511:12 | -506 | #[repr(u8, align(2))] - | ^^^^^^^^ +511 | #[repr(u8, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:512:12 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-msrv/enum.rs:517:12 | -512 | #[repr(i8, align(2))] - | ^^^^^^^^ +517 | #[repr(i8, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:518:18 +error: this conflicts with another representation hint + --> tests/ui-msrv/enum.rs:523:18 | -518 | #[repr(align(1), align(2))] - | ^^^^^^^^ +523 | #[repr(align(1), align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/enum.rs:524:8 +error: this conflicts with another representation hint + --> tests/ui-msrv/enum.rs:529:18 | -524 | #[repr(align(2), align(4))] - | ^^^^^^^^ +529 | #[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 @@ -298,28 +326,19 @@ error[E0277]: the trait bound `NotFromZeros: FromZeros` is not satisfied = 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 `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 + --> tests/ui-msrv/enum.rs:191:10 | -186 | #[derive(FromBytes)] +191 | #[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 `(): PaddingFree` is not satisfied - --> tests/ui-msrv/enum.rs:533:10 + --> tests/ui-msrv/enum.rs:538:10 | -533 | #[derive(IntoBytes)] +538 | #[derive(IntoBytes)] | ^^^^^^^^^ the trait `PaddingFree` is not implemented for `()` | = help: the following implementations were found: @@ -328,9 +347,9 @@ error[E0277]: the trait bound `(): PaddingFree` is not satisfi = 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 `(): PaddingFree` is not satisfied - --> tests/ui-msrv/enum.rs:544:10 + --> tests/ui-msrv/enum.rs:549:10 | -544 | #[derive(IntoBytes)] +549 | #[derive(IntoBytes)] | ^^^^^^^^^ the trait `PaddingFree` is not implemented for `()` | = help: the following implementations were found: @@ -339,9 +358,9 @@ error[E0277]: the trait bound `(): PaddingFree` is not satisfi = 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 `(): PaddingFree` is not satisfied - --> tests/ui-msrv/enum.rs:550:10 + --> tests/ui-msrv/enum.rs:555:10 | -550 | #[derive(IntoBytes)] +555 | #[derive(IntoBytes)] | ^^^^^^^^^ the trait `PaddingFree` is not implemented for `()` | = help: the following implementations were found: diff --git a/zerocopy-derive/tests/ui-msrv/struct.stderr b/zerocopy-derive/tests/ui-msrv/struct.stderr index 9cc7ac22a17..b546a49f7c2 100644 --- a/zerocopy-derive/tests/ui-msrv/struct.stderr +++ b/zerocopy-derive/tests/ui-msrv/struct.stderr @@ -1,37 +1,43 @@ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/struct.rs:137:11 +error: this conflicts with another representation hint + --> tests/ui-msrv/struct.rs:133:11 | -137 | #[repr(C, align(2))] - | ^^^^^^^^ +133 | #[repr(C, C)] // zerocopy-derive conservatively treats these as conflicting reprs + | ^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/struct.rs:141:21 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-msrv/struct.rs:143:11 | -141 | #[repr(transparent, align(2))] - | ^^^^^^^^ +143 | #[repr(C, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/struct.rs:147:16 +error: this conflicts with another representation hint + --> tests/ui-msrv/struct.rs:147:8 | -147 | #[repr(packed, align(2))] - | ^^^^^^^^ +147 | #[repr(transparent, align(2))] + | ^^^^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/struct.rs:151:18 +error: this conflicts with another representation hint + --> tests/ui-msrv/struct.rs:153:16 | -151 | #[repr(align(1), align(2))] - | ^^^^^^^^ +153 | #[repr(packed, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/struct.rs:155:8 +error: this conflicts with another representation hint + --> tests/ui-msrv/struct.rs:157:18 | -155 | #[repr(align(2), align(4))] - | ^^^^^^^^ +157 | #[repr(align(1), align(2))] + | ^^^^^ + +error: this conflicts with another representation hint + --> tests/ui-msrv/struct.rs:161:18 + | +161 | #[repr(align(2), align(4))] + | ^^^^^ error[E0692]: transparent struct cannot have other repr hints - --> tests/ui-msrv/struct.rs:141:8 + --> tests/ui-msrv/struct.rs:147:8 | -141 | #[repr(transparent, align(2))] +147 | #[repr(transparent, align(2))] | ^^^^^^^^^^^ ^^^^^^^^ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time diff --git a/zerocopy-derive/tests/ui-msrv/union.stderr b/zerocopy-derive/tests/ui-msrv/union.stderr index 26272a50572..5079dd721fe 100644 --- a/zerocopy-derive/tests/ui-msrv/union.stderr +++ b/zerocopy-derive/tests/ui-msrv/union.stderr @@ -6,29 +6,29 @@ error: unsupported on types with type parameters | = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: cannot derive Unaligned with repr(align(N > 1)) +error: cannot derive `Unaligned` on type with alignment greater than 1 --> tests/ui-msrv/union.rs:51:11 | 51 | #[repr(C, align(2))] - | ^^^^^^^^ + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) +error: this conflicts with another representation hint --> tests/ui-msrv/union.rs:67:16 | 67 | #[repr(packed, align(2))] - | ^^^^^^^^ + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) +error: this conflicts with another representation hint --> tests/ui-msrv/union.rs:73:18 | 73 | #[repr(align(1), align(2))] - | ^^^^^^^^ + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-msrv/union.rs:79:8 +error: this conflicts with another representation hint + --> tests/ui-msrv/union.rs:79:18 | 79 | #[repr(align(2), align(4))] - | ^^^^^^^^ + | ^^^^^ error[E0277]: the trait bound `UnsafeCell<()>: zerocopy::Immutable` is not satisfied --> tests/ui-msrv/union.rs:24:10 diff --git a/zerocopy-derive/tests/ui-nightly/enum.rs b/zerocopy-derive/tests/ui-nightly/enum.rs index 0b17423de86..558c0512377 100644 --- a/zerocopy-derive/tests/ui-nightly/enum.rs +++ b/zerocopy-derive/tests/ui-nightly/enum.rs @@ -142,47 +142,52 @@ enum FromZeros7 { // #[derive(FromBytes)] -#[repr(C)] enum FromBytes1 { A, } #[derive(FromBytes)] -#[repr(usize)] +#[repr(C)] enum FromBytes2 { A, } #[derive(FromBytes)] -#[repr(isize)] +#[repr(usize)] enum FromBytes3 { A, } #[derive(FromBytes)] -#[repr(u32)] +#[repr(isize)] enum FromBytes4 { A, } #[derive(FromBytes)] -#[repr(i32)] +#[repr(u32)] enum FromBytes5 { A, } #[derive(FromBytes)] -#[repr(u64)] +#[repr(i32)] enum FromBytes6 { A, } #[derive(FromBytes)] -#[repr(i64)] +#[repr(u64)] enum FromBytes7 { A, } +#[derive(FromBytes)] +#[repr(i64)] +enum FromBytes8 { + A, +} + #[derive(FromBytes)] #[repr(u8)] enum FooU8 { diff --git a/zerocopy-derive/tests/ui-nightly/enum.stderr b/zerocopy-derive/tests/ui-nightly/enum.stderr index 04db0af8b67..5bc7a7cd50d 100644 --- a/zerocopy-derive/tests/ui-nightly/enum.stderr +++ b/zerocopy-derive/tests/ui-nightly/enum.stderr @@ -10,19 +10,21 @@ error: unrecognized representation hint 25 | #[repr(foo)] | ^^^ -error: unsupported representation for deriving zerocopy trait(s) on an enum - --> tests/ui-nightly/enum.rs:31:8 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout + --> tests/ui-nightly/enum.rs:30:10 | -31 | #[repr(transparent)] - | ^^^^^^^^^^^ +30 | #[derive(FromBytes)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: conflicting representation hints - --> tests/ui-nightly/enum.rs:37:8 +error: this conflicts with another representation hint + --> tests/ui-nightly/enum.rs:37:12 | 37 | #[repr(u8, u16)] - | ^^^^^^^ + | ^^^ -error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-nightly/enum.rs:42:10 | 42 | #[derive(FromBytes)] @@ -30,7 +32,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = note: this error originates in the derive macro `FromBytes` (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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-nightly/enum.rs:69:10 | 69 | #[derive(TryFromBytes)] @@ -38,7 +40,7 @@ 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: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-nightly/enum.rs:74:10 | 74 | #[derive(TryFromBytes)] @@ -46,7 +48,7 @@ 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: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-nightly/enum.rs:92:10 | 92 | #[derive(FromZeros)] @@ -54,7 +56,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = 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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-nightly/enum.rs:97:10 | 97 | #[derive(FromZeros)] @@ -62,7 +64,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = 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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-nightly/enum.rs:103:10 | 103 | #[derive(FromZeros)] @@ -101,125 +103,151 @@ error: FromZeros only supported on enums with a variant that has a discriminant 138 | | } | |_^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:145:8 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout + --> tests/ui-nightly/enum.rs:144:10 | -145 | #[repr(C)] +144 | #[derive(FromBytes)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:150:8 + | +150 | #[repr(C)] | ^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:151:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:156:8 | -151 | #[repr(usize)] +156 | #[repr(usize)] | ^^^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:157:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:162:8 | -157 | #[repr(isize)] +162 | #[repr(isize)] | ^^^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:163:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:168:8 | -163 | #[repr(u32)] +168 | #[repr(u32)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:169:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:174:8 | -169 | #[repr(i32)] +174 | #[repr(i32)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:175:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:180:8 | -175 | #[repr(u64)] +180 | #[repr(u64)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-nightly/enum.rs:181:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-nightly/enum.rs:186:8 | -181 | #[repr(i64)] +186 | #[repr(i64)] | ^^^ -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:452:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:456:10 | -452 | #[repr(C)] - | ^ +456 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:458:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:462:10 | -458 | #[repr(u16)] - | ^^^ +462 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:464:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:468:10 | -464 | #[repr(i16)] - | ^^^ +468 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:470:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:474:10 | -470 | #[repr(u32)] - | ^^^ +474 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:476:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:480:10 | -476 | #[repr(i32)] - | ^^^ +480 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:482:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:486:10 | -482 | #[repr(u64)] - | ^^^ +486 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:488:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:492:10 | -488 | #[repr(i64)] - | ^^^ +492 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:494:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:498:10 | -494 | #[repr(usize)] - | ^^^^^ +498 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-nightly/enum.rs:500:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-nightly/enum.rs:504:10 | -500 | #[repr(isize)] - | ^^^^^ +504 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:506:12 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-nightly/enum.rs:511:12 | -506 | #[repr(u8, align(2))] +511 | #[repr(u8, align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:512:12 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-nightly/enum.rs:517:12 | -512 | #[repr(i8, align(2))] +517 | #[repr(i8, align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:518:18 +error: this conflicts with another representation hint + --> tests/ui-nightly/enum.rs:523:18 | -518 | #[repr(align(1), align(2))] +523 | #[repr(align(1), align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/enum.rs:524:8 +error: this conflicts with another representation hint + --> tests/ui-nightly/enum.rs:529:18 | -524 | #[repr(align(2), align(4))] - | ^^^^^^^^ +529 | #[repr(align(2), align(4))] + | ^^^^^^^^ error[E0565]: meta item in `repr` must be an identifier --> tests/ui-nightly/enum.rs:19:8 @@ -365,34 +393,10 @@ 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` - | - = note: Consider adding `#[derive(TryFromBytes)]` to `NotFromZeros` - = help: the following other types implement trait `TryFromBytes`: - () - *const T - *mut T - ::is_bit_valid::___ZerocopyVariantStruct_A - ::is_bit_valid::___ZerocopyVariantStruct_A - AtomicBool - AtomicI16 - AtomicI32 - 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 + --> tests/ui-nightly/enum.rs:191:10 | -186 | #[derive(FromBytes)] +191 | #[derive(FromBytes)] | ^^^^^^^^^ the trait `FromBytes` is not implemented for `bool` | = note: Consider adding `#[derive(FromBytes)]` to `bool` @@ -414,9 +418,9 @@ help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | error[E0277]: `IntoBytes1` has inter-field padding - --> tests/ui-nightly/enum.rs:533:10 + --> tests/ui-nightly/enum.rs:538:10 | -533 | #[derive(IntoBytes)] +538 | #[derive(IntoBytes)] | ^^^^^^^^^ types with padding cannot implement `IntoBytes` | = help: the trait `PaddingFree` is not implemented for `()` @@ -432,9 +436,9 @@ help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | error[E0277]: `IntoBytes2` has inter-field padding - --> tests/ui-nightly/enum.rs:544:10 + --> tests/ui-nightly/enum.rs:549:10 | -544 | #[derive(IntoBytes)] +549 | #[derive(IntoBytes)] | ^^^^^^^^^ types with padding cannot implement `IntoBytes` | = help: the trait `PaddingFree` is not implemented for `()` @@ -450,9 +454,9 @@ help: add `#![feature(trivial_bounds)]` to the crate attributes to enable | error[E0277]: `IntoBytes3` has inter-field padding - --> tests/ui-nightly/enum.rs:550:10 + --> tests/ui-nightly/enum.rs:555:10 | -550 | #[derive(IntoBytes)] +555 | #[derive(IntoBytes)] | ^^^^^^^^^ types with padding cannot implement `IntoBytes` | = help: the trait `PaddingFree` is not implemented for `()` diff --git a/zerocopy-derive/tests/ui-nightly/struct.rs b/zerocopy-derive/tests/ui-nightly/struct.rs index e75fd593103..ab6b48d82e4 100644 --- a/zerocopy-derive/tests/ui-nightly/struct.rs +++ b/zerocopy-derive/tests/ui-nightly/struct.rs @@ -129,6 +129,12 @@ struct IntoBytes4 { b: [u8], } +#[derive(IntoBytes)] +#[repr(C, C)] // zerocopy-derive conservatively treats these as conflicting reprs +struct IntoBytes5 { + a: u8, +} + // // Unaligned errors // diff --git a/zerocopy-derive/tests/ui-nightly/struct.stderr b/zerocopy-derive/tests/ui-nightly/struct.stderr index feef5cb800d..c41070cb582 100644 --- a/zerocopy-derive/tests/ui-nightly/struct.stderr +++ b/zerocopy-derive/tests/ui-nightly/struct.stderr @@ -1,37 +1,43 @@ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/struct.rs:137:11 +error: this conflicts with another representation hint + --> tests/ui-nightly/struct.rs:133:11 | -137 | #[repr(C, align(2))] +133 | #[repr(C, C)] // zerocopy-derive conservatively treats these as conflicting reprs + | ^ + +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-nightly/struct.rs:143:11 + | +143 | #[repr(C, align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/struct.rs:141:21 +error: this conflicts with another representation hint + --> tests/ui-nightly/struct.rs:147:8 | -141 | #[repr(transparent, align(2))] - | ^^^^^^^^ +147 | #[repr(transparent, align(2))] + | ^^^^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/struct.rs:147:16 +error: this conflicts with another representation hint + --> tests/ui-nightly/struct.rs:153:16 | -147 | #[repr(packed, align(2))] +153 | #[repr(packed, align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/struct.rs:151:18 +error: this conflicts with another representation hint + --> tests/ui-nightly/struct.rs:157:18 | -151 | #[repr(align(1), align(2))] +157 | #[repr(align(1), align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/struct.rs:155:8 +error: this conflicts with another representation hint + --> tests/ui-nightly/struct.rs:161:18 | -155 | #[repr(align(2), align(4))] - | ^^^^^^^^ +161 | #[repr(align(2), align(4))] + | ^^^^^^^^ error[E0692]: transparent struct cannot have other repr hints - --> tests/ui-nightly/struct.rs:141:8 + --> tests/ui-nightly/struct.rs:147:8 | -141 | #[repr(transparent, align(2))] +147 | #[repr(transparent, align(2))] | ^^^^^^^^^^^ ^^^^^^^^ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time @@ -313,7 +319,7 @@ note: required by a bound in `macro_util::__size_of::size_of` | ^^^^^ required by this bound in `size_of` error[E0587]: type has conflicting packed and align representation hints - --> tests/ui-nightly/struct.rs:148:1 + --> tests/ui-nightly/struct.rs:154:1 | -148 | struct Unaligned3; +154 | struct Unaligned3; | ^^^^^^^^^^^^^^^^^ diff --git a/zerocopy-derive/tests/ui-nightly/union.stderr b/zerocopy-derive/tests/ui-nightly/union.stderr index 7650f84c8e5..66aec9a2848 100644 --- a/zerocopy-derive/tests/ui-nightly/union.stderr +++ b/zerocopy-derive/tests/ui-nightly/union.stderr @@ -6,29 +6,29 @@ error: unsupported on types with type parameters | = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: cannot derive Unaligned with repr(align(N > 1)) +error: cannot derive `Unaligned` on type with alignment greater than 1 --> tests/ui-nightly/union.rs:51:11 | 51 | #[repr(C, align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) +error: this conflicts with another representation hint --> tests/ui-nightly/union.rs:67:16 | 67 | #[repr(packed, align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) +error: this conflicts with another representation hint --> tests/ui-nightly/union.rs:73:18 | 73 | #[repr(align(1), align(2))] | ^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-nightly/union.rs:79:8 +error: this conflicts with another representation hint + --> tests/ui-nightly/union.rs:79:18 | 79 | #[repr(align(2), align(4))] - | ^^^^^^^^ + | ^^^^^^^^ error[E0277]: the trait bound `UnsafeCell<()>: zerocopy::Immutable` is not satisfied --> tests/ui-nightly/union.rs:24:10 diff --git a/zerocopy-derive/tests/ui-stable/enum.stderr b/zerocopy-derive/tests/ui-stable/enum.stderr index 60f94aa3c41..6d4340d17c0 100644 --- a/zerocopy-derive/tests/ui-stable/enum.stderr +++ b/zerocopy-derive/tests/ui-stable/enum.stderr @@ -10,19 +10,21 @@ error: unrecognized representation hint 25 | #[repr(foo)] | ^^^ -error: unsupported representation for deriving zerocopy trait(s) on an enum - --> tests/ui-stable/enum.rs:31:8 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout + --> tests/ui-stable/enum.rs:30:10 | -31 | #[repr(transparent)] - | ^^^^^^^^^^^ +30 | #[derive(FromBytes)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: conflicting representation hints - --> tests/ui-stable/enum.rs:37:1 +error: this conflicts with another representation hint + --> tests/ui-stable/enum.rs:37:12 | 37 | #[repr(u8, u16)] - | ^ + | ^^^ -error: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-stable/enum.rs:42:10 | 42 | #[derive(FromBytes)] @@ -30,7 +32,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = note: this error originates in the derive macro `FromBytes` (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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-stable/enum.rs:69:10 | 69 | #[derive(TryFromBytes)] @@ -38,7 +40,7 @@ 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: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-stable/enum.rs:74:10 | 74 | #[derive(TryFromBytes)] @@ -46,7 +48,7 @@ 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: must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-stable/enum.rs:92:10 | 92 | #[derive(FromZeros)] @@ -54,7 +56,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = 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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-stable/enum.rs:97:10 | 97 | #[derive(FromZeros)] @@ -62,7 +64,7 @@ error: must have a non-align #[repr(...)] attribute in order to guarantee this t | = 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 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout --> tests/ui-stable/enum.rs:103:10 | 103 | #[derive(FromZeros)] @@ -101,125 +103,151 @@ error: FromZeros only supported on enums with a variant that has a discriminant 138 | | } | |_^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:145:8 +error: must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout + --> tests/ui-stable/enum.rs:144:10 + | +144 | #[derive(FromBytes)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:150:8 | -145 | #[repr(C)] +150 | #[repr(C)] | ^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:151:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:156:8 | -151 | #[repr(usize)] +156 | #[repr(usize)] | ^^^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:157:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:162:8 | -157 | #[repr(isize)] +162 | #[repr(isize)] | ^^^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:163:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:168:8 | -163 | #[repr(u32)] +168 | #[repr(u32)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:169:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:174:8 | -169 | #[repr(i32)] +174 | #[repr(i32)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:175:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:180:8 | -175 | #[repr(u64)] +180 | #[repr(u64)] | ^^^ -error: FromBytes requires repr of "u8", "u16", "i8", or "i16" - --> tests/ui-stable/enum.rs:181:8 +error: `FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16` + --> tests/ui-stable/enum.rs:186:8 | -181 | #[repr(i64)] +186 | #[repr(i64)] | ^^^ -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:452:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:456:10 | -452 | #[repr(C)] - | ^ +456 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:458:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:462:10 | -458 | #[repr(u16)] - | ^^^ +462 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:464:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:468:10 | -464 | #[repr(i16)] - | ^^^ +468 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:470:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:474:10 | -470 | #[repr(u32)] - | ^^^ +474 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:476:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:480:10 | -476 | #[repr(i32)] - | ^^^ +480 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:482:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:486:10 | -482 | #[repr(u64)] - | ^^^ +486 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:488:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:492:10 | -488 | #[repr(i64)] - | ^^^ +492 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:494:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:498:10 | -494 | #[repr(usize)] - | ^^^^^ +498 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1))) - --> tests/ui-stable/enum.rs:500:8 +error: must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment + --> tests/ui-stable/enum.rs:504:10 | -500 | #[repr(isize)] - | ^^^^^ +504 | #[derive(Unaligned)] + | ^^^^^^^^^ + | + = note: this error originates in the derive macro `Unaligned` (in Nightly builds, run with -Z macro-backtrace for more info) -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:506:12 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-stable/enum.rs:511:12 | -506 | #[repr(u8, align(2))] - | ^^^^^^^^ +511 | #[repr(u8, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:512:12 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-stable/enum.rs:517:12 | -512 | #[repr(i8, align(2))] - | ^^^^^^^^ +517 | #[repr(i8, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:518:18 +error: this conflicts with another representation hint + --> tests/ui-stable/enum.rs:523:18 | -518 | #[repr(align(1), align(2))] - | ^^^^^^^^ +523 | #[repr(align(1), align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/enum.rs:524:8 +error: this conflicts with another representation hint + --> tests/ui-stable/enum.rs:529:18 | -524 | #[repr(align(2), align(4))] - | ^^^^^^^^ +529 | #[repr(align(2), align(4))] + | ^^^^^ error[E0565]: meta item in `repr` must be an identifier --> tests/ui-stable/enum.rs:19:8 @@ -345,30 +373,10 @@ error[E0277]: the trait bound `NotFromZeros: FromZeros` is not satisfied = 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` - | - = note: Consider adding `#[derive(TryFromBytes)]` to `NotFromZeros` - = help: the following other types implement trait `TryFromBytes`: - () - *const T - *mut T - ::is_bit_valid::___ZerocopyVariantStruct_A - ::is_bit_valid::___ZerocopyVariantStruct_A - AtomicBool - AtomicI16 - AtomicI32 - 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 + --> tests/ui-stable/enum.rs:191:10 | -186 | #[derive(FromBytes)] +191 | #[derive(FromBytes)] | ^^^^^^^^^ the trait `FromBytes` is not implemented for `bool` | = note: Consider adding `#[derive(FromBytes)]` to `bool` @@ -386,9 +394,9 @@ error[E0277]: the trait bound `bool: FromBytes` is not satisfied = note: this error originates in the derive macro `FromBytes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `IntoBytes1` has inter-field padding - --> tests/ui-stable/enum.rs:533:10 + --> tests/ui-stable/enum.rs:538:10 | -533 | #[derive(IntoBytes)] +538 | #[derive(IntoBytes)] | ^^^^^^^^^ types with padding cannot implement `IntoBytes` | = help: the trait `PaddingFree` is not implemented for `()` @@ -400,9 +408,9 @@ error[E0277]: `IntoBytes1` has inter-field padding = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `IntoBytes2` has inter-field padding - --> tests/ui-stable/enum.rs:544:10 + --> tests/ui-stable/enum.rs:549:10 | -544 | #[derive(IntoBytes)] +549 | #[derive(IntoBytes)] | ^^^^^^^^^ types with padding cannot implement `IntoBytes` | = help: the trait `PaddingFree` is not implemented for `()` @@ -414,9 +422,9 @@ error[E0277]: `IntoBytes2` has inter-field padding = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `IntoBytes3` has inter-field padding - --> tests/ui-stable/enum.rs:550:10 + --> tests/ui-stable/enum.rs:555:10 | -550 | #[derive(IntoBytes)] +555 | #[derive(IntoBytes)] | ^^^^^^^^^ types with padding cannot implement `IntoBytes` | = help: the trait `PaddingFree` is not implemented for `()` diff --git a/zerocopy-derive/tests/ui-stable/struct.stderr b/zerocopy-derive/tests/ui-stable/struct.stderr index da842f1a154..6d9e6e6d51a 100644 --- a/zerocopy-derive/tests/ui-stable/struct.stderr +++ b/zerocopy-derive/tests/ui-stable/struct.stderr @@ -1,37 +1,43 @@ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/struct.rs:137:11 +error: this conflicts with another representation hint + --> tests/ui-stable/struct.rs:133:11 | -137 | #[repr(C, align(2))] - | ^^^^^^^^ +133 | #[repr(C, C)] // zerocopy-derive conservatively treats these as conflicting reprs + | ^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/struct.rs:141:21 +error: cannot derive `Unaligned` on type with alignment greater than 1 + --> tests/ui-stable/struct.rs:143:11 | -141 | #[repr(transparent, align(2))] - | ^^^^^^^^ +143 | #[repr(C, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/struct.rs:147:16 +error: this conflicts with another representation hint + --> tests/ui-stable/struct.rs:147:8 | -147 | #[repr(packed, align(2))] - | ^^^^^^^^ +147 | #[repr(transparent, align(2))] + | ^^^^^^^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/struct.rs:151:18 +error: this conflicts with another representation hint + --> tests/ui-stable/struct.rs:153:16 | -151 | #[repr(align(1), align(2))] - | ^^^^^^^^ +153 | #[repr(packed, align(2))] + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/struct.rs:155:8 +error: this conflicts with another representation hint + --> tests/ui-stable/struct.rs:157:18 | -155 | #[repr(align(2), align(4))] - | ^^^^^^^^ +157 | #[repr(align(1), align(2))] + | ^^^^^ + +error: this conflicts with another representation hint + --> tests/ui-stable/struct.rs:161:18 + | +161 | #[repr(align(2), align(4))] + | ^^^^^ error[E0692]: transparent struct cannot have other repr hints - --> tests/ui-stable/struct.rs:141:8 + --> tests/ui-stable/struct.rs:147:8 | -141 | #[repr(transparent, align(2))] +147 | #[repr(transparent, align(2))] | ^^^^^^^^^^^ ^^^^^^^^ error[E0277]: the size for values of type `[u8]` cannot be known at compilation time @@ -277,7 +283,7 @@ note: required by a bound in `macro_util::__size_of::size_of` | ^^^^^ required by this bound in `size_of` error[E0587]: type has conflicting packed and align representation hints - --> tests/ui-stable/struct.rs:148:1 + --> tests/ui-stable/struct.rs:154:1 | -148 | struct Unaligned3; +154 | struct Unaligned3; | ^^^^^^^^^^^^^^^^^ diff --git a/zerocopy-derive/tests/ui-stable/union.stderr b/zerocopy-derive/tests/ui-stable/union.stderr index eb3f7a09d97..83467641c33 100644 --- a/zerocopy-derive/tests/ui-stable/union.stderr +++ b/zerocopy-derive/tests/ui-stable/union.stderr @@ -6,29 +6,29 @@ error: unsupported on types with type parameters | = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) -error: cannot derive Unaligned with repr(align(N > 1)) +error: cannot derive `Unaligned` on type with alignment greater than 1 --> tests/ui-stable/union.rs:51:11 | 51 | #[repr(C, align(2))] - | ^^^^^^^^ + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) +error: this conflicts with another representation hint --> tests/ui-stable/union.rs:67:16 | 67 | #[repr(packed, align(2))] - | ^^^^^^^^ + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) +error: this conflicts with another representation hint --> tests/ui-stable/union.rs:73:18 | 73 | #[repr(align(1), align(2))] - | ^^^^^^^^ + | ^^^^^ -error: cannot derive Unaligned with repr(align(N > 1)) - --> tests/ui-stable/union.rs:79:8 +error: this conflicts with another representation hint + --> tests/ui-stable/union.rs:79:18 | 79 | #[repr(align(2), align(4))] - | ^^^^^^^^ + | ^^^^^ error[E0277]: the trait bound `UnsafeCell<()>: zerocopy::Immutable` is not satisfied --> tests/ui-stable/union.rs:24:10