Skip to main content

zerocopy_derive/derive/
known_layout.rs

1// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2
3use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{parse_quote, Data, Error, Type};
6
7use crate::{
8    repr::StructUnionRepr,
9    util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, SelfBounds, Trait},
10};
11
12fn derive_known_layout_for_repr_c_struct<'a>(
13    ctx: &'a Ctx,
14    repr: &StructUnionRepr,
15    fields: &[(&'a syn::Visibility, TokenStream, &'a Type)],
16) -> Option<(SelfBounds<'a>, TokenStream, Option<TokenStream>)> {
17    let (trailing_field, leading_fields) = fields.split_last()?;
18
19    let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
20    let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
21
22    let core = ctx.core_path();
23    let repr_align = repr
24        .get_align()
25        .map(|align| {
26            let align = align.t.get();
27            quote!(#core::num::NonZeroUsize::new(#align as usize))
28        })
29        .unwrap_or_else(|| quote!(#core::option::Option::None));
30    let repr_packed = repr
31        .get_packed()
32        .map(|packed| {
33            let packed = packed.get();
34            quote!(#core::num::NonZeroUsize::new(#packed as usize))
35        })
36        .unwrap_or_else(|| quote!(#core::option::Option::None));
37
38    let zerocopy_crate = &ctx.zerocopy_crate;
39    let make_methods = |trailing_field_ty| {
40        quote! {
41            // SAFETY:
42            // - The returned pointer has the same address and provenance as
43            //   `bytes`:
44            //   - The recursive call to `raw_from_ptr_len` preserves both
45            //     address and provenance.
46            //   - The `as` cast preserves both address and provenance.
47            //   - `NonNull::new_unchecked` preserves both address and
48            //     provenance.
49            // - If `Self` is a slice DST, the returned pointer encodes
50            //   `elems` elements in the trailing slice:
51            //   - This is true of the recursive call to `raw_from_ptr_len`.
52            //   - `trailing.as_ptr() as *mut Self` preserves trailing slice
53            //     element count [1].
54            //   - `NonNull::new_unchecked` preserves trailing slice element
55            //     count.
56            //
57            // [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
58            //
59            //   `*const T`` / `*mut T` can be cast to `*const U` / `*mut U`
60            //   with the following behavior:
61            //     ...
62            //     - If `T` and `U` are both unsized, the pointer is also
63            //       returned unchanged. In particular, the metadata is
64            //       preserved exactly.
65            //
66            //       For instance, a cast from `*const [T]` to `*const [U]`
67            //       preserves the number of elements. ... The same holds
68            //       for str and any compound type whose unsized tail is a
69            //       slice type, such as struct `Foo(i32, [u8])` or
70            //       `(u64, Foo)`.
71            #[inline(always)]
72            fn raw_from_ptr_len(
73                bytes: #core::ptr::NonNull<u8>,
74                meta: <Self as #zerocopy_crate::KnownLayout>::PointerMetadata,
75            ) -> #core::ptr::NonNull<Self> {
76                let trailing = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::raw_from_ptr_len(bytes, meta);
77                let slf = trailing.as_ptr() as *mut Self;
78                // SAFETY: Constructed from `trailing`, which is non-null.
79                unsafe { #core::ptr::NonNull::new_unchecked(slf) }
80            }
81
82            #[inline(always)]
83            fn pointer_to_metadata(ptr: *mut Self) -> <Self as #zerocopy_crate::KnownLayout>::PointerMetadata {
84                <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
85            }
86        }
87    };
88
89    let inner_extras = {
90        let leading_fields_tys = leading_fields_tys.clone();
91        let methods = make_methods(*trailing_field_ty);
92        let (_, ty_generics, _) = ctx.ast.generics.split_for_impl();
93
94        quote!(
95            type PointerMetadata = <#trailing_field_ty as #zerocopy_crate::KnownLayout>::PointerMetadata;
96
97            type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
98
99            // SAFETY: `LAYOUT` accurately describes the layout of `Self`.
100            // The documentation of `DstLayout::for_repr_c_struct` vows that
101            // invocations in this manner will accurately describe a type,
102            // so long as:
103            //
104            //  - that type is `repr(C)`,
105            //  - its fields are enumerated in the order they appear,
106            //  - the presence of `repr_align` and `repr_packed` are
107            //    correctly accounted for.
108            //
109            // We respect all three of these preconditions here. This
110            // expansion is only used if `is_repr_c_struct`, we enumerate
111            // the fields in order, and we extract the values of `align(N)`
112            // and `packed(N)`.
113            const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_repr_c_struct(
114                #repr_align,
115                #repr_packed,
116                &[
117                    #(#zerocopy_crate::DstLayout::for_type::<#leading_fields_tys>(),)*
118                    <#trailing_field_ty as #zerocopy_crate::KnownLayout>::LAYOUT
119                ],
120            );
121
122            #methods
123        )
124    };
125
126    let outer_extras = {
127        let ident = &ctx.ast.ident;
128        let vis = &ctx.ast.vis;
129        let params = &ctx.ast.generics.params;
130        let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl();
131
132        let predicates = if let Some(where_clause) = where_clause {
133            where_clause.predicates.clone()
134        } else {
135            Default::default()
136        };
137
138        // Generate a valid ident for a type-level handle to a field of a
139        // given `name`.
140        let field_index = |name: &TokenStream| ident!(("__Zerocopy_Field_{}", name), ident.span());
141
142        let field_indices: Vec<_> =
143            fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
144
145        // Define the collection of type-level field handles.
146        let field_defs = field_indices.iter().zip(fields).map(|(idx, (vis, _, _))| {
147            quote! {
148                #vis struct #idx;
149            }
150        });
151
152        let field_impls = field_indices.iter().zip(fields).map(|(idx, (_, _, ty))| quote! {
153            // SAFETY: `#ty` is the type of `#ident`'s field at `#idx`.
154            //
155            // We implement `Field` for each field of the struct to create a
156            // projection from the field index to its type. This allows us
157            // to refer to the field's type in a way that respects `Self`
158            // hygiene. If we just copy-pasted the tokens of `#ty`, we
159            // would not respect `Self` hygiene, as `Self` would refer to
160            // the helper struct we are generating, not the derive target
161            // type.
162            unsafe impl #impl_generics #zerocopy_crate::util::macro_util::Field<#idx> for #ident #ty_generics
163            where
164                #predicates
165            {
166                type Type = #ty;
167            }
168        });
169
170        let trailing_field_index = field_index(trailing_field_name);
171        let leading_field_indices =
172            leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
173
174        // We use `Field` to project the type of the trailing field. This is
175        // required to ensure that if the field type uses `Self`, it
176        // resolves to the derive target type, not the helper struct we are
177        // generating.
178        let trailing_field_ty = quote! {
179            <#ident #ty_generics as
180                #zerocopy_crate::util::macro_util::Field<#trailing_field_index>
181            >::Type
182        };
183
184        let methods = make_methods(&parse_quote! {
185            <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
186        });
187
188        let core = ctx.core_path();
189
190        quote! {
191            #(#field_defs)*
192
193            #(#field_impls)*
194
195            // SAFETY: This has the same layout as the derive target type,
196            // except that it admits uninit bytes. This is ensured by using
197            // the same repr as the target type, and by using field types
198            // which have the same layout as the target type's fields,
199            // except that they admit uninit bytes. We indirect through
200            // `Field` to ensure that occurrences of `Self` resolve to
201            // `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116).
202            #repr
203            #[doc(hidden)]
204            #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
205                #(#core::mem::MaybeUninit<
206                    <#ident #ty_generics as
207                        #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
208                    >::Type
209                >,)*
210                // NOTE(#2302): We wrap in `ManuallyDrop` here in case the
211                // type we're operating on is both generic and
212                // `repr(packed)`. In that case, Rust needs to know that the
213                // type is *either* `Sized` or has a trivial `Drop`.
214                // `ManuallyDrop` has a trivial `Drop`, and so satisfies
215                // this requirement.
216                #core::mem::ManuallyDrop<
217                    <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
218                >
219            )
220            where
221                #trailing_field_ty: #zerocopy_crate::KnownLayout,
222                #predicates;
223
224            // SAFETY: We largely defer to the `KnownLayout` implementation
225            // on the derive target type (both by using the same tokens, and
226            // by deferring to impl via type-level indirection). This is
227            // sound, since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed
228            // to have the same layout as the derive target type, except
229            // that `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes.
230            unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
231            where
232                #trailing_field_ty: #zerocopy_crate::KnownLayout,
233                #predicates
234            {
235                fn only_derive_is_allowed_to_implement_this_trait() {}
236
237                type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
238
239                type MaybeUninit = Self;
240
241                const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
242
243                #methods
244            }
245        }
246    };
247
248    Some((SelfBounds::None, inner_extras, Some(outer_extras)))
249}
250
251pub(crate) fn derive(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
252    // If this is a `repr(C)` struct, then `c_struct_repr` contains the entire
253    // `repr` attribute.
254    let c_struct_repr = match &ctx.ast.data {
255        Data::Struct(..) => {
256            let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
257            if repr.is_c() {
258                Some(repr)
259            } else {
260                None
261            }
262        }
263        Data::Enum(..) | Data::Union(..) => None,
264    };
265
266    let fields = ctx.ast.data.fields();
267
268    let (self_bounds, inner_extras, outer_extras) = c_struct_repr
269        .as_ref()
270        .and_then(|repr| {
271            derive_known_layout_for_repr_c_struct(ctx, repr, &fields)
272        })
273        .unwrap_or_else(|| {
274            let zerocopy_crate = &ctx.zerocopy_crate;
275            let core = ctx.core_path();
276
277            // For enums, unions, and non-`repr(C)` structs, we require that
278            // `Self` is sized, and as a result don't need to reason about the
279            // internals of the type.
280            (
281                SelfBounds::SIZED,
282                quote!(
283                    type PointerMetadata = ();
284                    type MaybeUninit =
285                        #core::mem::MaybeUninit<Self>;
286
287                    // SAFETY: `LAYOUT` is guaranteed to accurately describe the
288                    // layout of `Self`, because that is the documented safety
289                    // contract of `DstLayout::for_type`.
290                    const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
291
292                    // SAFETY: `.cast` preserves address and provenance.
293                    //
294                    // FIXME(#429): Add documentation to `.cast` that promises that
295                    // it preserves provenance.
296                    #[inline(always)]
297                    fn raw_from_ptr_len(bytes: #core::ptr::NonNull<u8>, _meta: ()) -> #core::ptr::NonNull<Self> {
298                        bytes.cast::<Self>()
299                    }
300
301                    #[inline(always)]
302                    fn pointer_to_metadata(_ptr: *mut Self) -> () {}
303                ),
304                None,
305            )
306        });
307    Ok(match &ctx.ast.data {
308        Data::Struct(strct) => {
309            let require_trait_bound_on_field_types =
310                if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) {
311                    FieldBounds::None
312                } else {
313                    FieldBounds::TRAILING_SELF
314                };
315
316            // A bound on the trailing field is required, since structs are
317            // unsized if their trailing field is unsized. Reflecting the layout
318            // of an usized trailing field requires that the field is
319            // `KnownLayout`.
320            ImplBlockBuilder::new(
321                ctx,
322                strct,
323                Trait::KnownLayout,
324                require_trait_bound_on_field_types,
325            )
326            .self_type_trait_bounds(self_bounds)
327            .inner_extras(inner_extras)
328            .outer_extras(outer_extras)
329            .build()
330        }
331        Data::Enum(enm) => {
332            // A bound on the trailing field is not required, since enums cannot
333            // currently be unsized.
334            ImplBlockBuilder::new(ctx, enm, Trait::KnownLayout, FieldBounds::None)
335                .self_type_trait_bounds(SelfBounds::SIZED)
336                .inner_extras(inner_extras)
337                .outer_extras(outer_extras)
338                .build()
339        }
340        Data::Union(unn) => {
341            // A bound on the trailing field is not required, since unions
342            // cannot currently be unsized.
343            ImplBlockBuilder::new(ctx, unn, Trait::KnownLayout, FieldBounds::None)
344                .self_type_trait_bounds(SelfBounds::SIZED)
345                .inner_extras(inner_extras)
346                .outer_extras(outer_extras)
347                .build()
348        }
349    })
350}