Skip to main content

zerocopy_derive/derive/
unaligned.rs

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