1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use syn::ext::IdentExt;
6
7use crate::bindgen::ir::ty::{IntKind, PrimitiveType};
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
10pub enum ReprStyle {
11 #[default]
12 Rust,
13 C,
14 Transparent,
15}
16
17#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18pub struct ReprType {
19 kind: IntKind,
20 signed: bool,
21}
22
23impl ReprType {
24 pub(crate) fn to_primitive(self) -> PrimitiveType {
25 PrimitiveType::Integer {
26 kind: self.kind,
27 signed: self.signed,
28 zeroable: true,
29 }
30 }
31}
32
33#[derive(Debug, Copy, Clone, PartialEq, Eq)]
34pub enum ReprAlign {
35 Packed,
36 Align(u64),
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
40pub struct Repr {
41 pub style: ReprStyle,
42 pub ty: Option<ReprType>,
43 pub align: Option<ReprAlign>,
44}
45
46impl Repr {
47 pub fn load(attrs: &[syn::Attribute]) -> Result<Repr, String> {
48 let ids = attrs
49 .iter()
50 .filter_map(|attr| {
51 if let syn::Meta::List(syn::MetaList { path, nested, .. }) =
52 attr.parse_meta().ok()?
53 {
54 if path.is_ident("repr") {
55 return Some(nested.into_iter().collect::<Vec<_>>());
56 }
57 }
58 None
59 })
60 .flatten()
61 .filter_map(|meta| match meta {
62 syn::NestedMeta::Meta(syn::Meta::Path(path)) => Some((
63 path.segments.first().unwrap().ident.unraw().to_string(),
64 None,
65 )),
66 syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList { path, nested, .. })) => {
67 Some((
68 path.segments.first().unwrap().ident.unraw().to_string(),
69 Some(
70 nested
71 .iter()
72 .filter_map(|meta| match meta {
73 // Only used for #[repr(align(...))].
74 syn::NestedMeta::Lit(syn::Lit::Int(literal)) => {
75 Some(literal.base10_digits().to_string())
76 }
77 // Only single levels of nesting supported at the moment.
78 _ => None,
79 })
80 .collect::<Vec<_>>(),
81 ),
82 ))
83 }
84 _ => None,
85 });
86
87 let mut repr = Repr::default();
88 for id in ids {
89 let (int_kind, signed) = match (id.0.as_ref(), id.1) {
90 ("u8", None) => (IntKind::B8, false),
91 ("u16", None) => (IntKind::B16, false),
92 ("u32", None) => (IntKind::B32, false),
93 ("u64", None) => (IntKind::B64, false),
94 ("usize", None) => (IntKind::Size, false),
95 ("i8", None) => (IntKind::B8, true),
96 ("i16", None) => (IntKind::B16, true),
97 ("i32", None) => (IntKind::B32, true),
98 ("i64", None) => (IntKind::B64, true),
99 ("isize", None) => (IntKind::Size, true),
100 ("C", None) => {
101 repr.style = ReprStyle::C;
102 continue;
103 }
104 ("transparent", None) => {
105 repr.style = ReprStyle::Transparent;
106 continue;
107 }
108 ("packed", args) => {
109 // #[repr(packed(n))] not supported because of some open questions about how
110 // to calculate the native alignment of types. See mozilla/cbindgen#433.
111 if args.is_some() {
112 return Err(
113 "Not-yet-implemented #[repr(packed(...))] encountered.".to_string()
114 );
115 }
116 let align = ReprAlign::Packed;
117 // Only permit a single alignment-setting repr.
118 if let Some(old_align) = repr.align {
119 return Err(format!(
120 "Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
121 old_align, align
122 ));
123 }
124 repr.align = Some(align);
125 continue;
126 }
127 ("align", Some(args)) => {
128 // #[repr(align(...))] only allows a single argument.
129 if args.len() != 1 {
130 return Err(format!(
131 "Unsupported #[repr(align({}))], align must have exactly one argument.",
132 args.join(", ")
133 ));
134 }
135 // Must be a positive integer.
136 let align = match args.first().unwrap().parse::<u64>() {
137 Ok(align) => align,
138 Err(_) => {
139 return Err(format!("Non-numeric #[repr(align({}))].", args.join(", ")))
140 }
141 };
142 // Must be a power of 2.
143 if !align.is_power_of_two() || align == 0 {
144 return Err(format!("Invalid alignment to #[repr(align({}))].", align));
145 }
146 // Only permit a single alignment-setting repr.
147 if let Some(old_align) = repr.align {
148 return Err(format!(
149 "Conflicting #[repr(align(...))] type hints {:?} and {:?}.",
150 old_align,
151 ReprAlign::Align(align)
152 ));
153 }
154 repr.align = Some(ReprAlign::Align(align));
155 continue;
156 }
157 (path, args) => match args {
158 None => return Err(format!("Unsupported #[repr({})].", path)),
159 Some(args) => {
160 return Err(format!(
161 "Unsupported #[repr({}({}))].",
162 path,
163 args.join(", ")
164 ));
165 }
166 },
167 };
168 let ty = ReprType {
169 kind: int_kind,
170 signed,
171 };
172 if let Some(old_ty) = repr.ty {
173 return Err(format!(
174 "Conflicting #[repr(...)] type hints {:?} and {:?}.",
175 old_ty, ty
176 ));
177 }
178 repr.ty = Some(ty);
179 }
180 Ok(repr)
181 }
182}
183