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}