Skip to main content

zerocopy_derive/derive/
try_from_bytes.rs

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