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 | |
5 | use syn::ext::IdentExt; |
6 | |
7 | use crate::bindgen::ir::ty::{IntKind, PrimitiveType}; |
8 | |
9 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Default)] |
10 | pub enum ReprStyle { |
11 | #[default] |
12 | Rust, |
13 | C, |
14 | Transparent, |
15 | } |
16 | |
17 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
18 | pub struct ReprType { |
19 | kind: IntKind, |
20 | signed: bool, |
21 | } |
22 | |
23 | impl 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)] |
34 | pub enum ReprAlign { |
35 | Packed, |
36 | Align(u64), |
37 | } |
38 | |
39 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Default)] |
40 | pub struct Repr { |
41 | pub style: ReprStyle, |
42 | pub ty: Option<ReprType>, |
43 | pub align: Option<ReprAlign>, |
44 | } |
45 | |
46 | impl 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 | |