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};
10use crate::bindgen::declarationtyperesolver::DeclarationTypeResolver;
11use crate::bindgen::dependencies::Dependencies;
12use crate::bindgen::ir::{
13 AnnotationSet, AnnotationValue, Cfg, ConditionWrite, DeprecatedNoteKind, Documentation, Field,
14 GenericArgument, GenericParams, GenericPath, Item, ItemContainer, Literal, Path, Repr,
15 ReprStyle, Struct, ToCondition, Type,
16};
17use crate::bindgen::library::Library;
18use crate::bindgen::mangle;
19use crate::bindgen::monomorph::Monomorphs;
20use crate::bindgen::rename::{IdentifierType, RenameRule};
21use crate::bindgen::reserved;
22use crate::bindgen::writer::{ListType, Source, SourceWriter};
23
24#[allow(clippy::large_enum_variant)]
25#[derive(Debug, Clone)]
26pub enum VariantBody {
27 Empty(AnnotationSet),
28 Body {
29 /// The variant field / export name.
30 name: String,
31 /// The struct with all the items.
32 body: Struct,
33 /// A separate named struct is not created for this variant,
34 /// an unnamed struct is inlined at the point of use instead.
35 /// This is a reasonable thing to do only for tuple variants with a single field.
36 inline: bool,
37 /// Generated cast methods return the variant's only field instead of the variant itself.
38 /// For backward compatibility casts are inlined in a slightly
39 /// larger set of cases than whole variants.
40 inline_casts: bool,
41 },
42}
43
44impl VariantBody {
45 fn empty() -> Self {
46 Self::Empty(AnnotationSet::new())
47 }
48
49 fn annotations(&self) -> &AnnotationSet {
50 match *self {
51 Self::Empty(ref anno) => anno,
52 Self::Body { ref body, .. } => &body.annotations,
53 }
54 }
55
56 fn is_empty(&self) -> bool {
57 match *self {
58 Self::Empty(..) => true,
59 Self::Body { .. } => false,
60 }
61 }
62
63 fn specialize(
64 &self,
65 generic_values: &[GenericArgument],
66 mappings: &[(&Path, &GenericArgument)],
67 config: &Config,
68 ) -> Self {
69 match *self {
70 Self::Empty(ref annos) => Self::Empty(annos.clone()),
71 Self::Body {
72 ref name,
73 ref body,
74 inline,
75 inline_casts,
76 } => Self::Body {
77 name: name.clone(),
78 body: body.specialize(generic_values, mappings, config),
79 inline,
80 inline_casts,
81 },
82 }
83 }
84}
85
86#[derive(Debug, Clone)]
87pub struct EnumVariant {
88 pub name: String,
89 pub export_name: String,
90 pub discriminant: Option<Literal>,
91 pub body: VariantBody,
92 pub cfg: Option<Cfg>,
93 pub documentation: Documentation,
94}
95
96impl EnumVariant {
97 fn load(
98 inline_tag_field: bool,
99 variant: &syn::Variant,
100 generic_params: GenericParams,
101 mod_cfg: Option<&Cfg>,
102 self_path: &Path,
103 enum_annotations: &AnnotationSet,
104 config: &Config,
105 ) -> Result<Self, String> {
106 let discriminant = match variant.discriminant {
107 Some((_, ref expr)) => Some(Literal::load(expr)?),
108 None => None,
109 };
110
111 fn parse_fields(
112 inline_tag_field: bool,
113 fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
114 self_path: &Path,
115 inline_name: Option<&str>,
116 ) -> Result<Vec<Field>, String> {
117 let mut res = Vec::new();
118
119 if inline_tag_field {
120 res.push(Field::from_name_and_type(
121 inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)),
122 Type::Path(GenericPath::new(Path::new("Tag"), vec![])),
123 ));
124 }
125
126 for (i, field) in fields.iter().enumerate() {
127 if let Some(mut ty) = Type::load(&field.ty)? {
128 ty.replace_self_with(self_path);
129 res.push(Field {
130 name: inline_name.map_or_else(
131 || match field.ident {
132 Some(ref ident) => ident.unraw().to_string(),
133 None => i.to_string(),
134 },
135 |name| name.to_string(),
136 ),
137 ty,
138 cfg: Cfg::load(&field.attrs),
139 annotations: AnnotationSet::load(&field.attrs)?,
140 documentation: Documentation::load(&field.attrs),
141 });
142 }
143 }
144
145 Ok(res)
146 }
147
148 let variant_cfg = Cfg::append(mod_cfg, Cfg::load(&variant.attrs));
149 let mut annotations = AnnotationSet::load(&variant.attrs)?;
150 if let Some(b) = enum_annotations.bool("derive-ostream") {
151 annotations.add_default("derive-ostream", AnnotationValue::Bool(b));
152 }
153
154 let body_rule = enum_annotations
155 .parse_atom::<RenameRule>("rename-variant-name-fields")
156 .unwrap_or(config.enumeration.rename_variant_name_fields);
157
158 let body = match variant.fields {
159 syn::Fields::Unit => VariantBody::Empty(annotations),
160 syn::Fields::Named(ref fields) => {
161 let path = Path::new(format!("{}_Body", variant.ident));
162 let name = body_rule
163 .apply(
164 &variant.ident.unraw().to_string(),
165 IdentifierType::StructMember,
166 )
167 .into_owned();
168 VariantBody::Body {
169 body: Struct::new(
170 path,
171 generic_params,
172 parse_fields(inline_tag_field, &fields.named, self_path, None)?,
173 inline_tag_field,
174 true,
175 None,
176 false,
177 None,
178 annotations,
179 Documentation::none(),
180 ),
181 name,
182 inline: false,
183 inline_casts: false,
184 }
185 }
186 syn::Fields::Unnamed(ref fields) => {
187 let path = Path::new(format!("{}_Body", variant.ident));
188 let name = body_rule
189 .apply(
190 &variant.ident.unraw().to_string(),
191 IdentifierType::StructMember,
192 )
193 .into_owned();
194 let inline_casts = fields.unnamed.len() == 1;
195 // In C++ types with destructors cannot be put into unnamed structs like the
196 // inlining requires, and it's hard to detect such types.
197 // Besides that for C++ we generate casts/getters that can be used instead of
198 // direct field accesses and also have a benefit of being checked.
199 // As a result we don't currently inline variant definitions in C++ mode at all.
200 let inline = inline_casts && config.language != Language::Cxx;
201 let inline_name = if inline { Some(&*name) } else { None };
202 VariantBody::Body {
203 body: Struct::new(
204 path,
205 generic_params,
206 parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?,
207 inline_tag_field,
208 true,
209 None,
210 false,
211 None,
212 annotations,
213 Documentation::none(),
214 ),
215 name,
216 inline,
217 inline_casts,
218 }
219 }
220 };
221
222 Ok(EnumVariant::new(
223 variant.ident.unraw().to_string(),
224 discriminant,
225 body,
226 variant_cfg,
227 Documentation::load(&variant.attrs),
228 ))
229 }
230
231 pub fn new(
232 name: String,
233 discriminant: Option<Literal>,
234 body: VariantBody,
235 cfg: Option<Cfg>,
236 documentation: Documentation,
237 ) -> Self {
238 let export_name = name.clone();
239 Self {
240 name,
241 export_name,
242 discriminant,
243 body,
244 cfg,
245 documentation,
246 }
247 }
248
249 fn simplify_standard_types(&mut self, config: &Config) {
250 if let VariantBody::Body { ref mut body, .. } = self.body {
251 body.simplify_standard_types(config);
252 }
253 }
254
255 fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
256 if let VariantBody::Body { ref body, .. } = self.body {
257 body.add_dependencies(library, out);
258 }
259 }
260
261 fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
262 if let VariantBody::Body { ref mut body, .. } = self.body {
263 body.resolve_declaration_types(resolver);
264 }
265 }
266
267 fn specialize(
268 &self,
269 generic_values: &[GenericArgument],
270 mappings: &[(&Path, &GenericArgument)],
271 config: &Config,
272 ) -> Self {
273 Self::new(
274 mangle::mangle_name(&self.name, generic_values, &config.export.mangle),
275 self.discriminant.clone(),
276 self.body.specialize(generic_values, mappings, config),
277 self.cfg.clone(),
278 self.documentation.clone(),
279 )
280 }
281
282 fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
283 if let VariantBody::Body { ref body, .. } = self.body {
284 body.add_monomorphs(library, out);
285 }
286 }
287
288 fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
289 if let VariantBody::Body { ref mut body, .. } = self.body {
290 body.mangle_paths(monomorphs);
291 }
292 }
293}
294
295impl Source for EnumVariant {
296 fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
297 let condition: Option = self.cfg.to_condition(config);
298 // Cython doesn't support conditional enum variants.
299 if config.language != Language::Cython {
300 condition.write_before(config, out);
301 }
302 self.documentation.write(config, out);
303 write!(out, "{}", self.export_name);
304 if let Some(discriminant: &Literal) = &self.discriminant {
305 if config.language == Language::Cython {
306 // For extern Cython declarations the enumerator value is ignored,
307 // but still useful as documentation, so we write it as a comment.
308 out.write(text:" #")
309 }
310 out.write(text:" = ");
311 discriminant.write(config, out);
312 }
313 out.write(text:",");
314 if config.language != Language::Cython {
315 condition.write_after(config, out);
316 }
317 }
318}
319
320#[derive(Debug, Clone)]
321pub struct Enum {
322 pub path: Path,
323 pub export_name: String,
324 pub generic_params: GenericParams,
325 pub repr: Repr,
326 pub variants: Vec<EnumVariant>,
327 pub tag: Option<String>,
328 pub cfg: Option<Cfg>,
329 pub annotations: AnnotationSet,
330 pub documentation: Documentation,
331}
332
333impl Enum {
334 /// Name of the generated tag enum.
335 fn tag_name(&self) -> &str {
336 self.tag.as_deref().unwrap_or_else(|| self.export_name())
337 }
338
339 /// Enum with data turns into a union of structs with each struct having its own tag field.
340 fn inline_tag_field(repr: &Repr) -> bool {
341 repr.style != ReprStyle::C
342 }
343
344 pub fn add_monomorphs(&self, library: &Library, out: &mut Monomorphs) {
345 if self.generic_params.len() > 0 {
346 return;
347 }
348
349 for v in &self.variants {
350 v.add_monomorphs(library, out);
351 }
352 }
353
354 fn can_derive_eq(&self) -> bool {
355 if self.tag.is_none() {
356 return false;
357 }
358
359 self.variants.iter().all(|variant| match variant.body {
360 VariantBody::Empty(..) => true,
361 VariantBody::Body { ref body, .. } => body.can_derive_eq(),
362 })
363 }
364
365 pub fn mangle_paths(&mut self, monomorphs: &Monomorphs) {
366 for variant in &mut self.variants {
367 variant.mangle_paths(monomorphs);
368 }
369 }
370
371 pub fn load(
372 item: &syn::ItemEnum,
373 mod_cfg: Option<&Cfg>,
374 config: &Config,
375 ) -> Result<Enum, String> {
376 let repr = Repr::load(&item.attrs)?;
377 if repr.style == ReprStyle::Rust && repr.ty.is_none() {
378 return Err("Enum is not marked with a valid #[repr(prim)] or #[repr(C)].".to_owned());
379 }
380 // TODO: Implement translation of aligned enums.
381 if repr.align.is_some() {
382 return Err("Enum is marked with #[repr(align(...))] or #[repr(packed)].".to_owned());
383 }
384
385 let path = Path::new(item.ident.unraw().to_string());
386 let generic_params = GenericParams::load(&item.generics)?;
387
388 let mut variants = Vec::new();
389 let mut has_data = false;
390
391 let annotations = AnnotationSet::load(&item.attrs)?;
392
393 for variant in item.variants.iter() {
394 let variant = EnumVariant::load(
395 Self::inline_tag_field(&repr),
396 variant,
397 generic_params.clone(),
398 mod_cfg,
399 &path,
400 &annotations,
401 config,
402 )?;
403 has_data = has_data || !variant.body.is_empty();
404 variants.push(variant);
405 }
406
407 if let Some(names) = annotations.list("enum-trailing-values") {
408 for name in names {
409 variants.push(EnumVariant::new(
410 name,
411 None,
412 VariantBody::empty(),
413 None,
414 Documentation::none(),
415 ));
416 }
417 }
418
419 if config.enumeration.add_sentinel(&annotations) {
420 variants.push(EnumVariant::new(
421 "Sentinel".to_owned(),
422 None,
423 VariantBody::empty(),
424 None,
425 Documentation::simple(" Must be last for serialization purposes"),
426 ));
427 }
428
429 let tag = if has_data {
430 Some("Tag".to_string())
431 } else {
432 None
433 };
434
435 Ok(Enum::new(
436 path,
437 generic_params,
438 repr,
439 variants,
440 tag,
441 Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
442 annotations,
443 Documentation::load(&item.attrs),
444 ))
445 }
446
447 #[allow(clippy::too_many_arguments)]
448 pub fn new(
449 path: Path,
450 generic_params: GenericParams,
451 repr: Repr,
452 variants: Vec<EnumVariant>,
453 tag: Option<String>,
454 cfg: Option<Cfg>,
455 annotations: AnnotationSet,
456 documentation: Documentation,
457 ) -> Self {
458 let export_name = path.name().to_owned();
459 Self {
460 path,
461 export_name,
462 generic_params,
463 repr,
464 variants,
465 tag,
466 cfg,
467 annotations,
468 documentation,
469 }
470 }
471}
472
473impl Item for Enum {
474 fn path(&self) -> &Path {
475 &self.path
476 }
477
478 fn export_name(&self) -> &str {
479 &self.export_name
480 }
481
482 fn cfg(&self) -> Option<&Cfg> {
483 self.cfg.as_ref()
484 }
485
486 fn annotations(&self) -> &AnnotationSet {
487 &self.annotations
488 }
489
490 fn annotations_mut(&mut self) -> &mut AnnotationSet {
491 &mut self.annotations
492 }
493
494 fn container(&self) -> ItemContainer {
495 ItemContainer::Enum(self.clone())
496 }
497
498 fn collect_declaration_types(&self, resolver: &mut DeclarationTypeResolver) {
499 if self.tag.is_some() {
500 if self.repr.style == ReprStyle::C {
501 resolver.add_struct(&self.path);
502 } else {
503 resolver.add_union(&self.path);
504 }
505 } else if self.repr.style == ReprStyle::C {
506 resolver.add_enum(&self.path);
507 } else {
508 // This is important to handle conflicting names with opaque items.
509 resolver.add_none(&self.path);
510 }
511 }
512
513 fn resolve_declaration_types(&mut self, resolver: &DeclarationTypeResolver) {
514 for &mut ref mut var in &mut self.variants {
515 var.resolve_declaration_types(resolver);
516 }
517 }
518
519 fn rename_for_config(&mut self, config: &Config) {
520 config.export.rename(&mut self.export_name);
521
522 if config.language != Language::Cxx && self.tag.is_some() {
523 // it makes sense to always prefix Tag with type name in C
524 let new_tag = format!("{}_Tag", self.export_name);
525 if self.repr.style == ReprStyle::Rust {
526 for variant in &mut self.variants {
527 if let VariantBody::Body { ref mut body, .. } = variant.body {
528 let path = Path::new(new_tag.clone());
529 let generic_path = GenericPath::new(path, vec![]);
530 body.fields[0].ty = Type::Path(generic_path);
531 }
532 }
533 }
534 self.tag = Some(new_tag);
535 }
536
537 for variant in &mut self.variants {
538 reserved::escape(&mut variant.export_name);
539 if let Some(discriminant) = &mut variant.discriminant {
540 discriminant.rename_for_config(config);
541 }
542 if let VariantBody::Body {
543 ref mut name,
544 ref mut body,
545 ..
546 } = variant.body
547 {
548 body.rename_for_config(config);
549 reserved::escape(name);
550 }
551 }
552
553 if config.enumeration.prefix_with_name
554 || self.annotations.bool("prefix-with-name").unwrap_or(false)
555 {
556 let separator = if config.export.mangle.remove_underscores {
557 ""
558 } else {
559 "_"
560 };
561
562 for variant in &mut self.variants {
563 variant.export_name =
564 format!("{}{}{}", self.export_name, separator, variant.export_name);
565 if let VariantBody::Body { ref mut body, .. } = variant.body {
566 body.export_name =
567 format!("{}{}{}", self.export_name, separator, body.export_name());
568 }
569 }
570 }
571
572 let rules = self
573 .annotations
574 .parse_atom::<RenameRule>("rename-all")
575 .unwrap_or(config.enumeration.rename_variants);
576
577 if let Some(r) = rules.not_none() {
578 self.variants = self
579 .variants
580 .iter()
581 .map(|variant| {
582 EnumVariant::new(
583 r.apply(
584 &variant.export_name,
585 IdentifierType::EnumVariant {
586 prefix: &self.export_name,
587 },
588 )
589 .into_owned(),
590 variant.discriminant.clone(),
591 match variant.body {
592 VariantBody::Empty(..) => variant.body.clone(),
593 VariantBody::Body {
594 ref name,
595 ref body,
596 inline,
597 inline_casts,
598 } => VariantBody::Body {
599 name: r.apply(name, IdentifierType::StructMember).into_owned(),
600 body: body.clone(),
601 inline,
602 inline_casts,
603 },
604 },
605 variant.cfg.clone(),
606 variant.documentation.clone(),
607 )
608 })
609 .collect();
610 }
611 }
612
613 fn instantiate_monomorph(
614 &self,
615 generic_values: &[GenericArgument],
616 library: &Library,
617 out: &mut Monomorphs,
618 ) {
619 let mappings = self.generic_params.call(self.path.name(), generic_values);
620
621 for variant in &self.variants {
622 if let VariantBody::Body { ref body, .. } = variant.body {
623 body.instantiate_monomorph(generic_values, library, out);
624 }
625 }
626
627 let mangled_path = mangle::mangle_path(
628 &self.path,
629 generic_values,
630 &library.get_config().export.mangle,
631 );
632
633 let monomorph = Enum::new(
634 mangled_path,
635 GenericParams::default(),
636 self.repr,
637 self.variants
638 .iter()
639 .map(|v| v.specialize(generic_values, &mappings, library.get_config()))
640 .collect(),
641 self.tag.clone(),
642 self.cfg.clone(),
643 self.annotations.clone(),
644 self.documentation.clone(),
645 );
646
647 out.insert_enum(library, self, monomorph, generic_values.to_owned());
648 }
649
650 fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
651 for variant in &self.variants {
652 variant.add_dependencies(library, out);
653 }
654 }
655}
656
657impl Source for Enum {
658 fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
659 let size = self.repr.ty.map(|ty| ty.to_primitive().to_repr_c(config));
660 let has_data = self.tag.is_some();
661 let inline_tag_field = Self::inline_tag_field(&self.repr);
662 let tag_name = self.tag_name();
663
664 let condition = self.cfg.to_condition(config);
665 condition.write_before(config, out);
666
667 self.documentation.write(config, out);
668 self.generic_params.write(config, out);
669
670 // If the enum has data, we need to emit a struct or union for the data
671 // and enum for the tag. C++ supports nested type definitions, so we open
672 // the struct or union here and define the tag enum inside it (*).
673 if has_data && config.language == Language::Cxx {
674 self.open_struct_or_union(config, out, inline_tag_field);
675 }
676
677 // Emit the tag enum and everything related to it.
678 self.write_tag_enum(config, out, size, has_data, tag_name);
679
680 // If the enum has data, we need to emit structs for the variants and gather them together.
681 if has_data {
682 self.write_variant_defs(config, out);
683 out.new_line();
684 out.new_line();
685
686 // Open the struct or union for the data (**), gathering all the variants with data
687 // together, unless it's C++, then we have already opened that struct/union at (*) and
688 // are currently inside it.
689 if config.language != Language::Cxx {
690 self.open_struct_or_union(config, out, inline_tag_field);
691 }
692
693 // Emit tag field that is separate from all variants.
694 self.write_tag_field(config, out, size, inline_tag_field, tag_name);
695 out.new_line();
696
697 // Open union of all variants with data, only in the non-inline tag scenario.
698 // Cython extern declarations don't manage layouts, layouts are defined entierly by the
699 // corresponding C code. So we can inline the unnamed union into the struct and get the
700 // same observable result. Moreother we have to do it because Cython doesn't support
701 // unnamed unions.
702 if !inline_tag_field && config.language != Language::Cython {
703 out.write("union");
704 out.open_brace();
705 }
706
707 // Emit fields for all variants with data.
708 self.write_variant_fields(config, out, inline_tag_field);
709
710 // Close union of all variants with data, only in the non-inline tag scenario.
711 // See the comment about Cython on `open_brace`.
712 if !inline_tag_field && config.language != Language::Cython {
713 out.close_brace(true);
714 }
715
716 // Emit convenience methods for the struct or enum for the data.
717 self.write_derived_functions_data(config, out, tag_name);
718
719 // Emit the post_body section, if relevant.
720 if let Some(body) = config.export.post_body(&self.path) {
721 out.new_line();
722 out.write_raw_block(body);
723 }
724
725 // Close the struct or union opened either at (*) or at (**).
726 if config.language == Language::C && config.style.generate_typedef() {
727 out.close_brace(false);
728 write!(out, " {};", self.export_name);
729 } else {
730 out.close_brace(true);
731 }
732 }
733
734 condition.write_after(config, out);
735 }
736}
737
738impl Enum {
739 /// Emit the tag enum and convenience methods for it.
740 /// For enums with data this is only a part of the output,
741 /// but for enums without data it's the whole output (modulo doc comments etc.).
742 fn write_tag_enum<F: Write>(
743 &self,
744 config: &Config,
745 out: &mut SourceWriter<F>,
746 size: Option<&str>,
747 has_data: bool,
748 tag_name: &str,
749 ) {
750 // Open the tag enum.
751 match config.language {
752 Language::C => {
753 if let Some(prim) = size {
754 // If we need to specify size, then we have no choice but to create a typedef,
755 // so `config.style` is not respected.
756 write!(out, "enum");
757 if let Some(note) = self
758 .annotations
759 .deprecated_note(config, DeprecatedNoteKind::Enum)
760 {
761 write!(out, " {}", note);
762 }
763 write!(out, " {}", tag_name);
764
765 if config.cpp_compatible_c() {
766 out.new_line();
767 out.write("#ifdef __cplusplus");
768 out.new_line();
769 write!(out, " : {}", prim);
770 out.new_line();
771 out.write("#endif // __cplusplus");
772 out.new_line();
773 }
774 } else {
775 if config.style.generate_typedef() {
776 out.write("typedef ");
777 }
778 out.write("enum");
779 if let Some(note) = self
780 .annotations
781 .deprecated_note(config, DeprecatedNoteKind::Enum)
782 {
783 write!(out, " {}", note);
784 }
785 if config.style.generate_tag() {
786 write!(out, " {}", tag_name);
787 }
788 }
789 }
790 Language::Cxx => {
791 if config.enumeration.enum_class(&self.annotations) {
792 out.write("enum class");
793 } else {
794 out.write("enum");
795 }
796
797 if self.annotations.must_use(config) {
798 if let Some(ref anno) = config.enumeration.must_use {
799 write!(out, " {}", anno)
800 }
801 }
802
803 if let Some(note) = self
804 .annotations
805 .deprecated_note(config, DeprecatedNoteKind::Enum)
806 {
807 write!(out, " {}", note);
808 }
809
810 write!(out, " {}", tag_name);
811 if let Some(prim) = size {
812 write!(out, " : {}", prim);
813 }
814 }
815 Language::Cython => {
816 if size.is_some() {
817 // If we need to specify size, then we have no choice but to create a typedef,
818 // so `config.style` is not respected.
819 write!(out, "cdef enum");
820 } else {
821 write!(out, "{}enum {}", config.style.cython_def(), tag_name);
822 }
823 }
824 }
825 out.open_brace();
826
827 // Emit enumerators for the tag enum.
828 for (i, variant) in self.variants.iter().enumerate() {
829 if i != 0 {
830 out.new_line()
831 }
832 variant.write(config, out);
833 }
834
835 // Close the tag enum.
836 if config.language == Language::C && size.is_none() && config.style.generate_typedef() {
837 out.close_brace(false);
838 write!(out, " {};", tag_name);
839 } else {
840 out.close_brace(true);
841 }
842
843 // Emit typedef specifying the tag enum's size if necessary.
844 // In C++ enums can "inherit" from numeric types (`enum E: uint8_t { ... }`),
845 // but in C `typedef uint8_t E` is the only way to give a fixed size to `E`.
846 if let Some(prim) = size {
847 if config.cpp_compatible_c() {
848 out.new_line_if_not_start();
849 out.write("#ifndef __cplusplus");
850 }
851
852 if config.language != Language::Cxx {
853 out.new_line();
854 write!(out, "{} {} {};", config.language.typedef(), prim, tag_name);
855 }
856
857 if config.cpp_compatible_c() {
858 out.new_line_if_not_start();
859 out.write("#endif // __cplusplus");
860 }
861 }
862
863 // Emit convenience methods for the tag enum.
864 self.write_derived_functions_enum(config, out, has_data, tag_name);
865 }
866
867 /// The code here mirrors the beginning of `Struct::write` and `Union::write`.
868 fn open_struct_or_union<F: Write>(
869 &self,
870 config: &Config,
871 out: &mut SourceWriter<F>,
872 inline_tag_field: bool,
873 ) {
874 match config.language {
875 Language::C if config.style.generate_typedef() => out.write("typedef "),
876 Language::C | Language::Cxx => {}
877 Language::Cython => out.write(config.style.cython_def()),
878 }
879
880 out.write(if inline_tag_field { "union" } else { "struct" });
881
882 if self.annotations.must_use(config) {
883 if let Some(ref anno) = config.structure.must_use {
884 write!(out, " {}", anno);
885 }
886 }
887
888 if let Some(note) = self
889 .annotations
890 .deprecated_note(config, DeprecatedNoteKind::Struct)
891 {
892 write!(out, " {} ", note);
893 }
894
895 if config.language != Language::C || config.style.generate_tag() {
896 write!(out, " {}", self.export_name());
897 }
898
899 out.open_brace();
900
901 // Emit the pre_body section, if relevant.
902 if let Some(body) = config.export.pre_body(&self.path) {
903 out.write_raw_block(body);
904 out.new_line();
905 }
906 }
907
908 /// Emit struct definitions for variants having data.
909 fn write_variant_defs<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
910 for variant in &self.variants {
911 if let VariantBody::Body {
912 ref body,
913 inline: false,
914 ..
915 } = variant.body
916 {
917 out.new_line();
918 out.new_line();
919 let condition = variant.cfg.to_condition(config);
920 // Cython doesn't support conditional enum variants.
921 if config.language != Language::Cython {
922 condition.write_before(config, out);
923 }
924 body.write(config, out);
925 if config.language != Language::Cython {
926 condition.write_after(config, out);
927 }
928 }
929 }
930 }
931
932 /// Emit tag field that is separate from all variants.
933 /// For non-inline tag scenario this is *the* tag field, and it does not exist in the variants.
934 /// For the inline tag scenario this is just a convenience and another way
935 /// to refer to the same tag that exist in all the variants.
936 fn write_tag_field<F: Write>(
937 &self,
938 config: &Config,
939 out: &mut SourceWriter<F>,
940 size: Option<&str>,
941 inline_tag_field: bool,
942 tag_name: &str,
943 ) {
944 // C++ allows accessing only common initial sequence of union
945 // fields so we have to wrap the tag field into an anonymous struct.
946 let wrap_tag = inline_tag_field && config.language == Language::Cxx;
947
948 if wrap_tag {
949 out.write("struct");
950 out.open_brace();
951 }
952
953 if config.language == Language::C && size.is_none() && !config.style.generate_typedef() {
954 out.write("enum ");
955 }
956
957 write!(out, "{} tag;", tag_name);
958
959 if wrap_tag {
960 out.close_brace(true);
961 }
962 }
963
964 /// Emit fields for all variants with data.
965 fn write_variant_fields<F: Write>(
966 &self,
967 config: &Config,
968 out: &mut SourceWriter<F>,
969 inline_tag_field: bool,
970 ) {
971 let mut first = true;
972 for variant in &self.variants {
973 if let VariantBody::Body {
974 name, body, inline, ..
975 } = &variant.body
976 {
977 if !first {
978 out.new_line();
979 }
980 first = false;
981 let condition = variant.cfg.to_condition(config);
982 // Cython doesn't support conditional enum variants.
983 if config.language != Language::Cython {
984 condition.write_before(config, out);
985 }
986 if *inline {
987 // Write definition of an inlined variant with data.
988 // Cython extern declarations don't manage layouts, layouts are defined entierly
989 // by the corresponding C code. So we can inline the unnamed struct and get the
990 // same observable result. Moreother we have to do it because Cython doesn't
991 // support unnamed structs.
992 // For the same reason with Cython we can omit per-variant tags (the first
993 // field) to avoid extra noise, the main `tag` is enough in this case.
994 if config.language != Language::Cython {
995 out.write("struct");
996 out.open_brace();
997 }
998 let start_field =
999 usize::from(inline_tag_field && config.language == Language::Cython);
1000 out.write_vertical_source_list(&body.fields[start_field..], ListType::Cap(";"));
1001 if config.language != Language::Cython {
1002 out.close_brace(true);
1003 }
1004 } else if config.style.generate_typedef() || config.language == Language::Cython {
1005 write!(out, "{} {};", body.export_name(), name);
1006 } else {
1007 write!(out, "struct {} {};", body.export_name(), name);
1008 }
1009 if config.language != Language::Cython {
1010 condition.write_after(config, out);
1011 }
1012 }
1013 }
1014 }
1015
1016 // Emit convenience methods for enums themselves.
1017 fn write_derived_functions_enum<F: Write>(
1018 &self,
1019 config: &Config,
1020 out: &mut SourceWriter<F>,
1021 has_data: bool,
1022 tag_name: &str,
1023 ) {
1024 if config.language != Language::Cxx {
1025 return;
1026 }
1027
1028 // Emit an ostream function if required.
1029 if config.enumeration.derive_ostream(&self.annotations) {
1030 // For enums without data, this emits the serializer function for the
1031 // enum. For enums with data, this emits the serializer function for
1032 // the tag enum. In the latter case we need a couple of minor changes
1033 // due to the function living inside the top-level struct or enum.
1034 let stream = config
1035 .function
1036 .rename_args
1037 .apply("stream", IdentifierType::FunctionArg);
1038 let instance = config
1039 .function
1040 .rename_args
1041 .apply("instance", IdentifierType::FunctionArg);
1042
1043 out.new_line();
1044 out.new_line();
1045 // For enums without data, we mark the function inline because the
1046 // header might get included into multiple compilation units that
1047 // get linked together, and not marking it inline would result in
1048 // multiply-defined symbol errors. For enums with data we don't have
1049 // the same problem, but mark it as a friend function of the
1050 // containing union/struct.
1051 // Note also that for enums with data, the case labels for switch
1052 // statements apparently need to be qualified to the top-level
1053 // generated struct or union. This is why the generated case labels
1054 // below use the A::B::C format for enums with data, with A being
1055 // self.export_name(). Failure to have that qualification results
1056 // in a surprising compilation failure for the generated header.
1057 write!(
1058 out,
1059 "{} std::ostream& operator<<(std::ostream& {}, const {}& {})",
1060 if has_data { "friend" } else { "inline" },
1061 stream,
1062 tag_name,
1063 instance,
1064 );
1065
1066 out.open_brace();
1067 if has_data {
1068 // C++ name resolution rules are weird.
1069 write!(
1070 out,
1071 "using {} = {}::{};",
1072 tag_name,
1073 self.export_name(),
1074 tag_name
1075 );
1076 out.new_line();
1077 }
1078 write!(out, "switch ({})", instance);
1079 out.open_brace();
1080 let vec: Vec<_> = self
1081 .variants
1082 .iter()
1083 .map(|x| {
1084 format!(
1085 "case {}::{}: {} << \"{}\"; break;",
1086 tag_name, x.export_name, stream, x.export_name
1087 )
1088 })
1089 .collect();
1090 out.write_vertical_source_list(&vec[..], ListType::Join(""));
1091 out.close_brace(false);
1092 out.new_line();
1093
1094 write!(out, "return {};", stream);
1095 out.close_brace(false);
1096
1097 if has_data {
1098 // For enums with data, this emits the serializer function for
1099 // the top-level union or struct.
1100 out.new_line();
1101 out.new_line();
1102 write!(
1103 out,
1104 "friend std::ostream& operator<<(std::ostream& {}, const {}& {})",
1105 stream,
1106 self.export_name(),
1107 instance,
1108 );
1109
1110 out.open_brace();
1111
1112 // C++ name resolution rules are weird.
1113 write!(
1114 out,
1115 "using {} = {}::{};",
1116 tag_name,
1117 self.export_name(),
1118 tag_name
1119 );
1120 out.new_line();
1121
1122 write!(out, "switch ({}.tag)", instance);
1123 out.open_brace();
1124 let vec: Vec<_> = self
1125 .variants
1126 .iter()
1127 .map(|x| {
1128 let tag_str = format!("\"{}\"", x.export_name);
1129 if let VariantBody::Body {
1130 ref name, ref body, ..
1131 } = x.body
1132 {
1133 format!(
1134 "case {}::{}: {} << {}{}{}.{}; break;",
1135 tag_name,
1136 x.export_name,
1137 stream,
1138 if body.has_tag_field { "" } else { &tag_str },
1139 if body.has_tag_field { "" } else { " << " },
1140 instance,
1141 name,
1142 )
1143 } else {
1144 format!(
1145 "case {}::{}: {} << {}; break;",
1146 tag_name, x.export_name, stream, tag_str,
1147 )
1148 }
1149 })
1150 .collect();
1151 out.write_vertical_source_list(&vec[..], ListType::Join(""));
1152 out.close_brace(false);
1153 out.new_line();
1154
1155 write!(out, "return {};", stream);
1156 out.close_brace(false);
1157 }
1158 }
1159 }
1160
1161 // Emit convenience methods for structs or unions produced for enums with data.
1162 fn write_derived_functions_data<F: Write>(
1163 &self,
1164 config: &Config,
1165 out: &mut SourceWriter<F>,
1166 tag_name: &str,
1167 ) {
1168 if config.language != Language::Cxx {
1169 return;
1170 }
1171
1172 if config.enumeration.derive_helper_methods(&self.annotations) {
1173 for variant in &self.variants {
1174 out.new_line();
1175 out.new_line();
1176
1177 let condition = variant.cfg.to_condition(config);
1178 condition.write_before(config, out);
1179
1180 let arg_renamer = |name: &str| {
1181 config
1182 .function
1183 .rename_args
1184 .apply(name, IdentifierType::FunctionArg)
1185 .into_owned()
1186 };
1187
1188 macro_rules! write_attrs {
1189 ($op:expr) => {{
1190 if let Some(Some(attrs)) =
1191 variant
1192 .body
1193 .annotations()
1194 .atom(concat!("variant-", $op, "-attributes"))
1195 {
1196 write!(out, "{} ", attrs);
1197 }
1198 }};
1199 }
1200
1201 write_attrs!("constructor");
1202 write!(out, "static {} {}(", self.export_name, variant.export_name);
1203
1204 if let VariantBody::Body { ref body, .. } = variant.body {
1205 let skip_fields = body.has_tag_field as usize;
1206 let vec: Vec<_> = body
1207 .fields
1208 .iter()
1209 .skip(skip_fields)
1210 .map(|field| {
1211 Field::from_name_and_type(
1212 // const-ref args to constructor
1213 arg_renamer(&field.name),
1214 Type::const_ref_to(&field.ty),
1215 )
1216 })
1217 .collect();
1218 out.write_vertical_source_list(&vec[..], ListType::Join(","));
1219 }
1220
1221 write!(out, ")");
1222 out.open_brace();
1223
1224 write!(out, "{} result;", self.export_name);
1225
1226 if let VariantBody::Body {
1227 name: ref variant_name,
1228 ref body,
1229 ..
1230 } = variant.body
1231 {
1232 let skip_fields = body.has_tag_field as usize;
1233 for field in body.fields.iter().skip(skip_fields) {
1234 out.new_line();
1235 match field.ty {
1236 Type::Array(ref ty, ref length) => {
1237 // arrays are not assignable in C++ so we
1238 // need to manually copy the elements
1239 write!(out, "for (int i = 0; i < {}; i++)", length.as_str());
1240 out.open_brace();
1241 write!(out, "::new (&result.{}.{}[i]) (", variant_name, field.name);
1242 ty.write(config, out);
1243 write!(out, ")({}[i]);", arg_renamer(&field.name));
1244 out.close_brace(false);
1245 }
1246 ref ty => {
1247 write!(out, "::new (&result.{}.{}) (", variant_name, field.name);
1248 ty.write(config, out);
1249 write!(out, ")({});", arg_renamer(&field.name));
1250 }
1251 }
1252 }
1253 }
1254
1255 out.new_line();
1256 write!(out, "result.tag = {}::{};", tag_name, variant.export_name);
1257 out.new_line();
1258 write!(out, "return result;");
1259 out.close_brace(false);
1260
1261 out.new_line();
1262 out.new_line();
1263
1264 write_attrs!("is");
1265 // FIXME: create a config for method case
1266 write!(out, "bool Is{}() const", variant.export_name);
1267 out.open_brace();
1268 write!(out, "return tag == {}::{};", tag_name, variant.export_name);
1269 out.close_brace(false);
1270
1271 let assert_name = match config.enumeration.cast_assert_name {
1272 Some(ref n) => &**n,
1273 None => "assert",
1274 };
1275
1276 let mut derive_casts = |const_casts: bool| {
1277 let (member_name, body, inline_casts) = match variant.body {
1278 VariantBody::Body {
1279 ref name,
1280 ref body,
1281 inline_casts,
1282 ..
1283 } => (name, body, inline_casts),
1284 VariantBody::Empty(..) => return,
1285 };
1286
1287 let skip_fields = body.has_tag_field as usize;
1288 let field_count = body.fields.len() - skip_fields;
1289 if field_count == 0 {
1290 return;
1291 }
1292
1293 out.new_line();
1294 out.new_line();
1295
1296 if const_casts {
1297 write_attrs!("const-cast");
1298 } else {
1299 write_attrs!("mut-cast");
1300 }
1301 if inline_casts {
1302 let field = body.fields.last().unwrap();
1303 let return_type = field.ty.clone();
1304 let return_type = Type::Ptr {
1305 ty: Box::new(return_type),
1306 is_const: const_casts,
1307 is_ref: true,
1308 is_nullable: false,
1309 };
1310 return_type.write(config, out);
1311 } else if const_casts {
1312 write!(out, "const {}&", body.export_name());
1313 } else {
1314 write!(out, "{}&", body.export_name());
1315 }
1316
1317 write!(out, " As{}()", variant.export_name);
1318 if const_casts {
1319 write!(out, " const");
1320 }
1321 out.open_brace();
1322 write!(out, "{}(Is{}());", assert_name, variant.export_name);
1323 out.new_line();
1324 write!(out, "return {}", member_name);
1325 if inline_casts {
1326 write!(out, "._0");
1327 }
1328 write!(out, ";");
1329 out.close_brace(false);
1330 };
1331
1332 if config.enumeration.derive_const_casts(&self.annotations) {
1333 derive_casts(true)
1334 }
1335
1336 if config.enumeration.derive_mut_casts(&self.annotations) {
1337 derive_casts(false)
1338 }
1339
1340 condition.write_after(config, out);
1341 }
1342 }
1343
1344 let other = config
1345 .function
1346 .rename_args
1347 .apply("other", IdentifierType::FunctionArg);
1348
1349 macro_rules! write_attrs {
1350 ($op:expr) => {{
1351 if let Some(Some(attrs)) = self.annotations.atom(concat!($op, "-attributes")) {
1352 write!(out, "{} ", attrs);
1353 }
1354 }};
1355 }
1356
1357 if self.can_derive_eq() && config.structure.derive_eq(&self.annotations) {
1358 out.new_line();
1359 out.new_line();
1360 write_attrs!("eq");
1361 write!(
1362 out,
1363 "bool operator==(const {}& {}) const",
1364 self.export_name, other
1365 );
1366 out.open_brace();
1367 write!(out, "if (tag != {}.tag)", other);
1368 out.open_brace();
1369 write!(out, "return false;");
1370 out.close_brace(false);
1371 out.new_line();
1372 write!(out, "switch (tag)");
1373 out.open_brace();
1374 let mut exhaustive = true;
1375 for variant in &self.variants {
1376 if let VariantBody::Body {
1377 name: ref variant_name,
1378 ..
1379 } = variant.body
1380 {
1381 let condition = variant.cfg.to_condition(config);
1382 condition.write_before(config, out);
1383 write!(
1384 out,
1385 "case {}::{}: return {} == {}.{};",
1386 self.tag.as_ref().unwrap(),
1387 variant.export_name,
1388 variant_name,
1389 other,
1390 variant_name
1391 );
1392 condition.write_after(config, out);
1393 out.new_line();
1394 } else {
1395 exhaustive = false;
1396 }
1397 }
1398 if !exhaustive {
1399 write!(out, "default: break;");
1400 }
1401 out.close_brace(false);
1402
1403 out.new_line();
1404 write!(out, "return true;");
1405
1406 out.close_brace(false);
1407
1408 if config.structure.derive_neq(&self.annotations) {
1409 out.new_line();
1410 out.new_line();
1411 write_attrs!("neq");
1412 write!(
1413 out,
1414 "bool operator!=(const {}& {}) const",
1415 self.export_name, other
1416 );
1417 out.open_brace();
1418 write!(out, "return !(*this == {});", other);
1419 out.close_brace(false);
1420 }
1421 }
1422
1423 if config
1424 .enumeration
1425 .private_default_tagged_enum_constructor(&self.annotations)
1426 {
1427 out.new_line();
1428 out.new_line();
1429 write!(out, "private:");
1430 out.new_line();
1431 write!(out, "{}()", self.export_name);
1432 out.open_brace();
1433 out.close_brace(false);
1434 out.new_line();
1435 write!(out, "public:");
1436 out.new_line();
1437 }
1438
1439 if config
1440 .enumeration
1441 .derive_tagged_enum_destructor(&self.annotations)
1442 {
1443 out.new_line();
1444 out.new_line();
1445 write_attrs!("destructor");
1446 write!(out, "~{}()", self.export_name);
1447 out.open_brace();
1448 write!(out, "switch (tag)");
1449 out.open_brace();
1450 let mut exhaustive = true;
1451 for variant in &self.variants {
1452 if let VariantBody::Body {
1453 ref name, ref body, ..
1454 } = variant.body
1455 {
1456 let condition = variant.cfg.to_condition(config);
1457 condition.write_before(config, out);
1458 write!(
1459 out,
1460 "case {}::{}: {}.~{}(); break;",
1461 self.tag.as_ref().unwrap(),
1462 variant.export_name,
1463 name,
1464 body.export_name(),
1465 );
1466 condition.write_after(config, out);
1467 out.new_line();
1468 } else {
1469 exhaustive = false;
1470 }
1471 }
1472 if !exhaustive {
1473 write!(out, "default: break;");
1474 }
1475 out.close_brace(false);
1476 out.close_brace(false);
1477 }
1478
1479 if config
1480 .enumeration
1481 .derive_tagged_enum_copy_constructor(&self.annotations)
1482 {
1483 out.new_line();
1484 out.new_line();
1485 write_attrs!("copy-constructor");
1486 write!(
1487 out,
1488 "{}(const {}& {})",
1489 self.export_name, self.export_name, other
1490 );
1491 out.new_line();
1492 write!(out, " : tag({}.tag)", other);
1493 out.open_brace();
1494 write!(out, "switch (tag)");
1495 out.open_brace();
1496 let mut exhaustive = true;
1497 for variant in &self.variants {
1498 if let VariantBody::Body {
1499 ref name, ref body, ..
1500 } = variant.body
1501 {
1502 let condition = variant.cfg.to_condition(config);
1503 condition.write_before(config, out);
1504 write!(
1505 out,
1506 "case {}::{}: ::new (&{}) ({})({}.{}); break;",
1507 self.tag.as_ref().unwrap(),
1508 variant.export_name,
1509 name,
1510 body.export_name(),
1511 other,
1512 name,
1513 );
1514 condition.write_after(config, out);
1515 out.new_line();
1516 } else {
1517 exhaustive = false;
1518 }
1519 }
1520 if !exhaustive {
1521 write!(out, "default: break;");
1522 }
1523 out.close_brace(false);
1524 out.close_brace(false);
1525
1526 if config
1527 .enumeration
1528 .derive_tagged_enum_copy_assignment(&self.annotations)
1529 {
1530 out.new_line();
1531 write_attrs!("copy-assignment");
1532 write!(
1533 out,
1534 "{}& operator=(const {}& {})",
1535 self.export_name, self.export_name, other
1536 );
1537 out.open_brace();
1538 write!(out, "if (this != &{})", other);
1539 out.open_brace();
1540 write!(out, "this->~{}();", self.export_name);
1541 out.new_line();
1542 write!(out, "new (this) {}({});", self.export_name, other);
1543 out.close_brace(false);
1544 out.new_line();
1545 write!(out, "return *this;");
1546 out.close_brace(false);
1547 }
1548 }
1549 }
1550
1551 pub fn simplify_standard_types(&mut self, config: &Config) {
1552 for variant in &mut self.variants {
1553 variant.simplify_standard_types(config);
1554 }
1555 }
1556}
1557