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