Skip to main content

zerocopy_derive/derive/
unaligned.rs

1// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2
3use proc_macro2::{Span, TokenStream};
4use syn::{Data, DataEnum, DataStruct, DataUnion, Error};
5
6use crate::{
7    repr::{EnumRepr, StructUnionRepr},
8    util::{Ctx, FieldBounds, ImplBlockBuilder, Trait},
9};
10
11pub(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)`
24fn 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)`
45fn 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)`
64fn 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}