chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,190 @@
use proc_macro2::{Span, TokenStream};
use syn::{
parse_quote, Data, DataEnum, DataStruct, DataUnion, Error, Expr, ExprLit, ExprUnary, Lit, UnOp,
WherePredicate,
};
use crate::{
derive::try_from_bytes::derive_try_from_bytes,
repr::{CompoundRepr, EnumRepr, Repr, Spanned},
util::{enum_size_from_repr, Ctx, FieldBounds, ImplBlockBuilder, Trait, TraitBound},
};
/// Returns `Ok(index)` if variant `index` of the enum has a discriminant of
/// zero. If `Err(bool)` is returned, the boolean is true if the enum has
/// unknown discriminants (e.g. discriminants set to const expressions which we
/// can't evaluate in a proc macro). If the enum has unknown discriminants, then
/// it might have a zero variant that we just can't detect.
pub(crate) fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
// Discriminants can be anywhere in the range [i128::MIN, u128::MAX] because
// the discriminant type may be signed or unsigned. Since we only care about
// tracking the discriminant when it's less than or equal to zero, we can
// avoid u128 -> i128 conversions and bounds checking by making the "next
// discriminant" value implicitly negative.
// Technically 64 bits is enough, but 128 is better for future compatibility
// with https://github.com/rust-lang/rust/issues/56071
let mut next_negative_discriminant = Some(0);
// Sometimes we encounter explicit discriminants that we can't know the
// value of (e.g. a constant expression that requires evaluation). These
// could evaluate to zero or a negative number, but we can't assume that
// they do (no false positives allowed!). So we treat them like strictly-
// positive values that can't result in any zero variants, and track whether
// we've encountered any unknown discriminants.
let mut has_unknown_discriminants = false;
for (i, v) in enm.variants.iter().enumerate() {
match v.discriminant.as_ref() {
// Implicit discriminant
None => {
match next_negative_discriminant.as_mut() {
Some(0) => return Ok(i),
// n is nonzero so subtraction is always safe
Some(n) => *n -= 1,
None => (),
}
}
// Explicit positive discriminant
Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
match int.base10_parse::<u128>().ok() {
Some(0) => return Ok(i),
Some(_) => next_negative_discriminant = None,
None => {
// Numbers should never fail to parse, but just in case:
has_unknown_discriminants = true;
next_negative_discriminant = None;
}
}
}
// Explicit negative discriminant
Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
match int.base10_parse::<u128>().ok() {
Some(0) => return Ok(i),
// x is nonzero so subtraction is always safe
Some(x) => next_negative_discriminant = Some(x - 1),
None => {
// Numbers should never fail to parse, but just in
// case:
has_unknown_discriminants = true;
next_negative_discriminant = None;
}
}
}
// Unknown negative discriminant (e.g. const repr)
_ => {
has_unknown_discriminants = true;
next_negative_discriminant = None;
}
},
// Unknown discriminant (e.g. const expr)
_ => {
has_unknown_discriminants = true;
next_negative_discriminant = None;
}
}
}
Err(has_unknown_discriminants)
}
pub(crate) fn derive_from_zeros(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
let try_from_bytes = derive_try_from_bytes(ctx, top_level)?;
let from_zeros = match &ctx.ast.data {
Data::Struct(strct) => derive_from_zeros_struct(ctx, strct),
Data::Enum(enm) => derive_from_zeros_enum(ctx, enm)?,
Data::Union(unn) => derive_from_zeros_union(ctx, unn),
};
Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
}
pub(crate) fn derive_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
let from_zeros = derive_from_zeros(ctx, top_level)?;
let from_bytes = match &ctx.ast.data {
Data::Struct(strct) => derive_from_bytes_struct(ctx, strct),
Data::Enum(enm) => derive_from_bytes_enum(ctx, enm)?,
Data::Union(unn) => derive_from_bytes_union(ctx, unn),
};
Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
}
fn derive_from_zeros_struct(ctx: &Ctx, strct: &DataStruct) -> TokenStream {
ImplBlockBuilder::new(ctx, strct, Trait::FromZeros, FieldBounds::ALL_SELF).build()
}
fn derive_from_zeros_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
// We don't actually care what the repr is; we just care that it's one of
// the allowed ones.
match repr {
Repr::Compound(Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ }, _) => {
}
Repr::Transparent(_) | Repr::Compound(Spanned { t: CompoundRepr::Rust, span: _ }, _) => {
return ctx.error_or_skip(
Error::new(
Span::call_site(),
"must have #[repr(C)] or #[repr(Int)] 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 ctx.error_or_skip(Error::new_spanned(
&ctx.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.",
));
}
// Does not have unknown variants
Err(false) => {
return ctx.error_or_skip(Error::new_spanned(
&ctx.ast,
"FromZeros only supported on enums with a variant that has a discriminant of `0`",
));
}
};
let zerocopy_crate = &ctx.zerocopy_crate;
let explicit_bounds = zero_variant
.fields
.iter()
.map(|field| {
let ty = &field.ty;
parse_quote! { #ty: #zerocopy_crate::FromZeros }
})
.collect::<Vec<WherePredicate>>();
Ok(ImplBlockBuilder::new(ctx, enm, Trait::FromZeros, FieldBounds::Explicit(explicit_bounds))
.build())
}
fn derive_from_zeros_union(ctx: &Ctx, unn: &DataUnion) -> TokenStream {
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
ImplBlockBuilder::new(ctx, unn, Trait::FromZeros, field_type_trait_bounds).build()
}
fn derive_from_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> TokenStream {
ImplBlockBuilder::new(ctx, strct, Trait::FromBytes, FieldBounds::ALL_SELF).build()
}
fn derive_from_bytes_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
let variants_required = 1usize << enum_size_from_repr(&repr)?;
if enm.variants.len() != variants_required {
return ctx.error_or_skip(Error::new_spanned(
&ctx.ast,
format!(
"FromBytes only supported on {} enum with {} variants",
repr.repr_type_name(),
variants_required
),
));
}
Ok(ImplBlockBuilder::new(ctx, enm, Trait::FromBytes, FieldBounds::ALL_SELF).build())
}
fn derive_from_bytes_union(ctx: &Ctx, unn: &DataUnion) -> TokenStream {
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
ImplBlockBuilder::new(ctx, unn, Trait::FromBytes, field_type_trait_bounds).build()
}

View File

