| // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT | |
| // | |
| 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!(("__z{}", 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!(("__z{}", 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_id, | |
| 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 | |
| } | |
| ) | |
| } |