15f85604cSMiguel Ojeda // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT 25f85604cSMiguel Ojeda 3b437b383SMiguel Ojeda use proc_macro2::TokenStream; 4b437b383SMiguel Ojeda use quote::quote; 5b437b383SMiguel Ojeda use syn::{ 6b437b383SMiguel Ojeda parse_quote, spanned::Spanned as _, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, 7b437b383SMiguel Ojeda Expr, Fields, Ident, Index, Type, 8b437b383SMiguel Ojeda }; 9b437b383SMiguel Ojeda 10b437b383SMiguel Ojeda use crate::{ 11b437b383SMiguel Ojeda repr::{EnumRepr, StructUnionRepr}, 12b437b383SMiguel Ojeda util::{ 13b437b383SMiguel Ojeda const_block, enum_size_from_repr, generate_tag_enum, Ctx, DataExt, FieldBounds, 14b437b383SMiguel Ojeda ImplBlockBuilder, Trait, TraitBound, 15b437b383SMiguel Ojeda }, 16b437b383SMiguel Ojeda }; 17b437b383SMiguel Ojeda fn tag_ident(variant_ident: &Ident) -> Ident { 18b437b383SMiguel Ojeda ident!(("___ZEROCOPY_TAG_{}", variant_ident), variant_ident.span()) 19b437b383SMiguel Ojeda } 20b437b383SMiguel Ojeda 21b437b383SMiguel Ojeda /// Generates a constant for the tag associated with each variant of the enum. 22b437b383SMiguel Ojeda /// When we match on the enum's tag, each arm matches one of these constants. We 23b437b383SMiguel Ojeda /// have to use constants here because: 24b437b383SMiguel Ojeda /// 25b437b383SMiguel Ojeda /// - The type that we're matching on is not the type of the tag, it's an 26b437b383SMiguel Ojeda /// integer of the same size as the tag type and with the same bit patterns. 27b437b383SMiguel Ojeda /// - We can't read the enum tag as an enum because the bytes may not represent 28b437b383SMiguel Ojeda /// a valid variant. 29b437b383SMiguel Ojeda /// - Patterns do not currently support const expressions, so we have to assign 30b437b383SMiguel Ojeda /// these constants to names rather than use them inline in the `match` 31b437b383SMiguel Ojeda /// statement. 32b437b383SMiguel Ojeda fn generate_tag_consts(data: &DataEnum) -> TokenStream { 33b437b383SMiguel Ojeda let tags = data.variants.iter().map(|v| { 34b437b383SMiguel Ojeda let variant_ident = &v.ident; 35b437b383SMiguel Ojeda let tag_ident = tag_ident(variant_ident); 36b437b383SMiguel Ojeda 37b437b383SMiguel Ojeda quote! { 38b437b383SMiguel Ojeda // This casts the enum variant to its discriminant, and then 39b437b383SMiguel Ojeda // converts the discriminant to the target integral type via a 40b437b383SMiguel Ojeda // numeric cast [1]. 41b437b383SMiguel Ojeda // 42b437b383SMiguel Ojeda // Because these are the same size, this is defined to be a no-op 43b437b383SMiguel Ojeda // and therefore is a lossless conversion [2]. 44b437b383SMiguel Ojeda // 45b437b383SMiguel Ojeda // [1] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#enum-cast: 46b437b383SMiguel Ojeda // 47b437b383SMiguel Ojeda // Casts an enum to its discriminant. 48b437b383SMiguel Ojeda // 49b437b383SMiguel Ojeda // [2] Per https://doc.rust-lang.org/1.81.0/reference/expressions/operator-expr.html#numeric-cast: 50b437b383SMiguel Ojeda // 51b437b383SMiguel Ojeda // Casting between two integers of the same size (e.g. i32 -> u32) 52b437b383SMiguel Ojeda // is a no-op. 53b437b383SMiguel Ojeda const #tag_ident: ___ZerocopyTagPrimitive = 54b437b383SMiguel Ojeda ___ZerocopyTag::#variant_ident as ___ZerocopyTagPrimitive; 55b437b383SMiguel Ojeda } 56b437b383SMiguel Ojeda }); 57b437b383SMiguel Ojeda 58b437b383SMiguel Ojeda quote! { 59b437b383SMiguel Ojeda #(#tags)* 60b437b383SMiguel Ojeda } 61b437b383SMiguel Ojeda } 62b437b383SMiguel Ojeda 63b437b383SMiguel Ojeda fn variant_struct_ident(variant_ident: &Ident) -> Ident { 64b437b383SMiguel Ojeda ident!(("___ZerocopyVariantStruct_{}", variant_ident), variant_ident.span()) 65b437b383SMiguel Ojeda } 66b437b383SMiguel Ojeda 67b437b383SMiguel Ojeda /// Generates variant structs for the given enum variant. 68b437b383SMiguel Ojeda /// 69b437b383SMiguel Ojeda /// These are structs associated with each variant of an enum. They are 70b437b383SMiguel Ojeda /// `repr(C)` tuple structs with the same fields as the variant after a 71b437b383SMiguel Ojeda /// `MaybeUninit<___ZerocopyInnerTag>`. 72b437b383SMiguel Ojeda /// 73b437b383SMiguel Ojeda /// In order to unify the generated types for `repr(C)` and `repr(int)` enums, 74b437b383SMiguel Ojeda /// we use a "fused" representation with fields for both an inner tag and an 75b437b383SMiguel Ojeda /// outer tag. Depending on the repr, we will set one of these tags to the tag 76b437b383SMiguel Ojeda /// type and the other to `()`. This lets us generate the same code but put the 77b437b383SMiguel Ojeda /// tags in different locations. 78b437b383SMiguel Ojeda fn generate_variant_structs(ctx: &Ctx, data: &DataEnum) -> TokenStream { 79b437b383SMiguel Ojeda let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 80b437b383SMiguel Ojeda 81b437b383SMiguel Ojeda let enum_name = &ctx.ast.ident; 82b437b383SMiguel Ojeda 83b437b383SMiguel Ojeda // All variant structs have a `PhantomData<MyEnum<...>>` field because we 84b437b383SMiguel Ojeda // don't know which generic parameters each variant will use, and unused 85b437b383SMiguel Ojeda // generic parameters are a compile error. 86b437b383SMiguel Ojeda let core = ctx.core_path(); 87b437b383SMiguel Ojeda let phantom_ty = quote! { 88b437b383SMiguel Ojeda #core::marker::PhantomData<#enum_name #ty_generics> 89b437b383SMiguel Ojeda }; 90b437b383SMiguel Ojeda 91b437b383SMiguel Ojeda let variant_structs = data.variants.iter().filter_map(|variant| { 92b437b383SMiguel Ojeda // We don't generate variant structs for unit variants because we only 93b437b383SMiguel Ojeda // need to check the tag. This helps cut down our generated code a bit. 94b437b383SMiguel Ojeda if matches!(variant.fields, Fields::Unit) { 95b437b383SMiguel Ojeda return None; 96b437b383SMiguel Ojeda } 97b437b383SMiguel Ojeda 98b437b383SMiguel Ojeda let variant_struct_ident = variant_struct_ident(&variant.ident); 99b437b383SMiguel Ojeda let field_types = variant.fields.iter().map(|f| &f.ty); 100b437b383SMiguel Ojeda 101b437b383SMiguel Ojeda let variant_struct = parse_quote! { 102b437b383SMiguel Ojeda #[repr(C)] 103b437b383SMiguel Ojeda struct #variant_struct_ident #impl_generics ( 104b437b383SMiguel Ojeda #core::mem::MaybeUninit<___ZerocopyInnerTag>, 105b437b383SMiguel Ojeda #(#field_types,)* 106b437b383SMiguel Ojeda #phantom_ty, 107b437b383SMiguel Ojeda ) #where_clause; 108b437b383SMiguel Ojeda }; 109b437b383SMiguel Ojeda 110b437b383SMiguel Ojeda // We do this rather than emitting `#[derive(::zerocopy::TryFromBytes)]` 111b437b383SMiguel Ojeda // because that is not hygienic, and this is also more performant. 112b437b383SMiguel Ojeda let try_from_bytes_impl = 113b437b383SMiguel Ojeda derive_try_from_bytes(&ctx.with_input(&variant_struct), Trait::TryFromBytes) 114b437b383SMiguel Ojeda .expect("derive_try_from_bytes should not fail on synthesized type"); 115b437b383SMiguel Ojeda 116b437b383SMiguel Ojeda Some(quote! { 117b437b383SMiguel Ojeda #variant_struct 118b437b383SMiguel Ojeda #try_from_bytes_impl 119b437b383SMiguel Ojeda }) 120b437b383SMiguel Ojeda }); 121b437b383SMiguel Ojeda 122b437b383SMiguel Ojeda quote! { 123b437b383SMiguel Ojeda #(#variant_structs)* 124b437b383SMiguel Ojeda } 125b437b383SMiguel Ojeda } 126b437b383SMiguel Ojeda 127b437b383SMiguel Ojeda fn variants_union_field_ident(ident: &Ident) -> Ident { 128b437b383SMiguel Ojeda // Field names are prefixed with `__field_` to prevent name collision 129b437b383SMiguel Ojeda // with the `__nonempty` field. 130b437b383SMiguel Ojeda ident!(("__field_{}", ident), ident.span()) 131b437b383SMiguel Ojeda } 132b437b383SMiguel Ojeda 133b437b383SMiguel Ojeda fn generate_variants_union(ctx: &Ctx, data: &DataEnum) -> TokenStream { 134b437b383SMiguel Ojeda let generics = &ctx.ast.generics; 135b437b383SMiguel Ojeda let (_, ty_generics, _) = generics.split_for_impl(); 136b437b383SMiguel Ojeda 137b437b383SMiguel Ojeda let fields = data.variants.iter().filter_map(|variant| { 138b437b383SMiguel Ojeda // We don't generate variant structs for unit variants because we only 139b437b383SMiguel Ojeda // need to check the tag. This helps cut down our generated code a bit. 140b437b383SMiguel Ojeda if matches!(variant.fields, Fields::Unit) { 141b437b383SMiguel Ojeda return None; 142b437b383SMiguel Ojeda } 143b437b383SMiguel Ojeda 144b437b383SMiguel Ojeda let field_name = variants_union_field_ident(&variant.ident); 145b437b383SMiguel Ojeda let variant_struct_ident = variant_struct_ident(&variant.ident); 146b437b383SMiguel Ojeda 147b437b383SMiguel Ojeda let core = ctx.core_path(); 148b437b383SMiguel Ojeda Some(quote! { 149b437b383SMiguel Ojeda #field_name: #core::mem::ManuallyDrop<#variant_struct_ident #ty_generics>, 150b437b383SMiguel Ojeda }) 151b437b383SMiguel Ojeda }); 152b437b383SMiguel Ojeda 153b437b383SMiguel Ojeda let variants_union = parse_quote! { 154b437b383SMiguel Ojeda #[repr(C)] 155b437b383SMiguel Ojeda union ___ZerocopyVariants #generics { 156b437b383SMiguel Ojeda #(#fields)* 157b437b383SMiguel Ojeda // Enums can have variants with no fields, but unions must 158b437b383SMiguel Ojeda // have at least one field. So we just add a trailing unit 159b437b383SMiguel Ojeda // to ensure that this union always has at least one field. 160b437b383SMiguel Ojeda // Because this union is `repr(C)`, this unit type does not 161b437b383SMiguel Ojeda // affect the layout. 162b437b383SMiguel Ojeda __nonempty: (), 163b437b383SMiguel Ojeda } 164b437b383SMiguel Ojeda }; 165b437b383SMiguel Ojeda 166b437b383SMiguel Ojeda let has_field = 167b437b383SMiguel Ojeda derive_has_field_struct_union(&ctx.with_input(&variants_union), &variants_union.data); 168b437b383SMiguel Ojeda 169b437b383SMiguel Ojeda quote! { 170b437b383SMiguel Ojeda #variants_union 171b437b383SMiguel Ojeda #has_field 172b437b383SMiguel Ojeda } 173b437b383SMiguel Ojeda } 174b437b383SMiguel Ojeda 175b437b383SMiguel Ojeda /// Generates an implementation of `is_bit_valid` for an arbitrary enum. 176b437b383SMiguel Ojeda /// 177b437b383SMiguel Ojeda /// The general process is: 178b437b383SMiguel Ojeda /// 179b437b383SMiguel Ojeda /// 1. Generate a tag enum. This is an enum with the same repr, variants, and 180b437b383SMiguel Ojeda /// corresponding discriminants as the original enum, but without any fields 181b437b383SMiguel Ojeda /// on the variants. This gives us access to an enum where the variants have 182b437b383SMiguel Ojeda /// the same discriminants as the one we're writing `is_bit_valid` for. 183b437b383SMiguel Ojeda /// 2. Make constants from the variants of the tag enum. We need these because 184b437b383SMiguel Ojeda /// we can't put const exprs in match arms. 185b437b383SMiguel Ojeda /// 3. Generate variant structs. These are structs which have the same fields as 186b437b383SMiguel Ojeda /// each variant of the enum, and are `#[repr(C)]` with an optional "inner 187b437b383SMiguel Ojeda /// tag". 188b437b383SMiguel Ojeda /// 4. Generate a variants union, with one field for each variant struct type. 189b437b383SMiguel Ojeda /// 5. And finally, our raw enum is a `#[repr(C)]` struct of an "outer tag" and 190b437b383SMiguel Ojeda /// the variants union. 191b437b383SMiguel Ojeda /// 192b437b383SMiguel Ojeda /// See these reference links for fully-worked example decompositions. 193b437b383SMiguel Ojeda /// 194b437b383SMiguel Ojeda /// - `repr(C)`: <https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields> 195b437b383SMiguel Ojeda /// - `repr(int)`: <https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields> 196b437b383SMiguel Ojeda /// - `repr(C, int)`: <https://doc.rust-lang.org/reference/type-layout.html#combining-primitive-representations-of-enums-with-fields-and-reprc> 197b437b383SMiguel Ojeda pub(crate) fn derive_is_bit_valid( 198b437b383SMiguel Ojeda ctx: &Ctx, 199b437b383SMiguel Ojeda data: &DataEnum, 200b437b383SMiguel Ojeda repr: &EnumRepr, 201b437b383SMiguel Ojeda ) -> Result<TokenStream, Error> { 202b437b383SMiguel Ojeda let trait_path = Trait::TryFromBytes.crate_path(ctx); 203b437b383SMiguel Ojeda let tag_enum = generate_tag_enum(ctx, repr, data); 204b437b383SMiguel Ojeda let tag_consts = generate_tag_consts(data); 205b437b383SMiguel Ojeda 206b437b383SMiguel Ojeda let (outer_tag_type, inner_tag_type) = if repr.is_c() { 207b437b383SMiguel Ojeda (quote! { ___ZerocopyTag }, quote! { () }) 208b437b383SMiguel Ojeda } else if repr.is_primitive() { 209b437b383SMiguel Ojeda (quote! { () }, quote! { ___ZerocopyTag }) 210b437b383SMiguel Ojeda } else { 211b437b383SMiguel Ojeda return Err(Error::new( 212b437b383SMiguel Ojeda ctx.ast.span(), 213b437b383SMiguel Ojeda "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout", 214b437b383SMiguel Ojeda )); 215b437b383SMiguel Ojeda }; 216b437b383SMiguel Ojeda 217b437b383SMiguel Ojeda let variant_structs = generate_variant_structs(ctx, data); 218b437b383SMiguel Ojeda let variants_union = generate_variants_union(ctx, data); 219b437b383SMiguel Ojeda 220b437b383SMiguel Ojeda let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 221b437b383SMiguel Ojeda 222b437b383SMiguel Ojeda let zerocopy_crate = &ctx.zerocopy_crate; 223b437b383SMiguel Ojeda let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None) 224b437b383SMiguel Ojeda .inner_extras(quote! { 225b437b383SMiguel Ojeda type Tag = ___ZerocopyTag; 226b437b383SMiguel Ojeda type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized; 227b437b383SMiguel Ojeda }) 228b437b383SMiguel Ojeda .build(); 229b437b383SMiguel Ojeda let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| { 230b437b383SMiguel Ojeda let variant_ident = &variant.unwrap().ident; 231b437b383SMiguel Ojeda let variants_union_field_ident = variants_union_field_ident(variant_ident); 232b437b383SMiguel Ojeda let field: Box<syn::Type> = parse_quote!(()); 233b437b383SMiguel Ojeda fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| { 234b437b383SMiguel Ojeda // Rust does not presently support explicit visibility modifiers on 235b437b383SMiguel Ojeda // enum fields, but we guard against the possibility to ensure this 236b437b383SMiguel Ojeda // derive remains sound. 237b437b383SMiguel Ojeda assert!(matches!(vis, syn::Visibility::Inherited)); 238b437b383SMiguel Ojeda let variant_struct_field_index = Index::from(idx + 1); 239b437b383SMiguel Ojeda let (_, ty_generics, _) = ctx.ast.generics.split_for_impl(); 240b437b383SMiguel Ojeda let has_field_trait = Trait::HasField { 241b437b383SMiguel Ojeda variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }), 242b437b383SMiguel Ojeda // Since Rust does not presently support explicit visibility 243b437b383SMiguel Ojeda // modifiers on enum fields, any public type is suitable here; 244b437b383SMiguel Ojeda // we use `()`. 245b437b383SMiguel Ojeda field: field.clone(), 246b437b383SMiguel Ojeda field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }), 247b437b383SMiguel Ojeda }; 248b437b383SMiguel Ojeda let has_field_path = has_field_trait.crate_path(ctx); 249b437b383SMiguel Ojeda let has_field = ImplBlockBuilder::new( 250b437b383SMiguel Ojeda ctx, 251b437b383SMiguel Ojeda data, 252b437b383SMiguel Ojeda has_field_trait, 253b437b383SMiguel Ojeda FieldBounds::None, 254b437b383SMiguel Ojeda ) 255b437b383SMiguel Ojeda .inner_extras(quote! { 256b437b383SMiguel Ojeda type Type = #ty; 257b437b383SMiguel Ojeda 258b437b383SMiguel Ojeda #[inline(always)] 259b437b383SMiguel Ojeda fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type { 260b437b383SMiguel Ojeda use #zerocopy_crate::pointer::cast::{CastSized, Projection}; 261b437b383SMiguel Ojeda 262b437b383SMiguel Ojeda slf.project::<___ZerocopyRawEnum #ty_generics, CastSized>() 263b437b383SMiguel Ojeda .project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>>() 264b437b383SMiguel Ojeda .project::<_, Projection<_, { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>>() 265b437b383SMiguel Ojeda .project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(value) }>>() 266b437b383SMiguel Ojeda .project::<_, Projection<_, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>>() 267b437b383SMiguel Ojeda .as_ptr() 268b437b383SMiguel Ojeda } 269b437b383SMiguel Ojeda }) 270b437b383SMiguel Ojeda .build(); 271b437b383SMiguel Ojeda 272b437b383SMiguel Ojeda let project = ImplBlockBuilder::new( 273b437b383SMiguel Ojeda ctx, 274b437b383SMiguel Ojeda data, 275b437b383SMiguel Ojeda Trait::ProjectField { 276b437b383SMiguel Ojeda variant_id: parse_quote!({ #zerocopy_crate::ident_id!(#variant_ident) }), 277b437b383SMiguel Ojeda // Since Rust does not presently support explicit visibility 278b437b383SMiguel Ojeda // modifiers on enum fields, any public type is suitable 279b437b383SMiguel Ojeda // here; we use `()`. 280b437b383SMiguel Ojeda field: field.clone(), 281b437b383SMiguel Ojeda field_id: parse_quote!({ #zerocopy_crate::ident_id!(#ident) }), 282b437b383SMiguel Ojeda invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)), 283b437b383SMiguel Ojeda }, 284b437b383SMiguel Ojeda FieldBounds::None, 285b437b383SMiguel Ojeda ) 286b437b383SMiguel Ojeda .param_extras(vec![ 287b437b383SMiguel Ojeda parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing), 288b437b383SMiguel Ojeda parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment), 289b437b383SMiguel Ojeda ]) 290b437b383SMiguel Ojeda .inner_extras(quote! { 291b437b383SMiguel Ojeda type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible; 292b437b383SMiguel Ojeda type Invariants = (Aliasing, Alignment, #zerocopy_crate::invariant::Initialized); 293b437b383SMiguel Ojeda }) 294b437b383SMiguel Ojeda .build(); 295b437b383SMiguel Ojeda 296b437b383SMiguel Ojeda quote! { 297b437b383SMiguel Ojeda #has_field 298b437b383SMiguel Ojeda #project 299b437b383SMiguel Ojeda } 300b437b383SMiguel Ojeda }) 301b437b383SMiguel Ojeda }); 302b437b383SMiguel Ojeda 303b437b383SMiguel Ojeda let core = ctx.core_path(); 304b437b383SMiguel Ojeda let match_arms = data.variants.iter().map(|variant| { 305b437b383SMiguel Ojeda let tag_ident = tag_ident(&variant.ident); 306b437b383SMiguel Ojeda let variant_struct_ident = variant_struct_ident(&variant.ident); 307b437b383SMiguel Ojeda let variants_union_field_ident = variants_union_field_ident(&variant.ident); 308b437b383SMiguel Ojeda 309b437b383SMiguel Ojeda if matches!(variant.fields, Fields::Unit) { 310b437b383SMiguel Ojeda // Unit variants don't need any further validation beyond checking 311b437b383SMiguel Ojeda // the tag. 312b437b383SMiguel Ojeda quote! { 313b437b383SMiguel Ojeda #tag_ident => true 314b437b383SMiguel Ojeda } 315b437b383SMiguel Ojeda } else { 316b437b383SMiguel Ojeda quote! { 317b437b383SMiguel Ojeda #tag_ident => { 318b437b383SMiguel Ojeda // SAFETY: Since we know that the tag is `#tag_ident`, we 319b437b383SMiguel Ojeda // know that no other `&`s exist which refer to this enum 320b437b383SMiguel Ojeda // as any other variant. 321b437b383SMiguel Ojeda let variant_md = variants.cast::< 322b437b383SMiguel Ojeda _, 323b437b383SMiguel Ojeda #zerocopy_crate::pointer::cast::Projection< 324b437b383SMiguel Ojeda // #zerocopy_crate::ReadOnly<_>, 325b437b383SMiguel Ojeda _, 326b437b383SMiguel Ojeda { #zerocopy_crate::REPR_C_UNION_VARIANT_ID }, 327b437b383SMiguel Ojeda { #zerocopy_crate::ident_id!(#variants_union_field_ident) } 328b437b383SMiguel Ojeda >, 329b437b383SMiguel Ojeda _ 330b437b383SMiguel Ojeda >(); 331b437b383SMiguel Ojeda let variant = variant_md.cast::< 332b437b383SMiguel Ojeda #zerocopy_crate::ReadOnly<#variant_struct_ident #ty_generics>, 333b437b383SMiguel Ojeda #zerocopy_crate::pointer::cast::CastSized, 334b437b383SMiguel Ojeda (#zerocopy_crate::pointer::BecauseRead, _) 335b437b383SMiguel Ojeda >(); 336b437b383SMiguel Ojeda < 337b437b383SMiguel Ojeda #variant_struct_ident #ty_generics as #trait_path 338b437b383SMiguel Ojeda >::is_bit_valid(variant) 339b437b383SMiguel Ojeda } 340b437b383SMiguel Ojeda } 341b437b383SMiguel Ojeda } 342b437b383SMiguel Ojeda }); 343b437b383SMiguel Ojeda 344b437b383SMiguel Ojeda let generics = &ctx.ast.generics; 345b437b383SMiguel Ojeda let raw_enum: DeriveInput = parse_quote! { 346b437b383SMiguel Ojeda #[repr(C)] 347b437b383SMiguel Ojeda struct ___ZerocopyRawEnum #generics { 348b437b383SMiguel Ojeda tag: ___ZerocopyOuterTag, 349b437b383SMiguel Ojeda variants: ___ZerocopyVariants #ty_generics, 350b437b383SMiguel Ojeda } 351b437b383SMiguel Ojeda }; 352b437b383SMiguel Ojeda 353b437b383SMiguel Ojeda let self_ident = &ctx.ast.ident; 354b437b383SMiguel Ojeda let invariants_eq_impl = quote! { 355b437b383SMiguel Ojeda // SAFETY: `___ZerocopyRawEnum` is designed to have the same layout, 356b437b383SMiguel Ojeda // validity, and invariants as `Self`. 357b437b383SMiguel Ojeda unsafe impl #impl_generics #zerocopy_crate::pointer::InvariantsEq<___ZerocopyRawEnum #ty_generics> for #self_ident #ty_generics #where_clause {} 358b437b383SMiguel Ojeda }; 359b437b383SMiguel Ojeda 360b437b383SMiguel Ojeda let raw_enum_projections = 361b437b383SMiguel Ojeda derive_has_field_struct_union(&ctx.with_input(&raw_enum), &raw_enum.data); 362b437b383SMiguel Ojeda 363b437b383SMiguel Ojeda let raw_enum = quote! { 364b437b383SMiguel Ojeda #raw_enum 365b437b383SMiguel Ojeda #invariants_eq_impl 366b437b383SMiguel Ojeda #raw_enum_projections 367b437b383SMiguel Ojeda }; 368b437b383SMiguel Ojeda 369b437b383SMiguel Ojeda Ok(quote! { 370b437b383SMiguel Ojeda // SAFETY: We use `is_bit_valid` to validate that the bit pattern of the 371b437b383SMiguel Ojeda // enum's tag corresponds to one of the enum's discriminants. Then, we 372b437b383SMiguel Ojeda // check the bit validity of each field of the corresponding variant. 373b437b383SMiguel Ojeda // Thus, this is a sound implementation of `is_bit_valid`. 374b437b383SMiguel Ojeda #[inline] 375b437b383SMiguel Ojeda fn is_bit_valid<___ZcAlignment>( 376b437b383SMiguel Ojeda mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 377b437b383SMiguel Ojeda ) -> #core::primitive::bool 378b437b383SMiguel Ojeda where 379b437b383SMiguel Ojeda ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 380b437b383SMiguel Ojeda { 381b437b383SMiguel Ojeda #tag_enum 382b437b383SMiguel Ojeda 383b437b383SMiguel Ojeda type ___ZerocopyTagPrimitive = #zerocopy_crate::util::macro_util::SizeToTag< 384b437b383SMiguel Ojeda { #core::mem::size_of::<___ZerocopyTag>() }, 385b437b383SMiguel Ojeda >; 386b437b383SMiguel Ojeda 387b437b383SMiguel Ojeda #tag_consts 388b437b383SMiguel Ojeda 389b437b383SMiguel Ojeda type ___ZerocopyOuterTag = #outer_tag_type; 390b437b383SMiguel Ojeda type ___ZerocopyInnerTag = #inner_tag_type; 391b437b383SMiguel Ojeda 392b437b383SMiguel Ojeda #variant_structs 393b437b383SMiguel Ojeda 394b437b383SMiguel Ojeda #variants_union 395b437b383SMiguel Ojeda 396b437b383SMiguel Ojeda #raw_enum 397b437b383SMiguel Ojeda 398b437b383SMiguel Ojeda #has_tag 399b437b383SMiguel Ojeda 400b437b383SMiguel Ojeda #(#has_fields)* 401b437b383SMiguel Ojeda 402b437b383SMiguel Ojeda let tag = { 403b437b383SMiguel Ojeda // SAFETY: 404b437b383SMiguel Ojeda // - The provided cast addresses a subset of the bytes addressed 405b437b383SMiguel Ojeda // by `candidate` because it addresses the starting tag of the 406b437b383SMiguel Ojeda // enum. 407b437b383SMiguel Ojeda // - Because the pointer is cast from `candidate`, it has the 408b437b383SMiguel Ojeda // same provenance as it. 409b437b383SMiguel Ojeda // - There are no `UnsafeCell`s in the tag because it is a 410b437b383SMiguel Ojeda // primitive integer. 411b437b383SMiguel Ojeda // - `tag_ptr` is casted from `candidate`, whose referent is 412b437b383SMiguel Ojeda // `Initialized`. Since we have not written uninitialized 413b437b383SMiguel Ojeda // bytes into the referent, `tag_ptr` is also `Initialized`. 414b437b383SMiguel Ojeda // 415b437b383SMiguel Ojeda // FIXME(#2874): Revise this to a `cast` once `candidate` 416b437b383SMiguel Ojeda // references a `ReadOnly<Self>`. 417b437b383SMiguel Ojeda let tag_ptr = unsafe { 418b437b383SMiguel Ojeda candidate.reborrow().project_transmute_unchecked::< 419b437b383SMiguel Ojeda _, 420b437b383SMiguel Ojeda #zerocopy_crate::invariant::Initialized, 421b437b383SMiguel Ojeda #zerocopy_crate::pointer::cast::CastSized 422b437b383SMiguel Ojeda >() 423b437b383SMiguel Ojeda }; 424b437b383SMiguel Ojeda tag_ptr.recall_validity::<_, (_, (_, _))>().read::<#zerocopy_crate::BecauseImmutable>() 425b437b383SMiguel Ojeda }; 426b437b383SMiguel Ojeda 427b437b383SMiguel Ojeda let mut raw_enum = candidate.cast::< 428b437b383SMiguel Ojeda #zerocopy_crate::ReadOnly<___ZerocopyRawEnum #ty_generics>, 429b437b383SMiguel Ojeda #zerocopy_crate::pointer::cast::CastSized, 430b437b383SMiguel Ojeda (#zerocopy_crate::pointer::BecauseRead, _) 431b437b383SMiguel Ojeda >(); 432b437b383SMiguel Ojeda 433b437b383SMiguel Ojeda let variants = #zerocopy_crate::into_inner!(raw_enum.project::< 434b437b383SMiguel Ojeda _, 435b437b383SMiguel Ojeda { #zerocopy_crate::STRUCT_VARIANT_ID }, 436b437b383SMiguel Ojeda { #zerocopy_crate::ident_id!(variants) } 437b437b383SMiguel Ojeda >()); 438b437b383SMiguel Ojeda 439b437b383SMiguel Ojeda match tag { 440b437b383SMiguel Ojeda #(#match_arms,)* 441b437b383SMiguel Ojeda _ => false, 442b437b383SMiguel Ojeda } 443b437b383SMiguel Ojeda } 444b437b383SMiguel Ojeda }) 445b437b383SMiguel Ojeda } 446b437b383SMiguel Ojeda pub(crate) fn derive_try_from_bytes(ctx: &Ctx, top_level: Trait) -> Result<TokenStream, Error> { 447b437b383SMiguel Ojeda match &ctx.ast.data { 448b437b383SMiguel Ojeda Data::Struct(strct) => derive_try_from_bytes_struct(ctx, strct, top_level), 449b437b383SMiguel Ojeda Data::Enum(enm) => derive_try_from_bytes_enum(ctx, enm, top_level), 450b437b383SMiguel Ojeda Data::Union(unn) => Ok(derive_try_from_bytes_union(ctx, unn, top_level)), 451b437b383SMiguel Ojeda } 452b437b383SMiguel Ojeda } 453b437b383SMiguel Ojeda fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream { 454b437b383SMiguel Ojeda let fields = ctx.ast.data.fields(); 455b437b383SMiguel Ojeda if fields.is_empty() { 456b437b383SMiguel Ojeda return quote! {}; 457b437b383SMiguel Ojeda } 458b437b383SMiguel Ojeda 459b437b383SMiguel Ojeda let field_tokens = fields.iter().map(|(vis, ident, _)| { 460*29b2a2b9SMiguel Ojeda let ident = ident!(("__z{}", ident), ident.span()); 461b437b383SMiguel Ojeda quote!( 462b437b383SMiguel Ojeda #vis enum #ident {} 463b437b383SMiguel Ojeda ) 464b437b383SMiguel Ojeda }); 465b437b383SMiguel Ojeda 466b437b383SMiguel Ojeda let zerocopy_crate = &ctx.zerocopy_crate; 467b437b383SMiguel Ojeda let variant_id: Box<Expr> = match &ctx.ast.data { 468b437b383SMiguel Ojeda Data::Struct(_) => parse_quote!({ #zerocopy_crate::STRUCT_VARIANT_ID }), 469b437b383SMiguel Ojeda Data::Union(_) => { 470b437b383SMiguel Ojeda let is_repr_c = StructUnionRepr::from_attrs(&ctx.ast.attrs) 471b437b383SMiguel Ojeda .map(|repr| repr.is_c()) 472b437b383SMiguel Ojeda .unwrap_or(false); 473b437b383SMiguel Ojeda if is_repr_c { 474b437b383SMiguel Ojeda parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID }) 475b437b383SMiguel Ojeda } else { 476b437b383SMiguel Ojeda parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID }) 477b437b383SMiguel Ojeda } 478b437b383SMiguel Ojeda } 479b437b383SMiguel Ojeda _ => unreachable!(), 480b437b383SMiguel Ojeda }; 481b437b383SMiguel Ojeda 482b437b383SMiguel Ojeda let core = ctx.core_path(); 483b437b383SMiguel Ojeda let has_tag = ImplBlockBuilder::new(ctx, data, Trait::HasTag, FieldBounds::None) 484b437b383SMiguel Ojeda .inner_extras(quote! { 485b437b383SMiguel Ojeda type Tag = (); 486b437b383SMiguel Ojeda type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit; 487b437b383SMiguel Ojeda }) 488b437b383SMiguel Ojeda .build(); 489b437b383SMiguel Ojeda let has_fields = fields.iter().map(move |(_, ident, ty)| { 490*29b2a2b9SMiguel Ojeda let field_token = ident!(("__z{}", ident), ident.span()); 491b437b383SMiguel Ojeda let field: Box<Type> = parse_quote!(#field_token); 492b437b383SMiguel Ojeda let field_id: Box<Expr> = parse_quote!({ #zerocopy_crate::ident_id!(#ident) }); 493b437b383SMiguel Ojeda let has_field_trait = Trait::HasField { 494b437b383SMiguel Ojeda variant_id: variant_id.clone(), 495b437b383SMiguel Ojeda field: field.clone(), 496b437b383SMiguel Ojeda field_id: field_id.clone(), 497b437b383SMiguel Ojeda }; 498b437b383SMiguel Ojeda let has_field_path = has_field_trait.crate_path(ctx); 499b437b383SMiguel Ojeda ImplBlockBuilder::new( 500b437b383SMiguel Ojeda ctx, 501b437b383SMiguel Ojeda data, 502b437b383SMiguel Ojeda has_field_trait, 503b437b383SMiguel Ojeda FieldBounds::None, 504b437b383SMiguel Ojeda ) 505b437b383SMiguel Ojeda .inner_extras(quote! { 506b437b383SMiguel Ojeda type Type = #ty; 507b437b383SMiguel Ojeda 508b437b383SMiguel Ojeda #[inline(always)] 509b437b383SMiguel Ojeda fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type { 510b437b383SMiguel Ojeda let slf = slf.as_ptr(); 511b437b383SMiguel Ojeda // SAFETY: By invariant on `PtrInner`, `slf` is a non-null 512b437b383SMiguel Ojeda // pointer whose referent is zero-sized or lives in a valid 513b437b383SMiguel Ojeda // allocation. Since `#ident` is a struct or union field of 514b437b383SMiguel Ojeda // `Self`, this projection preserves or shrinks the referent 515b437b383SMiguel Ojeda // size, and so the resulting referent also fits in the same 516b437b383SMiguel Ojeda // allocation. 517b437b383SMiguel Ojeda unsafe { #core::ptr::addr_of_mut!((*slf).#ident) } 518b437b383SMiguel Ojeda } 519b437b383SMiguel Ojeda }).outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) { 520b437b383SMiguel Ojeda let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs) 521b437b383SMiguel Ojeda .map(|repr| repr.get_packed().is_none()) 522b437b383SMiguel Ojeda .unwrap(); 523b437b383SMiguel Ojeda let alignment = if fields_preserve_alignment { 524b437b383SMiguel Ojeda quote! { Alignment } 525b437b383SMiguel Ojeda } else { 526b437b383SMiguel Ojeda quote! { #zerocopy_crate::invariant::Unaligned } 527b437b383SMiguel Ojeda }; 528b437b383SMiguel Ojeda // SAFETY: See comments on items. 529b437b383SMiguel Ojeda ImplBlockBuilder::new( 530b437b383SMiguel Ojeda ctx, 531b437b383SMiguel Ojeda data, 532b437b383SMiguel Ojeda Trait::ProjectField { 533b437b383SMiguel Ojeda variant_id: variant_id.clone(), 534b437b383SMiguel Ojeda field: field.clone(), 535b437b383SMiguel Ojeda field_id: field_id.clone(), 536b437b383SMiguel Ojeda invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)), 537b437b383SMiguel Ojeda }, 538b437b383SMiguel Ojeda FieldBounds::None, 539b437b383SMiguel Ojeda ) 540b437b383SMiguel Ojeda .param_extras(vec![ 541b437b383SMiguel Ojeda parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing), 542b437b383SMiguel Ojeda parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment), 543b437b383SMiguel Ojeda ]) 544b437b383SMiguel Ojeda .inner_extras(quote! { 545b437b383SMiguel Ojeda // SAFETY: Projection into structs is always infallible. 546b437b383SMiguel Ojeda type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible; 547b437b383SMiguel Ojeda // SAFETY: The alignment of the projected `Ptr` is `Unaligned` 548b437b383SMiguel Ojeda // if the structure is packed; otherwise inherited from the 549b437b383SMiguel Ojeda // outer `Ptr`. If the validity of the outer pointer is 550b437b383SMiguel Ojeda // `Initialized`, so too is the validity of its fields. 551b437b383SMiguel Ojeda type Invariants = (Aliasing, #alignment, #zerocopy_crate::invariant::Initialized); 552b437b383SMiguel Ojeda }) 553b437b383SMiguel Ojeda .build() 554b437b383SMiguel Ojeda } else { 555b437b383SMiguel Ojeda quote! {} 556b437b383SMiguel Ojeda }) 557b437b383SMiguel Ojeda .build() 558b437b383SMiguel Ojeda }); 559b437b383SMiguel Ojeda 560b437b383SMiguel Ojeda const_block(field_tokens.into_iter().chain(Some(has_tag)).chain(has_fields).map(Some)) 561b437b383SMiguel Ojeda } 562b437b383SMiguel Ojeda fn derive_try_from_bytes_struct( 563b437b383SMiguel Ojeda ctx: &Ctx, 564b437b383SMiguel Ojeda strct: &DataStruct, 565b437b383SMiguel Ojeda top_level: Trait, 566b437b383SMiguel Ojeda ) -> Result<TokenStream, Error> { 567b437b383SMiguel Ojeda let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| { 568b437b383SMiguel Ojeda let zerocopy_crate = &ctx.zerocopy_crate; 569b437b383SMiguel Ojeda let fields = strct.fields(); 570b437b383SMiguel Ojeda let field_names = fields.iter().map(|(_vis, name, _ty)| name); 571b437b383SMiguel Ojeda let field_tys = fields.iter().map(|(_vis, _name, ty)| ty); 572b437b383SMiguel Ojeda let core = ctx.core_path(); 573b437b383SMiguel Ojeda quote!( 574b437b383SMiguel Ojeda // SAFETY: We use `is_bit_valid` to validate that each field is 575b437b383SMiguel Ojeda // bit-valid, and only return `true` if all of them are. The bit 576b437b383SMiguel Ojeda // validity of a struct is just the composition of the bit 577b437b383SMiguel Ojeda // validities of its fields, so this is a sound implementation 578b437b383SMiguel Ojeda // of `is_bit_valid`. 579b437b383SMiguel Ojeda #[inline] 580b437b383SMiguel Ojeda fn is_bit_valid<___ZcAlignment>( 581b437b383SMiguel Ojeda mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 582b437b383SMiguel Ojeda ) -> #core::primitive::bool 583b437b383SMiguel Ojeda where 584b437b383SMiguel Ojeda ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 585b437b383SMiguel Ojeda { 586b437b383SMiguel Ojeda true #(&& { 587b437b383SMiguel Ojeda let field_candidate = #zerocopy_crate::into_inner!(candidate.reborrow().project::< 588b437b383SMiguel Ojeda _, 589b437b383SMiguel Ojeda { #zerocopy_crate::STRUCT_VARIANT_ID }, 590b437b383SMiguel Ojeda { #zerocopy_crate::ident_id!(#field_names) } 591b437b383SMiguel Ojeda >()); 592b437b383SMiguel Ojeda <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate) 593b437b383SMiguel Ojeda })* 594b437b383SMiguel Ojeda } 595b437b383SMiguel Ojeda ) 596b437b383SMiguel Ojeda }); 597b437b383SMiguel Ojeda Ok(ImplBlockBuilder::new(ctx, strct, Trait::TryFromBytes, FieldBounds::ALL_SELF) 598b437b383SMiguel Ojeda .inner_extras(extras) 599b437b383SMiguel Ojeda .outer_extras(derive_has_field_struct_union(ctx, strct)) 600b437b383SMiguel Ojeda .build()) 601b437b383SMiguel Ojeda } 602b437b383SMiguel Ojeda fn derive_try_from_bytes_union(ctx: &Ctx, unn: &DataUnion, top_level: Trait) -> TokenStream { 603b437b383SMiguel Ojeda let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf]); 604b437b383SMiguel Ojeda 605b437b383SMiguel Ojeda let zerocopy_crate = &ctx.zerocopy_crate; 606b437b383SMiguel Ojeda let variant_id: Box<Expr> = { 607b437b383SMiguel Ojeda let is_repr_c = 608b437b383SMiguel Ojeda StructUnionRepr::from_attrs(&ctx.ast.attrs).map(|repr| repr.is_c()).unwrap_or(false); 609b437b383SMiguel Ojeda if is_repr_c { 610b437b383SMiguel Ojeda parse_quote!({ #zerocopy_crate::REPR_C_UNION_VARIANT_ID }) 611b437b383SMiguel Ojeda } else { 612b437b383SMiguel Ojeda parse_quote!({ #zerocopy_crate::UNION_VARIANT_ID }) 613b437b383SMiguel Ojeda } 614b437b383SMiguel Ojeda }; 615b437b383SMiguel Ojeda 616b437b383SMiguel Ojeda let extras = try_gen_trivial_is_bit_valid(ctx, top_level).unwrap_or_else(|| { 617b437b383SMiguel Ojeda let fields = unn.fields(); 618b437b383SMiguel Ojeda let field_names = fields.iter().map(|(_vis, name, _ty)| name); 619b437b383SMiguel Ojeda let field_tys = fields.iter().map(|(_vis, _name, ty)| ty); 620b437b383SMiguel Ojeda let core = ctx.core_path(); 621b437b383SMiguel Ojeda quote!( 622b437b383SMiguel Ojeda // SAFETY: We use `is_bit_valid` to validate that any field is 623b437b383SMiguel Ojeda // bit-valid; we only return `true` if at least one of them is. 624b437b383SMiguel Ojeda // The bit validity of a union is not yet well defined in Rust, 625b437b383SMiguel Ojeda // but it is guaranteed to be no more strict than this 626b437b383SMiguel Ojeda // definition. See #696 for a more in-depth discussion. 627b437b383SMiguel Ojeda #[inline] 628b437b383SMiguel Ojeda fn is_bit_valid<___ZcAlignment>( 629b437b383SMiguel Ojeda mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 630b437b383SMiguel Ojeda ) -> #core::primitive::bool 631b437b383SMiguel Ojeda where 632b437b383SMiguel Ojeda ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 633b437b383SMiguel Ojeda { 634b437b383SMiguel Ojeda false #(|| { 635b437b383SMiguel Ojeda // SAFETY: 636b437b383SMiguel Ojeda // - Since `ReadOnly<Self>: Immutable` unconditionally, 637b437b383SMiguel Ojeda // neither `*slf` nor the returned pointer's referent 638b437b383SMiguel Ojeda // permit interior mutation. 639b437b383SMiguel Ojeda // - Both source and destination validity are 640b437b383SMiguel Ojeda // `Initialized`, which is always a sound 641b437b383SMiguel Ojeda // transmutation. 642b437b383SMiguel Ojeda let field_candidate = unsafe { 643b437b383SMiguel Ojeda candidate.reborrow().project_transmute_unchecked::< 644b437b383SMiguel Ojeda _, 645b437b383SMiguel Ojeda _, 646b437b383SMiguel Ojeda #zerocopy_crate::pointer::cast::Projection< 647b437b383SMiguel Ojeda _, 648b437b383SMiguel Ojeda #variant_id, 649b437b383SMiguel Ojeda { #zerocopy_crate::ident_id!(#field_names) } 650b437b383SMiguel Ojeda > 651b437b383SMiguel Ojeda >() 652b437b383SMiguel Ojeda }; 653b437b383SMiguel Ojeda 654b437b383SMiguel Ojeda <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate) 655b437b383SMiguel Ojeda })* 656b437b383SMiguel Ojeda } 657b437b383SMiguel Ojeda ) 658b437b383SMiguel Ojeda }); 659b437b383SMiguel Ojeda ImplBlockBuilder::new(ctx, unn, Trait::TryFromBytes, field_type_trait_bounds) 660b437b383SMiguel Ojeda .inner_extras(extras) 661b437b383SMiguel Ojeda .outer_extras(derive_has_field_struct_union(ctx, unn)) 662b437b383SMiguel Ojeda .build() 663b437b383SMiguel Ojeda } 664b437b383SMiguel Ojeda fn derive_try_from_bytes_enum( 665b437b383SMiguel Ojeda ctx: &Ctx, 666b437b383SMiguel Ojeda enm: &DataEnum, 667b437b383SMiguel Ojeda top_level: Trait, 668b437b383SMiguel Ojeda ) -> Result<TokenStream, Error> { 669b437b383SMiguel Ojeda let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?; 670b437b383SMiguel Ojeda 671b437b383SMiguel Ojeda // If an enum has no fields, it has a well-defined integer representation, 672b437b383SMiguel Ojeda // and every possible bit pattern corresponds to a valid discriminant tag, 673b437b383SMiguel Ojeda // then it *could* be `FromBytes` (even if the user hasn't derived 674b437b383SMiguel Ojeda // `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N 675b437b383SMiguel Ojeda // variants. 676b437b383SMiguel Ojeda let could_be_from_bytes = enum_size_from_repr(&repr) 677b437b383SMiguel Ojeda .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size) 678b437b383SMiguel Ojeda .unwrap_or(false); 679b437b383SMiguel Ojeda 680b437b383SMiguel Ojeda let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ctx, top_level); 681b437b383SMiguel Ojeda let extra = match (trivial_is_bit_valid, could_be_from_bytes) { 682b437b383SMiguel Ojeda (Some(is_bit_valid), _) => is_bit_valid, 683b437b383SMiguel Ojeda // SAFETY: It would be sound for the enum to implement `FromBytes`, as 684b437b383SMiguel Ojeda // required by `gen_trivial_is_bit_valid_unchecked`. 685b437b383SMiguel Ojeda (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(ctx) }, 686b437b383SMiguel Ojeda (None, false) => match derive_is_bit_valid(ctx, enm, &repr) { 687b437b383SMiguel Ojeda Ok(extra) => extra, 688b437b383SMiguel Ojeda Err(_) if ctx.skip_on_error => return Ok(TokenStream::new()), 689b437b383SMiguel Ojeda Err(e) => return Err(e), 690b437b383SMiguel Ojeda }, 691b437b383SMiguel Ojeda }; 692b437b383SMiguel Ojeda 693b437b383SMiguel Ojeda Ok(ImplBlockBuilder::new(ctx, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF) 694b437b383SMiguel Ojeda .inner_extras(extra) 695b437b383SMiguel Ojeda .build()) 696b437b383SMiguel Ojeda } 697b437b383SMiguel Ojeda fn try_gen_trivial_is_bit_valid(ctx: &Ctx, top_level: Trait) -> Option<proc_macro2::TokenStream> { 698b437b383SMiguel Ojeda // If the top-level trait is `FromBytes` and `Self` has no type parameters, 699b437b383SMiguel Ojeda // then the `FromBytes` derive will fail compilation if `Self` is not 700b437b383SMiguel Ojeda // actually soundly `FromBytes`, and so we can rely on that for our 701b437b383SMiguel Ojeda // `is_bit_valid` impl. It's plausible that we could make changes - or Rust 702b437b383SMiguel Ojeda // could make changes (such as the "trivial bounds" language feature) - that 703b437b383SMiguel Ojeda // make this no longer true. To hedge against these, we include an explicit 704b437b383SMiguel Ojeda // `Self: FromBytes` check in the generated `is_bit_valid`, which is 705b437b383SMiguel Ojeda // bulletproof. 706b437b383SMiguel Ojeda // 707b437b383SMiguel Ojeda // If `ctx.skip_on_error` is true, we can't rely on the `FromBytes` derive 708b437b383SMiguel Ojeda // to fail compilation if `Self` is not actually soundly `FromBytes`. 709b437b383SMiguel Ojeda if matches!(top_level, Trait::FromBytes) 710b437b383SMiguel Ojeda && ctx.ast.generics.params.is_empty() 711b437b383SMiguel Ojeda && !ctx.skip_on_error 712b437b383SMiguel Ojeda { 713b437b383SMiguel Ojeda let zerocopy_crate = &ctx.zerocopy_crate; 714b437b383SMiguel Ojeda let core = ctx.core_path(); 715b437b383SMiguel Ojeda Some(quote!( 716b437b383SMiguel Ojeda // SAFETY: See inline. 717b437b383SMiguel Ojeda #[inline(always)] 718b437b383SMiguel Ojeda fn is_bit_valid<___ZcAlignment>( 719b437b383SMiguel Ojeda _candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 720b437b383SMiguel Ojeda ) -> #core::primitive::bool 721b437b383SMiguel Ojeda where 722b437b383SMiguel Ojeda ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 723b437b383SMiguel Ojeda { 724b437b383SMiguel Ojeda if false { 725b437b383SMiguel Ojeda fn assert_is_from_bytes<T>() 726b437b383SMiguel Ojeda where 727b437b383SMiguel Ojeda T: #zerocopy_crate::FromBytes, 728b437b383SMiguel Ojeda T: ?#core::marker::Sized, 729b437b383SMiguel Ojeda { 730b437b383SMiguel Ojeda } 731b437b383SMiguel Ojeda 732b437b383SMiguel Ojeda assert_is_from_bytes::<Self>(); 733b437b383SMiguel Ojeda } 734b437b383SMiguel Ojeda 735b437b383SMiguel Ojeda // SAFETY: The preceding code only compiles if `Self: 736b437b383SMiguel Ojeda // FromBytes`. Thus, this code only compiles if all initialized 737b437b383SMiguel Ojeda // byte sequences represent valid instances of `Self`. 738b437b383SMiguel Ojeda true 739b437b383SMiguel Ojeda } 740b437b383SMiguel Ojeda )) 741b437b383SMiguel Ojeda } else { 742b437b383SMiguel Ojeda None 743b437b383SMiguel Ojeda } 744b437b383SMiguel Ojeda } 745b437b383SMiguel Ojeda 746b437b383SMiguel Ojeda /// # Safety 747b437b383SMiguel Ojeda /// 748b437b383SMiguel Ojeda /// All initialized bit patterns must be valid for `Self`. 749b437b383SMiguel Ojeda unsafe fn gen_trivial_is_bit_valid_unchecked(ctx: &Ctx) -> proc_macro2::TokenStream { 750b437b383SMiguel Ojeda let zerocopy_crate = &ctx.zerocopy_crate; 751b437b383SMiguel Ojeda let core = ctx.core_path(); 752b437b383SMiguel Ojeda quote!( 753b437b383SMiguel Ojeda // SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has 754b437b383SMiguel Ojeda // promised that all initialized bit patterns are valid for `Self`. 755b437b383SMiguel Ojeda #[inline(always)] 756b437b383SMiguel Ojeda fn is_bit_valid<___ZcAlignment>( 757b437b383SMiguel Ojeda _candidate: #zerocopy_crate::Maybe<'_, Self, ___ZcAlignment>, 758b437b383SMiguel Ojeda ) -> #core::primitive::bool 759b437b383SMiguel Ojeda where 760b437b383SMiguel Ojeda ___ZcAlignment: #zerocopy_crate::invariant::Alignment, 761b437b383SMiguel Ojeda { 762b437b383SMiguel Ojeda true 763b437b383SMiguel Ojeda } 764b437b383SMiguel Ojeda ) 765b437b383SMiguel Ojeda } 766