@@ -0,0 +1,162 @@
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type};
use crate::{
repr::{EnumRepr, StructUnionRepr},
util::{
generate_tag_enum, Ctx, DataExt, FieldBounds, ImplBlockBuilder, PaddingCheck, Trait,
TraitBound,
},
};
pub(crate) fn derive_into_bytes(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
match &ctx.ast.data {
Data::Struct(strct) => derive_into_bytes_struct(ctx, strct),
Data::Enum(enm) => derive_into_bytes_enum(ctx, enm),
Data::Union(unn) => derive_into_bytes_union(ctx, unn),
}
}
fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error> {
let repr = StructUnionRepr::from_attrs(&ctx.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_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
// of that field) and we require that field to be `IntoBytes` (meaning
// there's no padding in that field).
// - repr(packed): Any inter-field padding bytes are removed, meaning
// that any padding bytes would need to come from the fields, all of
// which we require to be `IntoBytes` (meaning they don't have any
// padding). Note that this holds regardless of other `repr`
// attributes, including `repr(Rust)`. [1]
//
// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-alignment-modifiers:
//
// An important consequence of these rules is that a type with
// `#[repr(packed(1))]`` (or `#[repr(packed)]``) will have no
// inter-field padding.
(None, false)
} else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
// No padding check needed. A repr(C) struct with zero or one field has
// no padding unless #[repr(align)] explicitly adds padding, which we
// check for in this branch's condition.
(None, false)
} else if ctx.ast.generics.params.is_empty() {
// Is the last field a syntactic slice, i.e., `[SomeType]`.
let is_syntactic_dst =
strct.fields().last().map(|(_, _, ty)| matches!(ty, Type::Slice(_))).unwrap_or(false);
// Since there are no generics, we can emit a padding check. All reprs
// guarantee that fields won't overlap [1], so the padding check is
// sound. This is more permissive than the next case, which requires
// that all field types implement `Unaligned`.
//
// [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-rust-representation:
//
// The only data layout guarantees made by [`repr(Rust)`] are those
// required for soundness. They are:
// ...
// 2. The fields do not overlap.
// ...
if is_c && is_syntactic_dst {
(Some(PaddingCheck::ReprCStruct), false)
} else {
(Some(PaddingCheck::Struct), false)
}
} else if is_c && !repr.is_align_gt_1() {
// 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
// padding unless #[repr(align)] explicitly adds padding, which we check
// for in this branch's condition.
//
// FIXME(#10): Support type parameters for non-transparent, non-packed
// structs without requiring `Unaligned`.
(None, true)
} else {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout",
));
};
let field_bounds = if require_unaligned_fields {
FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
} else {
FieldBounds::ALL_SELF
};
Ok(ImplBlockBuilder::new(ctx, strct, Trait::IntoBytes, field_bounds)
.padding_check(padding_check)
.build())
}
fn derive_into_bytes_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
if !repr.is_c() && !repr.is_primitive() {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
));
}
let tag_type_definition = generate_tag_enum(ctx, &repr, enm);
Ok(ImplBlockBuilder::new(ctx, enm, Trait::IntoBytes, FieldBounds::ALL_SELF)
.padding_check(PaddingCheck::Enum { tag_type_definition })
.build())
}
fn derive_into_bytes_union(ctx: &Ctx, unn: &DataUnion) -> Result<TokenStream, Error> {
// See #1792 for more context.
//
// By checking for `zerocopy_derive_union_into_bytes` both here and in the
// generated code, we ensure that `--cfg zerocopy_derive_union_into_bytes`
// need only be passed *either* when compiling this crate *or* when
// compiling the user's crate. The former is preferable, but in some
// situations (such as when cross-compiling using `cargo build --target`),
// it doesn't get propagated to this crate's build by default.
let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
quote!()
} else {
let core = ctx.core_path();
let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
quote!(
#[allow(unused_attributes, unexpected_cfgs)]
const _: () = {
#[cfg(not(zerocopy_derive_union_into_bytes))]
#core::compile_error!(#error_message);
};
)
};
// FIXME(#10): Support type parameters.
if !ctx.ast.generics.params.is_empty() {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"unsupported on types with type parameters",
));
}
// Because we don't support generics, we don't need to worry about
// special-casing different reprs. So long as there is *some* repr which
// guarantees the layout, our `PaddingCheck::Union` guarantees that there is
// no padding.
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
));
}
let impl_block = ImplBlockBuilder::new(ctx, unn, Trait::IntoBytes, FieldBounds::ALL_SELF)
.padding_check(PaddingCheck::Union)
.build();
Ok(quote!(#cfg_compile_error #impl_block))
}

View File

@@ -0,0 +1,348 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{parse_quote, Data, Error, Type};
use crate::{
repr::StructUnionRepr,
util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, SelfBounds, Trait},
};
fn derive_known_layout_for_repr_c_struct<'a>(
ctx: &'a Ctx,
repr: &StructUnionRepr,
fields: &[(&'a syn::Visibility, TokenStream, &'a Type)],
) -> Option<(SelfBounds<'a>, TokenStream, Option<TokenStream>)> {
let (trailing_field, leading_fields) = fields.split_last()?;
let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
let core = ctx.core_path();
let repr_align = repr
.get_align()
.map(|align| {
let align = align.t.get();
quote!(#core::num::NonZeroUsize::new(#align as usize))
})
.unwrap_or_else(|| quote!(#core::option::Option::None));
let repr_packed = repr
.get_packed()
.map(|packed| {
let packed = packed.get();
quote!(#core::num::NonZeroUsize::new(#packed as usize))
})
.unwrap_or_else(|| quote!(#core::option::Option::None));
let zerocopy_crate = &ctx.zerocopy_crate;
let make_methods = |trailing_field_ty| {
quote! {
// SAFETY:
// - The returned pointer has the same address and provenance as
// `bytes`:
// - The recursive call to `raw_from_ptr_len` preserves both
// address and provenance.
// - The `as` cast preserves both address and provenance.
// - `NonNull::new_unchecked` preserves both address and
// provenance.
// - If `Self` is a slice DST, the returned pointer encodes
// `elems` elements in the trailing slice:
// - This is true of the recursive call to `raw_from_ptr_len`.
// - `trailing.as_ptr() as *mut Self` preserves trailing slice
// element count [1].
// - `NonNull::new_unchecked` preserves trailing slice element
// count.
//
// [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
//
// `*const T`` / `*mut T` can be cast to `*const U` / `*mut U`
// with the following behavior:
// ...
// - If `T` and `U` are both unsized, the pointer is also
// returned unchanged. In particular, the metadata is
// preserved exactly.
//
// For instance, a cast from `*const [T]` to `*const [U]`
// preserves the number of elements. ... The same holds
// for str and any compound type whose unsized tail is a
// slice type, such as struct `Foo(i32, [u8])` or
// `(u64, Foo)`.
#[inline(always)]
fn raw_from_ptr_len(
bytes: #core::ptr::NonNull<u8>,
meta: <Self as #zerocopy_crate::KnownLayout>::PointerMetadata,
) -> #core::ptr::NonNull<Self> {
let trailing = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::raw_from_ptr_len(bytes, meta);
let slf = trailing.as_ptr() as *mut Self;
// SAFETY: Constructed from `trailing`, which is non-null.
unsafe { #core::ptr::NonNull::new_unchecked(slf) }
}
#[inline(always)]
fn pointer_to_metadata(ptr: *mut Self) -> <Self as #zerocopy_crate::KnownLayout>::PointerMetadata {
<#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
}
}
};
let inner_extras = {
let leading_fields_tys = leading_fields_tys.clone();
let methods = make_methods(*trailing_field_ty);
let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
quote!(
type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
// SAFETY: `LAYOUT` accurately describes the layout of `Self`.
// The documentation of `DstLayout::for_repr_c_struct` vows that
// invocations in this manner will accurately describe a type,
// so long as:
//
// - that type is `repr(C)`,
// - its fields are enumerated in the order they appear,
// - the presence of `repr_align` and `repr_packed` are
// correctly accounted for.
//
// We respect all three of these preconditions here. This
// expansion is only used if `is_repr_c_struct`, we enumerate
// the fields in order, and we extract the values of `align(N)`
// and `packed(N)`.
const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_repr_c_struct(
#repr_align,
#repr_packed,
&[
#(#zerocopy_crate::DstLayout::for_type::<#leading_fields_tys>(),)*
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::LAYOUT
],
);
#methods
)
};
let outer_extras = {
let ident = &ctx.ast.ident;
let vis = &ctx.ast.vis;
let params = &ctx.ast.generics.params;
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let predicates = if let Some(where_clause) = where_clause {
where_clause.predicates.clone()
} else {
Default::default()
};
// Generate a valid ident for a type-level handle to a field of a
// given `name`.
let field_index = |name: &TokenStream| ident!(("__Zerocopy_Field_{}", name), ident.span());
let field_indices: Vec<_> =
fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
// Define the collection of type-level field handles.
let field_defs = field_indices.iter().zip(fields).map(|(idx, (vis, _, _))| {
quote! {
#vis struct #idx;
}
});
let field_impls = field_indices.iter().zip(fields).map(|(idx, (_, _, ty))| quote! {
// SAFETY: `#ty` is the type of `#ident`'s field at `#idx`.
//
// We implement `Field` for each field of the struct to create a
// projection from the field index to its type. This allows us
// to refer to the field's type in a way that respects `Self`
// hygiene. If we just copy-pasted the tokens of `#ty`, we
// would not respect `Self` hygiene, as `Self` would refer to
// the helper struct we are generating, not the derive target
// type.
unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
where
#predicates
{
type Type = #ty;
}
});
let trailing_field_index = field_index(trailing_field_name);
let leading_field_indices =
leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
// We use `Field` to project the type of the trailing field. This is
// required to ensure that if the field type uses `Self`, it
// resolves to the derive target type, not the helper struct we are
// generating.
let trailing_field_ty = quote! {
<#ident #ty_generics as
#zerocopy_crate::util::macro_util::Field<#trailing_field_index>
>::Type
};
let methods = make_methods(&parse_quote! {
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
});
let core = ctx.core_path();
quote! {
#(#field_defs)*
#(#field_impls)*
// SAFETY: This has the same layout as the derive target type,
// except that it admits uninit bytes. This is ensured by using
// the same repr as the target type, and by using field types
// which have the same layout as the target type's fields,
// except that they admit uninit bytes. We indirect through
// `Field` to ensure that occurrences of `Self` resolve to
// `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116).
#repr
#[doc(hidden)]
#vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
#(#core::mem::MaybeUninit<
<#ident #ty_generics as
#zerocopy_crate::util::macro_util::Field<#leading_field_indices>
>::Type
>,)*
// NOTE(#2302): We wrap in `ManuallyDrop` here in case the
// type we're operating on is both generic and
// `repr(packed)`. In that case, Rust needs to know that the
// type is *either* `Sized` or has a trivial `Drop`.
// `ManuallyDrop` has a trivial `Drop`, and so satisfies
// this requirement.
#core::mem::ManuallyDrop<
<#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
>
)
where
#trailing_field_ty: #zerocopy_crate::KnownLayout,
#predicates;
// SAFETY: We largely defer to the `KnownLayout` implementation
// on the derive target type (both by using the same tokens, and
// by deferring to impl via type-level indirection). This is
// sound, since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed
// to have the same layout as the derive target type, except
// that `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes.
unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
where
#trailing_field_ty: #zerocopy_crate::KnownLayout,
#predicates
{
fn only_derive_is_allowed_to_implement_this_trait() {}
type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
type MaybeUninit = Self;
const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
#methods
}
}
};
Some((SelfBounds::None, inner_extras, Some(outer_extras)))
}
pub(crate) fn derive(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
// If this is a `repr(C)` struct, then `c_struct_repr` contains the entire
// `repr` attribute.
let c_struct_repr = match &ctx.ast.data {
Data::Struct(..) => {
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
if repr.is_c() {
Some(repr)
} else {
None
}
}
Data::Enum(..) | Data::Union(..) => None,
};
let fields = ctx.ast.data.fields();
let (self_bounds, inner_extras, outer_extras) = c_struct_repr
.as_ref()
.and_then(|repr| {
derive_known_layout_for_repr_c_struct(ctx, repr, &fields)
})
.unwrap_or_else(|| {
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
// For enums, unions, and non-`repr(C)` structs, we require that
// `Self` is sized, and as a result don't need to reason about the
// internals of the type.
(
SelfBounds::SIZED,
quote!(
type PointerMetadata = ();
type MaybeUninit =
#core::mem::MaybeUninit<Self>;
// SAFETY: `LAYOUT` is guaranteed to accurately describe the
// layout of `Self`, because that is the documented safety
// contract of `DstLayout::for_type`.
const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
// SAFETY: `.cast` preserves address and provenance.
//
// FIXME(#429): Add documentation to `.cast` that promises that
// it preserves provenance.
#[inline(always)]
fn raw_from_ptr_len(bytes: #core::ptr::NonNull<u8>, _meta: ()) -> #core::ptr::NonNull<Self> {
bytes.cast::<Self>()
}
#[inline(always)]
fn pointer_to_metadata(_ptr: *mut Self) -> () {}
),
None,
)
});
Ok(match &ctx.ast.data {
Data::Struct(strct) => {
let require_trait_bound_on_field_types =
if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) {
FieldBounds::None
} else {
FieldBounds::TRAILING_SELF
};
// A bound on the trailing field is required, since structs are
// unsized if their trailing field is unsized. Reflecting the layout
// of an usized trailing field requires that the field is
// `KnownLayout`.
ImplBlockBuilder::new(
ctx,
strct,
Trait::KnownLayout,
require_trait_bound_on_field_types,
)
.self_type_trait_bounds(self_bounds)
.inner_extras(inner_extras)
.outer_extras(outer_extras)
.build()
}
Data::Enum(enm) => {
// A bound on the trailing field is not required, since enums cannot
// currently be unsized.
ImplBlockBuilder::new(ctx, enm, Trait::KnownLayout, FieldBounds::None)
.self_type_trait_bounds(SelfBounds::SIZED)
.inner_extras(inner_extras)
.outer_extras(outer_extras)
.build()
}
Data::Union(unn) => {
// A bound on the trailing field is not required, since unions
// cannot currently be unsized.
ImplBlockBuilder::new(ctx, unn, Trait::KnownLayout, FieldBounds::None)
.self_type_trait_bounds(SelfBounds::SIZED)
.inner_extras(inner_extras)
.outer_extras(outer_extras)
.build()
}
})
}

130
vendor/zerocopy-derive/src/derive/mod.rs vendored Normal file
View File

@@ -0,0 +1,130 @@
pub mod from_bytes;
pub mod into_bytes;
pub mod known_layout;
pub mod try_from_bytes;
pub mod unaligned;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, Error};
use crate::{
repr::StructUnionRepr,
util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait},
};
pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream {
match &ctx.ast.data {
Data::Struct(strct) => {
ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build()
}
Data::Enum(enm) => {
ImplBlockBuilder::new(ctx, enm, Trait::Immutable, FieldBounds::ALL_SELF).build()
}
Data::Union(unn) => {
ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build()
}
}
}
pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
// This doesn't delegate to `impl_block` because `impl_block` assumes it is
// deriving a `zerocopy`-defined trait, and these trait impls share a common
// shape that `Hash` does not. In particular, `zerocopy` traits contain a
// method that only `zerocopy_derive` macros are supposed to implement, and
// `impl_block` generating this trait method is incompatible with `Hash`.
let type_ident = &ctx.ast.ident;
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let where_predicates = where_clause.map(|clause| &clause.predicates);
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
Ok(quote! {
impl #impl_generics #core::hash::Hash for #type_ident #ty_generics
where
Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
#where_predicates
{
fn hash<H: #core::hash::Hasher>(&self, state: &mut H) {
#core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(self))
}
fn hash_slice<H: #core::hash::Hasher>(data: &[Self], state: &mut H) {
#core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(data))
}
}
})
}
pub(crate) fn derive_eq(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
// This doesn't delegate to `impl_block` because `impl_block` assumes it is
// deriving a `zerocopy`-defined trait, and these trait impls share a common
// shape that `Eq` does not. In particular, `zerocopy` traits contain a
// method that only `zerocopy_derive` macros are supposed to implement, and
// `impl_block` generating this trait method is incompatible with `Eq`.
let type_ident = &ctx.ast.ident;
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let where_predicates = where_clause.map(|clause| &clause.predicates);
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
Ok(quote! {
impl #impl_generics #core::cmp::PartialEq for #type_ident #ty_generics
where
Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
#where_predicates
{
fn eq(&self, other: &Self) -> bool {
#core::cmp::PartialEq::eq(
#zerocopy_crate::IntoBytes::as_bytes(self),
#zerocopy_crate::IntoBytes::as_bytes(other),
)
}
}
impl #impl_generics #core::cmp::Eq for #type_ident #ty_generics
where
Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
#where_predicates
{
}
})
}
pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
match &ctx.ast.data {
Data::Struct(_) => {}
Data::Enum(_) | Data::Union(_) => {
return Err(Error::new(Span::call_site(), "can only be applied to structs"));
}
};
if repr.get_packed().is_some() {
return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
}
if !(repr.is_c() || repr.is_transparent()) {
return Err(Error::new(
Span::call_site(),
"must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable",
));
}
let fields = ctx.ast.data.fields();
let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
trailing_field
} else {
return Err(Error::new(Span::call_site(), "must at least one field"));
};
let zerocopy_crate = &ctx.zerocopy_crate;
// SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)`
// and is not packed; its trailing field is guaranteed to be well-aligned
// for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing
// slice of the trailing field is also well-aligned for its type.
Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF)
.inner_extras(quote! {
type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem;
})
.build())
}

View File

