Skip to main content

pin_init_internal/
zeroable.rs

1// SPDX-License-Identifier: GPL-2.0
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{parse_quote, Data, DeriveInput, Field, Fields};
6
7use crate::{diagnostics::ErrorGuaranteed, DiagCtxt};
8
9pub(crate) fn derive(
10    input: DeriveInput,
11    dcx: &mut DiagCtxt,
12) -> Result<TokenStream, ErrorGuaranteed> {
13    let fields = match input.data {
14        Data::Struct(data_struct) => data_struct.fields,
15        Data::Union(data_union) => Fields::Named(data_union.fields),
16        Data::Enum(data_enum) => {
17            return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum"));
18        }
19    };
20    let name = input.ident;
21    let mut generics = input.generics;
22    for param in generics.type_params_mut() {
23        param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
24    }
25    let (impl_gen, ty_gen, whr) = generics.split_for_impl();
26    let field_type = fields.iter().map(|field| &field.ty);
27    Ok(quote! {
28        // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
29        #[automatically_derived]
30        unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
31            #whr
32        {}
33        const _: () = {
34            fn assert_zeroable<T: ?::core::marker::Sized + ::pin_init::Zeroable>() {}
35            fn ensure_zeroable #impl_gen ()
36                #whr
37            {
38                #(
39                    assert_zeroable::<#field_type>();
40                )*
41            }
42        };
43    })
44}
45
46pub(crate) fn maybe_derive(
47    input: DeriveInput,
48    dcx: &mut DiagCtxt,
49) -> Result<TokenStream, ErrorGuaranteed> {
50    let fields = match input.data {
51        Data::Struct(data_struct) => data_struct.fields,
52        Data::Union(data_union) => Fields::Named(data_union.fields),
53        Data::Enum(data_enum) => {
54            return Err(dcx.error(data_enum.enum_token, "cannot derive `Zeroable` for an enum"));
55        }
56    };
57    let name = input.ident;
58    let mut generics = input.generics;
59    for param in generics.type_params_mut() {
60        param.bounds.insert(0, parse_quote!(::pin_init::Zeroable));
61    }
62    for Field { ty, .. } in fields {
63        generics
64            .make_where_clause()
65            .predicates
66            // the `for<'__dummy>` HRTB makes this not error without the `trivial_bounds`
67            // feature <https://github.com/rust-lang/rust/issues/48214#issuecomment-2557829956>.
68            .push(parse_quote!(#ty: for<'__dummy> ::pin_init::Zeroable));
69    }
70    let (impl_gen, ty_gen, whr) = generics.split_for_impl();
71    Ok(quote! {
72        // SAFETY: Every field type implements `Zeroable` and padding bytes may be zero.
73        #[automatically_derived]
74        unsafe impl #impl_gen ::pin_init::Zeroable for #name #ty_gen
75            #whr
76        {}
77    })
78}