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 
31 mod ext;
32 mod repr;
33 
34 use {
35     proc_macro2::Span,
36     quote::quote,
37     syn::{
38         parse_quote, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit,
39         GenericParam, Ident, Lit,
40     },
41 };
42 
43 use {crate::ext::*, crate::repr::*};
44 
45 // Unwraps a `Result<_, Vec<Error>>`, converting any `Err` value into a
46 // `TokenStream` and returning it.
47 macro_rules! try_or_print {
48     ($e:expr) => {
49         match $e {
50             Ok(x) => x,
51             Err(errors) => return print_all_errors(errors).into(),
52         }
53     };
54 }
55 
56 // TODO(https://github.com/rust-lang/rust/issues/54140): Some errors could be
57 // made better if we could add multiple lines of error output like this:
58 //
59 // error: unsupported representation
60 //   --> enum.rs:28:8
61 //    |
62 // 28 | #[repr(transparent)]
63 //    |
64 // help: required by the derive of FromBytes
65 //
66 // Instead, we have more verbose error messages like "unsupported representation
67 // for deriving FromZeroes, FromBytes, AsBytes, or Unaligned on an enum"
68 //
69 // This will probably require Span::error
70 // (https://doc.rust-lang.org/nightly/proc_macro/struct.Span.html#method.error),
71 // which is currently unstable. Revisit this once it's stable.
72 
73 #[proc_macro_derive(KnownLayout)]
derive_known_layout(ts: proc_macro::TokenStream) -> proc_macro::TokenStream74 pub fn derive_known_layout(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
75     let ast = syn::parse_macro_input!(ts as DeriveInput);
76 
77     let is_repr_c_struct = match &ast.data {
78         Data::Struct(..) => {
79             let reprs = try_or_print!(repr::reprs::<Repr>(&ast.attrs));
80             if reprs.iter().any(|(_meta, repr)| repr == &Repr::C) {
81                 Some(reprs)
82             } else {
83                 None
84             }
85         }
86         Data::Enum(..) | Data::Union(..) => None,
87     };
88 
89     let fields = ast.data.field_types();
90 
91     let (require_self_sized, extras) = if let (
92         Some(reprs),
93         Some((trailing_field, leading_fields)),
94     ) = (is_repr_c_struct, fields.split_last())
95     {
96         let repr_align = reprs
97             .iter()
98             .find_map(
99                 |(_meta, repr)| {
100                     if let Repr::Align(repr_align) = repr {
101                         Some(repr_align)
102                     } else {
103                         None
104                     }
105                 },
106             )
107             .map(|repr_align| quote!(NonZeroUsize::new(#repr_align as usize)))
108             .unwrap_or(quote!(None));
109 
110         let repr_packed = reprs
111             .iter()
112             .find_map(|(_meta, repr)| match repr {
113                 Repr::Packed => Some(1),
114                 Repr::PackedN(repr_packed) => Some(*repr_packed),
115                 _ => None,
116             })
117             .map(|repr_packed| quote!(NonZeroUsize::new(#repr_packed as usize)))
118             .unwrap_or(quote!(None));
119 
120         (
121             false,
122             quote!(
123                 // SAFETY: `LAYOUT` accurately describes the layout of `Self`.
124                 // The layout of `Self` is reflected using a sequence of
125                 // invocations of `DstLayout::{new_zst,extend,pad_to_align}`.
126                 // The documentation of these items vows that invocations in
127                 // this manner will acurately describe a type, so long as:
128                 //
129                 //  - that type is `repr(C)`,
130                 //  - its fields are enumerated in the order they appear,
131                 //  - the presence of `repr_align` and `repr_packed` are correctly accounted for.
132                 //
133                 // We respect all three of these preconditions here. This
134                 // expansion is only used if `is_repr_c_struct`, we enumerate
135                 // the fields in order, and we extract the values of `align(N)`
136                 // and `packed(N)`.
137                 const LAYOUT: ::zerocopy::DstLayout = {
138                     use ::zerocopy::macro_util::core_reexport::num::NonZeroUsize;
139                     use ::zerocopy::{DstLayout, KnownLayout};
140 
141                     let repr_align = #repr_align;
142                     let repr_packed = #repr_packed;
143 
144                     DstLayout::new_zst(repr_align)
145                         #(.extend(DstLayout::for_type::<#leading_fields>(), repr_packed))*
146                         .extend(<#trailing_field as KnownLayout>::LAYOUT, repr_packed)
147                         .pad_to_align()
148                 };
149 
150                 // SAFETY:
151                 // - The recursive call to `raw_from_ptr_len` preserves both address and provenance.
152                 // - The `as` cast preserves both address and provenance.
153                 // - `NonNull::new_unchecked` preserves both address and provenance.
154                 #[inline(always)]
155                 fn raw_from_ptr_len(
156                     bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull<u8>,
157                     elems: usize,
158                 ) -> ::zerocopy::macro_util::core_reexport::ptr::NonNull<Self> {
159                     use ::zerocopy::{KnownLayout};
160                     let trailing = <#trailing_field as KnownLayout>::raw_from_ptr_len(bytes, elems);
161                     let slf = trailing.as_ptr() as *mut Self;
162                     // SAFETY: Constructed from `trailing`, which is non-null.
163                     unsafe { ::zerocopy::macro_util::core_reexport::ptr::NonNull::new_unchecked(slf) }
164                 }
165             ),
166         )
167     } else {
168         // For enums, unions, and non-`repr(C)` structs, we require that
169         // `Self` is sized, and as a result don't need to reason about the
170         // internals of the type.
171         (
172             true,
173             quote!(
174                 // SAFETY: `LAYOUT` is guaranteed to accurately describe the
175                 // layout of `Self`, because that is the documented safety
176                 // contract of `DstLayout::for_type`.
177                 const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_type::<Self>();
178 
179                 // SAFETY: `.cast` preserves address and provenance.
180                 //
181                 // TODO(#429): Add documentation to `.cast` that promises that
182                 // it preserves provenance.
183                 #[inline(always)]
184                 fn raw_from_ptr_len(
185                     bytes: ::zerocopy::macro_util::core_reexport::ptr::NonNull<u8>,
186                     _elems: usize,
187                 ) -> ::zerocopy::macro_util::core_reexport::ptr::NonNull<Self> {
188                     bytes.cast::<Self>()
189                 }
190             ),
191         )
192     };
193 
194     match &ast.data {
195         Data::Struct(strct) => {
196             let require_trait_bound_on_field_types = if require_self_sized {
197                 RequireBoundedFields::No
198             } else {
199                 RequireBoundedFields::Trailing
200             };
201 
202             // A bound on the trailing field is required, since structs are
203             // unsized if their trailing field is unsized. Reflecting the layout
204             // of an usized trailing field requires that the field is
205             // `KnownLayout`.
206             impl_block(
207                 &ast,
208                 strct,
209                 Trait::KnownLayout,
210                 require_trait_bound_on_field_types,
211                 require_self_sized,
212                 None,
213                 Some(extras),
214             )
215         }
216         Data::Enum(enm) => {
217             // A bound on the trailing field is not required, since enums cannot
218             // currently be unsized.
219             impl_block(
220                 &ast,
221                 enm,
222                 Trait::KnownLayout,
223                 RequireBoundedFields::No,
224                 true,
225                 None,
226                 Some(extras),
227             )
228         }
229         Data::Union(unn) => {
230             // A bound on the trailing field is not required, since unions
231             // cannot currently be unsized.
232             impl_block(
233                 &ast,
234                 unn,
235                 Trait::KnownLayout,
236                 RequireBoundedFields::No,
237                 true,
238                 None,
239                 Some(extras),
240             )
241         }
242     }
243     .into()
244 }
245 
246 #[proc_macro_derive(FromZeroes)]
derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream247 pub fn derive_from_zeroes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
248     let ast = syn::parse_macro_input!(ts as DeriveInput);
249     match &ast.data {
250         Data::Struct(strct) => derive_from_zeroes_struct(&ast, strct),
251         Data::Enum(enm) => derive_from_zeroes_enum(&ast, enm),
252         Data::Union(unn) => derive_from_zeroes_union(&ast, unn),
253     }
254     .into()
255 }
256 
257 #[proc_macro_derive(FromBytes)]
derive_from_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream258 pub fn derive_from_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
259     let ast = syn::parse_macro_input!(ts as DeriveInput);
260     match &ast.data {
261         Data::Struct(strct) => derive_from_bytes_struct(&ast, strct),
262         Data::Enum(enm) => derive_from_bytes_enum(&ast, enm),
263         Data::Union(unn) => derive_from_bytes_union(&ast, unn),
264     }
265     .into()
266 }
267 
268 #[proc_macro_derive(AsBytes)]
derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream269 pub fn derive_as_bytes(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
270     let ast = syn::parse_macro_input!(ts as DeriveInput);
271     match &ast.data {
272         Data::Struct(strct) => derive_as_bytes_struct(&ast, strct),
273         Data::Enum(enm) => derive_as_bytes_enum(&ast, enm),
274         Data::Union(unn) => derive_as_bytes_union(&ast, unn),
275     }
276     .into()
277 }
278 
279 #[proc_macro_derive(Unaligned)]
derive_unaligned(ts: proc_macro::TokenStream) -> proc_macro::TokenStream280 pub fn derive_unaligned(ts: proc_macro::TokenStream) -> proc_macro::TokenStream {
281     let ast = syn::parse_macro_input!(ts as DeriveInput);
282     match &ast.data {
283         Data::Struct(strct) => derive_unaligned_struct(&ast, strct),
284         Data::Enum(enm) => derive_unaligned_enum(&ast, enm),
285         Data::Union(unn) => derive_unaligned_union(&ast, unn),
286     }
287     .into()
288 }
289 
290 const STRUCT_UNION_ALLOWED_REPR_COMBINATIONS: &[&[StructRepr]] = &[
291     &[StructRepr::C],
292     &[StructRepr::Transparent],
293     &[StructRepr::Packed],
294     &[StructRepr::C, StructRepr::Packed],
295 ];
296 
297 // A struct is `FromZeroes` if:
298 // - all fields are `FromZeroes`
299 
derive_from_zeroes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream300 fn derive_from_zeroes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
301     impl_block(ast, strct, Trait::FromZeroes, RequireBoundedFields::Yes, false, None, None)
302 }
303 
304 // An enum is `FromZeroes` if:
305 // - all of its variants are fieldless
306 // - one of the variants has a discriminant of `0`
307 
derive_from_zeroes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream308 fn derive_from_zeroes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
309     if !enm.is_c_like() {
310         return Error::new_spanned(ast, "only C-like enums can implement FromZeroes")
311             .to_compile_error();
312     }
313 
314     let has_explicit_zero_discriminant =
315         enm.variants.iter().filter_map(|v| v.discriminant.as_ref()).any(|(_, e)| {
316             if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = e {
317                 i.base10_parse::<usize>().ok() == Some(0)
318             } else {
319                 false
320             }
321         });
322     // If the first variant of an enum does not specify its discriminant, it is set to zero:
323     // https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
324     let has_implicit_zero_discriminant =
325         enm.variants.iter().next().map(|v| v.discriminant.is_none()) == Some(true);
326 
327     if !has_explicit_zero_discriminant && !has_implicit_zero_discriminant {
328         return Error::new_spanned(
329             ast,
330             "FromZeroes only supported on enums with a variant that has a discriminant of `0`",
331         )
332         .to_compile_error();
333     }
334 
335     impl_block(ast, enm, Trait::FromZeroes, RequireBoundedFields::Yes, false, None, None)
336 }
337 
338 // Like structs, unions are `FromZeroes` if
339 // - all fields are `FromZeroes`
340 
derive_from_zeroes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream341 fn derive_from_zeroes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
342     impl_block(ast, unn, Trait::FromZeroes, RequireBoundedFields::Yes, false, None, None)
343 }
344 
345 // A struct is `FromBytes` if:
346 // - all fields are `FromBytes`
347 
derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream348 fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
349     impl_block(ast, strct, Trait::FromBytes, RequireBoundedFields::Yes, false, None, None)
350 }
351 
352 // An enum is `FromBytes` if:
353 // - Every possible bit pattern must be valid, which means that every bit
354 //   pattern must correspond to a different enum variant. Thus, for an enum
355 //   whose layout takes up N bytes, there must be 2^N variants.
356 // - Since we must know N, only representations which guarantee the layout's
357 //   size are allowed. These are `repr(uN)` and `repr(iN)` (`repr(C)` implies an
358 //   implementation-defined size). `usize` and `isize` technically guarantee the
359 //   layout's size, but would require us to know how large those are on the
360 //   target platform. This isn't terribly difficult - we could emit a const
361 //   expression that could call `core::mem::size_of` in order to determine the
362 //   size and check against the number of enum variants, but a) this would be
363 //   platform-specific and, b) even on Rust's smallest bit width platform (32),
364 //   this would require ~4 billion enum variants, which obviously isn't a thing.
365 
derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream366 fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
367     if !enm.is_c_like() {
368         return Error::new_spanned(ast, "only C-like enums can implement FromBytes")
369             .to_compile_error();
370     }
371 
372     let reprs = try_or_print!(ENUM_FROM_BYTES_CFG.validate_reprs(ast));
373 
374     let variants_required = match reprs.as_slice() {
375         [EnumRepr::U8] | [EnumRepr::I8] => 1usize << 8,
376         [EnumRepr::U16] | [EnumRepr::I16] => 1usize << 16,
377         // `validate_reprs` has already validated that it's one of the preceding
378         // patterns.
379         _ => unreachable!(),
380     };
381     if enm.variants.len() != variants_required {
382         return Error::new_spanned(
383             ast,
384             format!(
385                 "FromBytes only supported on {} enum with {} variants",
386                 reprs[0], variants_required
387             ),
388         )
389         .to_compile_error();
390     }
391 
392     impl_block(ast, enm, Trait::FromBytes, RequireBoundedFields::Yes, false, None, None)
393 }
394 
395 #[rustfmt::skip]
396 const ENUM_FROM_BYTES_CFG: Config<EnumRepr> = {
397     use EnumRepr::*;
398     Config {
399         allowed_combinations_message: r#"FromBytes requires repr of "u8", "u16", "i8", or "i16""#,
400         derive_unaligned: false,
401         allowed_combinations: &[
402             &[U8],
403             &[U16],
404             &[I8],
405             &[I16],
406         ],
407         disallowed_but_legal_combinations: &[
408             &[C],
409             &[U32],
410             &[I32],
411             &[U64],
412             &[I64],
413             &[Usize],
414             &[Isize],
415         ],
416     }
417 };
418 
419 // Like structs, unions are `FromBytes` if
420 // - all fields are `FromBytes`
421 
derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream422 fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
423     impl_block(ast, unn, Trait::FromBytes, RequireBoundedFields::Yes, false, None, None)
424 }
425 
426 // A struct is `AsBytes` if:
427 // - all fields are `AsBytes`
428 // - `repr(C)` or `repr(transparent)` and
429 //   - no padding (size of struct equals sum of size of field types)
430 // - `repr(packed)`
431 
derive_as_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream432 fn derive_as_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
433     let reprs = try_or_print!(STRUCT_UNION_AS_BYTES_CFG.validate_reprs(ast));
434     let is_transparent = reprs.contains(&StructRepr::Transparent);
435     let is_packed = reprs.contains(&StructRepr::Packed);
436 
437     // TODO(#10): Support type parameters for non-transparent, non-packed
438     // structs.
439     if !ast.generics.params.is_empty() && !is_transparent && !is_packed {
440         return Error::new(
441             Span::call_site(),
442             "unsupported on generic structs that are not repr(transparent) or repr(packed)",
443         )
444         .to_compile_error();
445     }
446 
447     // We don't need a padding check if the struct is repr(transparent) or
448     // repr(packed).
449     // - repr(transparent): The layout and ABI of the whole struct is the same
450     //   as its only non-ZST field (meaning there's no padding outside of that
451     //   field) and we require that field to be `AsBytes` (meaning there's no
452     //   padding in that field).
453     // - repr(packed): Any inter-field padding bytes are removed, meaning that
454     //   any padding bytes would need to come from the fields, all of which
455     //   we require to be `AsBytes` (meaning they don't have any padding).
456     let padding_check = if is_transparent || is_packed { None } else { Some(PaddingCheck::Struct) };
457     impl_block(ast, strct, Trait::AsBytes, RequireBoundedFields::Yes, false, padding_check, None)
458 }
459 
460 const STRUCT_UNION_AS_BYTES_CFG: Config<StructRepr> = Config {
461     // Since `disallowed_but_legal_combinations` is empty, this message will
462     // never actually be emitted.
463     allowed_combinations_message: r#"AsBytes requires either a) repr "C" or "transparent" with all fields implementing AsBytes or, b) repr "packed""#,
464     derive_unaligned: false,
465     allowed_combinations: STRUCT_UNION_ALLOWED_REPR_COMBINATIONS,
466     disallowed_but_legal_combinations: &[],
467 };
468 
469 // An enum is `AsBytes` if it is C-like and has a defined repr.
470 
derive_as_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream471 fn derive_as_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
472     if !enm.is_c_like() {
473         return Error::new_spanned(ast, "only C-like enums can implement AsBytes")
474             .to_compile_error();
475     }
476 
477     // We don't care what the repr is; we only care that it is one of the
478     // allowed ones.
479     let _: Vec<repr::EnumRepr> = try_or_print!(ENUM_AS_BYTES_CFG.validate_reprs(ast));
480     impl_block(ast, enm, Trait::AsBytes, RequireBoundedFields::No, false, None, None)
481 }
482 
483 #[rustfmt::skip]
484 const ENUM_AS_BYTES_CFG: Config<EnumRepr> = {
485     use EnumRepr::*;
486     Config {
487         // Since `disallowed_but_legal_combinations` is empty, this message will
488         // never actually be emitted.
489         allowed_combinations_message: r#"AsBytes requires repr of "C", "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", or "isize""#,
490         derive_unaligned: false,
491         allowed_combinations: &[
492             &[C],
493             &[U8],
494             &[U16],
495             &[I8],
496             &[I16],
497             &[U32],
498             &[I32],
499             &[U64],
500             &[I64],
501             &[Usize],
502             &[Isize],
503         ],
504         disallowed_but_legal_combinations: &[],
505     }
506 };
507 
508 // A union is `AsBytes` if:
509 // - all fields are `AsBytes`
510 // - `repr(C)`, `repr(transparent)`, or `repr(packed)`
511 // - no padding (size of union equals size of each field type)
512 
derive_as_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream513 fn derive_as_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
514     // TODO(#10): Support type parameters.
515     if !ast.generics.params.is_empty() {
516         return Error::new(Span::call_site(), "unsupported on types with type parameters")
517             .to_compile_error();
518     }
519 
520     try_or_print!(STRUCT_UNION_AS_BYTES_CFG.validate_reprs(ast));
521 
522     impl_block(
523         ast,
524         unn,
525         Trait::AsBytes,
526         RequireBoundedFields::Yes,
527         false,
528         Some(PaddingCheck::Union),
529         None,
530     )
531 }
532 
533 // A struct is `Unaligned` if:
534 // - `repr(align)` is no more than 1 and either
535 //   - `repr(C)` or `repr(transparent)` and
536 //     - all fields `Unaligned`
537 //   - `repr(packed)`
538 
derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream539 fn derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream {
540     let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast));
541     let require_trait_bounds_on_field_types = (!reprs.contains(&StructRepr::Packed)).into();
542 
543     impl_block(ast, strct, Trait::Unaligned, require_trait_bounds_on_field_types, false, None, None)
544 }
545 
546 const STRUCT_UNION_UNALIGNED_CFG: Config<StructRepr> = Config {
547     // Since `disallowed_but_legal_combinations` is empty, this message will
548     // never actually be emitted.
549     allowed_combinations_message: r#"Unaligned requires either a) repr "C" or "transparent" with all fields implementing Unaligned or, b) repr "packed""#,
550     derive_unaligned: true,
551     allowed_combinations: STRUCT_UNION_ALLOWED_REPR_COMBINATIONS,
552     disallowed_but_legal_combinations: &[],
553 };
554 
555 // An enum is `Unaligned` if:
556 // - No `repr(align(N > 1))`
557 // - `repr(u8)` or `repr(i8)`
558 
derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream559 fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
560     if !enm.is_c_like() {
561         return Error::new_spanned(ast, "only C-like enums can implement Unaligned")
562             .to_compile_error();
563     }
564 
565     // The only valid reprs are `u8` and `i8`, and optionally `align(1)`. We
566     // don't actually care what the reprs are so long as they satisfy that
567     // requirement.
568     let _: Vec<repr::EnumRepr> = try_or_print!(ENUM_UNALIGNED_CFG.validate_reprs(ast));
569 
570     // C-like enums cannot currently have type parameters, so this value of true
571     // for `require_trait_bound_on_field_types` doesn't really do anything. But
572     // it's marginally more future-proof in case that restriction is lifted in
573     // the future.
574     impl_block(ast, enm, Trait::Unaligned, RequireBoundedFields::Yes, false, None, None)
575 }
576 
577 #[rustfmt::skip]
578 const ENUM_UNALIGNED_CFG: Config<EnumRepr> = {
579     use EnumRepr::*;
580     Config {
581         allowed_combinations_message:
582             r#"Unaligned requires repr of "u8" or "i8", and no alignment (i.e., repr(align(N > 1)))"#,
583         derive_unaligned: true,
584         allowed_combinations: &[
585             &[U8],
586             &[I8],
587         ],
588         disallowed_but_legal_combinations: &[
589             &[C],
590             &[U16],
591             &[U32],
592             &[U64],
593             &[Usize],
594             &[I16],
595             &[I32],
596             &[I64],
597             &[Isize],
598         ],
599     }
600 };
601 
602 // Like structs, a union is `Unaligned` if:
603 // - `repr(align)` is no more than 1 and either
604 //   - `repr(C)` or `repr(transparent)` and
605 //     - all fields `Unaligned`
606 //   - `repr(packed)`
607 
derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream608 fn derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream {
609     let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast));
610     let require_trait_bound_on_field_types = (!reprs.contains(&StructRepr::Packed)).into();
611 
612     impl_block(ast, unn, Trait::Unaligned, require_trait_bound_on_field_types, false, None, None)
613 }
614 
615 // This enum describes what kind of padding check needs to be generated for the
616 // associated impl.
617 enum PaddingCheck {
618     // Check that the sum of the fields' sizes exactly equals the struct's size.
619     Struct,
620     // Check that the size of each field exactly equals the union's size.
621     Union,
622 }
623 
624 impl PaddingCheck {
625     /// Returns the ident of the macro to call in order to validate that a type
626     /// passes the padding check encoded by `PaddingCheck`.
validator_macro_ident(&self) -> Ident627     fn validator_macro_ident(&self) -> Ident {
628         let s = match self {
629             PaddingCheck::Struct => "struct_has_padding",
630             PaddingCheck::Union => "union_has_padding",
631         };
632 
633         Ident::new(s, Span::call_site())
634     }
635 }
636 
637 #[derive(Debug, Eq, PartialEq)]
638 enum Trait {
639     KnownLayout,
640     FromZeroes,
641     FromBytes,
642     AsBytes,
643     Unaligned,
644 }
645 
646 impl Trait {
ident(&self) -> Ident647     fn ident(&self) -> Ident {
648         Ident::new(format!("{:?}", self).as_str(), Span::call_site())
649     }
650 }
651 
652 #[derive(Debug, Eq, PartialEq)]
653 enum RequireBoundedFields {
654     No,
655     Yes,
656     Trailing,
657 }
658 
659 impl From<bool> for RequireBoundedFields {
from(do_require: bool) -> Self660     fn from(do_require: bool) -> Self {
661         match do_require {
662             true => Self::Yes,
663             false => Self::No,
664         }
665     }
666 }
667 
impl_block<D: DataExt>( input: &DeriveInput, data: &D, trt: Trait, require_trait_bound_on_field_types: RequireBoundedFields, require_self_sized: bool, padding_check: Option<PaddingCheck>, extras: Option<proc_macro2::TokenStream>, ) -> proc_macro2::TokenStream668 fn impl_block<D: DataExt>(
669     input: &DeriveInput,
670     data: &D,
671     trt: Trait,
672     require_trait_bound_on_field_types: RequireBoundedFields,
673     require_self_sized: bool,
674     padding_check: Option<PaddingCheck>,
675     extras: Option<proc_macro2::TokenStream>,
676 ) -> proc_macro2::TokenStream {
677     // In this documentation, we will refer to this hypothetical struct:
678     //
679     //   #[derive(FromBytes)]
680     //   struct Foo<T, I: Iterator>
681     //   where
682     //       T: Copy,
683     //       I: Clone,
684     //       I::Item: Clone,
685     //   {
686     //       a: u8,
687     //       b: T,
688     //       c: I::Item,
689     //   }
690     //
691     // We extract the field types, which in this case are `u8`, `T`, and
692     // `I::Item`. We re-use the existing parameters and where clauses. If
693     // `require_trait_bound == true` (as it is for `FromBytes), we add where
694     // bounds for each field's type:
695     //
696     //   impl<T, I: Iterator> FromBytes for Foo<T, I>
697     //   where
698     //       T: Copy,
699     //       I: Clone,
700     //       I::Item: Clone,
701     //       T: FromBytes,
702     //       I::Item: FromBytes,
703     //   {
704     //   }
705     //
706     // NOTE: It is standard practice to only emit bounds for the type parameters
707     // themselves, not for field types based on those parameters (e.g., `T` vs
708     // `T::Foo`). For a discussion of why this is standard practice, see
709     // https://github.com/rust-lang/rust/issues/26925.
710     //
711     // The reason we diverge from this standard is that doing it that way for us
712     // would be unsound. E.g., consider a type, `T` where `T: FromBytes` but
713     // `T::Foo: !FromBytes`. It would not be sound for us to accept a type with
714     // a `T::Foo` field as `FromBytes` simply because `T: FromBytes`.
715     //
716     // While there's no getting around this requirement for us, it does have the
717     // pretty serious downside that, when lifetimes are involved, the trait
718     // solver ties itself in knots:
719     //
720     //     #[derive(Unaligned)]
721     //     #[repr(C)]
722     //     struct Dup<'a, 'b> {
723     //         a: PhantomData<&'a u8>,
724     //         b: PhantomData<&'b u8>,
725     //     }
726     //
727     //     error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
728     //      --> src/main.rs:6:10
729     //       |
730     //     6 | #[derive(Unaligned)]
731     //       |          ^^^^^^^^^
732     //       |
733     //       = note: required by `zerocopy::Unaligned`
734 
735     let type_ident = &input.ident;
736     let trait_ident = trt.ident();
737     let field_types = data.field_types();
738 
739     let bound_tt = |ty| parse_quote!(#ty: ::zerocopy::#trait_ident);
740     let field_type_bounds: Vec<_> = match (require_trait_bound_on_field_types, &field_types[..]) {
741         (RequireBoundedFields::Yes, _) => field_types.iter().map(bound_tt).collect(),
742         (RequireBoundedFields::No, _) | (RequireBoundedFields::Trailing, []) => vec![],
743         (RequireBoundedFields::Trailing, [.., last]) => vec![bound_tt(last)],
744     };
745 
746     // Don't bother emitting a padding check if there are no fields.
747     #[allow(
748         unstable_name_collisions, // See `BoolExt` below
749         clippy::incompatible_msrv, // https://github.com/rust-lang/rust-clippy/issues/12280
750     )]
751     let padding_check_bound = padding_check.and_then(|check| (!field_types.is_empty()).then_some(check)).map(|check| {
752         let fields = field_types.iter();
753         let validator_macro = check.validator_macro_ident();
754         parse_quote!(
755             ::zerocopy::macro_util::HasPadding<#type_ident, {::zerocopy::#validator_macro!(#type_ident, #(#fields),*)}>:
756                 ::zerocopy::macro_util::ShouldBe<false>
757         )
758     });
759 
760     let self_sized_bound = if require_self_sized { Some(parse_quote!(Self: Sized)) } else { None };
761 
762     let bounds = input
763         .generics
764         .where_clause
765         .as_ref()
766         .map(|where_clause| where_clause.predicates.iter())
767         .into_iter()
768         .flatten()
769         .chain(field_type_bounds.iter())
770         .chain(padding_check_bound.iter())
771         .chain(self_sized_bound.iter());
772 
773     // The parameters with trait bounds, but without type defaults.
774     let params = input.generics.params.clone().into_iter().map(|mut param| {
775         match &mut param {
776             GenericParam::Type(ty) => ty.default = None,
777             GenericParam::Const(cnst) => cnst.default = None,
778             GenericParam::Lifetime(_) => {}
779         }
780         quote!(#param)
781     });
782 
783     // The identifiers of the parameters without trait bounds or type defaults.
784     let param_idents = input.generics.params.iter().map(|param| match param {
785         GenericParam::Type(ty) => {
786             let ident = &ty.ident;
787             quote!(#ident)
788         }
789         GenericParam::Lifetime(l) => {
790             let ident = &l.lifetime;
791             quote!(#ident)
792         }
793         GenericParam::Const(cnst) => {
794             let ident = &cnst.ident;
795             quote!({#ident})
796         }
797     });
798 
799     quote! {
800         // TODO(#553): Add a test that generates a warning when
801         // `#[allow(deprecated)]` isn't present.
802         #[allow(deprecated)]
803         unsafe impl < #(#params),* > ::zerocopy::#trait_ident for #type_ident < #(#param_idents),* >
804         where
805             #(#bounds,)*
806         {
807             fn only_derive_is_allowed_to_implement_this_trait() {}
808 
809             #extras
810         }
811     }
812 }
813 
print_all_errors(errors: Vec<Error>) -> proc_macro2::TokenStream814 fn print_all_errors(errors: Vec<Error>) -> proc_macro2::TokenStream {
815     errors.iter().map(Error::to_compile_error).collect()
816 }
817 
818 // A polyfill for `Option::then_some`, which was added after our MSRV.
819 //
820 // TODO(#67): Remove this once our MSRV is >= 1.62.
821 #[allow(unused)]
822 trait BoolExt {
then_some<T>(self, t: T) -> Option<T>823     fn then_some<T>(self, t: T) -> Option<T>;
824 }
825 
826 #[allow(unused)]
827 impl BoolExt for bool {
then_some<T>(self, t: T) -> Option<T>828     fn then_some<T>(self, t: T) -> Option<T> {
829         if self {
830             Some(t)
831         } else {
832             None
833         }
834     }
835 }
836 
837 #[cfg(test)]
838 mod tests {
839     use super::*;
840 
841     #[test]
test_config_repr_orderings()842     fn test_config_repr_orderings() {
843         // Validate that the repr lists in the various configs are in the
844         // canonical order. If they aren't, then our algorithm to look up in
845         // those lists won't work.
846 
847         // TODO(https://github.com/rust-lang/rust/issues/53485): Remove once
848         // `Vec::is_sorted` is stabilized.
849         fn is_sorted_and_deduped<T: Clone + Ord>(ts: &[T]) -> bool {
850             let mut sorted = ts.to_vec();
851             sorted.sort();
852             sorted.dedup();
853             ts == sorted.as_slice()
854         }
855 
856         fn elements_are_sorted_and_deduped<T: Clone + Ord>(lists: &[&[T]]) -> bool {
857             lists.iter().all(|list| is_sorted_and_deduped(list))
858         }
859 
860         fn config_is_sorted<T: KindRepr + Clone>(config: &Config<T>) -> bool {
861             elements_are_sorted_and_deduped(config.allowed_combinations)
862                 && elements_are_sorted_and_deduped(config.disallowed_but_legal_combinations)
863         }
864 
865         assert!(config_is_sorted(&STRUCT_UNION_UNALIGNED_CFG));
866         assert!(config_is_sorted(&ENUM_FROM_BYTES_CFG));
867         assert!(config_is_sorted(&ENUM_UNALIGNED_CFG));
868     }
869 
870     #[test]
test_config_repr_no_overlap()871     fn test_config_repr_no_overlap() {
872         // Validate that no set of reprs appears in both the
873         // `allowed_combinations` and `disallowed_but_legal_combinations` lists.
874 
875         fn overlap<T: Eq>(a: &[T], b: &[T]) -> bool {
876             a.iter().any(|elem| b.contains(elem))
877         }
878 
879         fn config_overlaps<T: KindRepr + Eq>(config: &Config<T>) -> bool {
880             overlap(config.allowed_combinations, config.disallowed_but_legal_combinations)
881         }
882 
883         assert!(!config_overlaps(&STRUCT_UNION_UNALIGNED_CFG));
884         assert!(!config_overlaps(&ENUM_FROM_BYTES_CFG));
885         assert!(!config_overlaps(&ENUM_UNALIGNED_CFG));
886     }
887 }
888