@@ -0,0 +1,763 @@
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error,
Expr, Fields, Ident, Index, Type,
};
use crate::{
repr::{EnumRepr, StructUnionRepr},
util::{
const_block, enum_size_from_repr, generate_tag_enum, Ctx, DataExt, FieldBounds,
ImplBlockBuilder, Trait, TraitBound,
},
};
fn tag_ident(variant_ident: &Ident) -> Ident {
ident!(("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span())
}
/// Generates a constant for the tag associated with each variant of the enum.
/// When we match on the enum's tag, each arm matches one of these constants. We
/// have to use constants here because:
///
/// - The type that we're matching on is not the type of the tag, it's an
/// integer of the same size as the tag type and with the same bit patterns.
/// - We can't read the enum tag as an enum because the bytes may not represent
/// a valid variant.
/// - Patterns do not currently support const expressions, so we have to assign
/// these constants to names rather than use them inline in the `match`
/// statement.
fn generate_tag_consts(data: &DataEnum) -> TokenStream {
let tags = data.variants.iter().map(|v| {
let variant_ident = &v.ident;
let tag_ident = tag_ident(variant_ident);
quote! {
// This casts the enum variant to its discriminant, and then
// converts the discriminant to the target integral type via a
// numeric cast [1].
//
// Because these are the same size, this is defined to be a no-op
// and therefore is a lossless conversion [2].
//
// [1] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#enum-cast:
//
// Casts an enum to its discriminant.
//
// [2] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#numeric-cast:
//
// Casting between two integers of the same size (e.g. i32 -> u32)
// is a no-op.
const #tag_ident: ___ZerocopyTagPrimitive =
___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive;
}
});
quote! {
#(#tags)*
}
}
fn variant_struct_ident(variant_ident: &Ident) -> Ident {
ident!(("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span())
}
/// Generates variant structs for the given enum variant.
///
/// These are structs associated with each variant of an enum. They are
/// `repr(C)` tuple structs with the same fields as the variant after a
/// `MaybeUninit<___ZerocopyInnerTag>`.
///
/// In order to unify the generated types for `repr(C)` and `repr(int)` enums,
/// we use a "fused" representation with fields for both an inner tag and an
/// outer tag. Depending on the repr, we will set one of these tags to the tag
/// type and the other to `()`. This lets us generate the same code but put the
/// tags in different locations.
fn generate_variant_structs(ctx: &Ctx, data: &DataEnum) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let enum_name = &ctx.ast.ident;
// All variant structs have a `PhantomData<MyEnum<...>>` field because we
// don't know which generic parameters each variant will use, and unused
// generic parameters are a compile error.
let core = ctx.core_path();
let phantom_ty = quote! {
#core::marker::PhantomData<#enum_name #ty_generics>
};
let variant_structs = data.variants.iter().filter_map(|variant| {
// We don't generate variant structs for unit variants because we only
// need to check the tag. This helps cut down our generated code a bit.
if matches!(variant.fields, Fields::Unit) {
return None;
}
let variant_struct_ident = variant_struct_ident(&variant.ident);
let field_types = variant.fields.iter().map(|f| &f.ty);
let variant_struct = parse_quote! {
#[repr(C)]
struct #variant_struct_ident #impl_generics (
#core::mem::MaybeUninit<___ZerocopyInnerTag>,
#(#field_types,)*
#phantom_ty,
) #where_clause;
};
// 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(&ctx.with_input(&variant_struct), Trait::TryFromBytes)
.expect("derive_try_from_bytes should not fail on synthesized type");
Some(quote! {
#variant_struct
#try_from_bytes_impl
})
});
quote! {
#(#variant_structs)*
}
}
fn variants_union_field_ident(ident: &Ident) -> Ident {
// Field names are prefixed with `__field_` to prevent name collision
// with the `__nonempty` field.
ident!(("__field_{}", ident), ident.span())
}
fn generate_variants_union(ctx: &Ctx, data: &DataEnum) -> TokenStream {
let generics = &ctx.ast.generics;
let (_, ty_generics, _) = generics.split_for_impl();
let fields = data.variants.iter().filter_map(|variant| {
// We don't generate variant structs for unit variants because we only
// need to check the tag. This helps cut down our generated code a bit.
if matches!(variant.fields, Fields::Unit) {
return None;
}
let field_name = variants_union_field_ident(&variant.ident);
let variant_struct_ident = variant_struct_ident(&variant.ident);
let core = ctx.core_path();
Some(quote! {
#field_name: #core::mem::ManuallyDrop<#variant_struct_ident #ty_generics>,
})
});
let variants_union = parse_quote! {
#[repr(C)]
union ___ZerocopyVariants #generics {
#(#fields)*
// Enums can have variants with no fields, but unions must
// have at least one field. So we just add a trailing unit
// to ensure that this union always has at least one field.
// Because this union is `repr(C)`, this unit type does not
// affect the layout.
__nonempty: (),
}
};
let has_field =
derive_has_field_struct_union(&ctx.with_input(&variants_union), &variants_union.data);
quote! {
#variants_union
#has_field
}
}
/// Generates an implementation of `is_bit_valid` for an arbitrary enum.
///
/// The general process is:
///
/// 1. Generate a tag enum. This is an enum with the same repr, variants, and
/// corresponding discriminants as the original enum, but without any fields
/// on the variants. This gives us access to an enum where the variants have
/// the same discriminants as the one we're writing `is_bit_valid` for.
/// 2. Make constants from the variants of the tag enum. We need these because
/// we can't put const exprs in match arms.
/// 3. Generate variant structs. These are structs which have the same fields as
/// each variant of the enum, and are `#[repr(C)]` with an optional "inner
/// tag".
/// 4. Generate a variants union, with one field for each variant struct type.
/// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and
/// the variants union.
///
/// See these reference links for fully-worked example decompositions.
///
/// - `repr(C)`: <https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields>
/// - `repr(int)`: <https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields>
/// - `repr(C, int)`: <https://doc.rust-lang.org/reference/type-layout.html#combining-primitive-representations-of-enums-with-fields-and-reprc>
pub(crate) fn derive_is_bit_valid(
ctx: &Ctx,
data: &DataEnum,
repr: &EnumRepr,
) -> Result<TokenStream, Error> {
let trait_path = Trait::TryFromBytes.crate_path(ctx);
let tag_enum = generate_tag_enum(ctx, repr, data);
let tag_consts = generate_tag_consts(data);
let (outer_tag_type, inner_tag_type) = if repr.is_c() {
(quote! { ___ZerocopyTag }, quote! { () })
} else if repr.is_primitive() {
(quote! { () }, quote! { ___ZerocopyTag })
} else {
return Err(Error::new(
ctx.ast.span(),
"must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout",
));
};
let variant_structs = generate_variant_structs(ctx, data);
let variants_union = generate_variants_union(ctx, data);
let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
let zerocopy_crate = &ctx.zerocopy_crate;
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None)
.inner_extras(quote! {
type Tag = ___ZerocopyTag;
type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized;
})
.build();
let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| {
let variant_ident = &variant.unwrap().ident;
let variants_union_field_ident = variants_union_field_ident(variant_ident);
let field: Box<syn::Type> = parse_quote!(());
fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| {
// Rust does not presently support explicit visibility modifiers on
// enum fields, but we guard against the possibility to ensure this
// derive remains sound.
assert!(matches!(vis, syn::Visibility::Inherited));
let variant_struct_field_index = Index::from(idx + 1);
let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
let has_field_trait = Trait::HasField {
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
// Since Rust does not presently support explicit visibility
// modifiers on enum fields, any public type is suitable here;
// we use `()`.
field: field.clone(),
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
};
let has_field_path = has_field_trait.crate_path(ctx);
let has_field = ImplBlockBuilder::new(
ctx,
data,
has_field_trait,
FieldBounds::None,
)
.inner_extras(quote! {
type Type = #ty;
#[inline(always)]
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
use #zerocopy_crate::pointer::cast::{CastSized, Projection};
slf.project::<___ZerocopyRawEnum #ty_generics, CastSized>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>()
.project::<_, Projection<_, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>()
.project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>()
.as_ptr()
}
})
.build();
let project = ImplBlockBuilder::new(
ctx,
data,
Trait::ProjectField {
variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }),
// Since Rust does not presently support explicit visibility
// modifiers on enum fields, any public type is suitable
// here; we use `()`.
field: field.clone(),
field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }),
invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)),
},
FieldBounds::None,
)
.param_extras(vec![
parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing),
parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment),
])
.inner_extras(quote! {
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
type Invariants = (Aliasing, Alignment, #zerocopy_crate::invariant::Initialized);
})
.build();
quote! {
#has_field
#project
}
})
});
let core = ctx.core_path();
let match_arms = data.variants.iter().map(|variant| {
let tag_ident = tag_ident(&variant.ident);
let variant_struct_ident = variant_struct_ident(&variant.ident);
let variants_union_field_ident = variants_union_field_ident(&variant.ident);
if matches!(variant.fields, Fields::Unit) {
// Unit variants don't need any further validation beyond checking
// the tag.
quote! {
#tag_ident => true
}
} else {
quote! {
#tag_ident => {
// SAFETY: Since we know that the tag is `#tag_ident`, we
// know that no other `&`s exist which refer to this enum
// as any other variant.
let variant_md = variants.cast::<
_,
#zerocopy_crate::pointer::cast::Projection<
// #zerocopy_crate::ReadOnly<_>,
_,
{ #zerocopy_crate::REPR_C_UNION_VARIANT_ID },
{ #zerocopy_crate::ident_id!(#variants_union_field_ident) }
>,
_
>();
let variant = variant_md.cast::<
#zerocopy_crate::ReadOnly<#variant_struct_ident #ty_generics>,
#zerocopy_crate::pointer::cast::CastSized,
(#zerocopy_crate::pointer::BecauseRead, _)
>();
<
#variant_struct_ident #ty_generics as #trait_path
>::is_bit_valid(variant)
}
}
}
});
let generics = &ctx.ast.generics;
let raw_enum: DeriveInput = parse_quote! {
#[repr(C)]
struct ___ZerocopyRawEnum #generics {
tag: ___ZerocopyOuterTag,
variants: ___ZerocopyVariants #ty_generics,
}
};
let self_ident = &ctx.ast.ident;
let invariants_eq_impl = quote! {
// SAFETY: `___ZerocopyRawEnum` is designed to have the same layout,
// validity, and invariants as `Self`.
unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {}
};
let raw_enum_projections =
derive_has_field_struct_union(&ctx.with_input(&raw_enum), &raw_enum.data);
let raw_enum = quote! {
#raw_enum
#invariants_eq_impl
#raw_enum_projections
};
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.
// Thus, this is a sound implementation of `is_bit_valid`.
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
{
#tag_enum
type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag<
{ #core::mem::size_of::<___ZerocopyTag>() },
>;
#tag_consts
type ___ZerocopyOuterTag = #outer_tag_type;
type ___ZerocopyInnerTag = #inner_tag_type;
#variant_structs
#variants_union
#raw_enum
#has_tag
#(#has_fields)*
let tag = {
// SAFETY:
// - The provided cast addresses a subset of the bytes addressed
// by `candidate` because it addresses the starting tag of the
// enum.
// - Because the pointer is cast from `candidate`, it has the
// same provenance as it.
// - There are no `UnsafeCell`s in the tag because it is a
// primitive integer.
// - `tag_ptr` is casted from `candidate`, whose referent is
// `Initialized`. Since we have not written uninitialized
// bytes into the referent, `tag_ptr` is also `Initialized`.
//
// FIXME(#2874): Revise this to a `cast` once `candidate`
// references a `ReadOnly<Self>`.
let tag_ptr = unsafe {
candidate.reborrow().project_transmute_unchecked::<
_,
#zerocopy_crate::invariant::Initialized,
#zerocopy_crate::pointer::cast::CastSized
>()
};
tag_ptr.recall_validity::<_, (_, (_, _))>().read::<#zerocopy_crate::BecauseImmutable>()
};
let mut raw_enum = candidate.cast::<
#zerocopy_crate::ReadOnly<___ZerocopyRawEnum #ty_generics>,
#zerocopy_crate::pointer::cast::CastSized,
(#zerocopy_crate::pointer::BecauseRead, _)
>();
let variants = #zerocopy_crate::into_inner!(raw_enum.project::<
_,
{ #zerocopy_crate::STRUCT_VARIANT_ID },
{ #zerocopy_crate::ident_id!(variants) }
>());
match tag {
#(#match_arms,)*
_ => false,
}
}
})
}
pub(crate) fn derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> {
match &ctx.ast.data {
Data::Struct(strct) => derive_try_from_bytes_struct(ctx, strct, top_level),
Data::Enum(enm) => derive_try_from_bytes_enum(ctx, enm, top_level),
Data::Union(unn) => Ok(derive_try_from_bytes_union(ctx, unn, top_level)),
}
}
fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream {
let fields = ctx.ast.data.fields();
if fields.is_empty() {
return quote! {};
}
let field_tokens = fields.iter().map(|(vis, ident, _)| {
let ident = ident!(("ẕ{}", ident), ident.span());
quote!(
#vis enum #ident {}
)
});
let zerocopy_crate = &ctx.zerocopy_crate;
let variant_id: Box<Expr> = match &ctx.ast.data {
Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }),
Data::Union(_) => {
let is_repr_c = StructUnionRepr::from_attrs(&ctx.ast.attrs)
.map(|repr| repr.is_c())
.unwrap_or(false);
if is_repr_c {
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
} else {
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
}
}
_ => unreachable!(),
};
let core = ctx.core_path();
let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None)
.inner_extras(quote! {
type Tag = ();
type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit;
})
.build();
let has_fields = fields.iter().map(move |(_, ident, ty)| {
let field_token = ident!(("ẕ{}", ident), ident.span());
let field: Box<Type> = parse_quote!(#field_token);
let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) });
let has_field_trait = Trait::HasField {
variant_id: variant_id.clone(),
field: field.clone(),
field_id: field_id.clone(),
};
let has_field_path = has_field_trait.crate_path(ctx);
ImplBlockBuilder::new(
ctx,
data,
has_field_trait,
FieldBounds::None,
)
.inner_extras(quote! {
type Type = #ty;
#[inline(always)]
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
let slf = slf.as_ptr();
// SAFETY: By invariant on `PtrInner`, `slf` is a non-null
// pointer whose referent is zero-sized or lives in a valid
// allocation. Since `#ident` is a struct or union field of
// `Self`, this projection preserves or shrinks the referent
// size, and so the resulting referent also fits in the same
// allocation.
unsafe { #core::ptr::addr_of_mut!((*slf).#ident) }
}
}).outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) {
let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs)
.map(|repr| repr.get_packed().is_none())
.unwrap();
let alignment = if fields_preserve_alignment {
quote! { Alignment }
} else {
quote! { #zerocopy_crate::invariant::Unaligned }
};
// SAFETY: See comments on items.
ImplBlockBuilder::new(
ctx,
data,
Trait::ProjectField {
variant_id: variant_id.clone(),
field: field.clone(),
field_id: field_id.clone(),
invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)),
},
FieldBounds::None,
)
.param_extras(vec![
parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing),
parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment),
])
.inner_extras(quote! {
// SAFETY: Projection into structs is always infallible.
type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible;
// SAFETY: The alignment of the projected `Ptr` is `Unaligned`
// if the structure is packed; otherwise inherited from the
// outer `Ptr`. If the validity of the outer pointer is
// `Initialized`, so too is the validity of its fields.
type Invariants = (Aliasing, #alignment, #zerocopy_crate::invariant::Initialized);
})
.build()
} else {
quote! {}
})
.build()
});
const_block(field_tokens.into_iter().chain(Some(has_tag)).chain(has_fields).map(Some))
}
fn derive_try_from_bytes_struct(
ctx: &Ctx,
strct: &DataStruct,
top_level: Trait,
) -> Result<TokenStream, Error> {
let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| {
let zerocopy_crate = &ctx.zerocopy_crate;
let fields = strct.fields();
let field_names = fields.iter().map(|(_vis, name, _ty)| name);
let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
let core = ctx.core_path();
quote!(
// SAFETY: We use `is_bit_valid` to validate that each field is
// bit-valid, and only return `true` if all of them are. The bit
// validity of a struct is just the composition of the bit
// validities of its fields, so this is a sound implementation
// of `is_bit_valid`.
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
{
true #(&& {
let field_candidate = #zerocopy_crate::into_inner!(candidate.reborrow().project::<
_,
{ #zerocopy_crate::STRUCT_VARIANT_ID },
{ #zerocopy_crate::ident_id!(#field_names) }
>());
<#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
})*
}
)
});
Ok(ImplBlockBuilder::new(ctx, strct, Trait::TryFromBytes, FieldBounds::ALL_SELF)
.inner_extras(extras)
.outer_extras(derive_has_field_struct_union(ctx, strct))
.build())
}
fn derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) -> TokenStream {
let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]);
let zerocopy_crate = &ctx.zerocopy_crate;
let variant_id: Box<Expr> = {
let is_repr_c =
StructUnionRepr::from_attrs(&ctx.ast.attrs).map(|repr| repr.is_c()).unwrap_or(false);
if is_repr_c {
parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID })
} else {
parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID })
}
};
let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| {
let fields = unn.fields();
let field_names = fields.iter().map(|(_vis, name, _ty)| name);
let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
let core = ctx.core_path();
quote!(
// SAFETY: We use `is_bit_valid` to validate that any field is
// bit-valid; we only return `true` if at least one of them is.
// The bit validity of a union is not yet well defined in Rust,
// but it is guaranteed to be no more strict than this
// definition. See #696 for a more in-depth discussion.
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
{
false #(|| {
// SAFETY:
// - Since `ReadOnly<Self>: Immutable` unconditionally,
// neither `*slf` nor the returned pointer's referent
// permit interior mutation.
// - Both source and destination validity are
// `Initialized`, which is always a sound
// transmutation.
let field_candidate = unsafe {
candidate.reborrow().project_transmute_unchecked::<
_,
_,
#zerocopy_crate::pointer::cast::Projection<
_,
#variant_id,
{ #zerocopy_crate::ident_id!(#field_names) }
>
>()
};
<#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
})*
}
)
});
ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds)
.inner_extras(extras)
.outer_extras(derive_has_field_struct_union(ctx, unn))
.build()
}
fn derive_try_from_bytes_enum(
ctx: &Ctx,
enm: &DataEnum,
top_level: Trait,
) -> Result<TokenStream, Error> {
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
// If an enum has no fields, it has a well-defined integer representation,
// and every possible bit pattern corresponds to a valid discriminant tag,
// then it *could* be `FromBytes` (even if the user hasn't derived
// `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N
// variants.
let could_be_from_bytes = enum_size_from_repr(&repr)
.map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
.unwrap_or(false);
let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ctx, top_level);
let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
(Some(is_bit_valid), _) => is_bit_valid,
// SAFETY: It would be sound for the enum to implement `FromBytes`, as
// required by `gen_trivial_is_bit_valid_unchecked`.
(None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(ctx) },
(None, false) => match derive_is_bit_valid(ctx, enm, &repr) {
Ok(extra) => extra,
Err(_) if ctx.skip_on_error => return Ok(TokenStream::new()),
Err(e) => return Err(e),
},
};
Ok(ImplBlockBuilder::new(ctx, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF)
.inner_extras(extra)
.build())
}
fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> {
// If the top-level trait is `FromBytes` and `Self` has no type parameters,
// then the `FromBytes` derive will fail compilation if `Self` is not
// actually soundly `FromBytes`, and so we can rely on that for our
// `is_bit_valid` impl. It's plausible that we could make changes - or Rust
// could make changes (such as the "trivial bounds" language feature) - that
// make this no longer true. To hedge against these, we include an explicit
// `Self: FromBytes` check in the generated `is_bit_valid`, which is
// bulletproof.
//
// If `ctx.skip_on_error` is true, we can't rely on the `FromBytes` derive
// to fail compilation if `Self` is not actually soundly `FromBytes`.
if matches!(top_level, Trait::FromBytes)
&& ctx.ast.generics.params.is_empty()
&& !ctx.skip_on_error
{
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
Some(quote!(
// SAFETY: See inline.
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
_candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
{
if false {
fn assert_is_from_bytes<T>()
where
T: #zerocopy_crate::FromBytes,
T: ?#core::marker::Sized,
{
}
assert_is_from_bytes::<Self>();
}
// SAFETY: The preceding code only compiles if `Self:
// FromBytes`. Thus, this code only compiles if all initialized
// byte sequences represent valid instances of `Self`.
true
}
))
} else {
None
}
}
/// # Safety
///
/// All initialized bit patterns must be valid for `Self`.
unsafe fn gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream {
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
quote!(
// SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has
// promised that all initialized bit patterns are valid for `Self`.
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
_candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>,
) -> #core::primitive::bool
where
___ZcAlignment: #zerocopy_crate::invariant::Alignment,
{
true
}
)
}

