xref: /linux/rust/zerocopy-derive/derive/unaligned.rs (revision 056a5087d87ead77dedbe9cf5bde53b7cd4b4651)
1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2 
3 use proc_macro2::{Span, TokenStream};
4 use syn::{Data, DataEnum, DataStruct, DataUnion, Error};
5 
6 use crate::{
7     repr::{EnumRepr, StructUnionRepr},
8     util::{Ctx, FieldBounds, ImplBlockBuilder, Trait},
9 };
10 
11 pub(crate) fn derive_unaligned(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> {
12     match &ctx.ast.data {
13         Data::Struct(strct) => derive_unaligned_struct(ctx, strct),
14         Data::Enum(enm) => derive_unaligned_enum(ctx, enm),
15         Data::Union(unn) => derive_unaligned_union(ctx, unn),
16     }
17 }
18 
19 /// A struct is `Unaligned` if:
20 /// - `repr(align)` is no more than 1 and either
21 ///   - `repr(C)` or `repr(transparent)` and
22 ///     - all fields `Unaligned`
23 ///   - `repr(packed)`
24 fn derive_unaligned_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error> {
25     let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
26     repr.unaligned_validate_no_align_gt_1()?;
27 
28     let field_bounds = if repr.is_packed_1() {
29         FieldBounds::None
30     } else if repr.is_c() || repr.is_transparent() {
31         FieldBounds::ALL_SELF
32     } else {
33         return ctx.error_or_skip(Error::new(
34             Span::call_site(),
35             "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment",
36         ));
37     };
38 
39     Ok(ImplBlockBuilder::new(ctx, strct, Trait::Unaligned, field_bounds).build())
40 }
41 
42 /// An enum is `Unaligned` if:
43 /// - No `repr(align(N > 1))`
44 /// - `repr(u8)` or `repr(i8)`
45 fn derive_unaligned_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error> {
46     let repr = EnumRepr::from_attrs(&ctx.ast.attrs)?;
47     repr.unaligned_validate_no_align_gt_1()?;
48 
49     if !repr.is_u8() && !repr.is_i8() {
50         return ctx.error_or_skip(Error::new(
51             Span::call_site(),
52             "must have #[repr(u8)] or #[repr(i8)] attribute in order to guarantee this type's alignment",
53         ));
54     }
55 
56     Ok(ImplBlockBuilder::new(ctx, enm, Trait::Unaligned, FieldBounds::ALL_SELF).build())
57 }
58 
59 /// Like structs, a union is `Unaligned` if:
60 /// - `repr(align)` is no more than 1 and either
61 ///   - `repr(C)` or `repr(transparent)` and
62 ///     - all fields `Unaligned`
63 ///   - `repr(packed)`
64 fn derive_unaligned_union(ctx: &Ctx, unn: &DataUnion) -> Result<TokenStream, Error> {
65     let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?;
66     repr.unaligned_validate_no_align_gt_1()?;
67 
68     let field_type_trait_bounds = if repr.is_packed_1() {
69         FieldBounds::None
70     } else if repr.is_c() || repr.is_transparent() {
71         FieldBounds::ALL_SELF
72     } else {
73         return ctx.error_or_skip(Error::new(
74             Span::call_site(),
75             "must have #[repr(C)], #[repr(transparent)], or #[repr(packed)] attribute in order to guarantee this type's alignment",
76         ));
77     };
78 
79     Ok(ImplBlockBuilder::new(ctx, unn, Trait::Unaligned, field_type_trait_bounds).build())
80 }
81