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
derive_unaligned(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error>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)`
derive_unaligned_struct(ctx: &Ctx, strct: &DataStruct) -> Result<TokenStream, Error>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)`
derive_unaligned_enum(ctx: &Ctx, enm: &DataEnum) -> Result<TokenStream, Error>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)`
derive_unaligned_union(ctx: &Ctx, unn: &DataUnion) -> Result<TokenStream, Error>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