View File

@@ -0,0 +1,78 @@
use proc_macro2::{Span, TokenStream};
use syn::{Data, DataEnum, DataStruct, DataUnion, Error};
use crate::{
repr::{EnumRepr, StructUnionRepr},
util::{Ctx, FieldBounds, ImplBlockBuilder, Trait},
};
pub(crate) fn derive_unaligned(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
match &ctx.ast.data {
Data::Struct(strct) => derive_unaligned_struct(ctx, strct),
Data::Enum(enm) => derive_unaligned_enum(ctx, enm),
Data::Union(unn) => derive_unaligned_union(ctx, unn),
}
}
/// A struct 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_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error> {
let repr = StructUnionRepr::from_attrs(&ctx.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 {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment",
));
};
Ok(ImplBlockBuilder::new(ctx, strct, Trait::Unaligned, field_bounds).build())
}
/// An enum is `Unaligned` if:
/// - No `repr(align(N > 1))`
/// - `repr(u8)` or `repr(i8)`
fn derive_unaligned_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
repr.unaligned_validate_no_align_gt_1()?;
if !repr.is_u8() && !repr.is_i8() {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment",
));
}
Ok(ImplBlockBuilder::new(ctx, enm, Trait::Unaligned, FieldBounds::ALL_SELF).build())
}
/// 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(ctx: &Ctx, unn: &DataUnion) -> Result<TokenStream, Error> {
let repr = StructUnionRepr::from_attrs(&ctx.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 {
return ctx.error_or_skip(Error::new(
Span::call_site(),
"must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment",
));
};
Ok(ImplBlockBuilder::new(ctx, unn, Trait::Unaligned, field_type_trait_bounds).build())
}

144
vendor/zerocopy-derive/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,144 @@
// Copyright 2019 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
//! Derive macros for [zerocopy]'s traits.
//!
//! [zerocopy]: https://docs.rs/zerocopy
// Sometimes we want to use lints which were added after our MSRV.
// `unknown_lints` is `warn` by default and we deny warnings in CI, so without
// this attribute, any unknown lint would cause a CI failure when testing with
// our MSRV.
#![allow(unknown_lints)]
#![deny(renamed_and_removed_lints)]
#![deny(
clippy::all,
clippy::missing_safety_doc,
clippy::multiple_unsafe_ops_per_block,
clippy::undocumented_unsafe_blocks
)]
// We defer to own discretion on type complexity.
#![allow(clippy::type_complexity)]
// Inlining format args isn't supported on our MSRV.
#![allow(clippy::uninlined_format_args)]
#![deny(
rustdoc::bare_urls,
rustdoc::broken_intra_doc_links,
rustdoc::invalid_codeblock_attributes,
rustdoc::invalid_html_tags,
rustdoc::invalid_rust_codeblocks,
rustdoc::missing_crate_level_docs,
rustdoc::private_intra_doc_links
)]
#![recursion_limit = "128"]
macro_rules! ident {
(($fmt:literal $(, $arg:expr)*), $span:expr) => {
syn::Ident::new(&format!($fmt $(, crate::util::to_ident_str($arg))*), $span)
};
}
mod derive;
#[cfg(test)]
mod output_tests;
mod repr;
mod util;
use syn::{DeriveInput, Error};
use crate::util::*;
// FIXME(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:
//
// error: unsupported representation
// --> enum.rs:28:8
// |
// 28 | #[repr(transparent)]
// |
// help: required by the derive of FromBytes
//
// Instead, we have more verbose error messages like "unsupported representation
// for deriving FromZeros, FromBytes, IntoBytes, or Unaligned on an enum"
//
// This will probably require Span::error
// (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error),
// which is currently unstable. Revisit this once it's stable.
/// Defines a derive function named `$outer` which parses its input
/// `TokenStream` as a `DeriveInput` and then invokes the `$inner` function.
///
/// Note that the separate `$outer` parameter is required - proc macro functions
/// are currently required to live at the crate root, and so the caller must
/// specify the name in order to avoid name collisions.
macro_rules! derive {
($trait:ident => $outer:ident => $inner:path) => {
#[proc_macro_derive($trait, attributes(zerocopy))]
pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = syn::parse_macro_input!(ts as DeriveInput);
let ctx = match Ctx::try_from_derive_input(ast) {
Ok(ctx) => ctx,
Err(e) => return e.into_compile_error().into(),
};
let ts = $inner(&ctx, Trait::$trait).into_ts();
// We wrap in `const_block` as a backstop in case any derive fails
// to wrap its output in `const_block` (and thus fails to annotate)
// with the full set of `#[allow(...)]` attributes).
let ts = const_block([Some(ts)]);
#[cfg(test)]
crate::util::testutil::check_hygiene(ts.clone());
ts.into()
}
};
}
trait IntoTokenStream {
fn into_ts(self) -> proc_macro2::TokenStream;
}
impl IntoTokenStream for proc_macro2::TokenStream {
fn into_ts(self) -> proc_macro2::TokenStream {
self
}
}
impl IntoTokenStream for Result<proc_macro2::TokenStream, Error> {
fn into_ts(self) -> proc_macro2::TokenStream {
match self {
Ok(ts) => ts,
Err(err) => err.to_compile_error(),
}
}
}
derive!(KnownLayout => derive_known_layout => crate::derive::known_layout::derive);
derive!(Immutable => derive_immutable => crate::derive::derive_immutable);
derive!(TryFromBytes => derive_try_from_bytes => crate::derive::try_from_bytes::derive_try_from_bytes);
derive!(FromZeros => derive_from_zeros => crate::derive::from_bytes::derive_from_zeros);
derive!(FromBytes => derive_from_bytes => crate::derive::from_bytes::derive_from_bytes);
derive!(IntoBytes => derive_into_bytes => crate::derive::into_bytes::derive_into_bytes);
derive!(Unaligned => derive_unaligned => crate::derive::unaligned::derive_unaligned);
derive!(ByteHash => derive_hash => crate::derive::derive_hash);
derive!(ByteEq => derive_eq => crate::derive::derive_eq);
derive!(SplitAt => derive_split_at => crate::derive::derive_split_at);
/// Deprecated: prefer [`FromZeros`] instead.
#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
#[doc(hidden)]
#[proc_macro_derive(FromZeroes)]
pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
derive_from_zeros(ts)
}
/// Deprecated: prefer [`IntoBytes`] instead.
#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
#[doc(hidden)]
#[proc_macro_derive(AsBytes)]
pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
derive_into_bytes(ts)
}

View File

@@ -0,0 +1,17 @@
impl<T: Clone> ::zerocopy::util::macro_util::core_reexport::cmp::PartialEq for Foo<T>
where
Self: ::zerocopy::IntoBytes + ::zerocopy::Immutable,
Self: Sized,
{
fn eq(&self, other: &Self) -> bool {
::zerocopy::util::macro_util::core_reexport::cmp::PartialEq::eq(
::zerocopy::IntoBytes::as_bytes(self),
::zerocopy::IntoBytes::as_bytes(other),
)
}
}
impl<T: Clone> ::zerocopy::util::macro_util::core_reexport::cmp::Eq for Foo<T>
where
Self: ::zerocopy::IntoBytes + ::zerocopy::Immutable,
Self: Sized,
{}

View File

@@ -0,0 +1,68 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::TryFromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
_candidate: ::zerocopy::Maybe<'_, Self, ___ZcAlignment>,
) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
where
___ZcAlignment: ::zerocopy::invariant::Alignment,
{
if false {
fn assert_is_from_bytes<T>()
where
T: ::zerocopy::FromBytes,
T: ?::zerocopy::util::macro_util::core_reexport::marker::Sized,
{}
assert_is_from_bytes::<Self>();
}
true
}
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromZeros for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,68 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::TryFromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
_candidate: ::zerocopy::Maybe<'_, Self, ___ZcAlignment>,
) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
where
___ZcAlignment: ::zerocopy::invariant::Alignment,
{
if false {
fn assert_is_from_bytes<T>()
where
T: ::zerocopy::FromBytes,
T: ?::zerocopy::util::macro_util::core_reexport::marker::Sized,
{}
assert_is_from_bytes::<Self>();
}
true
}
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromZeros for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,148 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::TryFromBytes for Foo
where
u8: ::zerocopy::TryFromBytes,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
_candidate: ::zerocopy::Maybe<'_, Self, ___ZcAlignment>,
) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
where
___ZcAlignment: ::zerocopy::invariant::Alignment,
{
if false {
fn assert_is_from_bytes<T>()
where
T: ::zerocopy::FromBytes,
T: ?::zerocopy::util::macro_util::core_reexport::marker::Sized,
{}
assert_is_from_bytes::<Self>();
}
true
}
}
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
enum a {}
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::HasTag for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
type Tag = ();
type ProjectToTag = ::zerocopy::pointer::cast::CastToUnit;
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::HasField<
a,
{ ::zerocopy::UNION_VARIANT_ID },
{ ::zerocopy::ident_id!(a) },
> for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
type Type = u8;
#[inline(always)]
fn project(
slf: ::zerocopy::pointer::PtrInner<'_, Self>,
) -> *mut <Self as ::zerocopy::HasField<
a,
{ ::zerocopy::UNION_VARIANT_ID },
{ ::zerocopy::ident_id!(a) },
>>::Type {
let slf = slf.as_ptr();
unsafe {
::zerocopy::util::macro_util::core_reexport::ptr::addr_of_mut!(
(* slf).a
)
}
}
}
};
};
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromZeros for Foo
where
u8: ::zerocopy::FromZeros,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromBytes for Foo
where
u8: ::zerocopy::FromBytes,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,43 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::TryFromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: ::zerocopy::Maybe<'_, Self, ___ZcAlignment>,
) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
where
___ZcAlignment: ::zerocopy::invariant::Alignment,
{
true
}
}
};
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::FromZeros for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,24 @@
impl<T: Clone> ::zerocopy::util::macro_util::core_reexport::hash::Hash for Foo<T>
where
Self: ::zerocopy::IntoBytes + ::zerocopy::Immutable,
Self: Sized,
{
fn hash<H: ::zerocopy::util::macro_util::core_reexport::hash::Hasher>(
&self,
state: &mut H,
) {
::zerocopy::util::macro_util::core_reexport::hash::Hasher::write(
state,
::zerocopy::IntoBytes::as_bytes(self),
)
}
fn hash_slice<H: ::zerocopy::util::macro_util::core_reexport::hash::Hasher>(
data: &[Self],
state: &mut H,
) {
::zerocopy::util::macro_util::core_reexport::hash::Hasher::write(
state,
::zerocopy::IntoBytes::as_bytes(data),
)
}
}

View File

@@ -0,0 +1,17 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::Immutable for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(C)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(i128)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(i16)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(i32)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(i64)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(i8)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(isize)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(u128)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(u16)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(u32)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(u64)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(u8)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,40 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
#[repr(usize)]
#[allow(dead_code)]
pub enum ___ZerocopyTag {
Bar,
}
unsafe impl ::zerocopy::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
::zerocopy::enum_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
___ZerocopyTag, []
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,34 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
u8: ::zerocopy::IntoBytes,
u8: ::zerocopy::IntoBytes,
(): ::zerocopy::util::macro_util::PaddingFree<
Self,
{
::zerocopy::struct_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
[(u8), (u8)]
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,17 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,34 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::IntoBytes for Foo
where
u8: ::zerocopy::IntoBytes,
[Trailing]: ::zerocopy::IntoBytes,
(): ::zerocopy::util::macro_util::DynamicPaddingFree<
Self,
{
::zerocopy::repr_c_struct_has_padding!(
Self,
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
(::zerocopy::util::macro_util::core_reexport::option::Option::None::
< ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize >),
[(u8), ([Trailing])]
)
},
>,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,21 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl<Trailing> ::zerocopy::IntoBytes for Foo<Trailing>
where
u8: ::zerocopy::IntoBytes + ::zerocopy::Unaligned,
[Trailing]: ::zerocopy::IntoBytes + ::zerocopy::Unaligned,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,135 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl<T, U> ::zerocopy::KnownLayout for Foo<T, U>
where
U: ::zerocopy::KnownLayout,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
type PointerMetadata = <U as ::zerocopy::KnownLayout>::PointerMetadata;
type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit<T, U>;
const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_repr_c_struct(
::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize::new(
2u32 as usize,
),
::zerocopy::util::macro_util::core_reexport::option::Option::None,
&[
::zerocopy::DstLayout::for_type::<T>(),
<U as ::zerocopy::KnownLayout>::LAYOUT,
],
);
#[inline(always)]
fn raw_from_ptr_len(
bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<u8>,
meta: <Self as ::zerocopy::KnownLayout>::PointerMetadata,
) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<Self> {
let trailing = <U as ::zerocopy::KnownLayout>::raw_from_ptr_len(bytes, meta);
let slf = trailing.as_ptr() as *mut Self;
unsafe {
::zerocopy::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(
slf,
)
}
}
#[inline(always)]
fn pointer_to_metadata(
ptr: *mut Self,
) -> <Self as ::zerocopy::KnownLayout>::PointerMetadata {
<U>::pointer_to_metadata(ptr as *mut _)
}
}
struct __Zerocopy_Field_0;
struct __Zerocopy_Field_1;
unsafe impl<T, U> ::zerocopy::util::macro_util::Field<__Zerocopy_Field_0>
for Foo<T, U> {
type Type = T;
}
unsafe impl<T, U> ::zerocopy::util::macro_util::Field<__Zerocopy_Field_1>
for Foo<T, U> {
type Type = U;
}
#[repr(C)]
#[repr(align(2))]
#[doc(hidden)]
struct __ZerocopyKnownLayoutMaybeUninit<T, U>(
::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit<
<Foo<T, U> as ::zerocopy::util::macro_util::Field<__Zerocopy_Field_0>>::Type,
>,
::zerocopy::util::macro_util::core_reexport::mem::ManuallyDrop<
<<Foo<
T,
U,
> as ::zerocopy::util::macro_util::Field<
__Zerocopy_Field_1,
>>::Type as ::zerocopy::KnownLayout>::MaybeUninit,
>,
)
where
<Foo<
T,
U,
> as ::zerocopy::util::macro_util::Field<
__Zerocopy_Field_1,
>>::Type: ::zerocopy::KnownLayout;
unsafe impl<T, U> ::zerocopy::KnownLayout for __ZerocopyKnownLayoutMaybeUninit<T, U>
where
<Foo<
T,
U,
> as ::zerocopy::util::macro_util::Field<
__Zerocopy_Field_1,
>>::Type: ::zerocopy::KnownLayout,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
type PointerMetadata = <Foo<T, U> as ::zerocopy::KnownLayout>::PointerMetadata;
type MaybeUninit = Self;
const LAYOUT: ::zerocopy::DstLayout = <Foo<
T,
U,
> as ::zerocopy::KnownLayout>::LAYOUT;
#[inline(always)]
fn raw_from_ptr_len(
bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<u8>,
meta: <Self as ::zerocopy::KnownLayout>::PointerMetadata,
) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<Self> {
let trailing = <<<Foo<
T,
U,
> as ::zerocopy::util::macro_util::Field<
__Zerocopy_Field_1,
>>::Type as ::zerocopy::KnownLayout>::MaybeUninit as ::zerocopy::KnownLayout>::raw_from_ptr_len(
bytes,
meta,
);
let slf = trailing.as_ptr() as *mut Self;
unsafe {
::zerocopy::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(
slf,
)
}
}
#[inline(always)]
fn pointer_to_metadata(
ptr: *mut Self,
) -> <Self as ::zerocopy::KnownLayout>::PointerMetadata {
<<<Foo<
T,
U,
> as ::zerocopy::util::macro_util::Field<
__Zerocopy_Field_1,
>>::Type as ::zerocopy::KnownLayout>::MaybeUninit>::pointer_to_metadata(
ptr as *mut _,
)
}
}
};

