alnyan/yggdrasil: add #define-style enums

This commit is contained in:
Mark Poliakov 2024-11-28 14:03:10 +02:00
parent 3e96ed3258
commit b9c3b9a1fe
6 changed files with 102 additions and 11 deletions

View File

@ -360,7 +360,9 @@ impl CDecl {
if never_return && config.language != Language::Cython {
if let Some(ref no_return_attr) = config.function.no_return {
out.write_fmt(format_args!(" {}", no_return_attr));
if no_return_attr != "[[noreturn]]" {
out.write_fmt(format_args!(" {}", no_return_attr));
}
}
}

View File

@ -552,6 +552,18 @@ impl StructConfig {
}
}
/// Enum generation style.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum EnumStyle {
/// enum E { ... }
Enum,
/// enum class E { ... } (if C++)
EnumClass,
/// #define ... if a primitive enum, fall back to EnumClass otherwise
Define,
}
/// Settings to apply to generated enums.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "snake_case")]
@ -601,12 +613,14 @@ pub struct EnumConfig {
pub derive_tagged_enum_copy_assignment: bool,
/// Whether to generate a ostream serializer for the struct
pub derive_ostream: bool,
/// Declare the enum as an enum class.
/// Only relevant when targeting C++.
pub enum_class: bool,
// /// Declare the enum as an enum class.
// /// Only relevant when targeting C++.
// pub enum_class: bool,
/// Whether to generate empty, private default-constructors for tagged
/// enums.
pub private_default_tagged_enum_constructor: bool,
/// How to emit enum and its variants
pub enum_style: EnumStyle,
}
impl Default for EnumConfig {
@ -629,8 +643,8 @@ impl Default for EnumConfig {
derive_tagged_enum_copy_constructor: false,
derive_tagged_enum_copy_assignment: false,
derive_ostream: false,
enum_class: true,
private_default_tagged_enum_constructor: false,
enum_style: EnumStyle::EnumClass,
}
}
}
@ -688,7 +702,7 @@ impl EnumConfig {
if let Some(x) = annotations.bool("enum-class") {
return x;
}
self.enum_class
self.enum_style == EnumStyle::EnumClass
}
pub(crate) fn private_default_tagged_enum_constructor(
&self,

View File

@ -12,6 +12,7 @@ impl DeclarationType {
DeclarationType::Struct => "struct",
DeclarationType::Enum => "enum",
DeclarationType::Union => "union",
DeclarationType::IntEnum => unreachable!(),
}
}
}
@ -21,6 +22,7 @@ pub enum DeclarationType {
Struct,
Enum,
Union,
IntEnum,
}
#[derive(Default)]
@ -35,6 +37,10 @@ impl DeclarationTypeResolver {
}
}
pub fn add_int_enum(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::IntEnum));
}
pub fn add_enum(&mut self, path: &Path) {
self.insert(path, Some(DeclarationType::Enum));
}

View File

