zerocopy_derive/lib.rs
1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9//! Derive macros for [zerocopy]'s traits.
10//!
11//! [zerocopy]: https://docs.rs/zerocopy
12
13// Sometimes we want to use lints which were added after our MSRV.
14// `unknown_lints` is `warn` by default and we deny warnings in CI, so without
15// this attribute, any unknown lint would cause a CI failure when testing with
16// our MSRV.
17#![allow(unknown_lints)]
18#![deny(renamed_and_removed_lints)]
19#![deny(clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks)]
20#![deny(
21 rustdoc::bare_urls,
22 rustdoc::broken_intra_doc_links,
23 rustdoc::invalid_codeblock_attributes,
24 rustdoc::invalid_html_tags,
25 rustdoc::invalid_rust_codeblocks,
26 rustdoc::missing_crate_level_docs,
27 rustdoc::private_intra_doc_links
28)]
29#![recursion_limit = "128"]
30
31mod r#enum;
32mod ext;
33#[cfg(test)]
34mod output_tests;
35mod repr;
36
37use proc_macro2::{TokenStream, TokenTree};
38use quote::ToTokens;
39
40use {
41 proc_macro2::Span,
42 quote::quote,
43 syn::{
44 parse_quote, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit,
45 ExprUnary, GenericParam, Ident, Lit, Path, Type, UnOp, WherePredicate,
46 },
47};
48
49use {crate::ext::*, crate::repr::*};
50
51// TODO(https://github.com/rust-lang/rust/issues/54140): Some errors could be
52// made better if we could add multiple lines of error output like this:
53//
54// error: unsupported representation
55// --> enum.rs:28:8
56// |
57// 28 | #[repr(transparent)]
58// |
59// help: required by the derive of FromBytes
60//
61// Instead, we have more verbose error messages like "unsupported representation
62// for deriving FromZeros, FromBytes, IntoBytes, or Unaligned on an enum"
63//
64// This will probably require Span::error
65// (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error),
66// which is currently unstable. Revisit this once it's stable.
67
68/// Defines a derive function named `$outer` which parses its input
69/// `TokenStream` as a `DeriveInput` and then invokes the `$inner` function.
70///
71/// Note that the separate `$outer` parameter is required - proc macro functions
72/// are currently required to live at the crate root, and so the caller must
73/// specify the name in order to avoid name collisions.
74macro_rules! derive {
75 ($trait:ident => $outer:ident => $inner:ident) => {
76 #[proc_macro_derive($trait)]
77 pub fn $outer(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
78 let ast = syn::parse_macro_input!(ts as DeriveInput);
79 $inner(&ast, Trait::$trait).into_ts().into()
80 }
81 };
82}
83
84trait IntoTokenStream {
85 fn into_ts(self) -> TokenStream;
86}
87
88impl IntoTokenStream for TokenStream {
89 fn into_ts(self) -> TokenStream {
90 self
91 }
92}
93
94impl IntoTokenStream for Result<TokenStream, Error> {
95 fn into_ts(self) -> TokenStream {
96 match self {
97 Ok(ts) => ts,
98 Err(err) => err.to_compile_error(),
99 }
100 }
101}
102
103derive!(KnownLayout => derive_known_layout => derive_known_layout_inner);
104derive!(Immutable => derive_no_cell => derive_no_cell_inner);
105derive!(TryFromBytes => derive_try_from_bytes => derive_try_from_bytes_inner);
106derive!(FromZeros => derive_from_zeros => derive_from_zeros_inner);
107derive!(FromBytes => derive_from_bytes => derive_from_bytes_inner);
108derive!(IntoBytes => derive_into_bytes => derive_into_bytes_inner);
109derive!(Unaligned => derive_unaligned => derive_unaligned_inner);
110derive!(ByteHash => derive_hash => derive_hash_inner);
111derive!(ByteEq => derive_eq => derive_eq_inner);
112
113/// Deprecated: prefer [`FromZeros`] instead.
114#[deprecated(since = "0.8.0", note = "`FromZeroes` was renamed to `FromZeros`")]
115#[doc(hidden)]
116#[proc_macro_derive(FromZeroes)]
117pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
118 derive_from_zeros(ts)
119}
120
121/// Deprecated: prefer [`IntoBytes`] instead.
122#[deprecated(since = "0.8.0", note = "`AsBytes` was renamed to `IntoBytes`")]
123#[doc(hidden)]
124#[proc_macro_derive(AsBytes)]
125pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
126 derive_into_bytes(ts)
127}
128
129fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result<TokenStream, Error> {
130 let is_repr_c_struct = match &ast.data {
131 Data::Struct(..) => {
132 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
133 if repr.is_c() {
134 Some(repr)
135 } else {
136 None
137 }
138 }
139 Data::Enum(..) | Data::Union(..) => None,
140 };
141
142 let fields = ast.data.fields();
143
144 let (self_bounds, inner_extras, outer_extras) = if let (
145 Some(repr),
146 Some((trailing_field, leading_fields)),
147 ) = (is_repr_c_struct, fields.split_last())
148 {
149 let (_vis, trailing_field_name, trailing_field_ty) = trailing_field;
150 let leading_fields_tys = leading_fields.iter().map(|(_vis, _name, ty)| ty);
151
152 let core_path = quote!(::zerocopy::util::macro_util::core_reexport);
153 let repr_align = repr
154 .get_align()
155 .map(|align| {
156 let align = align.t.get();
157 quote!(#core_path::num::NonZeroUsize::new(#align as usize))
158 })
159 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
160 let repr_packed = repr
161 .get_packed()
162 .map(|packed| {
163 let packed = packed.get();
164 quote!(#core_path::num::NonZeroUsize::new(#packed as usize))
165 })
166 .unwrap_or_else(|| quote!(#core_path::option::Option::None));
167
168 let make_methods = |trailing_field_ty| {
169 quote! {
170 // SAFETY:
171 // - The returned pointer has the same address and provenance as
172 // `bytes`:
173 // - The recursive call to `raw_from_ptr_len` preserves both
174 // address and provenance.
175 // - The `as` cast preserves both address and provenance.
176 // - `NonNull::new_unchecked` preserves both address and
177 // provenance.
178 // - If `Self` is a slice DST, the returned pointer encodes
179 // `elems` elements in the trailing slice:
180 // - This is true of the recursive call to `raw_from_ptr_len`.
181 // - `trailing.as_ptr() as *mut Self` preserves trailing slice
182 // element count [1].
183 // - `NonNull::new_unchecked` preserves trailing slice element
184 // count.
185 //
186 // [1] Per https://doc.rust-lang.org/reference/expressions/operator-expr.html#pointer-to-pointer-cast:
187 //
188 // `*const T`` / `*mut T` can be cast to `*const U` / `*mut U`
189 // with the following behavior:
190 // ...
191 // - If `T` and `U` are both unsized, the pointer is also
192 // returned unchanged. In particular, the metadata is
193 // preserved exactly.
194 //
195 // For instance, a cast from `*const [T]` to `*const [U]`
196 // preserves the number of elements. ... The same holds
197 // for str and any compound type whose unsized tail is a
198 // slice type, such as struct `Foo(i32, [u8])` or `(u64, Foo)`.
199 #[inline(always)]
200 fn raw_from_ptr_len(
201 bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<u8>,
202 meta: Self::PointerMetadata,
203 ) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<Self> {
204 use ::zerocopy::KnownLayout;
205 let trailing = <#trailing_field_ty as KnownLayout>::raw_from_ptr_len(bytes, meta);
206 let slf = trailing.as_ptr() as *mut Self;
207 // SAFETY: Constructed from `trailing`, which is non-null.
208 unsafe { ::zerocopy::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
209 }
210
211 #[inline(always)]
212 fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata {
213 <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _)
214 }
215 }
216 };
217
218 let inner_extras = {
219 let leading_fields_tys = leading_fields_tys.clone();
220 let methods = make_methods(*trailing_field_ty);
221 let (_, ty_generics, _) = ast.generics.split_for_impl();
222
223 quote!(
224 type PointerMetadata = <#trailing_field_ty as ::zerocopy::KnownLayout>::PointerMetadata;
225
226 type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics;
227
228 // SAFETY: `LAYOUT` accurately describes the layout of `Self`.
229 // The layout of `Self` is reflected using a sequence of
230 // invocations of `DstLayout::{new_zst,extend,pad_to_align}`.
231 // The documentation of these items vows that invocations in
232 // this manner will acurately describe a type, so long as:
233 //
234 // - that type is `repr(C)`,
235 // - its fields are enumerated in the order they appear,
236 // - the presence of `repr_align` and `repr_packed` are correctly accounted for.
237 //
238 // We respect all three of these preconditions here. This
239 // expansion is only used if `is_repr_c_struct`, we enumerate
240 // the fields in order, and we extract the values of `align(N)`
241 // and `packed(N)`.
242 const LAYOUT: ::zerocopy::DstLayout = {
243 use ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize;
244 use ::zerocopy::{DstLayout, KnownLayout};
245
246 let repr_align = #repr_align;
247 let repr_packed = #repr_packed;
248
249 DstLayout::new_zst(repr_align)
250 #(.extend(DstLayout::for_type::<#leading_fields_tys>(), repr_packed))*
251 .extend(<#trailing_field_ty as KnownLayout>::LAYOUT, repr_packed)
252 .pad_to_align()
253 };
254
255 #methods
256 )
257 };
258
259 let outer_extras = {
260 let ident = &ast.ident;
261 let vis = &ast.vis;
262 let params = &ast.generics.params;
263 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
264
265 let predicates = if let Some(where_clause) = where_clause {
266 where_clause.predicates.clone()
267 } else {
268 Default::default()
269 };
270
271 // Generate a valid ident for a type-level handle to a field of a
272 // given `name`.
273 let field_index =
274 |name| Ident::new(&format!("__Zerocopy_Field_{}", name), ident.span());
275
276 let field_indices: Vec<_> =
277 fields.iter().map(|(_vis, name, _ty)| field_index(name)).collect();
278
279 // Define the collection of type-level field handles.
280 let field_defs = field_indices.iter().zip(&fields).map(|(idx, (vis, _, _))| {
281 quote! {
282 #[allow(non_camel_case_types)]
283 #vis struct #idx;
284 }
285 });
286
287 let field_impls = field_indices.iter().zip(&fields).map(|(idx, (_, _, ty))| quote! {
288 // SAFETY: `#ty` is the type of `#ident`'s field at `#idx`.
289 unsafe impl #impl_generics ::zerocopy::util::macro_util::Field<#idx> for #ident #ty_generics
290 where
291 #predicates
292 {
293 type Type = #ty;
294 }
295 });
296
297 let trailing_field_index = field_index(trailing_field_name);
298 let leading_field_indices =
299 leading_fields.iter().map(|(_vis, name, _ty)| field_index(name));
300
301 let trailing_field_ty = quote! {
302 <#ident #ty_generics as
303 ::zerocopy::util::macro_util::Field<#trailing_field_index>
304 >::Type
305 };
306
307 let methods = make_methods(&parse_quote! {
308 <#trailing_field_ty as ::zerocopy::KnownLayout>::MaybeUninit
309 });
310
311 quote! {
312 #(#field_defs)*
313
314 #(#field_impls)*
315
316 // SAFETY: This has the same layout as the derive target type,
317 // except that it admits uninit bytes. This is ensured by using
318 // the same repr as the target type, and by using field types
319 // which have the same layout as the target type's fields,
320 // except that they admit uninit bytes. We indirect through
321 // `Field` to ensure that occurrences of `Self` resolve to
322 // `#ty`, not `__ZerocopyKnownLayoutMaybeUninit` (see #2116).
323 #repr
324 #[doc(hidden)]
325 // Required on some rustc versions due to a lint that is only
326 // triggered when `derive(KnownLayout)` is applied to `repr(C)`
327 // structs that are generated by macros. See #2177 for details.
328 #[allow(private_bounds)]
329 #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> (
330 #(::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit<
331 <#ident #ty_generics as
332 ::zerocopy::util::macro_util::Field<#leading_field_indices>
333 >::Type
334 >,)*
335 // NOTE(#2302): We wrap in `ManuallyDrop` here in case the
336 // type we're operating on is both generic and
337 // `repr(packed)`. In that case, Rust needs to know that the
338 // type is *either* `Sized` or has a trivial `Drop`.
339 // `ManuallyDrop` has a trivial `Drop`, and so satisfies
340 // this requirement.
341 ::zerocopy::util::macro_util::core_reexport::mem::ManuallyDrop<
342 <#trailing_field_ty as ::zerocopy::KnownLayout>::MaybeUninit
343 >
344 )
345 where
346 #trailing_field_ty: ::zerocopy::KnownLayout,
347 #predicates;
348
349 // SAFETY: We largely defer to the `KnownLayout` implementation on
350 // the derive target type (both by using the same tokens, and by
351 // deferring to impl via type-level indirection). This is sound,
352 // since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed to
353 // have the same layout as the derive target type, except that
354 // `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes.
355 unsafe impl #impl_generics ::zerocopy::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics
356 where
357 #trailing_field_ty: ::zerocopy::KnownLayout,
358 #predicates
359 {
360 #[allow(clippy::missing_inline_in_public_items)]
361 fn only_derive_is_allowed_to_implement_this_trait() {}
362
363 type PointerMetadata = <#ident #ty_generics as ::zerocopy::KnownLayout>::PointerMetadata;
364
365 type MaybeUninit = Self;
366
367 const LAYOUT: ::zerocopy::DstLayout = <#ident #ty_generics as ::zerocopy::KnownLayout>::LAYOUT;
368
369 #methods
370 }
371 }
372 };
373
374 (SelfBounds::None, inner_extras, Some(outer_extras))
375 } else {
376 // For enums, unions, and non-`repr(C)` structs, we require that
377 // `Self` is sized, and as a result don't need to reason about the
378 // internals of the type.
379 (
380 SelfBounds::SIZED,
381 quote!(
382 type PointerMetadata = ();
383 type MaybeUninit =
384 ::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit<Self>;
385
386 // SAFETY: `LAYOUT` is guaranteed to accurately describe the
387 // layout of `Self`, because that is the documented safety
388 // contract of `DstLayout::for_type`.
389 const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_type::<Self>();
390
391 // SAFETY: `.cast` preserves address and provenance.
392 //
393 // TODO(#429): Add documentation to `.cast` that promises that
394 // it preserves provenance.
395 #[inline(always)]
396 fn raw_from_ptr_len(
397 bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<u8>,
398 _meta: (),
399 ) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull<Self>
400 {
401 bytes.cast::<Self>()
402 }
403
404 #[inline(always)]
405 fn pointer_to_metadata(_ptr: *mut Self) -> () {}
406 ),
407 None,
408 )
409 };
410
411 Ok(match &ast.data {
412 Data::Struct(strct) => {
413 let require_trait_bound_on_field_types = if self_bounds == SelfBounds::SIZED {
414 FieldBounds::None
415 } else {
416 FieldBounds::TRAILING_SELF
417 };
418
419 // A bound on the trailing field is required, since structs are
420 // unsized if their trailing field is unsized. Reflecting the layout
421 // of an usized trailing field requires that the field is
422 // `KnownLayout`.
423 impl_block(
424 ast,
425 strct,
426 Trait::KnownLayout,
427 require_trait_bound_on_field_types,
428 self_bounds,
429 None,
430 Some(inner_extras),
431 outer_extras,
432 )
433 }
434 Data::Enum(enm) => {
435 // A bound on the trailing field is not required, since enums cannot
436 // currently be unsized.
437 impl_block(
438 ast,
439 enm,
440 Trait::KnownLayout,
441 FieldBounds::None,
442 SelfBounds::SIZED,
443 None,
444 Some(inner_extras),
445 outer_extras,
446 )
447 }
448 Data::Union(unn) => {
449 // A bound on the trailing field is not required, since unions
450 // cannot currently be unsized.
451 impl_block(
452 ast,
453 unn,
454 Trait::KnownLayout,
455 FieldBounds::None,
456 SelfBounds::SIZED,
457 None,
458 Some(inner_extras),
459 outer_extras,
460 )
461 }
462 })
463}
464
465fn derive_no_cell_inner(ast: &DeriveInput, _top_level: Trait) -> TokenStream {
466 match &ast.data {
467 Data::Struct(strct) => impl_block(
468 ast,
469 strct,
470 Trait::Immutable,
471 FieldBounds::ALL_SELF,
472 SelfBounds::None,
473 None,
474 None,
475 None,
476 ),
477 Data::Enum(enm) => impl_block(
478 ast,
479 enm,
480 Trait::Immutable,
481 FieldBounds::ALL_SELF,
482 SelfBounds::None,
483 None,
484 None,
485 None,
486 ),
487 Data::Union(unn) => impl_block(
488 ast,
489 unn,
490 Trait::Immutable,
491 FieldBounds::ALL_SELF,
492 SelfBounds::None,
493 None,
494 None,
495 None,
496 ),
497 }
498}
499
500fn derive_try_from_bytes_inner(ast: &DeriveInput, top_level: Trait) -> Result<TokenStream, Error> {
501 match &ast.data {
502 Data::Struct(strct) => derive_try_from_bytes_struct(ast, strct, top_level),
503 Data::Enum(enm) => derive_try_from_bytes_enum(ast, enm, top_level),
504 Data::Union(unn) => Ok(derive_try_from_bytes_union(ast, unn, top_level)),
505 }
506}
507
508fn derive_from_zeros_inner(ast: &DeriveInput, top_level: Trait) -> Result<TokenStream, Error> {
509 let try_from_bytes = derive_try_from_bytes_inner(ast, top_level)?;
510 let from_zeros = match &ast.data {
511 Data::Struct(strct) => derive_from_zeros_struct(ast, strct),
512 Data::Enum(enm) => derive_from_zeros_enum(ast, enm)?,
513 Data::Union(unn) => derive_from_zeros_union(ast, unn),
514 };
515 Ok(IntoIterator::into_iter([try_from_bytes, from_zeros]).collect())
516}
517
518fn derive_from_bytes_inner(ast: &DeriveInput, top_level: Trait) -> Result<TokenStream, Error> {
519 let from_zeros = derive_from_zeros_inner(ast, top_level)?;
520 let from_bytes = match &ast.data {
521 Data::Struct(strct) => derive_from_bytes_struct(ast, strct),
522 Data::Enum(enm) => derive_from_bytes_enum(ast, enm)?,
523 Data::Union(unn) => derive_from_bytes_union(ast, unn),
524 };
525
526 Ok(IntoIterator::into_iter([from_zeros, from_bytes]).collect())
527}
528
529fn derive_into_bytes_inner(ast: &DeriveInput, _top_level: Trait) -> Result<TokenStream, Error> {
530 match &ast.data {
531 Data::Struct(strct) => derive_into_bytes_struct(ast, strct),
532 Data::Enum(enm) => derive_into_bytes_enum(ast, enm),
533 Data::Union(unn) => derive_into_bytes_union(ast, unn),
534 }
535}
536
537fn derive_unaligned_inner(ast: &DeriveInput, _top_level: Trait) -> Result<TokenStream, Error> {
538 match &ast.data {
539 Data::Struct(strct) => derive_unaligned_struct(ast, strct),
540 Data::Enum(enm) => derive_unaligned_enum(ast, enm),
541 Data::Union(unn) => derive_unaligned_union(ast, unn),
542 }
543}
544
545fn derive_hash_inner(ast: &DeriveInput, _top_level: Trait) -> Result<TokenStream, Error> {
546 // This doesn't delegate to `impl_block` because `impl_block` assumes it is deriving a
547 // `zerocopy`-defined trait, and these trait impls share a common shape that `Hash` does not.
548 // In particular, `zerocopy` traits contain a method that only `zerocopy_derive` macros
549 // are supposed to implement, and `impl_block` generating this trait method is incompatible
550 // with `Hash`.
551 let type_ident = &ast.ident;
552 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
553 let where_predicates = where_clause.map(|clause| &clause.predicates);
554 Ok(quote! {
555 // TODO(#553): Add a test that generates a warning when
556 // `#[allow(deprecated)]` isn't present.
557 #[allow(deprecated)]
558 // While there are not currently any warnings that this suppresses (that
559 // we're aware of), it's good future-proofing hygiene.
560 #[automatically_derived]
561 impl #impl_generics ::zerocopy::util::macro_util::core_reexport::hash::Hash for #type_ident #ty_generics
562 where
563 Self: ::zerocopy::IntoBytes + ::zerocopy::Immutable,
564 #where_predicates
565 {
566 fn hash<H>(&self, state: &mut H)
567 where
568 H: ::zerocopy::util::macro_util::core_reexport::hash::Hasher,
569 {
570 ::zerocopy::util::macro_util::core_reexport::hash::Hasher::write(
571 state,
572 ::zerocopy::IntoBytes::as_bytes(self)
573 )
574 }
575
576 fn hash_slice<H>(data: &[Self], state: &mut H)
577 where
578 H: ::zerocopy::util::macro_util::core_reexport::hash::Hasher,
579 {
580 ::zerocopy::util::macro_util::core_reexport::hash::Hasher::write(
581 state,
582 ::zerocopy::IntoBytes::as_bytes(data)
583 )
584 }
585 }
586 })
587}
588
589fn derive_eq_inner(ast: &DeriveInput, _top_level: Trait) -> Result<TokenStream, Error> {
590 // This doesn't delegate to `impl_block` because `impl_block` assumes it is deriving a
591 // `zerocopy`-defined trait, and these trait impls share a common shape that `Eq` does not.
592 // In particular, `zerocopy` traits contain a method that only `zerocopy_derive` macros
593 // are supposed to implement, and `impl_block` generating this trait method is incompatible
594 // with `Eq`.
595 let type_ident = &ast.ident;
596 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
597 let where_predicates = where_clause.map(|clause| &clause.predicates);
598 Ok(quote! {
599 // TODO(#553): Add a test that generates a warning when
600 // `#[allow(deprecated)]` isn't present.
601 #[allow(deprecated)]
602 // While there are not currently any warnings that this suppresses (that
603 // we're aware of), it's good future-proofing hygiene.
604 #[automatically_derived]
605 impl #impl_generics ::zerocopy::util::macro_util::core_reexport::cmp::PartialEq for #type_ident #ty_generics
606 where
607 Self: ::zerocopy::IntoBytes + ::zerocopy::Immutable,
608 #where_predicates
609 {
610 fn eq(&self, other: &Self) -> bool {
611 ::zerocopy::util::macro_util::core_reexport::cmp::PartialEq::eq(
612 ::zerocopy::IntoBytes::as_bytes(self),
613 ::zerocopy::IntoBytes::as_bytes(other),
614 )
615 }
616 }
617
618 // TODO(#553): Add a test that generates a warning when
619 // `#[allow(deprecated)]` isn't present.
620 #[allow(deprecated)]
621 // While there are not currently any warnings that this suppresses (that
622 // we're aware of), it's good future-proofing hygiene.
623 #[automatically_derived]
624 impl #impl_generics ::zerocopy::util::macro_util::core_reexport::cmp::Eq for #type_ident #ty_generics
625 where
626 Self: ::zerocopy::IntoBytes + ::zerocopy::Immutable,
627 #where_predicates
628 {
629 }
630 })
631}
632
633/// A struct is `TryFromBytes` if:
634/// - all fields are `TryFromBytes`
635fn derive_try_from_bytes_struct(
636 ast: &DeriveInput,
637 strct: &DataStruct,
638 top_level: Trait,
639) -> Result<TokenStream, Error> {
640 let extras = try_gen_trivial_is_bit_valid(ast, top_level).unwrap_or_else(|| {
641 let fields = strct.fields();
642 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
643 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
644 quote!(
645 // SAFETY: We use `is_bit_valid` to validate that each field is
646 // bit-valid, and only return `true` if all of them are. The bit
647 // validity of a struct is just the composition of the bit
648 // validities of its fields, so this is a sound implementation of
649 // `is_bit_valid`.
650 fn is_bit_valid<___ZerocopyAliasing>(
651 mut candidate: ::zerocopy::Maybe<Self, ___ZerocopyAliasing>,
652 ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
653 where
654 ___ZerocopyAliasing: ::zerocopy::pointer::invariant::Aliasing
655 + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>,
656 {
657 true #(&& {
658 // SAFETY:
659 // - `project` is a field projection, and so it addresses a
660 // subset of the bytes addressed by `slf`
661 // - ..., and so it preserves provenance
662 // - ..., and `*slf` is a struct, so `UnsafeCell`s exist at
663 // the same byte ranges in the returned pointer's referent
664 // as they do in `*slf`
665 let field_candidate = unsafe {
666 let project = |slf: *mut Self|
667 ::zerocopy::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#field_names);
668
669 candidate.reborrow().project(project)
670 };
671
672 <#field_tys as ::zerocopy::TryFromBytes>::is_bit_valid(field_candidate)
673 })*
674 }
675 )
676 });
677 Ok(impl_block(
678 ast,
679 strct,
680 Trait::TryFromBytes,
681 FieldBounds::ALL_SELF,
682 SelfBounds::None,
683 None,
684 Some(extras),
685 None,
686 ))
687}
688
689/// A union is `TryFromBytes` if:
690/// - all of its fields are `TryFromBytes` and `Immutable`
691fn derive_try_from_bytes_union(
692 ast: &DeriveInput,
693 unn: &DataUnion,
694 top_level: Trait,
695) -> TokenStream {
696 // TODO(#5): Remove the `Immutable` bound.
697 let field_type_trait_bounds =
698 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
699 let extras = try_gen_trivial_is_bit_valid(ast, top_level).unwrap_or_else(|| {
700 let fields = unn.fields();
701 let field_names = fields.iter().map(|(_vis, name, _ty)| name);
702 let field_tys = fields.iter().map(|(_vis, _name, ty)| ty);
703 quote!(
704 // SAFETY: We use `is_bit_valid` to validate that any field is
705 // bit-valid; we only return `true` if at least one of them is. The
706 // bit validity of a union is not yet well defined in Rust, but it
707 // is guaranteed to be no more strict than this definition. See #696
708 // for a more in-depth discussion.
709 fn is_bit_valid<___ZerocopyAliasing>(
710 mut candidate: ::zerocopy::Maybe<'_, Self, ___ZerocopyAliasing>
711 ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
712 where
713 ___ZerocopyAliasing: ::zerocopy::pointer::invariant::Aliasing
714 + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>,
715 {
716 false #(|| {
717 // SAFETY:
718 // - `project` is a field projection, and so it addresses a
719 // subset of the bytes addressed by `slf`
720 // - ..., and so it preserves provenance
721 // - Since `Self: Immutable` is enforced by
722 // `self_type_trait_bounds`, neither `*slf` nor the
723 // returned pointer's referent contain any `UnsafeCell`s
724 let field_candidate = unsafe {
725 let project = |slf: *mut Self|
726 ::zerocopy::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#field_names);
727
728 candidate.reborrow().project(project)
729 };
730
731 <#field_tys as ::zerocopy::TryFromBytes>::is_bit_valid(field_candidate)
732 })*
733 }
734 )
735 });
736 impl_block(
737 ast,
738 unn,
739 Trait::TryFromBytes,
740 field_type_trait_bounds,
741 SelfBounds::None,
742 None,
743 Some(extras),
744 None,
745 )
746}
747
748fn derive_try_from_bytes_enum(
749 ast: &DeriveInput,
750 enm: &DataEnum,
751 top_level: Trait,
752) -> Result<TokenStream, Error> {
753 let repr = EnumRepr::from_attrs(&ast.attrs)?;
754
755 // If an enum has no fields, it has a well-defined integer representation,
756 // and every possible bit pattern corresponds to a valid discriminant tag,
757 // then it *could* be `FromBytes` (even if the user hasn't derived
758 // `FromBytes`). This holds if, for `repr(uN)` or `repr(iN)`, there are 2^N
759 // variants.
760 let could_be_from_bytes = enum_size_from_repr(&repr)
761 .map(|size| enm.fields().is_empty() && enm.variants.len() == 1usize << size)
762 .unwrap_or(false);
763
764 let trivial_is_bit_valid = try_gen_trivial_is_bit_valid(ast, top_level);
765 let extra = match (trivial_is_bit_valid, could_be_from_bytes) {
766 (Some(is_bit_valid), _) => is_bit_valid,
767 // SAFETY: It would be sound for the enum to implement `FomBytes`, as
768 // required by `gen_trivial_is_bit_valid_unchecked`.
769 (None, true) => unsafe { gen_trivial_is_bit_valid_unchecked() },
770 (None, false) => r#enum::derive_is_bit_valid(&ast.ident, &repr, &ast.generics, enm)?,
771 };
772
773 Ok(impl_block(
774 ast,
775 enm,
776 Trait::TryFromBytes,
777 FieldBounds::ALL_SELF,
778 SelfBounds::None,
779 None,
780 Some(extra),
781 None,
782 ))
783}
784
785/// Attempts to generate a `TryFromBytes::is_bit_valid` instance that
786/// unconditionally returns true.
787///
788/// This should be used where possible. Using this impl is faster to codegen,
789/// faster to compile, and is friendlier on the optimizer.
790fn try_gen_trivial_is_bit_valid(
791 ast: &DeriveInput,
792 top_level: Trait,
793) -> Option<proc_macro2::TokenStream> {
794 // If the top-level trait is `FromBytes` and `Self` has no type parameters,
795 // then the `FromBytes` derive will fail compilation if `Self` is not
796 // actually soundly `FromBytes`, and so we can rely on that for our
797 // `is_bit_valid` impl. It's plausible that we could make changes - or Rust
798 // could make changes (such as the "trivial bounds" language feature) - that
799 // make this no longer true. To hedge against these, we include an explicit
800 // `Self: FromBytes` check in the generated `is_bit_valid`, which is
801 // bulletproof.
802 if top_level == Trait::FromBytes && ast.generics.params.is_empty() {
803 Some(quote!(
804 // SAFETY: See inline.
805 fn is_bit_valid<___ZerocopyAliasing>(
806 _candidate: ::zerocopy::Maybe<Self, ___ZerocopyAliasing>,
807 ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
808 where
809 ___ZerocopyAliasing: ::zerocopy::pointer::invariant::Aliasing
810 + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>,
811 {
812 if false {
813 fn assert_is_from_bytes<T>()
814 where
815 T: ::zerocopy::FromBytes,
816 T: ?::zerocopy::util::macro_util::core_reexport::marker::Sized,
817 {
818 }
819
820 assert_is_from_bytes::<Self>();
821 }
822
823 // SAFETY: The preceding code only compiles if `Self:
824 // FromBytes`. Thus, this code only compiles if all initialized
825 // byte sequences represent valid instances of `Self`.
826 true
827 }
828 ))
829 } else {
830 None
831 }
832}
833
834/// Generates a `TryFromBytes::is_bit_valid` instance that unconditionally
835/// returns true.
836///
837/// This should be used where possible, (although `try_gen_trivial_is_bit_valid`
838/// should be preferred over this for safety reasons). Using this impl is faster
839/// to codegen, faster to compile, and is friendlier on the optimizer.
840///
841/// # Safety
842///
843/// The caller must ensure that all initialized bit patterns are valid for
844/// `Self`.
845unsafe fn gen_trivial_is_bit_valid_unchecked() -> proc_macro2::TokenStream {
846 quote!(
847 // SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has
848 // promised that all initialized bit patterns are valid for `Self`.
849 fn is_bit_valid<___ZerocopyAliasing>(
850 _candidate: ::zerocopy::Maybe<Self, ___ZerocopyAliasing>,
851 ) -> ::zerocopy::util::macro_util::core_reexport::primitive::bool
852 where
853 ___ZerocopyAliasing: ::zerocopy::pointer::invariant::Aliasing
854 + ::zerocopy::pointer::invariant::AtLeast<::zerocopy::pointer::invariant::Shared>,
855 {
856 true
857 }
858 )
859}
860
861/// A struct is `FromZeros` if:
862/// - all fields are `FromZeros`
863fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> TokenStream {
864 impl_block(
865 ast,
866 strct,
867 Trait::FromZeros,
868 FieldBounds::ALL_SELF,
869 SelfBounds::None,
870 None,
871 None,
872 None,
873 )
874}
875
876/// Returns `Ok(index)` if variant `index` of the enum has a discriminant of
877/// zero. If `Err(bool)` is returned, the boolean is true if the enum has
878/// unknown discriminants (e.g. discriminants set to const expressions which we
879/// can't evaluate in a proc macro). If the enum has unknown discriminants, then
880/// it might have a zero variant that we just can't detect.
881fn find_zero_variant(enm: &DataEnum) -> Result<usize, bool> {
882 // Discriminants can be anywhere in the range [i128::MIN, u128::MAX] because
883 // the discriminant type may be signed or unsigned. Since we only care about
884 // tracking the discriminant when it's less than or equal to zero, we can
885 // avoid u128 -> i128 conversions and bounds checking by making the "next
886 // discriminant" value implicitly negative.
887 // Technically 64 bits is enough, but 128 is better for future compatibility
888 // with https://github.com/rust-lang/rust/issues/56071
889 let mut next_negative_discriminant = Some(0);
890
891 // Sometimes we encounter explicit discriminants that we can't know the
892 // value of (e.g. a constant expression that requires evaluation). These
893 // could evaluate to zero or a negative number, but we can't assume that
894 // they do (no false positives allowed!). So we treat them like strictly-
895 // positive values that can't result in any zero variants, and track whether
896 // we've encountered any unknown discriminants.
897 let mut has_unknown_discriminants = false;
898
899 for (i, v) in enm.variants.iter().enumerate() {
900 match v.discriminant.as_ref() {
901 // Implicit discriminant
902 None => {
903 match next_negative_discriminant.as_mut() {
904 Some(0) => return Ok(i),
905 // n is nonzero so subtraction is always safe
906 Some(n) => *n -= 1,
907 None => (),
908 }
909 }
910 // Explicit positive discriminant
911 Some((_, Expr::Lit(ExprLit { lit: Lit::Int(int), .. }))) => {
912 match int.base10_parse::<u128>().ok() {
913 Some(0) => return Ok(i),
914 Some(_) => next_negative_discriminant = None,
915 None => {
916 // Numbers should never fail to parse, but just in case:
917 has_unknown_discriminants = true;
918 next_negative_discriminant = None;
919 }
920 }
921 }
922 // Explicit negative discriminant
923 Some((_, Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }))) => match &**expr {
924 Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => {
925 match int.base10_parse::<u128>().ok() {
926 Some(0) => return Ok(i),
927 // x is nonzero so subtraction is always safe
928 Some(x) => next_negative_discriminant = Some(x - 1),
929 None => {
930 // Numbers should never fail to parse, but just in
931 // case:
932 has_unknown_discriminants = true;
933 next_negative_discriminant = None;
934 }
935 }
936 }
937 // Unknown negative discriminant (e.g. const repr)
938 _ => {
939 has_unknown_discriminants = true;
940 next_negative_discriminant = None;
941 }
942 },
943 // Unknown discriminant (e.g. const expr)
944 _ => {
945 has_unknown_discriminants = true;
946 next_negative_discriminant = None;
947 }
948 }
949 }
950
951 Err(has_unknown_discriminants)
952}
953
954/// An enum is `FromZeros` if:
955/// - one of the variants has a discriminant of `0`
956/// - that variant's fields are all `FromZeros`
957fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> Result<TokenStream, Error> {
958 let repr = EnumRepr::from_attrs(&ast.attrs)?;
959
960 // We don't actually care what the repr is; we just care that it's one of
961 // the allowed ones.
962 match repr {
963 Repr::Compound(
964 Spanned { t: CompoundRepr::C | CompoundRepr::Primitive(_), span: _ },
965 _,
966 ) => {}
967 Repr::Transparent(_)
968 | Repr::Compound(Spanned { t: CompoundRepr::Rust, span: _ }, _) => return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout")),
969 }
970
971 let zero_variant = match find_zero_variant(enm) {
972 Ok(index) => enm.variants.iter().nth(index).unwrap(),
973 // Has unknown variants
974 Err(true) => {
975 return Err(Error::new_spanned(
976 ast,
977 "FromZeros only supported on enums with a variant that has a discriminant of `0`\n\
978 help: This enum has discriminants which are not literal integers. One of those may \
979 define or imply which variant has a discriminant of zero. Use a literal integer to \
980 define or imply the variant with a discriminant of zero.",
981 ));
982 }
983 // Does not have unknown variants
984 Err(false) => {
985 return Err(Error::new_spanned(
986 ast,
987 "FromZeros only supported on enums with a variant that has a discriminant of `0`",
988 ));
989 }
990 };
991
992 let explicit_bounds = zero_variant
993 .fields
994 .iter()
995 .map(|field| {
996 let ty = &field.ty;
997 parse_quote! { #ty: ::zerocopy::FromZeros }
998 })
999 .collect::<Vec<WherePredicate>>();
1000
1001 Ok(impl_block(
1002 ast,
1003 enm,
1004 Trait::FromZeros,
1005 FieldBounds::Explicit(explicit_bounds),
1006 SelfBounds::None,
1007 None,
1008 None,
1009 None,
1010 ))
1011}
1012
1013/// Unions are `FromZeros` if
1014/// - all fields are `FromZeros` and `Immutable`
1015fn derive_from_zeros_union(ast: &DeriveInput, unn: &DataUnion) -> TokenStream {
1016 // TODO(#5): Remove the `Immutable` bound. It's only necessary for
1017 // compatibility with `derive(TryFromBytes)` on unions; not for soundness.
1018 let field_type_trait_bounds =
1019 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1020 impl_block(
1021 ast,
1022 unn,
1023 Trait::FromZeros,
1024 field_type_trait_bounds,
1025 SelfBounds::None,
1026 None,
1027 None,
1028 None,
1029 )
1030}
1031
1032/// A struct is `FromBytes` if:
1033/// - all fields are `FromBytes`
1034fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> TokenStream {
1035 impl_block(
1036 ast,
1037 strct,
1038 Trait::FromBytes,
1039 FieldBounds::ALL_SELF,
1040 SelfBounds::None,
1041 None,
1042 None,
1043 None,
1044 )
1045}
1046
1047/// An enum is `FromBytes` if:
1048/// - Every possible bit pattern must be valid, which means that every bit
1049/// pattern must correspond to a different enum variant. Thus, for an enum
1050/// whose layout takes up N bytes, there must be 2^N variants.
1051/// - Since we must know N, only representations which guarantee the layout's
1052/// size are allowed. These are `repr(uN)` and `repr(iN)` (`repr(C)` implies an
1053/// implementation-defined size). `usize` and `isize` technically guarantee the
1054/// layout's size, but would require us to know how large those are on the
1055/// target platform. This isn't terribly difficult - we could emit a const
1056/// expression that could call `core::mem::size_of` in order to determine the
1057/// size and check against the number of enum variants, but a) this would be
1058/// platform-specific and, b) even on Rust's smallest bit width platform (32),
1059/// this would require ~4 billion enum variants, which obviously isn't a thing.
1060/// - All fields of all variants are `FromBytes`.
1061fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> Result<TokenStream, Error> {
1062 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1063
1064 let variants_required = 1usize << enum_size_from_repr(&repr)?;
1065 if enm.variants.len() != variants_required {
1066 return Err(Error::new_spanned(
1067 ast,
1068 format!(
1069 "FromBytes only supported on {} enum with {} variants",
1070 repr.repr_type_name(),
1071 variants_required
1072 ),
1073 ));
1074 }
1075
1076 Ok(impl_block(
1077 ast,
1078 enm,
1079 Trait::FromBytes,
1080 FieldBounds::ALL_SELF,
1081 SelfBounds::None,
1082 None,
1083 None,
1084 None,
1085 ))
1086}
1087
1088// Returns `None` if the enum's size is not guaranteed by the repr.
1089fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
1090 use {CompoundRepr::*, PrimitiveRepr::*, Repr::*};
1091 match repr {
1092 Transparent(span)
1093 | Compound(
1094 Spanned { t: C | Rust | Primitive(U32 | I32 | U64 | I64 | Usize | Isize), span },
1095 _,
1096 ) => Err(Error::new(*span, "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`")),
1097 Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
1098 Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1099 }
1100}
1101
1102/// Unions are `FromBytes` if
1103/// - all fields are `FromBytes` and `Immutable`
1104fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> TokenStream {
1105 // TODO(#5): Remove the `Immutable` bound. It's only necessary for
1106 // compatibility with `derive(TryFromBytes)` on unions; not for soundness.
1107 let field_type_trait_bounds =
1108 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]);
1109 impl_block(
1110 ast,
1111 unn,
1112 Trait::FromBytes,
1113 field_type_trait_bounds,
1114 SelfBounds::None,
1115 None,
1116 None,
1117 None,
1118 )
1119}
1120
1121fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> Result<TokenStream, Error> {
1122 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1123
1124 let is_transparent = repr.is_transparent();
1125 let is_c = repr.is_c();
1126 let is_packed_1 = repr.is_packed_1();
1127 let num_fields = strct.fields().len();
1128
1129 let (padding_check, require_unaligned_fields) = if is_transparent || is_packed_1 {
1130 // No padding check needed.
1131 // - repr(transparent): The layout and ABI of the whole struct is the
1132 // same as its only non-ZST field (meaning there's no padding outside
1133 // of that field) and we require that field to be `IntoBytes` (meaning
1134 // there's no padding in that field).
1135 // - repr(packed): Any inter-field padding bytes are removed, meaning
1136 // that any padding bytes would need to come from the fields, all of
1137 // which we require to be `IntoBytes` (meaning they don't have any
1138 // padding). Note that this holds regardless of other `repr`
1139 // attributes, including `repr(Rust)`. [1]
1140 //
1141 // [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-alignment-modifiers:
1142 //
1143 // An important consequence of these rules is that a type with
1144 // `#[repr(packed(1))]`` (or `#[repr(packed)]``) will have no
1145 // inter-field padding.
1146 (None, false)
1147 } else if is_c && !repr.is_align_gt_1() && num_fields <= 1 {
1148 // No padding check needed. A repr(C) struct with zero or one field has
1149 // no padding unless #[repr(align)] explicitly adds padding, which we
1150 // check for in this branch's condition.
1151 (None, false)
1152 } else if ast.generics.params.is_empty() {
1153 // Since there are no generics, we can emit a padding check. All reprs
1154 // guarantee that fields won't overlap [1], so the padding check is
1155 // sound. This is more permissive than the next case, which requires
1156 // that all field types implement `Unaligned`.
1157 //
1158 // [1] Per https://doc.rust-lang.org/1.81.0/reference/type-layout.html#the-rust-representation:
1159 //
1160 // The only data layout guarantees made by [`repr(Rust)`] are those
1161 // required for soundness. They are:
1162 // ...
1163 // 2. The fields do not overlap.
1164 // ...
1165 (Some(PaddingCheck::Struct), false)
1166 } else if is_c && !repr.is_align_gt_1() {
1167 // We can't use a padding check since there are generic type arguments.
1168 // Instead, we require all field types to implement `Unaligned`. This
1169 // ensures that the `repr(C)` layout algorithm will not insert any
1170 // padding unless #[repr(align)] explicitly adds padding, which we check
1171 // for in this branch's condition.
1172 //
1173 // TODO(#10): Support type parameters for non-transparent, non-packed
1174 // structs without requiring `Unaligned`.
1175 (None, true)
1176 } else {
1177 return Err(Error::new(Span::call_site(), "must have a non-align #[repr(...)] attribute in order to guarantee this type's memory layout"));
1178 };
1179
1180 let field_bounds = if require_unaligned_fields {
1181 FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Unaligned)])
1182 } else {
1183 FieldBounds::ALL_SELF
1184 };
1185
1186 Ok(impl_block(
1187 ast,
1188 strct,
1189 Trait::IntoBytes,
1190 field_bounds,
1191 SelfBounds::None,
1192 padding_check,
1193 None,
1194 None,
1195 ))
1196}
1197
1198/// If the type is an enum:
1199/// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`,
1200/// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`).
1201/// - It must have no padding bytes.
1202/// - Its fields must be `IntoBytes`.
1203fn derive_into_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> Result<TokenStream, Error> {
1204 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1205 if !repr.is_c() && !repr.is_primitive() {
1206 return Err(Error::new(Span::call_site(), "must have #[repr(C)] or #[repr(Int)] attribute in order to guarantee this type's memory layout"));
1207 }
1208
1209 let tag_type_definition = r#enum::generate_tag_enum(&repr, enm);
1210 Ok(impl_block(
1211 ast,
1212 enm,
1213 Trait::IntoBytes,
1214 FieldBounds::ALL_SELF,
1215 SelfBounds::None,
1216 Some(PaddingCheck::Enum { tag_type_definition }),
1217 None,
1218 None,
1219 ))
1220}
1221
1222/// A union is `IntoBytes` if:
1223/// - all fields are `IntoBytes`
1224/// - `repr(C)`, `repr(transparent)`, or `repr(packed)`
1225/// - no padding (size of union equals size of each field type)
1226fn derive_into_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> Result<TokenStream, Error> {
1227 // See #1792 for more context.
1228 //
1229 // By checking for `zerocopy_derive_union_into_bytes` both here and in the
1230 // generated code, we ensure that `--cfg zerocopy_derive_union_into_bytes`
1231 // need only be passed *either* when compiling this crate *or* when
1232 // compiling the user's crate. The former is preferable, but in some
1233 // situations (such as when cross-compiling using `cargo build --target`),
1234 // it doesn't get propagated to this crate's build by default.
1235 let cfg_compile_error = if cfg!(zerocopy_derive_union_into_bytes) {
1236 quote!()
1237 } else {
1238 quote!(
1239 const _: () = {
1240 #[cfg(not(zerocopy_derive_union_into_bytes))]
1241 ::zerocopy::util::macro_util::core_reexport::compile_error!(
1242 "requires --cfg zerocopy_derive_union_into_bytes;
1243please let us know you use this feature: https://github.com/google/zerocopy/discussions/1802"
1244 );
1245 };
1246 )
1247 };
1248
1249 // TODO(#10): Support type parameters.
1250 if !ast.generics.params.is_empty() {
1251 return Err(Error::new(Span::call_site(), "unsupported on types with type parameters"));
1252 }
1253
1254 // Because we don't support generics, we don't need to worry about
1255 // special-casing different reprs. So long as there is *some* repr which
1256 // guarantees the layout, our `PaddingCheck::Union` guarantees that there is
1257 // no padding.
1258 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1259 if !repr.is_c() && !repr.is_transparent() && !repr.is_packed_1() {
1260 return Err(Error::new(
1261 Span::call_site(),
1262 "must be #[repr(C)], #[repr(packed)], or #[repr(transparent)]",
1263 ));
1264 }
1265
1266 let impl_block = impl_block(
1267 ast,
1268 unn,
1269 Trait::IntoBytes,
1270 FieldBounds::ALL_SELF,
1271 SelfBounds::None,
1272 Some(PaddingCheck::Union),
1273 None,
1274 None,
1275 );
1276 Ok(quote!(#cfg_compile_error #impl_block))
1277}
1278
1279/// A struct is `Unaligned` if:
1280/// - `repr(align)` is no more than 1 and either
1281/// - `repr(C)` or `repr(transparent)` and
1282/// - all fields `Unaligned`
1283/// - `repr(packed)`
1284fn derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> Result<TokenStream, Error> {
1285 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1286 repr.unaligned_validate_no_align_gt_1()?;
1287
1288 let field_bounds = if repr.is_packed_1() {
1289 FieldBounds::None
1290 } else if repr.is_c() || repr.is_transparent() {
1291 FieldBounds::ALL_SELF
1292 } else {
1293 return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment"));
1294 };
1295
1296 Ok(impl_block(ast, strct, Trait::Unaligned, field_bounds, SelfBounds::None, None, None, None))
1297}
1298
1299/// An enum is `Unaligned` if:
1300/// - No `repr(align(N > 1))`
1301/// - `repr(u8)` or `repr(i8)`
1302fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> Result<TokenStream, Error> {
1303 let repr = EnumRepr::from_attrs(&ast.attrs)?;
1304 repr.unaligned_validate_no_align_gt_1()?;
1305
1306 if !repr.is_u8() && !repr.is_i8() {
1307 return Err(Error::new(Span::call_site(), "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment"));
1308 }
1309
1310 Ok(impl_block(
1311 ast,
1312 enm,
1313 Trait::Unaligned,
1314 FieldBounds::ALL_SELF,
1315 SelfBounds::None,
1316 None,
1317 None,
1318 None,
1319 ))
1320}
1321
1322/// Like structs, a union is `Unaligned` if:
1323/// - `repr(align)` is no more than 1 and either
1324/// - `repr(C)` or `repr(transparent)` and
1325/// - all fields `Unaligned`
1326/// - `repr(packed)`
1327fn derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> Result<TokenStream, Error> {
1328 let repr = StructUnionRepr::from_attrs(&ast.attrs)?;
1329 repr.unaligned_validate_no_align_gt_1()?;
1330
1331 let field_type_trait_bounds = if repr.is_packed_1() {
1332 FieldBounds::None
1333 } else if repr.is_c() || repr.is_transparent() {
1334 FieldBounds::ALL_SELF
1335 } else {
1336 return Err(Error::new(Span::call_site(), "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment"));
1337 };
1338
1339 Ok(impl_block(
1340 ast,
1341 unn,
1342 Trait::Unaligned,
1343 field_type_trait_bounds,
1344 SelfBounds::None,
1345 None,
1346 None,
1347 None,
1348 ))
1349}
1350
1351/// This enum describes what kind of padding check needs to be generated for the
1352/// associated impl.
1353enum PaddingCheck {
1354 /// Check that the sum of the fields' sizes exactly equals the struct's
1355 /// size.
1356 Struct,
1357 /// Check that the size of each field exactly equals the union's size.
1358 Union,
1359 /// Check that every variant of the enum contains no padding.
1360 ///
1361 /// Because doing so requires a tag enum, this padding check requires an
1362 /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`.
1363 Enum { tag_type_definition: TokenStream },
1364}
1365
1366impl PaddingCheck {
1367 /// Returns the ident of the macro to call in order to validate that a type
1368 /// passes the padding check encoded by `PaddingCheck`.
1369 fn validator_macro_ident(&self) -> Ident {
1370 let s = match self {
1371 PaddingCheck::Struct => "struct_has_padding",
1372 PaddingCheck::Union => "union_has_padding",
1373 PaddingCheck::Enum { .. } => "enum_has_padding",
1374 };
1375
1376 Ident::new(s, Span::call_site())
1377 }
1378
1379 /// Sometimes performing the padding check requires some additional
1380 /// "context" code. For enums, this is the definition of the tag enum.
1381 fn validator_macro_context(&self) -> Option<&TokenStream> {
1382 match self {
1383 PaddingCheck::Struct | PaddingCheck::Union => None,
1384 PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
1385 }
1386 }
1387}
1388
1389#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1390enum Trait {
1391 KnownLayout,
1392 Immutable,
1393 TryFromBytes,
1394 FromZeros,
1395 FromBytes,
1396 IntoBytes,
1397 Unaligned,
1398 Sized,
1399 ByteHash,
1400 ByteEq,
1401}
1402
1403impl ToTokens for Trait {
1404 fn to_tokens(&self, tokens: &mut TokenStream) {
1405 // According to [1], the format of the derived `Debug`` output is not
1406 // stable and therefore not guaranteed to represent the variant names.
1407 // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can
1408 // return only a minimalized output or empty string. To make sure this
1409 // code will work in the future and independet of the compiler flag, we
1410 // translate the variants to their names manually here.
1411 //
1412 // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability
1413 // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html
1414 let s = match self {
1415 Trait::KnownLayout => "KnownLayout",
1416 Trait::Immutable => "Immutable",
1417 Trait::TryFromBytes => "TryFromBytes",
1418 Trait::FromZeros => "FromZeros",
1419 Trait::FromBytes => "FromBytes",
1420 Trait::IntoBytes => "IntoBytes",
1421 Trait::Unaligned => "Unaligned",
1422 Trait::Sized => "Sized",
1423 Trait::ByteHash => "ByteHash",
1424 Trait::ByteEq => "ByteEq",
1425 };
1426 let ident = Ident::new(s, Span::call_site());
1427 tokens.extend(core::iter::once(TokenTree::Ident(ident)));
1428 }
1429}
1430
1431impl Trait {
1432 fn crate_path(&self) -> Path {
1433 match self {
1434 Self::Sized => parse_quote!(::zerocopy::util::macro_util::core_reexport::marker::#self),
1435 _ => parse_quote!(::zerocopy::#self),
1436 }
1437 }
1438}
1439
1440#[derive(Debug, Eq, PartialEq)]
1441enum TraitBound {
1442 Slf,
1443 Other(Trait),
1444}
1445
1446enum FieldBounds<'a> {
1447 None,
1448 All(&'a [TraitBound]),
1449 Trailing(&'a [TraitBound]),
1450 Explicit(Vec<WherePredicate>),
1451}
1452
1453impl<'a> FieldBounds<'a> {
1454 const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
1455 const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
1456}
1457
1458#[derive(Debug, Eq, PartialEq)]
1459enum SelfBounds<'a> {
1460 None,
1461 All(&'a [Trait]),
1462}
1463
1464// TODO(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false positive.
1465// Explicit lifetimes are actually necessary here.
1466#[allow(clippy::needless_lifetimes)]
1467impl<'a> SelfBounds<'a> {
1468 const SIZED: Self = Self::All(&[Trait::Sized]);
1469}
1470
1471/// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.
1472fn normalize_bounds(slf: Trait, bounds: &[TraitBound]) -> impl '_ + Iterator<Item = Trait> {
1473 bounds.iter().map(move |bound| match bound {
1474 TraitBound::Slf => slf,
1475 TraitBound::Other(trt) => *trt,
1476 })
1477}
1478
1479#[allow(clippy::too_many_arguments)]
1480fn impl_block<D: DataExt>(
1481 input: &DeriveInput,
1482 data: &D,
1483 trt: Trait,
1484 field_type_trait_bounds: FieldBounds,
1485 self_type_trait_bounds: SelfBounds,
1486 padding_check: Option<PaddingCheck>,
1487 inner_extras: Option<TokenStream>,
1488 outer_extras: Option<TokenStream>,
1489) -> TokenStream {
1490 // In this documentation, we will refer to this hypothetical struct:
1491 //
1492 // #[derive(FromBytes)]
1493 // struct Foo<T, I: Iterator>
1494 // where
1495 // T: Copy,
1496 // I: Clone,
1497 // I::Item: Clone,
1498 // {
1499 // a: u8,
1500 // b: T,
1501 // c: I::Item,
1502 // }
1503 //
1504 // We extract the field types, which in this case are `u8`, `T`, and
1505 // `I::Item`. We re-use the existing parameters and where clauses. If
1506 // `require_trait_bound == true` (as it is for `FromBytes), we add where
1507 // bounds for each field's type:
1508 //
1509 // impl<T, I: Iterator> FromBytes for Foo<T, I>
1510 // where
1511 // T: Copy,
1512 // I: Clone,
1513 // I::Item: Clone,
1514 // T: FromBytes,
1515 // I::Item: FromBytes,
1516 // {
1517 // }
1518 //
1519 // NOTE: It is standard practice to only emit bounds for the type parameters
1520 // themselves, not for field types based on those parameters (e.g., `T` vs
1521 // `T::Foo`). For a discussion of why this is standard practice, see
1522 // https://github.com/rust-lang/rust/issues/26925.
1523 //
1524 // The reason we diverge from this standard is that doing it that way for us
1525 // would be unsound. E.g., consider a type, `T` where `T: FromBytes` but
1526 // `T::Foo: !FromBytes`. It would not be sound for us to accept a type with
1527 // a `T::Foo` field as `FromBytes` simply because `T: FromBytes`.
1528 //
1529 // While there's no getting around this requirement for us, it does have the
1530 // pretty serious downside that, when lifetimes are involved, the trait
1531 // solver ties itself in knots:
1532 //
1533 // #[derive(Unaligned)]
1534 // #[repr(C)]
1535 // struct Dup<'a, 'b> {
1536 // a: PhantomData<&'a u8>,
1537 // b: PhantomData<&'b u8>,
1538 // }
1539 //
1540 // error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
1541 // --> src/main.rs:6:10
1542 // |
1543 // 6 | #[derive(Unaligned)]
1544 // | ^^^^^^^^^
1545 // |
1546 // = note: required by `zerocopy::Unaligned`
1547
1548 let type_ident = &input.ident;
1549 let trait_path = trt.crate_path();
1550 let fields = data.fields();
1551 let variants = data.variants();
1552 let tag = data.tag();
1553
1554 fn bound_tt(ty: &Type, traits: impl Iterator<Item = Trait>) -> WherePredicate {
1555 let traits = traits.map(|t| t.crate_path());
1556 parse_quote!(#ty: #(#traits)+*)
1557 }
1558 let field_type_bounds: Vec<_> = match (field_type_trait_bounds, &fields[..]) {
1559 (FieldBounds::All(traits), _) => fields
1560 .iter()
1561 .map(|(_vis, _name, ty)| bound_tt(ty, normalize_bounds(trt, traits)))
1562 .collect(),
1563 (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
1564 (FieldBounds::Trailing(traits), [.., last]) => {
1565 vec![bound_tt(last.2, normalize_bounds(trt, traits))]
1566 }
1567 (FieldBounds::Explicit(bounds), _) => bounds,
1568 };
1569
1570 // Don't bother emitting a padding check if there are no fields.
1571 #[allow(unstable_name_collisions)] // See `BoolExt` below
1572 // Work around https://github.com/rust-lang/rust-clippy/issues/12280
1573 #[allow(clippy::incompatible_msrv)]
1574 let padding_check_bound =
1575 padding_check.and_then(|check| (!fields.is_empty()).then_some(check)).map(|check| {
1576 let variant_types = variants.iter().map(|var| {
1577 let types = var.iter().map(|(_vis, _name, ty)| ty);
1578 quote!([#(#types),*])
1579 });
1580 let validator_context = check.validator_macro_context();
1581 let validator_macro = check.validator_macro_ident();
1582 let t = tag.iter();
1583 parse_quote! {
1584 (): ::zerocopy::util::macro_util::PaddingFree<
1585 Self,
1586 {
1587 #validator_context
1588 ::zerocopy::#validator_macro!(Self, #(#t,)* #(#variant_types),*)
1589 }
1590 >
1591 }
1592 });
1593
1594 let self_bounds: Option<WherePredicate> = match self_type_trait_bounds {
1595 SelfBounds::None => None,
1596 SelfBounds::All(traits) => Some(bound_tt(&parse_quote!(Self), traits.iter().copied())),
1597 };
1598
1599 let bounds = input
1600 .generics
1601 .where_clause
1602 .as_ref()
1603 .map(|where_clause| where_clause.predicates.iter())
1604 .into_iter()
1605 .flatten()
1606 .chain(field_type_bounds.iter())
1607 .chain(padding_check_bound.iter())
1608 .chain(self_bounds.iter());
1609
1610 // The parameters with trait bounds, but without type defaults.
1611 let params = input.generics.params.clone().into_iter().map(|mut param| {
1612 match &mut param {
1613 GenericParam::Type(ty) => ty.default = None,
1614 GenericParam::Const(cnst) => cnst.default = None,
1615 GenericParam::Lifetime(_) => {}
1616 }
1617 quote!(#param)
1618 });
1619
1620 // The identifiers of the parameters without trait bounds or type defaults.
1621 let param_idents = input.generics.params.iter().map(|param| match param {
1622 GenericParam::Type(ty) => {
1623 let ident = &ty.ident;
1624 quote!(#ident)
1625 }
1626 GenericParam::Lifetime(l) => {
1627 let ident = &l.lifetime;
1628 quote!(#ident)
1629 }
1630 GenericParam::Const(cnst) => {
1631 let ident = &cnst.ident;
1632 quote!({#ident})
1633 }
1634 });
1635
1636 let impl_tokens = quote! {
1637 // TODO(#553): Add a test that generates a warning when
1638 // `#[allow(deprecated)]` isn't present.
1639 #[allow(deprecated)]
1640 // While there are not currently any warnings that this suppresses (that
1641 // we're aware of), it's good future-proofing hygiene.
1642 #[automatically_derived]
1643 unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
1644 where
1645 #(#bounds,)*
1646 {
1647 fn only_derive_is_allowed_to_implement_this_trait() {}
1648
1649 #inner_extras
1650 }
1651 };
1652
1653 if let Some(outer_extras) = outer_extras {
1654 // So that any items defined in `#outer_extras` don't conflict with
1655 // existing names defined in this scope.
1656 quote! {
1657 const _: () = {
1658 #impl_tokens
1659
1660 #outer_extras
1661 };
1662 }
1663 } else {
1664 impl_tokens
1665 }
1666}
1667
1668// A polyfill for `Option::then_some`, which was added after our MSRV.
1669//
1670// The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain
1671// versions, `b.then_some(...)` resolves to the inherent method rather than to
1672// this trait, and so this trait is considered unused.
1673//
1674// TODO(#67): Remove this once our MSRV is >= 1.62.
1675#[allow(unused)]
1676trait BoolExt {
1677 fn then_some<T>(self, t: T) -> Option<T>;
1678}
1679
1680impl BoolExt for bool {
1681 fn then_some<T>(self, t: T) -> Option<T> {
1682 if self {
1683 Some(t)
1684 } else {
1685 None
1686 }
1687 }
1688}