View File

@@ -0,0 +1,34 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::KnownLayout for Foo
where
Self: ::zerocopy::util::macro_util::core_reexport::marker::Sized,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
type PointerMetadata = ();
type MaybeUninit = ::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit<
Self,
>;
const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_type::<Self>();
#[inline(always)]
fn raw_from_ptr_len(
bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<u8>,
_meta: (),
) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<Self> {
bytes.cast::<Self>()
}
#[inline(always)]
fn pointer_to_metadata(_ptr: *mut Self) -> () {}
}
};

View File

@@ -0,0 +1,22 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl<T: ?Sized + Copy> ::zerocopy::SplitAt for Foo<T>
where
Self: Copy,
T: ::zerocopy::SplitAt,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
type Elem = <T as ::zerocopy::SplitAt>::Elem;
}
};

View File

@@ -0,0 +1,22 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl<T: ?Sized + Copy> ::zerocopy::SplitAt for Foo<T>
where
Self: Copy,
T: ::zerocopy::SplitAt,
{
fn only_derive_is_allowed_to_implement_this_trait() {}
type Elem = <T as ::zerocopy::SplitAt>::Elem;
}
};

View File

@@ -0,0 +1,26 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::TryFromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
#[inline]
fn is_bit_valid<___ZcAlignment>(
mut candidate: ::zerocopy::Maybe<'_, Self, ___ZcAlignment>,
) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
where
___ZcAlignment: ::zerocopy::invariant::Alignment,
{
true
}
}
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,26 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::TryFromBytes for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
#[inline(always)]
fn is_bit_valid<___ZcAlignment>(
_candidate: ::zerocopy::Maybe<'_, Self, ___ZcAlignment>,
) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
where
___ZcAlignment: ::zerocopy::invariant::Alignment,
{
true
}
}
};

View File

