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 std::io::Write;
6
7use syn::ext::IdentExt;
8
9use crate::bindgen::config::{Config, Language, LayoutConfig};
10use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
11use crate::bindgen::dependencies::Dependencies;
12use crate::bindgen::ir::{
13 AnnotationSet, Cfg, ConditionWrite, Documentation, Field, GenericArgument, GenericParams, Item,
14 ItemContainer, Path, Repr, ReprAlign, ReprStyle, ToCondition,
15};
16use crate::bindgen::library::Library;
17use crate::bindgen::mangle;
18use crate::bindgen::monomorph::Monomorphs;
19use crate::bindgen::rename::{IdentifierType, RenameRule};
20use crate::bindgen::utilities::IterHelpers;
21use crate::bindgen::writer::{ListType, Source, SourceWriter};
22
23#[derive(Debug, Clone)]
24pub struct Union {
25 pub path: Path,
26 pub export_name: String,
27 pub generic_params: GenericParams,
28 pub fields: Vec<Field>,
29 pub tuple_union: bool,
30 pub alignment: Option<ReprAlign>,
31 pub cfg: Option<Cfg>,
32 pub annotations: AnnotationSet,
33 pub documentation: Documentation,
34}
35
36impl Union {
37 pub fn load(
38 layout_config: &LayoutConfig,
39 item: &syn::ItemUnion,
40 mod_cfg: Option<&Cfg>,
41 ) -> Result<Union, String> {
42 let repr = Repr::load(&item.attrs)?;
43 if repr.style != ReprStyle::C {
44 return Err("Union is not marked #[repr(C)].".to_owned());
45 }
46
47 // Ensure we can safely represent the union given the configuration.
48 if let Some(align) = repr.align {
49 layout_config.ensure_safe_to_represent(&align)?;
50 }
51
52 let path = Path::new(item.ident.unraw().to_string());
53
54 let (fields, tuple_union) = {
55 let out = item
56 .fields
57 .named
58 .iter()
59 .try_skip_map(|field| Field::load(field, &path))?;
60 (out, false)
61 };
62
63 Ok(Union::new(
64 path,
65 GenericParams::load(&item.generics)?,
66 fields,
67 repr.align,
68 tuple_union,
69 Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
70 AnnotationSet::load(&item.attrs)?,
71 Documentation::load(&item.attrs),
72 ))
73 }
74
75 #[allow(clippy::too_many_arguments)]
76 pub fn new(
77 path: Path,
78 generic_params: GenericParams,
79 fields: Vec<Field>,
80 alignment: Option<ReprAlign>,
81 tuple_union: bool,
82 cfg: Option<Cfg>,
83 annotations: AnnotationSet,
84 documentation: Documentation,
85 ) -> Self {
86 let export_name = path.name().to_owned();
87 Self {
88 path,
89 export_name,
90 generic_params,
91 fields,
92 tuple_union,
93 alignment,
94 cfg,
95 annotations,
96 documentation,
97 }
98 }
99
100 pub fn simplify_standard_types(&mut self, config: &Config) {
101 for field in &mut self.fields {
102 field.ty.simplify_standard_types(config);
103 }
104 }
105
106 pub fn is_generic(&self) -> bool {
107 self.generic_params.len() > 0
108 }
109
110 pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
111 // Generic unions can instantiate monomorphs only once they've been
112 // instantiated. See `instantiate_monomorph` for more details.
113 if self.is_generic() {
114 return;
115 }
116
117 for field in &self.fields {
118 field.ty.add_monomorphs(library, out);
119 }
120 }
121
122 pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
123 for field in &mut self.fields {
124 field.ty.mangle_paths(monomorphs);
125 }
126 }
127}
128
129impl Item for Union {
130 fn path(&self) -> &Path {
131 &self.path
132 }
133
134 fn export_name(&self) -> &str {
135 &self.export_name
136 }
137
138 fn cfg(&self) -> Option<&Cfg> {
139 self.cfg.as_ref()
140 }
141
142 fn annotations(&self) -> &AnnotationSet {
143 &self.annotations
144 }
145
146 fn annotations_mut(&mut self) -> &mut AnnotationSet {
147 &mut self.annotations
148 }
149
150 fn container(&self) -> ItemContainer {
151 ItemContainer::Union(self.clone())
152 }
153
154 fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
155 resolver.add_union(&self.path);
156 }
157
158 fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
159 for field in &mut self.fields {
160 field.ty.resolve_declaration_types(resolver);
161 }
162 }
163
164 fn rename_for_config(&mut self, config: &Config) {
165 config.export.rename(&mut self.export_name);
166 for field in &mut self.fields {
167 field.ty.rename_for_config(config, &self.generic_params);
168 }
169
170 let rules = self
171 .annotations
172 .parse_atom::<RenameRule>("rename-all")
173 .unwrap_or(config.structure.rename_fields);
174
175 if let Some(o) = self.annotations.list("field-names") {
176 let mut overriden_fields = Vec::new();
177
178 for (i, field) in self.fields.iter().enumerate() {
179 if i >= o.len() {
180 overriden_fields.push(field.clone());
181 } else {
182 overriden_fields.push(Field {
183 name: o[i].clone(),
184 ty: field.ty.clone(),
185 cfg: field.cfg.clone(),
186 annotations: field.annotations.clone(),
187 documentation: field.documentation.clone(),
188 });
189 }
190 }
191
192 self.fields = overriden_fields;
193 } else if let Some(r) = rules.not_none() {
194 self.fields = self
195 .fields
196 .iter()
197 .map(|field| Field {
198 name: r
199 .apply(&field.name, IdentifierType::StructMember)
200 .into_owned(),
201 ty: field.ty.clone(),
202 cfg: field.cfg.clone(),
203 annotations: field.annotations.clone(),
204 documentation: field.documentation.clone(),
205 })
206 .collect();
207 } else if self.tuple_union {
208 // If we don't have any rules for a tuple union, prefix them with
209 // an underscore so it still compiles
210 for field in &mut self.fields {
211 field.name.insert(0, '_');
212 }
213 }
214 }
215
216 fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
217 for field in &self.fields {
218 field
219 .ty
220 .add_dependencies_ignoring_generics(&self.generic_params, library, out);
221 }
222 }
223
224 fn instantiate_monomorph(
225 &self,
226 generic_values: &[GenericArgument],
227 library: &Library,
228 out: &mut Monomorphs,
229 ) {
230 let mappings = self.generic_params.call(self.path.name(), generic_values);
231
232 let mangled_path = mangle::mangle_path(
233 &self.path,
234 generic_values,
235 &library.get_config().export.mangle,
236 );
237
238 let monomorph = Union::new(
239 mangled_path,
240 GenericParams::default(),
241 self.fields
242 .iter()
243 .map(|field| Field {
244 name: field.name.clone(),
245 ty: field.ty.specialize(&mappings),
246 cfg: field.cfg.clone(),
247 annotations: field.annotations.clone(),
248 documentation: field.documentation.clone(),
249 })
250 .collect(),
251 self.alignment,
252 self.tuple_union,
253 self.cfg.clone(),
254 self.annotations.clone(),
255 self.documentation.clone(),
256 );
257
258 out.insert_union(library, self, monomorph, generic_values.to_owned());
259 }
260}
261
262impl Source for Union {
263 fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
264 let condition = self.cfg.to_condition(config);
265 condition.write_before(config, out);
266
267 self.documentation.write(config, out);
268
269 self.generic_params.write(config, out);
270
271 // The following results in
272 // C++ or C with Tag as style:
273 // union Name {
274 // C with Type only style:
275 // typedef union {
276 // C with Both as style:
277 // typedef union Name {
278 match config.language {
279 Language::C if config.style.generate_typedef() => out.write("typedef "),
280 Language::C | Language::Cxx => {}
281 Language::Cython => out.write(config.style.cython_def()),
282 }
283
284 out.write("union");
285
286 // Cython supports `packed` on structs (see comments there), but not on unions.
287 if config.language != Language::Cython {
288 if let Some(align) = self.alignment {
289 match align {
290 ReprAlign::Packed => {
291 if let Some(ref anno) = config.layout.packed {
292 write!(out, " {}", anno);
293 }
294 }
295 ReprAlign::Align(n) => {
296 if let Some(ref anno) = config.layout.aligned_n {
297 write!(out, " {}({})", anno, n);
298 }
299 }
300 }
301 }
302 }
303
304 if config.language != Language::C || config.style.generate_tag() {
305 write!(out, " {}", self.export_name);
306 }
307
308 out.open_brace();
309
310 // Emit the pre_body section, if relevant
311 if let Some(body) = config.export.pre_body(&self.path) {
312 out.write_raw_block(body);
313 out.new_line();
314 }
315
316 out.write_vertical_source_list(&self.fields, ListType::Cap(";"));
317 if config.language == Language::Cython && self.fields.is_empty() {
318 out.write("pass");
319 }
320
321 // Emit the post_body section, if relevant
322 if let Some(body) = config.export.post_body(&self.path) {
323 out.new_line();
324 out.write_raw_block(body);
325 }
326
327 if config.language == Language::C && config.style.generate_typedef() {
328 out.close_brace(false);
329 write!(out, " {};", self.export_name);
330 } else {
331 out.close_brace(true);
332 }
333
334 condition.write_after(config, out);
335 }
336}
337