@ -21,6 +21,7 @@ use crate::bindgen::monomorph::Monomorphs;
use crate::bindgen::rename::{IdentifierType, RenameRule};
use crate::bindgen::reserved;
use crate::bindgen::writer::{ListType, SourceWriter};
use crate::bindgen::EnumStyle;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
@ -305,6 +306,7 @@ pub struct Enum {
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
pub define_enum: bool,
}
impl Enum {
@ -409,6 +411,8 @@ impl Enum {
None
};
let define_enum = config.enumeration.enum_style == EnumStyle::Define && !has_data;
Ok(Enum::new(
path,
generic_params,
@ -418,6 +422,7 @@ impl Enum {
Cfg::append(mod_cfg, Cfg::load(&item.attrs)),
annotations,
Documentation::load(&item.attrs),
define_enum,
))
}
@ -431,6 +436,7 @@ impl Enum {
cfg: Option<Cfg>,
annotations: AnnotationSet,
documentation: Documentation,
define_enum: bool,
) -> Self {
let export_name = path.name().to_owned();
Self {
@ -443,6 +449,7 @@ impl Enum {
cfg,
annotations,
documentation,
define_enum,
}
}
}
@ -483,7 +490,7 @@ impl Item for Enum {
} else {
resolver.add_union(&self.path);
}
} else if self.repr.style == ReprStyle::C {
} else if self.repr.style == ReprStyle::C && !self.define_enum {
resolver.add_enum(&self.path);
} else {
// This is important to handle conflicting names with opaque items.
@ -629,6 +636,7 @@ impl Item for Enum {
self.cfg.clone(),
self.annotations.clone(),
self.documentation.clone(),
false,
);
out.insert_enum(library, self, monomorph, generic_values.to_owned());
@ -642,6 +650,26 @@ impl Item for Enum {
}
impl Enum {
// TODO configure prefix for #define variants?
pub(crate) fn write_define_enum<
F: Write,
LB: LanguageBackend,
WV: Fn(&mut LB, &mut SourceWriter<F>, &EnumVariant),
>(
&self,
_config: &Config,
language_backend: &mut LB,
out: &mut SourceWriter<F>,
write_variant: WV,
) {
for (i, variant) in self.variants.iter().enumerate() {
if i != 0 {
out.new_line()
}
write_variant(language_backend, out, variant);
}
}
/// Emit the tag enum and convenience methods for it.
/// For enums with data this is only a part of the output,
/// but for enums without data it's the whole output (modulo doc comments etc.).

View File

@ -1,14 +1,14 @@
use crate::bindgen::ir::{
to_known_assoc_constant, ConditionWrite, DeprecatedNoteKind, Documentation, Enum, EnumVariant,
Field, GenericParams, Item, Literal, OpaqueItem, ReprAlign, Static, Struct, ToCondition, Type,
Typedef, Union,
Field, GenericParams, IntKind, Item, Literal, OpaqueItem, PrimitiveType, ReprAlign, Static,
Struct, ToCondition, Type, Typedef, Union,
};
use crate::bindgen::language_backend::LanguageBackend;
use crate::bindgen::rename::IdentifierType;
use crate::bindgen::writer::{ListType, SourceWriter};
use crate::bindgen::{cdecl, Bindings, Config, Language};
use crate::bindgen::{DocumentationLength, DocumentationStyle};
use crate::bindgen::{IsizeType, UsizeType};
use crate::bindgen::{EnumStyle, IsizeType, UsizeType};
use std::io::Write;
pub struct CLikeLanguageBackend<'a> {
@ -43,6 +43,18 @@ impl<'a> CLikeLanguageBackend<'a> {
condition.write_after(self.config, out);
}
fn write_enum_define<W: Write>(&mut self, out: &mut SourceWriter<W>, u: &EnumVariant) {
let discriminant = u.discriminant.as_ref().unwrap();
let condition = u.cfg.to_condition(self.config);
condition.write_before(self.config, out);
self.write_documentation(out, &u.documentation);
write!(out, "#define {} ", u.export_name);
self.write_literal(out, discriminant);
out.new_line();
}
fn write_field<W: Write>(&mut self, out: &mut SourceWriter<W>, f: &Field) {
let condition = f.cfg.to_condition(self.config);
condition.write_before(self.config, out);
@ -472,6 +484,7 @@ impl LanguageBackend for CLikeLanguageBackend<'_> {
fn write_enum<W: Write>(&mut self, out: &mut SourceWriter<W>, e: &Enum) {
let size = e.repr.ty.map(|ty| ty.to_primitive().to_repr_c(self.config));
let has_data = e.tag.is_some();
let inline_tag_field = Enum::inline_tag_field(&e.repr);
let tag_name = e.tag_name();
@ -481,6 +494,32 @@ impl LanguageBackend for CLikeLanguageBackend<'_> {
self.write_documentation(out, &e.documentation);
self.write_generic_param(out, &e.generic_params);
if !has_data && self.config.enumeration.enum_style == EnumStyle::Define {
self.write_type_def(
out,
&Typedef::new(
e.path.clone(),
e.generic_params.clone(),
Type::Primitive(PrimitiveType::Integer {
zeroable: false,
signed: true,
kind: IntKind::Int,
}),
e.cfg.clone(),
e.annotations.clone(),
e.documentation.clone(),
),
);
out.new_line();
// Write the enum as a series of #define ...
e.write_define_enum(self.config, self, out, Self::write_enum_define);
condition.write_after(self.config, out);
return;
}
// If the enum has data, we need to emit a struct or union for the data
// and enum for the tag. C++ supports nested type definitions, so we open
// the struct or union here and define the tag enum inside it (*).

View File

@ -1 +1,3 @@
usize_is_size_t = true
# usize_is_size_t = true
usize_type = "size_t"
isize_type = "ptrdiff_t"