Skip to main content

zerocopy_derive/derive/
known_layout.rs

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