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 as #zerocopy_crate::KnownLayout>::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 #[allow(missing_debug_implementations)]
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 #[allow(missing_debug_implementations)]
205 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
206 #(#core::mem::MaybeUninit<
207 <#ident #ty_generics as
208 #zerocopy_crate::util::macro_util::Field<#leading_field_indices>
209 >::Type
210 >,)*
211 // NOTE(#2302): We wrap in `ManuallyDrop` here in case the
212 // type we're operating on is both generic and
213 // `repr(packed)`. In that case, Rust needs to know that the
214 // type is *either* `Sized` or has a trivial `Drop`.
215 // `ManuallyDrop` has a trivial `Drop`, and so satisfies
216 // this requirement.
217 #core::mem::ManuallyDrop<
218 <#trailing_field_ty as #zerocopy_crate::KnownLayout>::MaybeUninit
219 >
220 )
221 where
222 #trailing_field_ty: #zerocopy_crate::KnownLayout,
223 #predicates;
224
225 // SAFETY: We largely defer to the `KnownLayout` implementation
226 // on the derive target type (both by using the same tokens, and
227 // by deferring to impl via type-level indirection). This is
228 // sound, since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed
229 // to have the same layout as the derive target type, except
230 // that `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes.
231 unsafe impl #impl_generics #zerocopy_crate::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
232 where
233 #trailing_field_ty: #zerocopy_crate::KnownLayout,
234 #predicates
235 {
236 fn only_derive_is_allowed_to_implement_this_trait() {}
237
238 type PointerMetadata = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::PointerMetadata;
239
240 type MaybeUninit = Self;
241
242 const LAYOUT: #zerocopy_crate::DstLayout = <#ident #ty_generics as #zerocopy_crate::KnownLayout>::LAYOUT;
243
244 #methods
245 }
246 }
247 };
248
249 Some((SelfBounds::None, inner_extras, Some(outer_extras)))
250}
251
252pub(crate) fn derive(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
253 // If this is a `repr(C)` struct, then `c_struct_repr` contains the entire
254 // `repr` attribute.
255 let c_struct_repr = match &ctx.ast.data {
256 Data::Struct(..) => {
257 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
258 if repr.is_c() {
259 Some(repr)
260 } else {
261 None
262 }
263 }
264 Data::Enum(..) | Data::Union(..) => None,
265 };
266
267 let fields = ctx.ast.data.fields();
268
269 let (self_bounds, inner_extras, outer_extras) = c_struct_repr
270 .as_ref()
271 .and_then(|repr| {
272 derive_known_layout_for_repr_c_struct(ctx, repr, &fields)
273 })
274 .unwrap_or_else(|| {
275 let zerocopy_crate = &ctx.zerocopy_crate;
276 let core = ctx.core_path();
277
278 // For enums, unions, and non-`repr(C)` structs, we require that
279 // `Self` is sized, and as a result don't need to reason about the
280 // internals of the type.
281 (
282 SelfBounds::SIZED,
283 quote!(
284 type PointerMetadata = ();
285 type MaybeUninit =
286 #core::mem::MaybeUninit<Self>;
287
288 // SAFETY: `LAYOUT` is guaranteed to accurately describe the
289 // layout of `Self`, because that is the documented safety
290 // contract of `DstLayout::for_type`.
291 const LAYOUT: #zerocopy_crate::DstLayout = #zerocopy_crate::DstLayout::for_type::<Self>();
292
293 // SAFETY: `.cast` preserves address and provenance.
294 //
295 // FIXME(#429): Add documentation to `.cast` that promises that
296 // it preserves provenance.
297 #[inline(always)]
298 fn raw_from_ptr_len(bytes: #core::ptr::NonNull<u8>, _meta: ()) -> #core::ptr::NonNull<Self> {
299 bytes.cast::<Self>()
300 }
301
302 #[inline(always)]
303 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
304 ),
305 None,
306 )
307 });
308 Ok(match &ctx.ast.data {
309 Data::Struct(strct) => {
310 let require_trait_bound_on_field_types =
311 if matches!(self_bounds, SelfBounds::All(&[Trait::Sized])) {
312 FieldBounds::None
313 } else {
314 FieldBounds::TRAILING_SELF
315 };
316
317 // A bound on the trailing field is required, since structs are
318 // unsized if their trailing field is unsized. Reflecting the layout
319 // of an usized trailing field requires that the field is
320 // `KnownLayout`.
321 ImplBlockBuilder::new(
322 ctx,
323 strct,
324 Trait::KnownLayout,
325 require_trait_bound_on_field_types,
326 )
327 .self_type_trait_bounds(self_bounds)
328 .inner_extras(inner_extras)
329 .outer_extras(outer_extras)
330 .build()
331 }
332 Data::Enum(enm) => {
333 // A bound on the trailing field is not required, since enums cannot
334 // currently be unsized.
335 ImplBlockBuilder::new(ctx, enm, Trait::KnownLayout, FieldBounds::None)
336 .self_type_trait_bounds(SelfBounds::SIZED)
337 .inner_extras(inner_extras)
338 .outer_extras(outer_extras)
339 .build()
340 }
341 Data::Union(unn) => {
342 // A bound on the trailing field is not required, since unions
343 // cannot currently be unsized.
344 ImplBlockBuilder::new(ctx, unn, Trait::KnownLayout, FieldBounds::None)
345 .self_type_trait_bounds(SelfBounds::SIZED)
346 .inner_extras(inner_extras)
347 .outer_extras(outer_extras)
348 .build()
349 }
350 })
351}