zerocopy_derive/
lib.rs

1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9//! Derive macros for [zerocopy]'s traits.
10//!
11//! [zerocopy]: https://docs.rs/zerocopy
12
13// Sometimes we want to use lints which were added after our MSRV.
14// `unknown_lints` is `warn` by default and we deny warnings in CI, so without
15// this attribute, any unknown lint would cause a CI failure when testing with
16// our MSRV.
17#![allow(unknown_lints)]
18#![deny(renamed_and_removed_lints)]
19#![deny(clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks)]
20// Inlining format args isn't supported on our MSRV.
21#![allow(clippy::uninlined_format_args)]
22#![deny(
23    rustdoc::bare_urls,
24    rustdoc::broken_intra_doc_links,
25    rustdoc::invalid_codeblock_attributes,
26    rustdoc::invalid_html_tags,
27    rustdoc::invalid_rust_codeblocks,
28    rustdoc::missing_crate_level_docs,
29    rustdoc::private_intra_doc_links
30)]
31#![recursion_limit = "128"]
32
33mod r#enum;
34mod ext;
35#[cfg(test)]
36mod output_tests;
37mod repr;
38
39use proc_macro2::{TokenStream, TokenTree};
40use quote::ToTokens;
41
42use {
43    proc_macro2::Span,
44    quote::quote,
45    syn::{
46        parse_quote, Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr,
47        ExprLit, ExprUnary, GenericParam, Ident, Lit, Meta, Path, Type, UnOp, WherePredicate,
48    },
49};
50
51use {crate::ext::*, crate::repr::*};
52
53// FIXME(https://github.com/rust-lang/rust/issues/54140): Some errors could be
54// made better if we could add multiple lines of error output like this:
55//
56// error: unsupported representation
57//   --> enum.rs:28:8
58//    |
59// 28 | #[repr(transparent)]
60//    |
61// help: required by the derive of FromBytes
62//
63// Instead, we have more verbose error messages like "unsupported representation
64// for deriving FromZeros, FromBytes, IntoBytes, or Unaligned on an enum"
65//
66// This will probably require Span::error
67// (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error),
68// which is currently unstable. Revisit this once it's stable.
69
70/// Defines a derive function named `$outer` which parses its input
71/// `TokenStream` as a `DeriveInput` and then invokes the `$inner` function.
72///
73/// Note that the separate `$outer` parameter is required - proc macro functions
74/// are currently required to live at the crate root, and so the caller must
75/// specify the name in order to avoid name collisions.
76macro_rules! derive {
77    ($trait:ident => $outer:ident => $inner:ident) => {
78        #[proc_macro_derive($trait, attributes(zerocopy))]
79        pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
80            let ast = syn::parse_macro_input!(ts as DeriveInput);
81            let zerocopy_crate = match extract_zerocopy_crate(&ast.attrs) {
82                Ok(zerocopy_crate) => zerocopy_crate,
83                Err(e) => return e.into_compile_error().into(),
84            };
85            $inner(&ast, Trait::$trait, &zerocopy_crate).into_ts().into()
86        }
87    };
88}
89
90trait IntoTokenStream {
91    fn into_ts(self) -> TokenStream;
92}
93
94impl IntoTokenStream for TokenStream {
95    fn into_ts(self) -> TokenStream {
96        self
97    }
98}
99
100impl IntoTokenStream for Result<TokenStream, Error> {
101    fn into_ts(self) -> TokenStream {
102        match self {
103            Ok(ts) => ts,
104            Err(err) => err.to_compile_error(),
105        }
106    }
107}
108
109/// Attempt to extract a crate path from the provided attributes. Defaults to `::zerocopy` if not
110/// found.
111fn extract_zerocopy_crate(attrs: &[Attribute]) -> Result<Path, Error> {
112    let mut path = parse_quote!(::zerocopy);
113
114    for attr in attrs {
115        if let Meta::List(ref meta_list) = attr.meta {
116            if meta_list.path.is_ident("zerocopy") {
117                attr.parse_nested_meta(|meta| {
118                    if meta.path.is_ident("crate") {
119                        let expr = meta.value().and_then(|value| value.parse());
120                        if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
121                            if let Ok(path_lit) = lit.parse() {
122                                path = path_lit;
123                                return Ok(());
124                            }
125                        }
126
127                        return Err(Error::new(
128                            Span::call_site(),
129                            "`crate` attribute requires a path as the value",
130                        ));
131                    }
132
133                    Err(Error::new(
134                        Span::call_site(),
135                        format!("unknown attribute encountered: {}", meta.path.into_token_stream()),
136                    ))
137                })?;
138            }
139        }
140    }
141
142    Ok(path)
143}
144
145derive!(KnownLayout => derive_known_layout => derive_known_layout_inner);
146derive!(Immutable => derive_no_cell => derive_no_cell_inner);
147derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner);
148derive!(FromZeros => derive_from_zeros => derive_from_zeros_inner);
149derive!(FromBytes => derive_from_bytes => derive_from_bytes_inner);
150derive!(IntoBytes => derive_into_bytes => derive_into_bytes_inner);
151derive!(Unaligned => derive_unaligned => derive_unaligned_inner);
152derive!(ByteHash => derive_hash => derive_hash_inner);
153derive!(ByteEq => derive_eq => derive_eq_inner);
154derive!(SplitAt => derive_split_at => derive_split_at_inner);
155
156/// Deprecated: prefer [`FromZeros`] instead.
157#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
158#[doc(hidden)]
159#[proc_macro_derive(FromZeroes)]
160pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
161    derive_from_zeros(ts)
162}
163
164/// Deprecated: prefer [`IntoBytes`] instead.
165#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
166#[doc(hidden)]
167#[proc_macro_derive(AsBytes)]
168pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
169    derive_into_bytes(ts)
170}
171
172fn derive_known_layout_inner(
173    ast: &DeriveInput,
174    _top_level: Trait,
175    zerocopy_crate: &Path,
176) -> Result<TokenStream, Error> {
177    let is_repr_c_struct = match &ast.data {
178        Data::Struct(..) => {
179            let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
180            if repr.is_c() {
181                Some(repr)
182            } else {
183                None
184            }
185        }
186        Data::Enum(..) | Data::Union(..) => None,
187    };
188
189    let fields = ast.data.fields();
190
191    let (self_bounds, inner_extras, outer_extras) = if let (
192        Some(repr),
193        Some((trailing_field, leading_fields)),
194    ) = (is_repr_c_struct, fields.split_last())
195    {
196        let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
197        let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
198
199        let core_path = quote!(#zerocopy_crate::util::macro_util::core_reexport);
200        let repr_align = repr
201            .get_align()
202            .map(|align| {
203                let align = align.t.get();
204                quote!(#core_path::num::NonZeroUsize::new(#align as usize))
205            })
206            .unwrap_or_else(|| quote!(#core_path::option::Option::None));
207        let repr_packed = repr
208            .get_packed()
209            .map(|packed| {
210                let packed = packed.get();
211                quote!(#core_path::num::NonZeroUsize::new(#packed as usize))
212            })
213            .unwrap_or_else(|| quote!(#core_path::option::Option::None));
214
215        let make_methods = |trailing_field_ty| {
216            quote! {
217                // SAFETY:
218                // - The returned pointer has the same address and provenance as
219                //   `bytes`:
220                //   - The recursive call to `raw_from_ptr_len` preserves both
221                //     address and provenance.
222                //   - The `as` cast preserves both address and provenance.
223                //   - `NonNull::new_unchecked` preserves both address and
224                //     provenance.
225                // - If `Self` is a slice DST, the returned pointer encodes
226                //   `elems` elements in the trailing slice:
227                //   - This is true of the recursive call to `raw_from_ptr_len`.
228                //   - `trailing.as_ptr() as *mut Self` preserves trailing slice
229                //     element count [1].
230                //   - `NonNull::new_unchecked` preserves trailing slice element
231                //     count.
232                //
233                // [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
234                //
235                //   `*const T`` / `*mut T` can be cast to `*const U` / `*mut U`
236                //   with the following behavior:
237                //     ...
238                //     - If `T` and `U` are both unsized, the pointer is also
239                //       returned unchanged. In particular, the metadata is
240                //       preserved exactly.
241                //
242                //       For instance, a cast from `*const [T]` to `*const [U]`
243                //       preserves the number of elements. ... The same holds
244                //       for str and any compound type whose unsized tail is a
245                //       slice type, such as struct `Foo(i32, [u8])` or `(u64, Foo)`.
246                #[inline(always)]
247                fn raw_from_ptr_len(
248                    bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
249                    meta: Self::PointerMetadata,
250                ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self> {
251                    use #zerocopy_crate::KnownLayout;
252                    let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
253                    let slf = trailing.as_ptr() as *mut Self;
254                    // SAFETY: Constructed from `trailing`, which is non-null.
255                    unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
256                }
257
258                #[inline(always)]
259                fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
260                    <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
261                }
262            }
263        };
264
265        let inner_extras = {
266            let leading_fields_tys = leading_fields_tys.clone();
267            let methods = make_methods(*trailing_field_ty);
268            let (_, ty_generics, _) = ast.generics.split_for_impl();
269
270            quote!(
271                type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
272
273                type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
274
275                // SAFETY: `LAYOUT` accurately describes the layout of `Self`.
276                // The layout of `Self` is reflected using a sequence of
277                // invocations of `DstLayout::{new_zst,extend,pad_to_align}`.
278                // The documentation of these items vows that invocations in
279                // this manner will accurately describe a type, so long as:
280                //
281                //  - that type is `repr(C)`,
282                //  - its fields are enumerated in the order they appear,
283                //  - the presence of `repr_align` and `repr_packed` are correctly accounted for.
284                //
285                // We respect all three of these preconditions here. This
286                // expansion is only used if `is_repr_c_struct`, we enumerate
287                // the fields in order, and we extract the values of `align(N)`
288                // and `packed(N)`.
289                const LAYOUT: #zerocopy_crate::DstLayout = {
290                    use #zerocopy_crate::util::macro_util::core_reexport::num::NonZeroUsize;
291                    use #zerocopy_crate::{DstLayout, KnownLayout};
292
293                    let repr_align = #repr_align;
294                    let repr_packed = #repr_packed;
295
296                    DstLayout::new_zst(repr_align)
297                        #(.extend(DstLayout::for_type::<#leading_fields_tys>(), repr_packed))*
298                        .extend(<#trailing_field_ty as KnownLayout>::LAYOUT, repr_packed)
299                        .pad_to_align()
300                };
301
302                #methods
303            )
304        };
305
306        let outer_extras = {
307            let ident = &ast.ident;
308            let vis = &ast.vis;
309            let params = &ast.generics.params;
310            let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
311
312            let predicates = if let Some(where_clause) = where_clause {
313                where_clause.predicates.clone()
314            } else {
315                Default::default()
316            };
317
318            // Generate a valid ident for a type-level handle to a field of a
319            // given `name`.
320            let field_index =
321                |name| Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span());
322
323            let field_indices: Vec<_> =
324                fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
325
326            // Define the collection of type-level field handles.
327            let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
328                quote! {
329                    #[allow(non_camel_case_types)]
330                    #vis struct #idx;
331                }
332            });
333
334            let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
335                // SAFETY: `#ty` is the type of `#ident`'s field at `#idx`.
336                unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
337                where
338                    #predicates
339                {
340                    type Type = #ty;
341                }
342            });
343
344            let trailing_field_index = field_index(trailing_field_name);
345            let leading_field_indices =
346                leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
347
348            let trailing_field_ty = quote! {
349                <#ident #ty_generics as
350                    #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
351                >::Type
352            };
353
354            let methods = make_methods(&parse_quote! {
355                <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
356            });
357
358            quote! {
359                #(#field_defs)*
360
361                #(#field_impls)*
362
363                // SAFETY: This has the same layout as the derive target type,
364                // except that it admits uninit bytes. This is ensured by using
365                // the same repr as the target type, and by using field types
366                // which have the same layout as the target type's fields,
367                // except that they admit uninit bytes. We indirect through
368                // `Field` to ensure that occurrences of `Self` resolve to
369                // `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116).
370                #repr
371                #[doc(hidden)]
372                // Required on some rustc versions due to a lint that is only
373                // triggered when `derive(KnownLayout)` is applied to `repr(C)`
374                // structs that are generated by macros. See #2177 for details.
375                #[allow(private_bounds)]
376                #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
377                    #(#zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<
378                        <#ident #ty_generics as
379                            #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
380                        >::Type
381                    >,)*
382                    // NOTE(#2302): We wrap in `ManuallyDrop` here in case the
383                    // type we're operating on is both generic and
384                    // `repr(packed)`. In that case, Rust needs to know that the
385                    // type is *either* `Sized` or has a trivial `Drop`.
386                    // `ManuallyDrop` has a trivial `Drop`, and so satisfies
387                    // this requirement.
388                    #zerocopy_crate::util::macro_util::core_reexport::mem::ManuallyDrop<
389                        <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
390                    >
391                )
392                where
393                    #trailing_field_ty: #zerocopy_crate::KnownLayout,
394                    #predicates;
395
396                // SAFETY: We largely defer to the `KnownLayout` implementation on
397                // the derive target type (both by using the same tokens, and by
398                // deferring to impl via type-level indirection). This is sound,
399                // since  `__ZerocopyKnownLayoutMaybeUninit` is guaranteed to
400                // have the same layout as the derive target type, except that
401                // `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes.
402                unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
403                where
404                    #trailing_field_ty: #zerocopy_crate::KnownLayout,
405                    #predicates
406                {
407                    #[allow(clippy::missing_inline_in_public_items)]
408                    fn only_derive_is_allowed_to_implement_this_trait() {}
409
410                    type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
411
412                    type MaybeUninit = Self;
413
414                    const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
415
416                    #methods
417                }
418            }
419        };
420
421        (SelfBounds::None, inner_extras, Some(outer_extras))
422    } else {
423        // For enums, unions, and non-`repr(C)` structs, we require that
424        // `Self` is sized, and as a result don't need to reason about the
425        // internals of the type.
426        (
427            SelfBounds::SIZED,
428            quote!(
429                type PointerMetadata = ();
430                type MaybeUninit =
431                    #zerocopy_crate::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
432
433                // SAFETY: `LAYOUT` is guaranteed to accurately describe the
434                // layout of `Self`, because that is the documented safety
435                // contract of `DstLayout::for_type`.
436                const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
437
438                // SAFETY: `.cast` preserves address and provenance.
439                //
440                // FIXME(#429): Add documentation to `.cast` that promises that
441                // it preserves provenance.
442                #[inline(always)]
443                fn raw_from_ptr_len(
444                    bytes: #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<u8>,
445                    _meta: (),
446                ) -> #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull<Self>
447                {
448                    bytes.cast::<Self>()
449                }
450
451                #[inline(always)]
452                fn pointer_to_metadata(_ptr: *mut Self) -> () {}
453            ),
454            None,
455        )
456    };
457
458    Ok(match &ast.data {
459        Data::Struct(strct) => {
460            let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED {
461                FieldBounds::None
462            } else {
463                FieldBounds::TRAILING_SELF
464            };
465
466            // A bound on the trailing field is required, since structs are
467            // unsized if their trailing field is unsized. Reflecting the layout
468            // of an usized trailing field requires that the field is
469            // `KnownLayout`.
470            ImplBlockBuilder::new(
471                ast,
472                strct,
473                Trait::KnownLayout,
474                require_trait_bound_on_field_types,
475                zerocopy_crate,
476            )
477            .self_type_trait_bounds(self_bounds)
478            .inner_extras(inner_extras)
479            .outer_extras(outer_extras)
480            .build()
481        }
482        Data::Enum(enm) => {
483            // A bound on the trailing field is not required, since enums cannot
484            // currently be unsized.
485            ImplBlockBuilder::new(ast, enm, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
486                .self_type_trait_bounds(SelfBounds::SIZED)
487                .inner_extras(inner_extras)
488                .outer_extras(outer_extras)
489                .build()
490        }
491        Data::Union(unn) => {
492            // A bound on the trailing field is not required, since unions
493            // cannot currently be unsized.
494            ImplBlockBuilder::new(ast, unn, Trait::KnownLayout, FieldBounds::None, zerocopy_crate)
495                .self_type_trait_bounds(SelfBounds::SIZED)
496                .inner_extras(inner_extras)
497                .outer_extras(outer_extras)
498                .build()
499        }
500    })
501}
502
503fn derive_no_cell_inner(
504    ast: &DeriveInput,
505    _top_level: Trait,
506    zerocopy_crate: &Path,
507) -> TokenStream {
508    match &ast.data {
509        Data::Struct(strct) => ImplBlockBuilder::new(
510            ast,
511            strct,
512            Trait::Immutable,
513            FieldBounds::ALL_SELF,
514            zerocopy_crate,
515        )
516        .build(),
517        Data::Enum(enm) => {
518            ImplBlockBuilder::new(ast, enm, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
519                .build()
520        }
521        Data::Union(unn) => {
522            ImplBlockBuilder::new(ast, unn, Trait::Immutable, FieldBounds::ALL_SELF, zerocopy_crate)
523                .build()
524        }
525    }
526}
527
528fn derive_try_from_bytes_inner(
529    ast: &DeriveInput,
530    top_level: Trait,
531    zerocopy_crate: &Path,
532) -> Result<TokenStream, Error> {
533    match &ast.data {
534        Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level, zerocopy_crate),
535        Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level, zerocopy_crate),
536        Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level, zerocopy_crate)),
537    }
538}
539
540fn derive_from_zeros_inner(
541    ast: &DeriveInput,
542    top_level: Trait,
543    zerocopy_crate: &Path,
544) -> Result<TokenStream, Error> {
545    let try_from_bytes = derive_try_from_bytes_inner(ast, top_level, zerocopy_crate)?;
546    let from_zeros = match &ast.data {
547        Data::Struct(strct) => derive_from_zeros_struct(ast, strct, zerocopy_crate),
548        Data::Enum(enm) => derive_from_zeros_enum(ast, enm, zerocopy_crate)?,
549        Data::Union(unn) => derive_from_zeros_union(ast, unn, zerocopy_crate),
550    };
551    Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
552}
553
554fn derive_from_bytes_inner(
555    ast: &DeriveInput,
556    top_level: Trait,
557    zerocopy_crate: &Path,
558) -> Result<TokenStream, Error> {
559    let from_zeros = derive_from_zeros_inner(ast, top_level, zerocopy_crate)?;
560    let from_bytes = match &ast.data {
561        Data::Struct(strct) => derive_from_bytes_struct(ast, strct, zerocopy_crate),
562        Data::Enum(enm) => derive_from_bytes_enum(ast, enm, zerocopy_crate)?,
563        Data::Union(unn) => derive_from_bytes_union(ast, unn, zerocopy_crate),
564    };
565
566    Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
567}
568
569fn derive_into_bytes_inner(
570    ast: &DeriveInput,
571    _top_level: Trait,
572    zerocopy_crate: &Path,
573) -> Result<TokenStream, Error> {
574    match &ast.data {
575        Data::Struct(strct) => derive_into_bytes_struct(ast, strct, zerocopy_crate),
576        Data::Enum(enm) => derive_into_bytes_enum(ast, enm, zerocopy_crate),
577        Data::Union(unn) => derive_into_bytes_union(ast, unn, zerocopy_crate),
578    }
579}
580
581fn derive_unaligned_inner(
582    ast: &DeriveInput,
583    _top_level: Trait,
584    zerocopy_crate: &Path,
585) -> Result<TokenStream, Error> {
586    match &ast.data {
587        Data::Struct(strct) => derive_unaligned_struct(ast, strct, zerocopy_crate),
588        Data::Enum(enm) => derive_unaligned_enum(ast, enm, zerocopy_crate),
589        Data::Union(unn) => derive_unaligned_union(ast, unn, zerocopy_crate),
590    }
591}
592
593fn derive_hash_inner(
594    ast: &DeriveInput,
595    _top_level: Trait,
596    zerocopy_crate: &Path,
597) -> Result<TokenStream, Error> {
598    // This doesn't delegate to `impl_block` because `impl_block` assumes it is deriving a
599    // `zerocopy`-defined trait, and these trait impls share a common shape that `Hash` does not.
600    // In particular, `zerocopy` traits contain a method that only `zerocopy_derive` macros
601    // are supposed to implement, and `impl_block` generating this trait method is incompatible
602    // with `Hash`.
603    let type_ident = &ast.ident;
604    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
605    let where_predicates = where_clause.map(|clause| &clause.predicates);
606    Ok(quote! {
607        // FIXME(#553): Add a test that generates a warning when
608        // `#[allow(deprecated)]` isn't present.
609        #[allow(deprecated)]
610        // While there are not currently any warnings that this suppresses (that
611        // we're aware of), it's good future-proofing hygiene.
612        #[automatically_derived]
613        impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
614        where
615            Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
616            #where_predicates
617        {
618            fn hash<H>(&self, state: &mut H)
619            where
620                H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
621            {
622                #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
623                    state,
624                    #zerocopy_crate::IntoBytes::as_bytes(self)
625                )
626            }
627
628            fn hash_slice<H>(data: &[Self], state: &mut H)
629            where
630                H: #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher,
631            {
632                #zerocopy_crate::util::macro_util::core_reexport::hash::Hasher::write(
633                    state,
634                    #zerocopy_crate::IntoBytes::as_bytes(data)
635                )
636            }
637        }
638    })
639}
640
641fn derive_eq_inner(
642    ast: &DeriveInput,
643    _top_level: Trait,
644    zerocopy_crate: &Path,
645) -> Result<TokenStream, Error> {
646    // This doesn't delegate to `impl_block` because `impl_block` assumes it is deriving a
647    // `zerocopy`-defined trait, and these trait impls share a common shape that `Eq` does not.
648    // In particular, `zerocopy` traits contain a method that only `zerocopy_derive` macros
649    // are supposed to implement, and `impl_block` generating this trait method is incompatible
650    // with `Eq`.
651    let type_ident = &ast.ident;
652    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
653    let where_predicates = where_clause.map(|clause| &clause.predicates);
654    Ok(quote! {
655        // FIXME(#553): Add a test that generates a warning when
656        // `#[allow(deprecated)]` isn't present.
657        #[allow(deprecated)]
658        // While there are not currently any warnings that this suppresses (that
659        // we're aware of), it's good future-proofing hygiene.
660        #[automatically_derived]
661        impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
662        where
663            Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
664            #where_predicates
665        {
666            fn eq(&self, other: &Self) -> bool {
667                #zerocopy_crate::util::macro_util::core_reexport::cmp::PartialEq::eq(
668                    #zerocopy_crate::IntoBytes::as_bytes(self),
669                    #zerocopy_crate::IntoBytes::as_bytes(other),
670                )
671            }
672        }
673
674        // FIXME(#553): Add a test that generates a warning when
675        // `#[allow(deprecated)]` isn't present.
676        #[allow(deprecated)]
677        // While there are not currently any warnings that this suppresses (that
678        // we're aware of), it's good future-proofing hygiene.
679        #[automatically_derived]
680        impl #impl_generics #zerocopy_crate::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
681        where
682            Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable,
683            #where_predicates
684        {
685        }
686    })
687}
688
689fn derive_split_at_inner(
690    ast: &DeriveInput,
691    _top_level: Trait,
692    zerocopy_crate: &Path,
693) -> Result<TokenStream, Error> {
694    let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
695
696    match &ast.data {
697        Data::Struct(_) => {}
698        Data::Enum(_) | Data::Union(_) => {
699            return Err(Error::new(Span::call_site(), "can only be applied to structs"));
700        }
701    };
702
703    if repr.get_packed().is_some() {
704        return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute"));
705    }
706
707    if !(repr.is_c() || repr.is_transparent()) {
708        return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable"));
709    }
710
711    let fields = ast.data.fields();
712    let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() {
713        trailing_field
714    } else {
715        return Err(Error::new(Span::call_site(), "must at least one field"));
716    };
717
718    // SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)`
719    // and is not packed; its trailing field is guaranteed to be well-aligned
720    // for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing
721    // slice of the trailing field is also well-aligned for its type.
722    Ok(ImplBlockBuilder::new(
723        ast,
724        &ast.data,
725        Trait::SplitAt,
726        FieldBounds::TRAILING_SELF,
727        zerocopy_crate,
728    )
729    .inner_extras(quote! {
730        type Elem = <#trailing_field as ::zerocopy::SplitAt>::Elem;
731    })
732    .build())
733}
734
735/// A struct is `TryFromBytes` if:
736/// - all fields are `TryFromBytes`
737fn derive_try_from_bytes_struct(
738    ast: &DeriveInput,
739    strct: &DataStruct,
740    top_level: Trait,
741    zerocopy_crate: &Path,
742) -> Result<TokenStream, Error> {
743    let extras =
744        try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
745            let fields = strct.fields();
746            let field_names = fields.iter().map(|(_vis, name, _ty)| name);
747            let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
748            quote!(
749                // SAFETY: We use `is_bit_valid` to validate that each field is
750                // bit-valid, and only return `true` if all of them are. The bit
751                // validity of a struct is just the composition of the bit
752                // validities of its fields, so this is a sound implementation of
753                // `is_bit_valid`.
754                fn is_bit_valid<___ZerocopyAliasing>(
755                    mut candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
756                ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
757                where
758                    ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
759                {
760                    use #zerocopy_crate::util::macro_util::core_reexport;
761
762                    true #(&& {
763                        // SAFETY:
764                        // - `project` is a field projection, and so it addresses a
765                        //   subset of the bytes addressed by `slf`
766                        // - ..., and so it preserves provenance
767                        // - ..., and `*slf` is a struct, so `UnsafeCell`s exist at
768                        //   the same byte ranges in the returned pointer's referent
769                        //   as they do in `*slf`
770                        let field_candidate = unsafe {
771                            let project = |slf: #zerocopy_crate::pointer::PtrInner<'_, Self>| {
772                                let slf = slf.as_non_null().as_ptr();
773                                let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
774                                // SAFETY: `cast_unsized_unchecked` promises that
775                                // `slf` will either reference a zero-sized byte
776                                // range, or else will reference a byte range that
777                                // is entirely contained within an allocated
778                                // object. In either case, this guarantees that
779                                // field projection will not wrap around the address
780                                // space, and so `field` will be non-null.
781                                unsafe { core_reexport::ptr::NonNull::new_unchecked(field) }
782                            };
783
784                            candidate.reborrow().cast_unsized_unchecked(project)
785                        };
786
787                        <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
788                    })*
789                }
790            )
791        });
792    Ok(ImplBlockBuilder::new(
793        ast,
794        strct,
795        Trait::TryFromBytes,
796        FieldBounds::ALL_SELF,
797        zerocopy_crate,
798    )
799    .inner_extras(extras)
800    .build())
801}
802
803/// A union is `TryFromBytes` if:
804/// - all of its fields are `TryFromBytes` and `Immutable`
805fn derive_try_from_bytes_union(
806    ast: &DeriveInput,
807    unn: &DataUnion,
808    top_level: Trait,
809    zerocopy_crate: &Path,
810) -> TokenStream {
811    // FIXME(#5): Remove the `Immutable` bound.
812    let field_type_trait_bounds =
813        FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
814    let extras =
815        try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate).unwrap_or_else(|| {
816            let fields = unn.fields();
817            let field_names = fields.iter().map(|(_vis, name, _ty)| name);
818            let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
819            quote!(
820                // SAFETY: We use `is_bit_valid` to validate that any field is
821                // bit-valid; we only return `true` if at least one of them is. The
822                // bit validity of a union is not yet well defined in Rust, but it
823                // is guaranteed to be no more strict than this definition. See #696
824                // for a more in-depth discussion.
825                fn is_bit_valid<___ZerocopyAliasing>(
826                    mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing>
827                ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
828                where
829                    ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
830                {
831                    use #zerocopy_crate::util::macro_util::core_reexport;
832
833                    false #(|| {
834                        // SAFETY:
835                        // - `project` is a field projection, and so it addresses a
836                        //   subset of the bytes addressed by `slf`
837                        // - ..., and so it preserves provenance
838                        // - Since `Self: Immutable` is enforced by
839                        //   `self_type_trait_bounds`, neither `*slf` nor the
840                        //   returned pointer's referent contain any `UnsafeCell`s
841                        let field_candidate = unsafe {
842                            let project = |slf: #zerocopy_crate::pointer::PtrInner<'_, Self>| {
843                                let slf = slf.as_non_null().as_ptr();
844                                let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
845                                // SAFETY: `cast_unsized_unchecked` promises that
846                                // `slf` will either reference a zero-sized byte
847                                // range, or else will reference a byte range that
848                                // is entirely contained within an allocated
849                                // object. In either case, this guarantees that
850                                // field projection will not wrap around the address
851                                // space, and so `field` will be non-null.
852                                unsafe { core_reexport::ptr::NonNull::new_unchecked(field) }
853                            };
854
855                            candidate.reborrow().cast_unsized_unchecked(project)
856                        };
857
858                        <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate)
859                    })*
860                }
861            )
862        });
863    ImplBlockBuilder::new(ast, unn, Trait::TryFromBytes, field_type_trait_bounds, zerocopy_crate)
864        .inner_extras(extras)
865        .build()
866}
867
868fn derive_try_from_bytes_enum(
869    ast: &DeriveInput,
870    enm: &DataEnum,
871    top_level: Trait,
872    zerocopy_crate: &Path,
873) -> Result<TokenStream, Error> {
874    let repr = EnumRepr::from_attrs(&ast.attrs)?;
875
876    // If an enum has no fields, it has a well-defined integer representation,
877    // and every possible bit pattern corresponds to a valid discriminant tag,
878    // then it *could* be `FromBytes` (even if the user hasn't derived
879    // `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N
880    // variants.
881    let could_be_from_bytes = enum_size_from_repr(&repr)
882        .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
883        .unwrap_or(false);
884
885    let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level, zerocopy_crate);
886    let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
887        (Some(is_bit_valid), _) => is_bit_valid,
888        // SAFETY: It would be sound for the enum to implement `FromBytes`, as
889        // required by `gen_trivial_is_bit_valid_unchecked`.
890        (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked(zerocopy_crate) },
891        (None, false) => {
892            r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm, zerocopy_crate)?
893        }
894    };
895
896    Ok(ImplBlockBuilder::new(ast, enm, Trait::TryFromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
897        .inner_extras(extra)
898        .build())
899}
900
901/// Attempts to generate a `TryFromBytes::is_bit_valid` instance that
902/// unconditionally returns true.
903///
904/// This is possible when the `top_level` trait is `FromBytes` and there are no
905/// generic type parameters. In this case, we know that compilation will succeed
906/// only if the type is unconditionally `FromBytes`. Type parameters are not
907/// supported because a type with type parameters could be `TryFromBytes` but
908/// not `FromBytes` depending on its type parameters, and so deriving a trivial
909/// `is_bit_valid` would be either unsound or, assuming we add a defensive
910/// `Self: FromBytes` bound (as we currently do), overly restrictive. Consider,
911/// for example, that `Foo<bool>` ought to be `TryFromBytes` but not `FromBytes`
912/// in this example:
913///
914/// ```rust,ignore
915/// #[derive(FromBytes)]
916/// #[repr(transparent)]
917/// struct Foo<T>(T);
918/// ```
919///
920/// This should be used where possible. Using this impl is faster to codegen,
921/// faster to compile, and is friendlier on the optimizer.
922fn try_gen_trivial_is_bit_valid(
923    ast: &DeriveInput,
924    top_level: Trait,
925    zerocopy_crate: &Path,
926) -> Option<proc_macro2::TokenStream> {
927    // If the top-level trait is `FromBytes` and `Self` has no type parameters,
928    // then the `FromBytes` derive will fail compilation if `Self` is not
929    // actually soundly `FromBytes`, and so we can rely on that for our
930    // `is_bit_valid` impl. It's plausible that we could make changes - or Rust
931    // could make changes (such as the "trivial bounds" language feature) - that
932    // make this no longer true. To hedge against these, we include an explicit
933    // `Self: FromBytes` check in the generated `is_bit_valid`, which is
934    // bulletproof.
935    if top_level == Trait::FromBytes && ast.generics.params.is_empty() {
936        Some(quote!(
937            // SAFETY: See inline.
938            fn is_bit_valid<___ZerocopyAliasing>(
939                _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
940            ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
941            where
942                ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
943            {
944                if false {
945                    fn assert_is_from_bytes<T>()
946                    where
947                        T: #zerocopy_crate::FromBytes,
948                        T: ?#zerocopy_crate::util::macro_util::core_reexport::marker::Sized,
949                    {
950                    }
951
952                    assert_is_from_bytes::<Self>();
953                }
954
955                // SAFETY: The preceding code only compiles if `Self:
956                // FromBytes`. Thus, this code only compiles if all initialized
957                // byte sequences represent valid instances of `Self`.
958                true
959            }
960        ))
961    } else {
962        None
963    }
964}
965
966/// Generates a `TryFromBytes::is_bit_valid` instance that unconditionally
967/// returns true.
968///
969/// This should be used where possible, (although `try_gen_trivial_is_bit_valid`
970/// should be preferred over this for safety reasons). Using this impl is faster
971/// to codegen, faster to compile, and is friendlier on the optimizer.
972///
973/// # Safety
974///
975/// The caller must ensure that all initialized bit patterns are valid for
976/// `Self`.
977unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macro2::TokenStream {
978    quote!(
979        // SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has
980        // promised that all initialized bit patterns are valid for `Self`.
981        fn is_bit_valid<___ZerocopyAliasing>(
982            _candidate: #zerocopy_crate::Maybe<Self, ___ZerocopyAliasing>,
983        ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool
984        where
985            ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference,
986        {
987            true
988        }
989    )
990}
991
992/// A struct is `FromZeros` if:
993/// - all fields are `FromZeros`
994fn derive_from_zeros_struct(
995    ast: &DeriveInput,
996    strct: &DataStruct,
997    zerocopy_crate: &Path,
998) -> TokenStream {
999    ImplBlockBuilder::new(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, zerocopy_crate)
1000        .build()
1001}
1002
1003/// Returns `Ok(index)` if variant `index` of the enum has a discriminant of
1004/// zero. If `Err(bool)` is returned, the boolean is true if the enum has
1005/// unknown discriminants (e.g. discriminants set to const expressions which we
1006/// can't evaluate in a proc macro). If the enum has unknown discriminants, then
1007/// it might have a zero variant that we just can't detect.
1008fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
1009    // Discriminants can be anywhere in the range [i128::MIN, u128::MAX] because
1010    // the discriminant type may be signed or unsigned. Since we only care about
1011    // tracking the discriminant when it's less than or equal to zero, we can
1012    // avoid u128 -> i128 conversions and bounds checking by making the "next
1013    // discriminant" value implicitly negative.
1014    // Technically 64 bits is enough, but 128 is better for future compatibility
1015    // with https://github.com/rust-lang/rust/issues/56071
1016    let mut next_negative_discriminant = Some(0);
1017
1018    // Sometimes we encounter explicit discriminants that we can't know the
1019    // value of (e.g. a constant expression that requires evaluation). These
1020    // could evaluate to zero or a negative number, but we can't assume that
1021    // they do (no false positives allowed!). So we treat them like strictly-
1022    // positive values that can't result in any zero variants, and track whether
1023    // we've encountered any unknown discriminants.
1024    let mut has_unknown_discriminants = false;
1025
1026    for (i, v) in enm.variants.iter().enumerate() {
1027        match v.discriminant.as_ref() {
1028            // Implicit discriminant
1029            None => {
1030                match next_negative_discriminant.as_mut() {
1031                    Some(0) => return Ok(i),
1032                    // n is nonzero so subtraction is always safe
1033                    Some(n) => *n -= 1,
1034                    None => (),
1035                }
1036            }
1037            // Explicit positive discriminant
1038            Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
1039                match int.base10_parse::<u128>().ok() {
1040                    Some(0) => return Ok(i),
1041                    Some(_) => next_negative_discriminant = None,
1042                    None => {
1043                        // Numbers should never fail to parse, but just in case:
1044                        has_unknown_discriminants = true;
1045                        next_negative_discriminant = None;
1046                    }
1047                }
1048            }
1049            // Explicit negative discriminant
1050            Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
1051                Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
1052                    match int.base10_parse::<u128>().ok() {
1053                        Some(0) => return Ok(i),
1054                        // x is nonzero so subtraction is always safe
1055                        Some(x) => next_negative_discriminant = Some(x - 1),
1056                        None => {
1057                            // Numbers should never fail to parse, but just in
1058                            // case:
1059                            has_unknown_discriminants = true;
1060                            next_negative_discriminant = None;
1061                        }
1062                    }
1063                }
1064                // Unknown negative discriminant (e.g. const repr)
1065                _ => {
1066                    has_unknown_discriminants = true;
1067                    next_negative_discriminant = None;
1068                }
1069            },
1070            // Unknown discriminant (e.g. const expr)
1071            _ => {
1072                has_unknown_discriminants = true;
1073                next_negative_discriminant = None;
1074            }
1075        }
1076    }
1077
1078    Err(has_unknown_discriminants)
1079}
1080
1081/// An enum is `FromZeros` if:
1082/// - one of the variants has a discriminant of `0`
1083/// - that variant's fields are all `FromZeros`
1084fn derive_from_zeros_enum(
1085    ast: &DeriveInput,
1086    enm: &DataEnum,
1087    zerocopy_crate: &Path,
1088) -> Result<TokenStream, Error> {
1089    let repr = EnumRepr::from_attrs(&ast.attrs)?;
1090
1091    // We don't actually care what the repr is; we just care that it's one of
1092    // the allowed ones.
1093    match repr {
1094         Repr::Compound(
1095            Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
1096            _,
1097        ) => {}
1098        Repr::Transparent(_)
1099        | Repr::Compound(Spanned { t: CompoundRepr::Rust, span: _ }, _) => return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout")),
1100    }
1101
1102    let zero_variant = match find_zero_variant(enm) {
1103        Ok(index) => enm.variants.iter().nth(index).unwrap(),
1104        // Has unknown variants
1105        Err(true) => {
1106            return Err(Error::new_spanned(
1107                ast,
1108                "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
1109                help: This enum has discriminants which are not literal integers. One of those may \
1110                define or imply which variant has a discriminant of zero. Use a literal integer to \
1111                define or imply the variant with a discriminant of zero.",
1112            ));
1113        }
1114        // Does not have unknown variants
1115        Err(false) => {
1116            return Err(Error::new_spanned(
1117                ast,
1118                "FromZeros only supported on enums with a variant that has a discriminant of `0`",
1119            ));
1120        }
1121    };
1122
1123    let explicit_bounds = zero_variant
1124        .fields
1125        .iter()
1126        .map(|field| {
1127            let ty = &field.ty;
1128            parse_quote! { #ty: #zerocopy_crate::FromZeros }
1129        })
1130        .collect::<Vec<WherePredicate>>();
1131
1132    Ok(ImplBlockBuilder::new(
1133        ast,
1134        enm,
1135        Trait::FromZeros,
1136        FieldBounds::Explicit(explicit_bounds),
1137        zerocopy_crate,
1138    )
1139    .build())
1140}
1141
1142/// Unions are `FromZeros` if
1143/// - all fields are `FromZeros` and `Immutable`
1144fn derive_from_zeros_union(
1145    ast: &DeriveInput,
1146    unn: &DataUnion,
1147    zerocopy_crate: &Path,
1148) -> TokenStream {
1149    // FIXME(#5): Remove the `Immutable` bound. It's only necessary for
1150    // compatibility with `derive(TryFromBytes)` on unions; not for soundness.
1151    let field_type_trait_bounds =
1152        FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1153    ImplBlockBuilder::new(ast, unn, Trait::FromZeros, field_type_trait_bounds, zerocopy_crate)
1154        .build()
1155}
1156
1157/// A struct is `FromBytes` if:
1158/// - all fields are `FromBytes`
1159fn derive_from_bytes_struct(
1160    ast: &DeriveInput,
1161    strct: &DataStruct,
1162    zerocopy_crate: &Path,
1163) -> TokenStream {
1164    ImplBlockBuilder::new(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1165        .build()
1166}
1167
1168/// An enum is `FromBytes` if:
1169/// - Every possible bit pattern must be valid, which means that every bit
1170///   pattern must correspond to a different enum variant. Thus, for an enum
1171///   whose layout takes up N bytes, there must be 2^N variants.
1172/// - Since we must know N, only representations which guarantee the layout's
1173///   size are allowed. These are `repr(uN)` and `repr(iN)` (`repr(C)` implies an
1174///   implementation-defined size). `usize` and `isize` technically guarantee the
1175///   layout's size, but would require us to know how large those are on the
1176///   target platform. This isn't terribly difficult - we could emit a const
1177///   expression that could call `core::mem::size_of` in order to determine the
1178///   size and check against the number of enum variants, but a) this would be
1179///   platform-specific and, b) even on Rust's smallest bit width platform (32),
1180///   this would require ~4 billion enum variants, which obviously isn't a thing.
1181/// - All fields of all variants are `FromBytes`.
1182fn derive_from_bytes_enum(
1183    ast: &DeriveInput,
1184    enm: &DataEnum,
1185    zerocopy_crate: &Path,
1186) -> Result<TokenStream, Error> {
1187    let repr = EnumRepr::from_attrs(&ast.attrs)?;
1188
1189    let variants_required = 1usize << enum_size_from_repr(&repr)?;
1190    if enm.variants.len() != variants_required {
1191        return Err(Error::new_spanned(
1192            ast,
1193            format!(
1194                "FromBytes only supported on {} enum with {} variants",
1195                repr.repr_type_name(),
1196                variants_required
1197            ),
1198        ));
1199    }
1200
1201    Ok(ImplBlockBuilder::new(ast, enm, Trait::FromBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1202        .build())
1203}
1204
1205// Returns `None` if the enum's size is not guaranteed by the repr.
1206fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1207    use {CompoundRepr::*, PrimitiveRepr::*, Repr::*};
1208    match repr {
1209        Transparent(span)
1210        | Compound(
1211            Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | Usize | Isize), span },
1212            _,
1213        ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1214        Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1215        Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1216    }
1217}
1218
1219/// Unions are `FromBytes` if
1220/// - all fields are `FromBytes` and `Immutable`
1221fn derive_from_bytes_union(
1222    ast: &DeriveInput,
1223    unn: &DataUnion,
1224    zerocopy_crate: &Path,
1225) -> TokenStream {
1226    // FIXME(#5): Remove the `Immutable` bound. It's only necessary for
1227    // compatibility with `derive(TryFromBytes)` on unions; not for soundness.
1228    let field_type_trait_bounds =
1229        FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1230    ImplBlockBuilder::new(ast, unn, Trait::FromBytes, field_type_trait_bounds, zerocopy_crate)
1231        .build()
1232}
1233
1234fn derive_into_bytes_struct(
1235    ast: &DeriveInput,
1236    strct: &DataStruct,
1237    zerocopy_crate: &Path,
1238) -> Result<TokenStream, Error> {
1239    let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1240
1241    let is_transparent = repr.is_transparent();
1242    let is_c = repr.is_c();
1243    let is_packed_1 = repr.is_packed_1();
1244    let num_fields = strct.fields().len();
1245
1246    let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1247        // No padding check needed.
1248        // - repr(transparent): The layout and ABI of the whole struct is the
1249        //   same as its only non-ZST field (meaning there's no padding outside
1250        //   of that field) and we require that field to be `IntoBytes` (meaning
1251        //   there's no padding in that field).
1252        // - repr(packed): Any inter-field padding bytes are removed, meaning
1253        //   that any padding bytes would need to come from the fields, all of
1254        //   which we require to be `IntoBytes` (meaning they don't have any
1255        //   padding). Note that this holds regardless of other `repr`
1256        //   attributes, including `repr(Rust)`. [1]
1257        //
1258        // [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-alignment-modifiers:
1259        //
1260        //   An important consequence of these rules is that a type with
1261        //   `#[repr(packed(1))]`` (or `#[repr(packed)]``) will have no
1262        //   inter-field padding.
1263        (None, false)
1264    } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1265        // No padding check needed. A repr(C) struct with zero or one field has
1266        // no padding unless #[repr(align)] explicitly adds padding, which we
1267        // check for in this branch's condition.
1268        (None, false)
1269    } else if ast.generics.params.is_empty() {
1270        // Since there are no generics, we can emit a padding check. All reprs
1271        // guarantee that fields won't overlap [1], so the padding check is
1272        // sound. This is more permissive than the next case, which requires
1273        // that all field types implement `Unaligned`.
1274        //
1275        // [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-rust-representation:
1276        //
1277        //   The only data layout guarantees made by [`repr(Rust)`] are those
1278        //   required for soundness. They are:
1279        //   ...
1280        //   2. The fields do not overlap.
1281        //   ...
1282        (Some(PaddingCheck::Struct), false)
1283    } else if is_c && !repr.is_align_gt_1() {
1284        // We can't use a padding check since there are generic type arguments.
1285        // Instead, we require all field types to implement `Unaligned`. This
1286        // ensures that the `repr(C)` layout algorithm will not insert any
1287        // padding unless #[repr(align)] explicitly adds padding, which we check
1288        // for in this branch's condition.
1289        //
1290        // FIXME(#10): Support type parameters for non-transparent, non-packed
1291        // structs without requiring `Unaligned`.
1292        (None, true)
1293    } else {
1294        return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1295    };
1296
1297    let field_bounds = if require_unaligned_fields {
1298        FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1299    } else {
1300        FieldBounds::ALL_SELF
1301    };
1302
1303    Ok(ImplBlockBuilder::new(ast, strct, Trait::IntoBytes, field_bounds, zerocopy_crate)
1304        .padding_check(padding_check)
1305        .build())
1306}
1307
1308/// If the type is an enum:
1309/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
1310///   `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
1311/// - It must have no padding bytes.
1312/// - Its fields must be `IntoBytes`.
1313fn derive_into_bytes_enum(
1314    ast: &DeriveInput,
1315    enm: &DataEnum,
1316    zerocopy_crate: &Path,
1317) -> Result<TokenStream, Error> {
1318    let repr = EnumRepr::from_attrs(&ast.attrs)?;
1319    if !repr.is_c() && !repr.is_primitive() {
1320        return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1321    }
1322
1323    let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1324    Ok(ImplBlockBuilder::new(ast, enm, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1325        .padding_check(PaddingCheck::Enum { tag_type_definition })
1326        .build())
1327}
1328
1329/// A union is `IntoBytes` if:
1330/// - all fields are `IntoBytes`
1331/// - `repr(C)`, `repr(transparent)`, or `repr(packed)`
1332/// - no padding (size of union equals size of each field type)
1333fn derive_into_bytes_union(
1334    ast: &DeriveInput,
1335    unn: &DataUnion,
1336    zerocopy_crate: &Path,
1337) -> Result<TokenStream, Error> {
1338    // See #1792 for more context.
1339    //
1340    // By checking for `zerocopy_derive_union_into_bytes` both here and in the
1341    // generated code, we ensure that `--cfg zerocopy_derive_union_into_bytes`
1342    // need only be passed *either* when compiling this crate *or* when
1343    // compiling the user's crate. The former is preferable, but in some
1344    // situations (such as when cross-compiling using `cargo build --target`),
1345    // it doesn't get propagated to this crate's build by default.
1346    let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1347        quote!()
1348    } else {
1349        let error_message = "requires --cfg zerocopy_derive_union_into_bytes;
1350please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802";
1351        quote!(
1352            const _: () = {
1353                #[cfg(not(zerocopy_derive_union_into_bytes))]
1354                #zerocopy_crate::util::macro_util::core_reexport::compile_error!(#error_message);
1355            };
1356        )
1357    };
1358
1359    // FIXME(#10): Support type parameters.
1360    if !ast.generics.params.is_empty() {
1361        return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1362    }
1363
1364    // Because we don't support generics, we don't need to worry about
1365    // special-casing different reprs. So long as there is *some* repr which
1366    // guarantees the layout, our `PaddingCheck::Union` guarantees that there is
1367    // no padding.
1368    let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1369    if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1370        return Err(Error::new(
1371            Span::call_site(),
1372            "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1373        ));
1374    }
1375
1376    let impl_block =
1377        ImplBlockBuilder::new(ast, unn, Trait::IntoBytes, FieldBounds::ALL_SELF, zerocopy_crate)
1378            .padding_check(PaddingCheck::Union)
1379            .build();
1380    Ok(quote!(#cfg_compile_error #impl_block))
1381}
1382
1383/// A struct is `Unaligned` if:
1384/// - `repr(align)` is no more than 1 and either
1385///   - `repr(C)` or `repr(transparent)` and
1386///     - all fields `Unaligned`
1387///   - `repr(packed)`
1388fn derive_unaligned_struct(
1389    ast: &DeriveInput,
1390    strct: &DataStruct,
1391    zerocopy_crate: &Path,
1392) -> Result<TokenStream, Error> {
1393    let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1394    repr.unaligned_validate_no_align_gt_1()?;
1395
1396    let field_bounds = if repr.is_packed_1() {
1397        FieldBounds::None
1398    } else if repr.is_c() || repr.is_transparent() {
1399        FieldBounds::ALL_SELF
1400    } else {
1401        return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment"));
1402    };
1403
1404    Ok(ImplBlockBuilder::new(ast, strct, Trait::Unaligned, field_bounds, zerocopy_crate).build())
1405}
1406
1407/// An enum is `Unaligned` if:
1408/// - No `repr(align(N > 1))`
1409/// - `repr(u8)` or `repr(i8)`
1410fn derive_unaligned_enum(
1411    ast: &DeriveInput,
1412    enm: &DataEnum,
1413    zerocopy_crate: &Path,
1414) -> Result<TokenStream, Error> {
1415    let repr = EnumRepr::from_attrs(&ast.attrs)?;
1416    repr.unaligned_validate_no_align_gt_1()?;
1417
1418    if !repr.is_u8() && !repr.is_i8() {
1419        return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1420    }
1421
1422    Ok(ImplBlockBuilder::new(ast, enm, Trait::Unaligned, FieldBounds::ALL_SELF, zerocopy_crate)
1423        .build())
1424}
1425
1426/// Like structs, a union is `Unaligned` if:
1427/// - `repr(align)` is no more than 1 and either
1428///   - `repr(C)` or `repr(transparent)` and
1429///     - all fields `Unaligned`
1430///   - `repr(packed)`
1431fn derive_unaligned_union(
1432    ast: &DeriveInput,
1433    unn: &DataUnion,
1434    zerocopy_crate: &Path,
1435) -> Result<TokenStream, Error> {
1436    let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1437    repr.unaligned_validate_no_align_gt_1()?;
1438
1439    let field_type_trait_bounds = if repr.is_packed_1() {
1440        FieldBounds::None
1441    } else if repr.is_c() || repr.is_transparent() {
1442        FieldBounds::ALL_SELF
1443    } else {
1444        return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment"));
1445    };
1446
1447    Ok(ImplBlockBuilder::new(ast, unn, Trait::Unaligned, field_type_trait_bounds, zerocopy_crate)
1448        .build())
1449}
1450
1451/// This enum describes what kind of padding check needs to be generated for the
1452/// associated impl.
1453enum PaddingCheck {
1454    /// Check that the sum of the fields' sizes exactly equals the struct's
1455    /// size.
1456    Struct,
1457    /// Check that the size of each field exactly equals the union's size.
1458    Union,
1459    /// Check that every variant of the enum contains no padding.
1460    ///
1461    /// Because doing so requires a tag enum, this padding check requires an
1462    /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`.
1463    Enum { tag_type_definition: TokenStream },
1464}
1465
1466impl PaddingCheck {
1467    /// Returns the ident of the macro to call in order to validate that a type
1468    /// passes the padding check encoded by `PaddingCheck`.
1469    fn validator_macro_ident(&self) -> Ident {
1470        let s = match self {
1471            PaddingCheck::Struct => "struct_has_padding",
1472            PaddingCheck::Union => "union_has_padding",
1473            PaddingCheck::Enum { .. } => "enum_has_padding",
1474        };
1475
1476        Ident::new(s, Span::call_site())
1477    }
1478
1479    /// Sometimes performing the padding check requires some additional
1480    /// "context" code. For enums, this is the definition of the tag enum.
1481    fn validator_macro_context(&self) -> Option<&TokenStream> {
1482        match self {
1483            PaddingCheck::Struct | PaddingCheck::Union => None,
1484            PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1485        }
1486    }
1487}
1488
1489#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1490enum Trait {
1491    KnownLayout,
1492    Immutable,
1493    TryFromBytes,
1494    FromZeros,
1495    FromBytes,
1496    IntoBytes,
1497    Unaligned,
1498    Sized,
1499    ByteHash,
1500    ByteEq,
1501    SplitAt,
1502}
1503
1504impl ToTokens for Trait {
1505    fn to_tokens(&self, tokens: &mut TokenStream) {
1506        // According to [1], the format of the derived `Debug`` output is not
1507        // stable and therefore not guaranteed to represent the variant names.
1508        // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can
1509        // return only a minimalized output or empty string. To make sure this
1510        // code will work in the future and independent of the compiler flag, we
1511        // translate the variants to their names manually here.
1512        //
1513        // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability
1514        // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html
1515        let s = match self {
1516            Trait::KnownLayout => "KnownLayout",
1517            Trait::Immutable => "Immutable",
1518            Trait::TryFromBytes => "TryFromBytes",
1519            Trait::FromZeros => "FromZeros",
1520            Trait::FromBytes => "FromBytes",
1521            Trait::IntoBytes => "IntoBytes",
1522            Trait::Unaligned => "Unaligned",
1523            Trait::Sized => "Sized",
1524            Trait::ByteHash => "ByteHash",
1525            Trait::ByteEq => "ByteEq",
1526            Trait::SplitAt => "SplitAt",
1527        };
1528        let ident = Ident::new(s, Span::call_site());
1529        tokens.extend(core::iter::once(TokenTree::Ident(ident)));
1530    }
1531}
1532
1533impl Trait {
1534    fn crate_path(&self, zerocopy_crate: &Path) -> Path {
1535        match self {
1536            Self::Sized => {
1537                parse_quote!(#zerocopy_crate::util::macro_util::core_reexport::marker::#self)
1538            }
1539            _ => parse_quote!(#zerocopy_crate::#self),
1540        }
1541    }
1542}
1543
1544#[derive(Debug, Eq, PartialEq)]
1545enum TraitBound {
1546    Slf,
1547    Other(Trait),
1548}
1549
1550enum FieldBounds<'a> {
1551    None,
1552    All(&'a [TraitBound]),
1553    Trailing(&'a [TraitBound]),
1554    Explicit(Vec<WherePredicate>),
1555}
1556
1557impl<'a> FieldBounds<'a> {
1558    const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1559    const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1560}
1561
1562#[derive(Debug, Eq, PartialEq)]
1563enum SelfBounds<'a> {
1564    None,
1565    All(&'a [Trait]),
1566}
1567
1568// FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false
1569// positive. Explicit lifetimes are actually necessary here.
1570#[allow(clippy::needless_lifetimes)]
1571impl<'a> SelfBounds<'a> {
1572    const SIZED: Self = Self::All(&[Trait::Sized]);
1573}
1574
1575/// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.
1576fn normalize_bounds(slf: Trait, bounds: &[TraitBound]) -> impl '_ + Iterator<Item = Trait> {
1577    bounds.iter().map(move |bound| match bound {
1578        TraitBound::Slf => slf,
1579        TraitBound::Other(trt) => *trt,
1580    })
1581}
1582
1583struct ImplBlockBuilder<'a, D: DataExt> {
1584    input: &'a DeriveInput,
1585    data: &'a D,
1586    trt: Trait,
1587    field_type_trait_bounds: FieldBounds<'a>,
1588    zerocopy_crate: &'a Path,
1589    self_type_trait_bounds: SelfBounds<'a>,
1590    padding_check: Option<PaddingCheck>,
1591    inner_extras: Option<TokenStream>,
1592    outer_extras: Option<TokenStream>,
1593}
1594
1595impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
1596    fn new(
1597        input: &'a DeriveInput,
1598        data: &'a D,
1599        trt: Trait,
1600        field_type_trait_bounds: FieldBounds<'a>,
1601        zerocopy_crate: &'a Path,
1602    ) -> Self {
1603        Self {
1604            input,
1605            data,
1606            trt,
1607            field_type_trait_bounds,
1608            zerocopy_crate,
1609            self_type_trait_bounds: SelfBounds::None,
1610            padding_check: None,
1611            inner_extras: None,
1612            outer_extras: None,
1613        }
1614    }
1615
1616    fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
1617        self.self_type_trait_bounds = self_type_trait_bounds;
1618        self
1619    }
1620
1621    fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
1622        self.padding_check = padding_check.into();
1623        self
1624    }
1625
1626    fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
1627        self.inner_extras = Some(inner_extras);
1628        self
1629    }
1630
1631    fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
1632        self.outer_extras = outer_extras.into();
1633        self
1634    }
1635
1636    fn build(self) -> TokenStream {
1637        // In this documentation, we will refer to this hypothetical struct:
1638        //
1639        //   #[derive(FromBytes)]
1640        //   struct Foo<T, I: Iterator>
1641        //   where
1642        //       T: Copy,
1643        //       I: Clone,
1644        //       I::Item: Clone,
1645        //   {
1646        //       a: u8,
1647        //       b: T,
1648        //       c: I::Item,
1649        //   }
1650        //
1651        // We extract the field types, which in this case are `u8`, `T`, and
1652        // `I::Item`. We re-use the existing parameters and where clauses. If
1653        // `require_trait_bound == true` (as it is for `FromBytes), we add where
1654        // bounds for each field's type:
1655        //
1656        //   impl<T, I: Iterator> FromBytes for Foo<T, I>
1657        //   where
1658        //       T: Copy,
1659        //       I: Clone,
1660        //       I::Item: Clone,
1661        //       T: FromBytes,
1662        //       I::Item: FromBytes,
1663        //   {
1664        //   }
1665        //
1666        // NOTE: It is standard practice to only emit bounds for the type
1667        // parameters themselves, not for field types based on those parameters
1668        // (e.g., `T` vs `T::Foo`). For a discussion of why this is standard
1669        // practice, see https://github.com/rust-lang/rust/issues/26925.
1670        //
1671        // The reason we diverge from this standard is that doing it that way
1672        // for us would be unsound. E.g., consider a type, `T` where `T:
1673        // FromBytes` but `T::Foo: !FromBytes`. It would not be sound for us to
1674        // accept a type with a `T::Foo` field as `FromBytes` simply because `T:
1675        // FromBytes`.
1676        //
1677        // While there's no getting around this requirement for us, it does have
1678        // the pretty serious downside that, when lifetimes are involved, the
1679        // trait solver ties itself in knots:
1680        //
1681        //     #[derive(Unaligned)]
1682        //     #[repr(C)]
1683        //     struct Dup<'a, 'b> {
1684        //         a: PhantomData<&'a u8>,
1685        //         b: PhantomData<&'b u8>,
1686        //     }
1687        //
1688        //     error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
1689        //      --> src/main.rs:6:10
1690        //       |
1691        //     6 | #[derive(Unaligned)]
1692        //       |          ^^^^^^^^^
1693        //       |
1694        //       = note: required by `zerocopy::Unaligned`
1695
1696        let type_ident = &self.input.ident;
1697        let trait_path = self.trt.crate_path(self.zerocopy_crate);
1698        let fields = self.data.fields();
1699        let variants = self.data.variants();
1700        let tag = self.data.tag();
1701        let zerocopy_crate = self.zerocopy_crate;
1702
1703        fn bound_tt(
1704            ty: &Type,
1705            traits: impl Iterator<Item = Trait>,
1706            zerocopy_crate: &Path,
1707        ) -> WherePredicate {
1708            let traits = traits.map(|t| t.crate_path(zerocopy_crate));
1709            parse_quote!(#ty: #(#traits)+*)
1710        }
1711        let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
1712            (FieldBounds::All(traits), _) => fields
1713                .iter()
1714                .map(|(_vis, _name, ty)| {
1715                    bound_tt(ty, normalize_bounds(self.trt, traits), zerocopy_crate)
1716                })
1717                .collect(),
1718            (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1719            (FieldBounds::Trailing(traits), [.., last]) => {
1720                vec![bound_tt(last.2, normalize_bounds(self.trt, traits), zerocopy_crate)]
1721            }
1722            (FieldBounds::Explicit(bounds), _) => bounds,
1723        };
1724
1725        // Don't bother emitting a padding check if there are no fields.
1726        #[allow(unstable_name_collisions)] // See `BoolExt` below
1727        let padding_check_bound = self
1728            .padding_check
1729            .and_then(|check| (!fields.is_empty()).then_some(check))
1730            .map(|check| {
1731                let variant_types = variants.iter().map(|var| {
1732                    let types = var.iter().map(|(_vis, _name, ty)| ty);
1733                    quote!([#(#types),*])
1734                });
1735                let validator_context = check.validator_macro_context();
1736                let validator_macro = check.validator_macro_ident();
1737                let t = tag.iter();
1738                parse_quote! {
1739                    (): #zerocopy_crate::util::macro_util::PaddingFree<
1740                        Self,
1741                        {
1742                            #validator_context
1743                            #zerocopy_crate::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1744                        }
1745                    >
1746                }
1747            });
1748
1749        let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
1750            SelfBounds::None => None,
1751            SelfBounds::All(traits) => {
1752                Some(bound_tt(&parse_quote!(Self), traits.iter().copied(), zerocopy_crate))
1753            }
1754        };
1755
1756        let bounds = self
1757            .input
1758            .generics
1759            .where_clause
1760            .as_ref()
1761            .map(|where_clause| where_clause.predicates.iter())
1762            .into_iter()
1763            .flatten()
1764            .chain(field_type_bounds.iter())
1765            .chain(padding_check_bound.iter())
1766            .chain(self_bounds.iter());
1767
1768        // The parameters with trait bounds, but without type defaults.
1769        let params = self.input.generics.params.clone().into_iter().map(|mut param| {
1770            match &mut param {
1771                GenericParam::Type(ty) => ty.default = None,
1772                GenericParam::Const(cnst) => cnst.default = None,
1773                GenericParam::Lifetime(_) => {}
1774            }
1775            quote!(#param)
1776        });
1777
1778        // The identifiers of the parameters without trait bounds or type
1779        // defaults.
1780        let param_idents = self.input.generics.params.iter().map(|param| match param {
1781            GenericParam::Type(ty) => {
1782                let ident = &ty.ident;
1783                quote!(#ident)
1784            }
1785            GenericParam::Lifetime(l) => {
1786                let ident = &l.lifetime;
1787                quote!(#ident)
1788            }
1789            GenericParam::Const(cnst) => {
1790                let ident = &cnst.ident;
1791                quote!({#ident})
1792            }
1793        });
1794
1795        let inner_extras = self.inner_extras;
1796        let impl_tokens = quote! {
1797            // FIXME(#553): Add a test that generates a warning when
1798            // `#[allow(deprecated)]` isn't present.
1799            #[allow(deprecated)]
1800            // While there are not currently any warnings that this suppresses
1801            // (that we're aware of), it's good future-proofing hygiene.
1802            #[automatically_derived]
1803            unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1804            where
1805                #(#bounds,)*
1806            {
1807                fn only_derive_is_allowed_to_implement_this_trait() {}
1808
1809                #inner_extras
1810            }
1811        };
1812
1813        if let Some(outer_extras) = self.outer_extras {
1814            // So that any items defined in `#outer_extras` don't conflict with
1815            // existing names defined in this scope.
1816            quote! {
1817                const _: () = {
1818                    #impl_tokens
1819
1820                    #outer_extras
1821                };
1822            }
1823        } else {
1824            impl_tokens
1825        }
1826    }
1827}
1828
1829// A polyfill for `Option::then_some`, which was added after our MSRV.
1830//
1831// The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain
1832// versions, `b.then_some(...)` resolves to the inherent method rather than to
1833// this trait, and so this trait is considered unused.
1834//
1835// FIXME(#67): Remove this once our MSRV is >= 1.62.
1836#[allow(unused)]
1837trait BoolExt {
1838    fn then_some<T>(self, t: T) -> Option<T>;
1839}
1840
1841impl BoolExt for bool {
1842    fn then_some<T>(self, t: T) -> Option<T> {
1843        if self {
1844            Some(t)
1845        } else {
1846            None
1847        }
1848    }
1849}