@@ -0,0 +1,17 @@
#[allow(
deprecated,
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
#[automatically_derived]
const _: () = {
unsafe impl ::zerocopy::Unaligned for Foo {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
};

View File

@@ -0,0 +1,960 @@
// Copyright 2024 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use std::path::Path;
use dissimilar::Chunk;
use proc_macro2::TokenStream;
use crate::IntoTokenStream;
macro_rules! use_as_trait_name {
($($alias:ident => $derive:path),* $(,)?) => {
$(use $derive as $alias;)*
};
}
// This permits invocations of `test!` to be more ergonomic, passing the name of
// the trait under test rather than the name of the inner derive function.
use_as_trait_name!(
KnownLayout => super::derive::known_layout::derive,
Immutable => super::derive::derive_immutable,
TryFromBytes => super::derive::try_from_bytes::derive_try_from_bytes,
FromZeros => super::derive::from_bytes::derive_from_zeros,
FromBytes => super::derive::from_bytes::derive_from_bytes,
IntoBytes => super::derive::into_bytes::derive_into_bytes,
Unaligned => super::derive::unaligned::derive_unaligned,
ByteHash => super::derive::derive_hash,
ByteEq => super::derive::derive_eq,
SplitAt => super::derive::derive_split_at,
);
/// Test that the given derive input expands to the expected output.
///
/// Equality is tested by formatting both token streams using `prettyplease` and
/// performing string equality on the results. This has the effect of making the
/// tests less brittle and robust against meaningless formatting changes.
// Adapted from https://github.com/joshlf/synstructure/blob/400499aaf54840056ff56718beb7810540e6be59/src/macros.rs#L212-L317
macro_rules! test {
($name:ident { $($i:tt)* } expands to { $($o:tt)* }) => {
{
#[allow(dead_code)]
fn ensure_compiles() {
$($i)*
$($o)*
}
test!($name { $($i)* } expands to { $($o)* } no_build);
}
};
($name:ident { $($i:tt)* } expands to { $($o:tt)* } no_build) => {
{
let ts: proc_macro2::TokenStream = quote::quote!( $($i)* );
let ast = syn::parse2::<syn::DeriveInput>(ts).unwrap();
let ctx = crate::Ctx::try_from_derive_input(ast).unwrap();
let res = $name(&ctx, crate::util::Trait::$name);
let expected_toks = quote::quote!( $($o)* );
let expected = pretty_print(expected_toks);
let actual = pretty_print(res.into_ts().into());
assert_eq_or_diff(&expected, &actual);
}
};
($name:ident { $($i:tt)* } expands to $path:expr) => {
test!($name { $($i)* } expands to $path; no_build);
};
($name:ident { $($i:tt)* } expands to $path:expr; no_build) => {
{
let ts: proc_macro2::TokenStream = quote::quote!( $($i)* );
let ast = syn::parse2::<syn::DeriveInput>(ts).unwrap();
let ctx = crate::Ctx::try_from_derive_input(ast).unwrap();
let res = $name(&ctx, crate::util::Trait::$name);
let actual = pretty_print(res.into_ts().into());
if std::env::var("ZEROCOPY_BLESS").is_ok() {
let path = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("src/output_tests")
.join($path);
std::fs::write(&path, &actual).expect("failed to bless output");
} else {
let expected_str = include_str!($path);
let expected_ts: proc_macro2::TokenStream = expected_str.parse().expect("failed to parse expected output");
let expected = pretty_print(expected_ts);
assert_eq_or_diff(&expected, &actual);
}
}
};
}
fn pretty_print(ts: TokenStream) -> String {
prettyplease::unparse(&syn::parse_file(&ts.to_string()).unwrap())
}
#[track_caller]
fn assert_eq_or_diff(expected: &str, actual: &str) {
if expected != actual {
let diff = dissimilar::diff(expected, actual)
.into_iter()
.flat_map(|chunk| {
let (prefix, chunk) = match chunk {
Chunk::Equal(chunk) => (" ", chunk),
Chunk::Delete(chunk) => ("-", chunk),
Chunk::Insert(chunk) => ("+", chunk),
};
[prefix, chunk, "\n"]
})
.collect::<String>();
panic!(
"\
test failed:
got:
```
{}
```
diff (expected vs got):
```
{}
```\n",
actual, diff
);
}
}
#[test]
fn test_known_layout_struct() {
test! {
KnownLayout {
struct Foo;
} expands to "expected/known_layout_struct.expected.rs"
}
}
#[test]
fn test_known_layout_repr_c_struct() {
test! {
KnownLayout {
#[repr(C, align(2))]
struct Foo<T, U>(T, U);
}
expands to "expected/known_layout_repr_c_struct.expected.rs"
}
}
#[test]
fn test_immutable() {
test! {
Immutable {
struct Foo;
} expands to "expected/immutable.expected.rs"
}
}
#[test]
fn test_try_from_bytes() {
test! {
TryFromBytes {
struct Foo;
} expands to "expected/try_from_bytes.expected.rs"
}
}
#[test]
fn test_from_zeros() {
test! {
FromZeros {
struct Foo;
} expands to "expected/from_zeros.expected.rs"
}
}
#[test]
fn test_from_bytes_struct() {
test! {
FromBytes {
struct Foo;
} expands to "expected/from_bytes_struct.expected.rs"
}
}
#[test]
fn test_from_bytes_union() {
test! {
FromBytes {
union Foo {
a: u8,
}
} expands to "expected/from_bytes_union.expected.rs"
}
}
#[test]
fn test_into_bytes_struct_empty() {
test! {
IntoBytes {
#[repr(C)]
struct Foo;
} expands to "expected/into_bytes_struct_empty.expected.rs"
}
}
#[test]
fn test_into_bytes_struct_basic() {
test! {
IntoBytes {
#[repr(C)]
struct Foo {
a: u8,
b: u8,
}
} expands to "expected/into_bytes_struct_basic.expected.rs"
}
}
#[test]
fn test_into_bytes_struct_trailing() {
test! {
IntoBytes {
#[repr(C)]
struct Foo {
a: u8,
b: [Trailing],
}
} expands to "expected/into_bytes_struct_trailing.expected.rs"
}
}
#[test]
fn test_into_bytes_struct_trailing_generic() {
test! {
IntoBytes {
#[repr(C)]
struct Foo<Trailing> {
a: u8,
b: [Trailing],
}
} expands to "expected/into_bytes_struct_trailing_generic.expected.rs"
}
}
#[test]
fn test_into_bytes_enum() {
macro_rules! test_repr {
($(#[$attr:meta])*) => {
$(test! {
IntoBytes {
#[$attr]
enum Foo {
Bar,
}
} expands to concat!("expected/into_bytes_enum.", stringify!($attr), ".expected.rs")
})*
};
}
test_repr! {
#[repr(C)]
#[repr(u8)]
#[repr(u16)]
#[repr(u32)]
#[repr(u64)]
#[repr(u128)]
#[repr(usize)]
#[repr(i8)]
#[repr(i16)]
#[repr(i32)]
#[repr(i64)]
#[repr(i128)]
#[repr(isize)]
}
}
#[test]
fn test_unaligned() {
test! {
Unaligned {
#[repr(C)]
struct Foo;
} expands to "expected/unaligned.expected.rs"
}
}
#[test]
fn test_try_from_bytes_enum() {
test! {
TryFromBytes {
#[repr(u8)]
enum ComplexWithGenerics<'a: 'static, const N: usize, X, Y: Deref>
where
X: Deref<Target = &'a [(X, Y); N]>,
{
UnitLike,
StructLike { a: u8, b: X, c: X::Target, d: Y::Target, e: [(X, Y); N] },
TupleLike(bool, Y, PhantomData<&'a [(X, Y); N]>),
}
} expands to "expected/try_from_bytes_enum_1.expected.rs"
}
test! {
TryFromBytes {
#[repr(u32)]
enum ComplexWithGenerics<'a: 'static, const N: usize, X, Y: Deref>
where
X: Deref<Target = &'a [(X, Y); N]>,
{
UnitLike,
StructLike { a: u8, b: X, c: X::Target, d: Y::Target, e: [(X, Y); N] },
TupleLike(bool, Y, PhantomData<&'a [(X, Y); N]>),
}
} expands to "expected/try_from_bytes_enum_2.expected.rs"
}
test! {
TryFromBytes {
#[repr(C)]
enum ComplexWithGenerics<'a: 'static, const N: usize, X, Y: Deref>
where
X: Deref<Target = &'a [(X, Y); N]>,
{
UnitLike,
StructLike { a: u8, b: X, c: X::Target, d: Y::Target, e: [(X, Y); N] },
TupleLike(bool, Y, PhantomData<&'a [(X, Y); N]>),
}
} expands to "expected/try_from_bytes_enum_3.expected.rs"
}
}
// This goes at the bottom because it's so verbose and it makes scrolling past
// other code a pain.
#[test]
fn test_from_bytes_enum() {
test! {
FromBytes {
#[repr(u8)]
enum Foo {
Variant0,
Variant1,
Variant2,
Variant3,
Variant4,
Variant5,
Variant6,
Variant7,
Variant8,
Variant9,
Variant10,
Variant11,
Variant12,
Variant13,
Variant14,
Variant15,
Variant16,
Variant17,
Variant18,
Variant19,
Variant20,
Variant21,
Variant22,
Variant23,
Variant24,
Variant25,
Variant26,
Variant27,
Variant28,
Variant29,
Variant30,
Variant31,
Variant32,
Variant33,
Variant34,
Variant35,
Variant36,
Variant37,
Variant38,
Variant39,
Variant40,
Variant41,
Variant42,
Variant43,
Variant44,
Variant45,
Variant46,
Variant47,
Variant48,
Variant49,
Variant50,
Variant51,
Variant52,
Variant53,
Variant54,
Variant55,
Variant56,
Variant57,
Variant58,
Variant59,
Variant60,
Variant61,
Variant62,
Variant63,
Variant64,
Variant65,
Variant66,
Variant67,
Variant68,
Variant69,
Variant70,
Variant71,
Variant72,
Variant73,
Variant74,
Variant75,
Variant76,
Variant77,
Variant78,
Variant79,
Variant80,
Variant81,
Variant82,
Variant83,
Variant84,
Variant85,
Variant86,
Variant87,
Variant88,
Variant89,
Variant90,
Variant91,
Variant92,
Variant93,
Variant94,
Variant95,
Variant96,
Variant97,
Variant98,
Variant99,
Variant100,
Variant101,
Variant102,
Variant103,
Variant104,
Variant105,
Variant106,
Variant107,
Variant108,
Variant109,
Variant110,
Variant111,
Variant112,
Variant113,
Variant114,
Variant115,
Variant116,
Variant117,
Variant118,
Variant119,
Variant120,
Variant121,
Variant122,
Variant123,
Variant124,
Variant125,
Variant126,
Variant127,
Variant128,
Variant129,
Variant130,
Variant131,
Variant132,
Variant133,
Variant134,
Variant135,
Variant136,
Variant137,
Variant138,
Variant139,
Variant140,
Variant141,
Variant142,
Variant143,
Variant144,
Variant145,
Variant146,
Variant147,
Variant148,
Variant149,
Variant150,
Variant151,
Variant152,
Variant153,
Variant154,
Variant155,
Variant156,
Variant157,
Variant158,
Variant159,
Variant160,
Variant161,
Variant162,
Variant163,
Variant164,
Variant165,
Variant166,
Variant167,
Variant168,
Variant169,
Variant170,
Variant171,
Variant172,
Variant173,
Variant174,
Variant175,
Variant176,
Variant177,
Variant178,
Variant179,
Variant180,
Variant181,
Variant182,
Variant183,
Variant184,
Variant185,
Variant186,
Variant187,
Variant188,
Variant189,
Variant190,
Variant191,
Variant192,
Variant193,
Variant194,
Variant195,
Variant196,
Variant197,
Variant198,
Variant199,
Variant200,
Variant201,
Variant202,
Variant203,
Variant204,
Variant205,
Variant206,
Variant207,
Variant208,
Variant209,
Variant210,
Variant211,
Variant212,
Variant213,
Variant214,
Variant215,
Variant216,
Variant217,
Variant218,
Variant219,
Variant220,
Variant221,
Variant222,
Variant223,
Variant224,
Variant225,
Variant226,
Variant227,
Variant228,
Variant229,
Variant230,
Variant231,
Variant232,
Variant233,
Variant234,
Variant235,
Variant236,
Variant237,
Variant238,
Variant239,
Variant240,
Variant241,
Variant242,
Variant243,
Variant244,
Variant245,
Variant246,
Variant247,
Variant248,
Variant249,
Variant250,
Variant251,
Variant252,
Variant253,
Variant254,
Variant255,
}
} expands to "expected/from_bytes_enum.expected.rs"
}
}
#[test]
fn test_try_from_bytes_trivial_is_bit_valid_enum() {
// Even when we aren't deriving `FromBytes` as the top-level trait,
// `TryFromBytes` on enums still detects whether we *could* derive
// `FromBytes`, and if so, performs the same "trivial `is_bit_valid`"
// optimization.
test! {
TryFromBytes {
#[repr(u8)]
enum Foo {
Variant0,
Variant1,
Variant2,
Variant3,
Variant4,
Variant5,
Variant6,
Variant7,
Variant8,
Variant9,
Variant10,
Variant11,
Variant12,
Variant13,
Variant14,
Variant15,
Variant16,
Variant17,
Variant18,
Variant19,
Variant20,
Variant21,
Variant22,
Variant23,
Variant24,
Variant25,
Variant26,
Variant27,
Variant28,
Variant29,
Variant30,
Variant31,
Variant32,
Variant33,
Variant34,
Variant35,
Variant36,
Variant37,
Variant38,
Variant39,
Variant40,
Variant41,
Variant42,
Variant43,
Variant44,
Variant45,
Variant46,
Variant47,
Variant48,
Variant49,
Variant50,
Variant51,
Variant52,
Variant53,
Variant54,
Variant55,
Variant56,
Variant57,
Variant58,
Variant59,
Variant60,
Variant61,
Variant62,
Variant63,
Variant64,
Variant65,
Variant66,
Variant67,
Variant68,
Variant69,
Variant70,
Variant71,
Variant72,
Variant73,
Variant74,
Variant75,
Variant76,
Variant77,
Variant78,
Variant79,
Variant80,
Variant81,
Variant82,
Variant83,
Variant84,
Variant85,
Variant86,
Variant87,
Variant88,
Variant89,
Variant90,
Variant91,
Variant92,
Variant93,
Variant94,
Variant95,
Variant96,
Variant97,
Variant98,
Variant99,
Variant100,
Variant101,
Variant102,
Variant103,
Variant104,
Variant105,
Variant106,
Variant107,
Variant108,
Variant109,
Variant110,
Variant111,
Variant112,
Variant113,
Variant114,
Variant115,
Variant116,
Variant117,
Variant118,
Variant119,
Variant120,
Variant121,
Variant122,
Variant123,
Variant124,
Variant125,
Variant126,
Variant127,
Variant128,
Variant129,
Variant130,
Variant131,
Variant132,
Variant133,
Variant134,
Variant135,
Variant136,
Variant137,
Variant138,
Variant139,
Variant140,
Variant141,
Variant142,
Variant143,
Variant144,
Variant145,
Variant146,
Variant147,
Variant148,
Variant149,
Variant150,
Variant151,
Variant152,
Variant153,
Variant154,
Variant155,
Variant156,
Variant157,
Variant158,
Variant159,
Variant160,
Variant161,
Variant162,
Variant163,
Variant164,
Variant165,
Variant166,
Variant167,
Variant168,
Variant169,
Variant170,
Variant171,
Variant172,
Variant173,
Variant174,
Variant175,
Variant176,
Variant177,
Variant178,
Variant179,
Variant180,
Variant181,
Variant182,
Variant183,
Variant184,
Variant185,
Variant186,
Variant187,
Variant188,
Variant189,
Variant190,
Variant191,
Variant192,
Variant193,
Variant194,
Variant195,
Variant196,
Variant197,
Variant198,
Variant199,
Variant200,
Variant201,
Variant202,
Variant203,
Variant204,
Variant205,
Variant206,
Variant207,
Variant208,
Variant209,
Variant210,
Variant211,
Variant212,
Variant213,
Variant214,
Variant215,
Variant216,
Variant217,
Variant218,
Variant219,
Variant220,
Variant221,
Variant222,
Variant223,
Variant224,
Variant225,
Variant226,
Variant227,
Variant228,
Variant229,
Variant230,
Variant231,
Variant232,
Variant233,
Variant234,
Variant235,
Variant236,
Variant237,
Variant238,
Variant239,
Variant240,
Variant241,
Variant242,
Variant243,
Variant244,
Variant245,
Variant246,
Variant247,
Variant248,
Variant249,
Variant250,
Variant251,
Variant252,
Variant253,
Variant254,
Variant255,
}
} expands to "expected/try_from_bytes_trivial_is_bit_valid_enum.expected.rs"
}
}
#[test]
fn test_hash() {
test! {
ByteHash {
struct Foo<T: Clone>(T) where Self: Sized;
} expands to "expected/hash.expected.rs"
}
}
#[test]
fn test_eq() {
test! {
ByteEq {
struct Foo<T: Clone>(T) where Self: Sized;
} expands to "expected/eq.expected.rs"
}
}
#[test]
fn test_split_at() {
test! {
SplitAt {
#[repr(C)]
struct Foo<T: ?Sized + Copy>(T) where Self: Copy;
} expands to "expected/split_at_repr_c.expected.rs"
}
test! {
SplitAt {
#[repr(transparent)]
struct Foo<T: ?Sized + Copy>(T) where Self: Copy;
} expands to "expected/split_at_repr_transparent.expected.rs"
}
test! {
SplitAt {
#[repr(packed)]
struct Foo<T: ?Sized + Copy>(T) where Self: Copy;
} expands to {
::core::compile_error! {
"must not have #[repr(packed)] attribute"
}
} no_build
}
test! {
SplitAt {
#[repr(packed(2))]
struct Foo<T: ?Sized + Copy>(T) where Self: Copy;
} expands to {
::core::compile_error! {
"must not have #[repr(packed)] attribute"
}
} no_build
}
test! {
SplitAt {
enum Foo {}
} expands to {
::core::compile_error! {
"can only be applied to structs"
}
} no_build
}
test! {
SplitAt {
union Foo { a: () }
} expands to {
::core::compile_error! {
"can only be applied to structs"
}
} no_build
}
test! {
SplitAt {
struct Foo<T: ?Sized + Copy>(T) where Self: Copy;
} expands to {
::core::compile_error! {
"must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"
}
} no_build
}
}

849
vendor/zerocopy-derive/src/repr.rs vendored Normal file
View File

@@ -0,0 +1,849 @@
// Copyright 2019 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use core::{
convert::{Infallible, TryFrom},
num::NonZeroU32,
};
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,
};
/// 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<Prim, Packed> {
/// `#[repr(transparent)]`
Transparent(Span),
/// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`
/// optionally combined with `repr(packed(...))` or `repr(align(...))`
Compound(Spanned<CompoundRepr<Prim>>, Option<Spanned<AlignRepr<Packed>>>),
}
/// A compound representation: `repr(C)`, `repr(Rust)`, or `repr(Int)`.
#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
pub(crate) enum CompoundRepr<Prim> {
C,
Rust,
Primitive(Prim),
}
/// `repr(Int)`
#[derive(Copy, Clone)]
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub(crate) enum PrimitiveRepr {
U8,
U16,
U32,
U64,
U128,
Usize,
I8,
I16,
I32,
I64,
I128,
Isize,
}
/// `repr(packed(...))` or `repr(align(...))`
#[cfg_attr(test, derive(Copy, Clone, Debug, Eq, PartialEq))]
pub(crate) enum AlignRepr<Packed> {
Packed(Packed),
Align(NonZeroU32),
}
/// The representations which can legally appear on a struct or union type.
pub(crate) type StructUnionRepr = Repr<Infallible, NonZeroU32>;
/// The representations which can legally appear on an enum type.
pub(crate) type EnumRepr = Repr<PrimitiveRepr, Infallible>;
impl<Prim, Packed> Repr<Prim, Packed> {
/// Gets the name of this "repr type" - the non-align `repr(X)` that is used
/// in prose to refer to this type.
///
/// 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<PrimitiveRepr>,
{
use CompoundRepr::*;
use PrimitiveRepr::*;
use 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)",
U128 => "repr(u128)",
Usize => "repr(usize)",
I8 => "repr(i8)",
I16 => "repr(i16)",
I32 => "repr(i32)",
I64 => "repr(i64)",
I128 => "repr(i128)",
Isize => "repr(isize)",
}),
},
}
}
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::*;
use Repr::*;
if let Compound(_, Some(Spanned { t: Packed(p), span: _ })) = self {
Some(p)
} else {
None
}
}
pub(crate) fn get_align(&self) -> Option<Spanned<NonZeroU32>> {
use AlignRepr::*;
use Repr::*;
if let Compound(_, Some(Spanned { t: Align(n), span })) = self {
Some(Spanned::new(*n, *span))
} else {
None
}
}
pub(crate) fn is_align_gt_1(&self) -> bool {
self.get_align().map(|n| n.t.get() > 1).unwrap_or(false)
}
/// 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 {
Ok(())
}
}
}
impl<Prim> Repr<Prim, NonZeroU32> {
/// 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)
}
}
impl<Packed> Repr<PrimitiveRepr, Packed> {
fn get_primitive(&self) -> Option<&PrimitiveRepr> {
use CompoundRepr::*;
use Repr::*;
if let Compound(Spanned { t: Primitive(p), span: _ }, _align) = self {
Some(p)
} else {
None
}
}
/// Does `self` describe a `#[repr(u8)]` type?
pub(crate) fn is_u8(&self) -> bool {
matches!(self.get_primitive(), Some(PrimitiveRepr::U8))
}
/// Does `self` describe a `#[repr(i8)]` type?
pub(crate) fn is_i8(&self) -> bool {
matches!(self.get_primitive(), Some(PrimitiveRepr::I8))
}
}
impl<Prim, Packed> ToTokens for Repr<Prim, Packed>
where
Prim: With<PrimitiveRepr> + Copy,
Packed: With<NonZeroU32> + 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);
}
}
}
}
}
impl<Prim: With<PrimitiveRepr> + Copy> ToTokens for Spanned<CompoundRepr<Prim>> {
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 ToTokens for Spanned<PrimitiveRepr> {
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)] }),
U128 => ts.append_all(quote_spanned! { self.span => #[repr(u128)] }),
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)] }),
I128 => ts.append_all(quote_spanned! { self.span => #[repr(i128)] }),
Isize => ts.append_all(quote_spanned! { self.span => #[repr(isize)] }),
}
}
}
impl<Packed: With<NonZeroU32> + Copy> ToTokens for Spanned<AlignRepr<Packed>> {
fn to_tokens(&self, ts: &mut TokenStream) {
use AlignRepr::*;
// We use `syn::Index` instead of `u32` because `quote_spanned!`
// serializes `u32` literals as `123u32`, not just `123`. Rust doesn't
// recognize that as a valid argument to `#[repr(align(...))]` or
// `#[repr(packed(...))]`.
let to_index = |n: NonZeroU32| syn::Index { index: n.get(), span: self.span };
match self.t {
Packed(n) => n.with(|n| {
let n = to_index(n);
ts.append_all(quote_spanned! { self.span => #[repr(packed(#n))] })
}),
Align(n) => {
let n = to_index(n);
ts.append_all(quote_spanned! { self.span => #[repr(align(#n))] })
}
}
}
}
/// 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,
U64,
U128,
Usize,
I8,
I16,
I32,
I64,
I128,
Isize,
Align(NonZeroU32),
PackedN(NonZeroU32),
Packed,
}
/// The error from converting from a `RawRepr`.
#[cfg_attr(test, derive(Debug, Eq, PartialEq))]
pub(crate) enum FromRawReprError<E> {
/// 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<Prim: With<PrimitiveRepr>> TryFrom<RawRepr> for CompoundRepr<Prim> {
type Error = FromRawReprError<UnsupportedReprError>;
fn try_from(
raw: RawRepr,
) -> Result<CompoundRepr<Prim>, FromRawReprError<UnsupportedReprError>> {
use RawRepr::*;
match raw {
C => Ok(CompoundRepr::C),
Rust => Ok(CompoundRepr::Rust),
raw @ (U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize) => {
Prim::try_with_or(
|| match raw {
U8 => Ok(PrimitiveRepr::U8),
U16 => Ok(PrimitiveRepr::U16),
U32 => Ok(PrimitiveRepr::U32),
U64 => Ok(PrimitiveRepr::U64),
U128 => Ok(PrimitiveRepr::U128),
Usize => Ok(PrimitiveRepr::Usize),
I8 => Ok(PrimitiveRepr::I8),
I16 => Ok(PrimitiveRepr::I16),
I32 => Ok(PrimitiveRepr::I32),
I64 => Ok(PrimitiveRepr::I64),
I128 => Ok(PrimitiveRepr::I128),
Isize => Ok(PrimitiveRepr::Isize),
Transparent | C | Rust | Align(_) | PackedN(_) | Packed => {
Err(UnsupportedReprError)
}
},
UnsupportedReprError,
)
.map(CompoundRepr::Primitive)
.map_err(FromRawReprError::Err)
}
Transparent | Align(_) | PackedN(_) | Packed => Err(FromRawReprError::None),
}
}
}
impl<Pcked: With<NonZeroU32>> TryFrom<RawRepr> for AlignRepr<Pcked> {
type Error = FromRawReprError<UnsupportedReprError>;
fn try_from(raw: RawRepr) -> Result<AlignRepr<Pcked>, FromRawReprError<UnsupportedReprError>> {
use RawRepr::*;
match raw {
Packed | PackedN(_) => Pcked::try_with_or(
|| match raw {
Packed => Ok(NonZeroU32::new(1).unwrap()),
PackedN(n) => Ok(n),
U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize
| Transparent | C | Rust | Align(_) => Err(UnsupportedReprError),
},
UnsupportedReprError,
)
.map(AlignRepr::Packed)
.map_err(FromRawReprError::Err),
Align(n) => Ok(AlignRepr::Align(n)),
U8 | U16 | U32 | U64 | U128 | Usize | I8 | I16 | I32 | I64 | I128 | Isize
| Transparent | C | Rust => Err(FromRawReprError::None),
}
}
}
/// 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<E> {
/// 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<RawRepr, Error = FromRawReprError<E>>>(
r: impl IntoIterator<Item = &'a Spanned<RawRepr>>,
) -> Result<Option<Spanned<R>>, Spanned<FromRawReprsError<E>>> {
// 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: Option<Spanned<R>>, raw| {
let new = match Spanned::<R>::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 let Some(found) = found {
// We already found an `R`, but this `RawRepr` also converts to an
// `R`, so that's a conflict.
//
// `Span::join` returns `None` if the two spans are from different
// files or if we're not on the nightly compiler. In that case, just
// use `new`'s span.
let span = found.span.join(new.span).unwrap_or(new.span);
Err(Spanned::new(FromRawReprsError::Conflict, 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<UnsupportedReprError>),
Unrecognized,
}
impl From<FromRawReprsError<UnsupportedReprError>> for FromAttrsError {
fn from(err: FromRawReprsError<UnsupportedReprError>) -> FromAttrsError {
FromAttrsError::FromRawReprs(err)
}
}
impl From<UnrecognizedReprError> for FromAttrsError {
fn from(_err: UnrecognizedReprError) -> FromAttrsError {
FromAttrsError::Unrecognized
}
}
impl From<Spanned<FromAttrsError>> for Error {
fn from(err: Spanned<FromAttrsError>) -> 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"),
}
}
}
impl<Prim, Packed> Repr<Prim, Packed> {
fn from_attrs_inner(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Spanned<FromAttrsError>>
where
Prim: With<PrimitiveRepr>,
Packed: With<NonZeroU32>,
{
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,
))
}
// An iterator can't produce a value only on the second call to
// `.next()`.
(None, Some(_)) => unreachable!(),
}
};
let compound: Option<Spanned<CompoundRepr<Prim>>> =
try_from_raw_reprs(raw_reprs.iter()).map_err(Spanned::from)?;
let align: Option<Spanned<AlignRepr<Packed>>> =
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,
))
}
}
}
impl<Prim, Packed> Repr<Prim, Packed> {
pub(crate) fn from_attrs(attrs: &[Attribute]) -> Result<Repr<Prim, Packed>, Error>
where
Prim: With<PrimitiveRepr>,
Packed: With<NonZeroU32>,
{
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<Vec<Spanned<RawRepr>>, Spanned<UnrecognizedReprError>> {
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<Meta, Comma> =
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<RawRepr, UnrecognizedReprError> {
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::<LitInt>()
.and_then(|int| int.base10_parse::<NonZeroU32>())
.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,
("u128", None) => U128,
("usize", None) => Usize,
("i8", None) => I8,
("i16", None) => I16,
("i32", None) => I32,
("i64", None) => I64,
("i128", None) => I128,
("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),
})
}
}
pub(crate) use util::*;
mod util {
use super::*;
/// A value with an associated span.
#[derive(Copy, Clone)]
#[cfg_attr(test, derive(Debug))]
pub(crate) struct Spanned<T> {
pub(crate) t: T,
pub(crate) span: Span,
}
impl<T> Spanned<T> {
pub(super) fn new(t: T, span: Span) -> Spanned<T> {
Spanned { t, span }
}
pub(super) fn from<U>(s: Spanned<U>) -> Spanned<T>
where
T: From<U>,
{
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.
pub(super) fn try_from<E, U>(
u: Spanned<U>,
) -> Result<Spanned<T>, FromRawReprError<Spanned<E>>>
where
T: TryFrom<U, Error = FromRawReprError<E>>,
{
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)),
})
}
}
// Used to permit implementing `With<T> for T: Inhabited` and for
// `Infallible` without a blanket impl conflict.
pub(crate) trait Inhabited {}
impl Inhabited for PrimitiveRepr {}
impl Inhabited for NonZeroU32 {}
pub(crate) trait With<T> {
fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O;
fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, err: E) -> Result<Self, E>
where
Self: Sized;
}
impl<T: Inhabited> With<T> for T {
fn with<O, F: FnOnce(T) -> O>(self, f: F) -> O {
f(self)
}
fn try_with_or<E, F: FnOnce() -> Result<T, E>>(f: F, _err: E) -> Result<Self, E> {
f()
}
}
impl<T> With<T> for Infallible {
fn with<O, F: FnOnce(T) -> O>(self, _f: F) -> O {
match self {}
}
fn try_with_or<E, F: FnOnce() -> Result<T, E>>(_f: F, err: E) -> Result<Self, E> {
Err(err)
}
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::*;
impl<T> From<T> for Spanned<T> {
fn from(t: T) -> Spanned<T> {
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<T: PartialEq> PartialEq for Spanned<T> {
fn eq(&self, other: &Spanned<T>) -> bool {
self.t.eq(&other.t)
}
}
impl<T: Eq> Eq for Spanned<T> {}
impl<Prim: PartialEq, Packed: PartialEq> PartialEq for Repr<Prim, Packed> {
fn eq(&self, other: &Repr<Prim, Packed>) -> bool {
match (self, other) {
(Repr::Transparent(_), Repr::Transparent(_)) => true,
(Repr::Compound(sc, sa), Repr::Compound(oc, oa)) => (sc, sa) == (oc, oa),
_ => false,
}
}
}
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<_, _>, _> = $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::*;
use CompoundRepr::*;
use PrimitiveRepr::*;
let nz = |n: u32| NonZeroU32::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::*;
use 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());
}
}

