Skip to main content

zerocopy_derive/derive/
try_from_bytes.rs

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