xref: /linux/rust/zerocopy-derive/derive/try_from_bytes.rs (revision 5f85604cf0877b0369dfd68cd50cf61c0f134819)
1*5f85604cSMiguel Ojeda // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2*5f85604cSMiguel 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, _)| {
460b437b383SMiguel Ojeda         let ident = ident!(("ẕ{}", 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)| {
490b437b383SMiguel Ojeda         let field_token = ident!(("ẕ{}", 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