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