843
vendor/zerocopy-derive/src/util.rs vendored Normal file
View File

@@ -0,0 +1,843 @@
// Copyright 2019 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// This file may not be copied, modified, or distributed except according to
// those terms.
use std::num::NonZeroU32;
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{
parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error,
Expr, ExprLit, Field, GenericParam, Ident, Index, Lit, LitStr, Meta, Path, Type, Variant,
Visibility, WherePredicate,
};
use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned};
pub(crate) struct Ctx {
pub(crate) ast: DeriveInput,
pub(crate) zerocopy_crate: Path,
// The value of the last `#[zerocopy(on_error = ...)]` attribute, or `false`
// if none is provided.
pub(crate) skip_on_error: bool,
// The span of the last `#[zerocopy(on_error = ...)]` attribute, if any.
pub(crate) on_error_span: Option<proc_macro2::Span>,
}
impl Ctx {
/// Attempt to extract a crate path from the provided attributes. Defaults to
/// `::zerocopy` if not found.
pub(crate) fn try_from_derive_input(ast: DeriveInput) -> Result<Self, Error> {
let mut path = parse_quote!(::zerocopy);
let mut skip_on_error = false;
let mut on_error_span = None;
for attr in &ast.attrs {
if let Meta::List(ref meta_list) = attr.meta {
if meta_list.path.is_ident("zerocopy") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("crate") {
let expr = meta.value().and_then(|value| value.parse());
if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
if let Ok(path_lit) = lit.parse::<Ident>() {
path = parse_quote!(::#path_lit);
return Ok(());
}
}
return Err(Error::new(
Span::call_site(),
"`crate` attribute requires a path as the value",
));
}
if meta.path.is_ident("on_error") {
on_error_span = Some(meta.path.span());
let value = meta.value()?;
let s: LitStr = value.parse()?;
match s.value().as_str() {
"skip" => skip_on_error = true,
"fail" => skip_on_error = false,
_ => return Err(Error::new(
s.span(),
"unrecognized value for `on_error` attribute from `zerocopy`; expected `skip` or `fail`",
)),
}
return Ok(());
}
Err(Error::new(
Span::call_site(),
format!(
"unknown attribute encountered: {}",
meta.path.into_token_stream()
),
))
})?;
}
}
}
Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span })
}
pub(crate) fn with_input(&self, input: &DeriveInput) -> Self {
Self {
ast: input.clone(),
zerocopy_crate: self.zerocopy_crate.clone(),
skip_on_error: self.skip_on_error,
on_error_span: self.on_error_span,
}
}
pub(crate) fn core_path(&self) -> TokenStream {
let zerocopy_crate = &self.zerocopy_crate;
quote!(#zerocopy_crate::util::macro_util::core_reexport)
}
pub(crate) fn cfg_compile_error(&self) -> TokenStream {
// By checking both during the compilation of the proc macro *and* in
// the generated code, we ensure that `--cfg
// zerocopy_unstable_derive_on_error` need only be passed *either* when
// compiling this crate *or* when compiling the user's crate. The former
// is preferable, but in some situations (such as when cross-compiling
// using `cargo build --target`), it doesn't get propagated to this
// crate's build by default.
if cfg!(zerocopy_unstable_derive_on_error) {
quote!()
} else if let Some(span) = self.on_error_span {
let core = self.core_path();
let error_message = "`on_error` is experimental; pass '--cfg zerocopy_unstable_derive_on_error' to enable";
quote::quote_spanned! {span=>
#[allow(unused_attributes, unexpected_cfgs)]
const _: () = {
#[cfg(not(zerocopy_unstable_derive_on_error))]
#core::compile_error!(#error_message);
};
}
} else {
quote!()
}
}
pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> {
if self.skip_on_error {
Ok(self.cfg_compile_error())
} else {
Err(error)
}
}
}
pub(crate) trait DataExt {
/// Extracts the names and types of all fields. For enums, extracts the
/// names and types of fields from each variant. For tuple structs, the
/// names are the indices used to index into the struct (ie, `0`, `1`, etc).
///
/// FIXME: Extracting field names for enums doesn't really make sense. Types
/// makes sense because we don't care about where they live - we just care
/// about transitive ownership. But for field names, we'd only use them when
/// generating is_bit_valid, which cares about where they live.
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>;
fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>;
fn tag(&self) -> Option<Ident>;
}
impl DataExt for Data {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
match self {
Data::Struct(strc) => strc.fields(),
Data::Enum(enm) => enm.fields(),
Data::Union(un) => un.fields(),
}
}
fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
match self {
Data::Struct(strc) => strc.variants(),
Data::Enum(enm) => enm.variants(),
Data::Union(un) => un.variants(),
}
}
fn tag(&self) -> Option<Ident> {
match self {
Data::Struct(strc) => strc.tag(),
Data::Enum(enm) => enm.tag(),
Data::Union(un) => un.tag(),
}
}
}
impl DataExt for DataStruct {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
map_fields(&self.fields)
}
fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
vec![(None, self.fields())]
}
fn tag(&self) -> Option<Ident> {
None
}
}
impl DataExt for DataEnum {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
map_fields(self.variants.iter().flat_map(|var| &var.fields))
}
fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
self.variants.iter().map(|var| (Some(var), map_fields(&var.fields))).collect()
}
fn tag(&self) -> Option<Ident> {
Some(Ident::new("___ZerocopyTag", Span::call_site()))
}
}
impl DataExt for DataUnion {
fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
map_fields(&self.fields.named)
}
fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
vec![(None, self.fields())]
}
fn tag(&self) -> Option<Ident> {
None
}
}
fn map_fields<'a>(
fields: impl 'a + IntoIterator<Item = &'a Field>,
) -> Vec<(&'a Visibility, TokenStream, &'a Type)> {
fields
.into_iter()
.enumerate()
.map(|(idx, f)| {
(
&f.vis,
f.ident
.as_ref()
.map(ToTokens::to_token_stream)
.unwrap_or_else(|| Index::from(idx).to_token_stream()),
&f.ty,
)
})
.collect()
}
pub(crate) fn to_ident_str(t: &impl ToString) -> String {
let s = t.to_string();
if let Some(stripped) = s.strip_prefix("r#") {
stripped.to_string()
} else {
s
}
}
/// This enum describes what kind of padding check needs to be generated for the
/// associated impl.
pub(crate) enum PaddingCheck {
/// Check that the sum of the fields' sizes exactly equals the struct's
/// size.
Struct,
/// Check that a `repr(C)` struct has no padding.
ReprCStruct,
/// Check that the size of each field exactly equals the union's size.
Union,
/// Check that every variant of the enum contains no padding.
///
/// Because doing so requires a tag enum, this padding check requires an
/// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`.
Enum { tag_type_definition: TokenStream },
}
impl PaddingCheck {
/// Returns the idents of the trait to use and the macro to call in order to
/// validate that a type passes the relevant padding check.
pub(crate) fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
let (trt, mcro) = match self {
PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
PaddingCheck::Union => ("PaddingFree", "union_padding"),
PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
};
let trt = Ident::new(trt, Span::call_site());
let mcro = Ident::new(mcro, Span::call_site());
(trt, mcro)
}
/// Sometimes performing the padding check requires some additional
/// "context" code. For enums, this is the definition of the tag enum.
pub(crate) fn validator_macro_context(&self) -> Option<&TokenStream> {
match self {
PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
}
}
}
#[derive(Clone)]
pub(crate) enum Trait {
KnownLayout,
HasTag,
HasField {
variant_id: Box<Expr>,
field: Box<Type>,
field_id: Box<Expr>,
},
ProjectField {
variant_id: Box<Expr>,
field: Box<Type>,
field_id: Box<Expr>,
invariants: Box<Type>,
},
Immutable,
TryFromBytes,
FromZeros,
FromBytes,
IntoBytes,
Unaligned,
Sized,
ByteHash,
ByteEq,
SplitAt,
}
impl ToTokens for Trait {
fn to_tokens(&self, tokens: &mut TokenStream) {
// According to [1], the format of the derived `Debug`` output is not
// stable and therefore not guaranteed to represent the variant names.
// Indeed with the (unstable) `fmt-debug` compiler flag [2], it can
// return only a minimalized output or empty string. To make sure this
// code will work in the future and independent of the compiler flag, we
// translate the variants to their names manually here.
//
// [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability
// [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html
let s = match self {
Trait::HasField { .. } => "HasField",
Trait::ProjectField { .. } => "ProjectField",
Trait::KnownLayout => "KnownLayout",
Trait::HasTag => "HasTag",
Trait::Immutable => "Immutable",
Trait::TryFromBytes => "TryFromBytes",
Trait::FromZeros => "FromZeros",
Trait::FromBytes => "FromBytes",
Trait::IntoBytes => "IntoBytes",
Trait::Unaligned => "Unaligned",
Trait::Sized => "Sized",
Trait::ByteHash => "ByteHash",
Trait::ByteEq => "ByteEq",
Trait::SplitAt => "SplitAt",
};
let ident = Ident::new(s, Span::call_site());
let arguments: Option<syn::AngleBracketedGenericArguments> = match self {
Trait::HasField { variant_id, field, field_id } => {
Some(parse_quote!(<#field, #variant_id, #field_id>))
}
Trait::ProjectField { variant_id, field, field_id, invariants } => {
Some(parse_quote!(<#field, #invariants, #variant_id, #field_id>))
}
Trait::KnownLayout
| Trait::HasTag
| Trait::Immutable
| Trait::TryFromBytes
| Trait::FromZeros
| Trait::FromBytes
| Trait::IntoBytes
| Trait::Unaligned
| Trait::Sized
| Trait::ByteHash
| Trait::ByteEq
| Trait::SplitAt => None,
};
tokens.extend(quote!(#ident #arguments));
}
}
impl Trait {
pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path {
let zerocopy_crate = &ctx.zerocopy_crate;
let core = ctx.core_path();
match self {
Self::Sized => parse_quote!(#core::marker::#self),
_ => parse_quote!(#zerocopy_crate::#self),
}
}
}
pub(crate) enum TraitBound {
Slf,
Other(Trait),
}
pub(crate) enum FieldBounds<'a> {
None,
All(&'a [TraitBound]),
Trailing(&'a [TraitBound]),
Explicit(Vec<WherePredicate>),
}
impl<'a> FieldBounds<'a> {
pub(crate) const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
pub(crate) const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
}
pub(crate) enum SelfBounds<'a> {
None,
All(&'a [Trait]),
}
// FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false
// positive. Explicit lifetimes are actually necessary here.
#[allow(clippy::needless_lifetimes)]
impl<'a> SelfBounds<'a> {
pub(crate) const SIZED: Self = Self::All(&[Trait::Sized]);
}
/// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.
pub(crate) fn normalize_bounds<'a>(
slf: &'a Trait,
bounds: &'a [TraitBound],
) -> impl 'a + Iterator<Item = Trait> {
bounds.iter().map(move |bound| match bound {
TraitBound::Slf => slf.clone(),
TraitBound::Other(trt) => trt.clone(),
})
}
pub(crate) struct ImplBlockBuilder<'a> {
ctx: &'a Ctx,
data: &'a dyn DataExt,
trt: Trait,
field_type_trait_bounds: FieldBounds<'a>,
self_type_trait_bounds: SelfBounds<'a>,
padding_check: Option<PaddingCheck>,
param_extras: Vec<GenericParam>,
inner_extras: Option<TokenStream>,
outer_extras: Option<TokenStream>,
}
impl<'a> ImplBlockBuilder<'a> {
pub(crate) fn new(
ctx: &'a Ctx,
data: &'a dyn DataExt,
trt: Trait,
field_type_trait_bounds: FieldBounds<'a>,
) -> Self {
Self {
ctx,
data,
trt,
field_type_trait_bounds,
self_type_trait_bounds: SelfBounds::None,
padding_check: None,
param_extras: Vec::new(),
inner_extras: None,
outer_extras: None,
}
}
pub(crate) fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
self.self_type_trait_bounds = self_type_trait_bounds;
self
}
pub(crate) fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
self.padding_check = padding_check.into();
self
}
pub(crate) fn param_extras(mut self, param_extras: Vec<GenericParam>) -> Self {
self.param_extras.extend(param_extras);
self
}
pub(crate) fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
self.inner_extras = Some(inner_extras);
self
}
pub(crate) fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
self.outer_extras = outer_extras.into();
self
}
pub(crate) fn build(self) -> TokenStream {
// In this documentation, we will refer to this hypothetical struct:
//
// #[derive(FromBytes)]
// struct Foo<T, I: Iterator>
// where
// T: Copy,
// I: Clone,
// I::Item: Clone,
// {
// a: u8,
// b: T,
// c: I::Item,
// }
//
// We extract the field types, which in this case are `u8`, `T`, and
// `I::Item`. We re-use the existing parameters and where clauses. If
// `require_trait_bound == true` (as it is for `FromBytes), we add where
// bounds for each field's type:
//
// impl<T, I: Iterator> FromBytes for Foo<T, I>
// where
// T: Copy,
// I: Clone,
// I::Item: Clone,
// T: FromBytes,
// I::Item: FromBytes,
// {
// }
//
// NOTE: It is standard practice to only emit bounds for the type
// parameters themselves, not for field types based on those parameters
// (e.g., `T` vs `T::Foo`). For a discussion of why this is standard
// practice, see https://github.com/rust-lang/rust/issues/26925.
//
// The reason we diverge from this standard is that doing it that way
// for us would be unsound. E.g., consider a type, `T` where `T:
// FromBytes` but `T::Foo: !FromBytes`. It would not be sound for us to
// accept a type with a `T::Foo` field as `FromBytes` simply because `T:
// FromBytes`.
//
// While there's no getting around this requirement for us, it does have
// the pretty serious downside that, when lifetimes are involved, the
// trait solver ties itself in knots:
//
// #[derive(Unaligned)]
// #[repr(C)]
// struct Dup<'a, 'b> {
// a: PhantomData<&'a u8>,
// b: PhantomData<&'b u8>,
// }
//
// error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
// --> src/main.rs:6:10
// |
// 6 | #[derive(Unaligned)]
// | ^^^^^^^^^
// |
// = note: required by `zerocopy::Unaligned`
let type_ident = &self.ctx.ast.ident;
let trait_path = self.trt.crate_path(self.ctx);
let fields = self.data.fields();
let variants = self.data.variants();
let tag = self.data.tag();
let zerocopy_crate = &self.ctx.zerocopy_crate;
fn bound_tt(ty: &Type, traits: impl Iterator<Item = Trait>, ctx: &Ctx) -> WherePredicate {
let traits = traits.map(|t| t.crate_path(ctx));
parse_quote!(#ty: #(#traits)+*)
}
let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
(FieldBounds::All(traits), _) => fields
.iter()
.map(|(_vis, _name, ty)| {
bound_tt(ty, normalize_bounds(&self.trt, traits), self.ctx)
})
.collect(),
(FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
(FieldBounds::Trailing(traits), [.., last]) => {
vec![bound_tt(last.2, normalize_bounds(&self.trt, traits), self.ctx)]
}
(FieldBounds::Explicit(bounds), _) => bounds,
};
let padding_check_bound = self
.padding_check
.map(|check| {
// Parse the repr for `align` and `packed` modifiers. Note that
// `Repr::<PrimitiveRepr, NonZeroU32>` is more permissive than
// what Rust supports for structs, enums, or unions, and thus
// reliably extracts these modifiers for any kind of type.
let repr =
Repr::<PrimitiveRepr, NonZeroU32>::from_attrs(&self.ctx.ast.attrs).unwrap();
let core = self.ctx.core_path();
let option = quote! { #core::option::Option };
let nonzero = quote! { #core::num::NonZeroUsize };
let none = quote! { #option::None::<#nonzero> };
let repr_align =
repr.get_align().map(|spanned| {
let n = spanned.t.get();
quote_spanned! { spanned.span => (#nonzero::new(#n as usize)) }
}).unwrap_or(quote! { (#none) });
let repr_packed =
repr.get_packed().map(|packed| {
let n = packed.get();
quote! { (#nonzero::new(#n as usize)) }
}).unwrap_or(quote! { (#none) });
let variant_types = variants.iter().map(|(_, fields)| {
let types = fields.iter().map(|(_vis, _name, ty)| ty);
quote!([#((#types)),*])
});
let validator_context = check.validator_macro_context();
let (trt, validator_macro) = check.validator_trait_and_macro_idents();
let t = tag.iter();
parse_quote! {
(): #zerocopy_crate::util::macro_util::#trt<
Self,
{
#validator_context
#zerocopy_crate::#validator_macro!(Self, #repr_align, #repr_packed, #(#t,)* #(#variant_types),*)
}
>
}
});
let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
SelfBounds::None => None,
SelfBounds::All(traits) => {
Some(bound_tt(&parse_quote!(Self), traits.iter().cloned(), self.ctx))
}
};
let bounds = self
.ctx
.ast
.generics
.where_clause
.as_ref()
.map(|where_clause| where_clause.predicates.iter())
.into_iter()
.flatten()
.chain(field_type_bounds.iter())
.chain(padding_check_bound.iter())
.chain(self_bounds.iter());
// The parameters with trait bounds, but without type defaults.
let mut params: Vec<_> = self
.ctx
.ast
.generics
.params
.clone()
.into_iter()
.map(|mut param| {
match &mut param {
GenericParam::Type(ty) => ty.default = None,
GenericParam::Const(cnst) => cnst.default = None,
GenericParam::Lifetime(_) => {}
}
parse_quote!(#param)
})
.chain(self.param_extras)
.collect();
// For MSRV purposes, ensure that lifetimes precede types precede const
// generics.
params.sort_by_cached_key(|param| match param {
GenericParam::Lifetime(_) => 0,
GenericParam::Type(_) => 1,
GenericParam::Const(_) => 2,
});
// The identifiers of the parameters without trait bounds or type
// defaults.
let param_idents = self.ctx.ast.generics.params.iter().map(|param| match param {
GenericParam::Type(ty) => {
let ident = &ty.ident;
quote!(#ident)
}
GenericParam::Lifetime(l) => {
let ident = &l.lifetime;
quote!(#ident)
}
GenericParam::Const(cnst) => {
let ident = &cnst.ident;
quote!({#ident})
}
});
let inner_extras = self.inner_extras;
let allow_trivial_bounds =
if self.ctx.skip_on_error { quote!(#[allow(trivial_bounds)]) } else { quote!() };
let impl_tokens = quote! {
#allow_trivial_bounds
unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
where
#(#bounds,)*
{
fn only_derive_is_allowed_to_implement_this_trait() {}
#inner_extras
}
};
let outer_extras = self.outer_extras.filter(|e| !e.is_empty());
let cfg_compile_error = self.ctx.cfg_compile_error();
const_block([Some(cfg_compile_error), Some(impl_tokens), outer_extras])
}
}
// A polyfill for `Option::then_some`, which was added after our MSRV.
//
// The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain
// versions, `b.then_some(...)` resolves to the inherent method rather than to
// this trait, and so this trait is considered unused.
//
// FIXME(#67): Remove this once our MSRV is >= 1.62.
#[allow(unused)]
trait BoolExt {
fn then_some<T>(self, t: T) -> Option<T>;
}
impl BoolExt for bool {
fn then_some<T>(self, t: T) -> Option<T> {
if self {
Some(t)
} else {
None
}
}
}
pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>) -> TokenStream {
let items = items.into_iter().flatten();
quote! {
#[allow(
// FIXME(#553): Add a test that generates a warning when
// `#[allow(deprecated)]` isn't present.
deprecated,
// Required on some rustc versions due to a lint that is only
// triggered when `derive(KnownLayout)` is applied to `repr(C)`
// structs that are generated by macros. See #2177 for details.
private_bounds,
non_local_definitions,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
non_ascii_idents,
clippy::missing_inline_in_public_items,
)]
#[deny(ambiguous_associated_items)]
// While there are not currently any warnings that this suppresses
// (that we're aware of), it's good future-proofing hygiene.
#[automatically_derived]
const _: () = {
#(#items)*
};
}
}
pub(crate) fn generate_tag_enum(ctx: &Ctx, repr: &EnumRepr, data: &DataEnum) -> TokenStream {
let zerocopy_crate = &ctx.zerocopy_crate;
let variants = data.variants.iter().map(|v| {
let ident = &v.ident;
if let Some((eq, discriminant)) = &v.discriminant {
quote! { #ident #eq #discriminant }
} else {
quote! { #ident }
}
});
// Don't include any `repr(align)` when generating the tag enum, as that
// could add padding after the tag but before any variants, which is not the
// correct behavior.
let repr = match repr {
EnumRepr::Transparent(span) => quote::quote_spanned! { *span => #[repr(transparent)] },
EnumRepr::Compound(c, _) => quote! { #c },
};
quote! {
#repr
#[allow(dead_code)]
pub enum ___ZerocopyTag {
#(#variants,)*
}
// SAFETY: `___ZerocopyTag` has no fields, and so it does not permit
// interior mutation.
unsafe impl #zerocopy_crate::Immutable for ___ZerocopyTag {
fn only_derive_is_allowed_to_implement_this_trait() {}
}
}
}
pub(crate) fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
use CompoundRepr::*;
use PrimitiveRepr::*;
use Repr::*;
match repr {
Transparent(span)
| Compound(
Spanned {
t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | 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),
}
}
#[cfg(test)]
pub(crate) mod testutil {
use proc_macro2::TokenStream;
use syn::visit::{self, Visit};
/// Checks for hygiene violations in the generated code.
///
/// # Panics
///
/// Panics if a hygiene violation is found.
pub(crate) fn check_hygiene(ts: TokenStream) {
struct AmbiguousItemVisitor;
impl<'ast> Visit<'ast> for AmbiguousItemVisitor {
fn visit_path(&mut self, i: &'ast syn::Path) {
if i.segments.len() > 1 && i.segments.first().unwrap().ident == "Self" {
panic!(
"Found ambiguous path `{}` in generated output. \
All associated item access must be fully qualified (e.g., `<Self as Trait>::Item`) \
to prevent hygiene issues.",
quote::quote!(#i)
);
}
visit::visit_path(self, i);
}
}
let file = syn::parse2::<syn::File>(ts).expect("failed to parse generated output as File");
AmbiguousItemVisitor.visit_file(&file);
}
#[test]
fn test_check_hygiene_success() {
check_hygiene(quote::quote! {
fn foo() {
let _ = <Self as Trait>::Item;
}
});
}
#[test]
#[should_panic(expected = "Found ambiguous path `Self :: Ambiguous`")]
fn test_check_hygiene_failure() {
check_hygiene(quote::quote! {
fn foo() {
let _ = Self::Ambiguous;
}
});
}
}