Initial support for tagged enums

This commit is contained in:
Ingvar Stepanyan
2017-11-15 05:12:09 +00:00
committed by Ryan Hunt
parent 2ccaa3e956
commit 3b61c8ead5
5 changed files with 226 additions and 63 deletions
+151 -38
View File
@@ -7,7 +7,8 @@ use std::io::Write;
use syn;
use bindgen::config::{Config, Language};
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, Item, ItemContainer, Repr};
use bindgen::ir::{AnnotationSet, Cfg, CfgWrite, Documentation, GenericParams, GenericPath, Item,
ItemContainer, Repr, Struct, Type};
use bindgen::rename::{IdentifierType, RenameRule};
use bindgen::utilities::find_first_some;
use bindgen::writer::{Source, SourceWriter};
@@ -16,7 +17,8 @@ use bindgen::writer::{Source, SourceWriter};
pub struct Enum {
pub name: String,
pub repr: Repr,
pub values: Vec<(String, u64, Documentation)>,
pub values: Vec<(String, u64, Option<Struct>, Documentation)>,
pub tag: Option<String>,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
pub documentation: Documentation,
@@ -40,38 +42,77 @@ impl Enum {
let mut values = Vec::new();
let mut current = 0;
let mut is_tagged = false;
for variant in variants {
match variant.data {
syn::VariantData::Unit => {
match variant.discriminant {
Some(syn::ConstExpr::Lit(syn::Lit::Int(i, _))) => {
current = i;
}
Some(_) => {
return Err("Unsupported discriminant.".to_owned());
}
None => { /* okay, we just use current */ }
}
match variant.discriminant {
Some(syn::ConstExpr::Lit(syn::Lit::Int(i, _))) => {
current = i;
}
Some(_) => {
return Err("Unsupported discriminant.".to_owned());
}
None => { /* okay, we just use current */ }
};
let body = match variant.data {
syn::VariantData::Unit => None,
syn::VariantData::Struct(ref fields) | syn::VariantData::Tuple(ref fields) => {
is_tagged = true;
Some(Struct {
name: format!("{}_Body", variant.ident),
generic_params: GenericParams::default(),
fields: {
let mut res = vec![
(
"tag".to_string(),
Type::Path(GenericPath {
name: "Tag".to_string(),
generics: vec![],
}),
Documentation::none(),
),
];
values.push((
variant.ident.to_string(),
current,
Documentation::load(&variant.attrs),
));
current = current + 1;
for (i, field) in fields.iter().enumerate() {
if let Some(ty) = Type::load(&field.ty)? {
res.push((
match field.ident {
Some(ref ident) => ident.to_string(),
None => i.to_string(),
},
ty,
Documentation::load(&field.attrs),
));
}
}
res
},
is_variant: true,
tuple_struct: match variant.data {
syn::VariantData::Tuple(_) => true,
_ => false,
},
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: AnnotationSet::load(attrs)?,
documentation: Documentation::none(),
})
}
_ => {
return Err("Unsupported variant.".to_owned());
}
}
};
values.push((
variant.ident.to_string(),
current,
body,
Documentation::load(&variant.attrs),
));
current = current + 1;
}
let annotations = AnnotationSet::load(attrs)?;
if let Some(variants) = annotations.list("enum-trailing-values") {
for variant in variants {
values.push((variant, current, Documentation::none()));
values.push((variant, current, None, Documentation::none()));
current = current + 1;
}
}
@@ -80,6 +121,11 @@ impl Enum {
name: name,
repr: repr,
values: values,
tag: if is_tagged {
Some("Tag".to_string())
} else {
None
},
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: annotations,
documentation: Documentation::load(attrs),
@@ -109,16 +155,34 @@ impl Item for Enum {
}
fn rename_for_config(&mut self, config: &Config) {
config.export.rename(&mut self.name);
if config.language == Language::C && self.tag.is_some() {
// it makes sense to always prefix Tag with type name in C
let new_tag = format!("{}_Tag", self.name);
for value in &mut self.values {
if let Some(ref mut body) = value.2 {
body.fields[0].1 = Type::Path(GenericPath {
name: new_tag.clone(),
generics: vec![],
});
}
}
self.tag = Some(new_tag);
}
if config.language == Language::C
&& (config.enumeration.prefix_with_name
|| self.annotations.bool("prefix-with-name").unwrap_or(false))
for value in &mut self.values {
if let Some(ref mut body) = value.2 {
body.rename_for_config(config);
}
}
if config.enumeration.prefix_with_name
|| self.annotations.bool("prefix-with-name").unwrap_or(false)
{
let old = ::std::mem::replace(&mut self.values, Vec::new());
for (name, value, doc) in old {
self.values
.push((format!("{}_{}", self.name, name), value, doc));
for value in &mut self.values {
value.0 = format!("{}_{}", self.name, value.0);
if let Some(ref mut body) = value.2 {
body.name = format!("{}_{}", self.name, body.name);
}
}
}
@@ -135,6 +199,7 @@ impl Item for Enum {
r.apply_to_pascal_case(&x.0, IdentifierType::EnumVariant(self)),
x.1.clone(),
x.2.clone(),
x.3.clone(),
)
})
.collect();
@@ -148,6 +213,19 @@ impl Source for Enum {
self.documentation.write(config, out);
let is_tagged = self.tag.is_some();
if is_tagged && config.language == Language::Cxx {
write!(out, "union {}", self.name);
out.open_brace();
}
let enum_name = if let Some(ref tag) = self.tag {
tag
} else {
&self.name
};
let size = match self.repr {
Repr::C => None,
Repr::USize => Some("uintptr_t"),
@@ -165,13 +243,13 @@ impl Source for Enum {
if size.is_none() {
out.write("typedef enum");
} else {
write!(out, "enum {}", self.name);
write!(out, "enum {}", enum_name);
}
} else {
if let Some(prim) = size {
write!(out, "enum class {} : {}", self.name, prim);
write!(out, "enum class {} : {}", enum_name, prim);
} else {
write!(out, "enum class {}", self.name);
write!(out, "enum class {}", enum_name);
}
}
out.open_brace();
@@ -179,7 +257,7 @@ impl Source for Enum {
if i != 0 {
out.new_line()
}
value.2.write(config, out);
value.3.write(config, out);
write!(out, "{} = {},", value.0, value.1);
}
if config.enumeration.add_sentinel(&self.annotations) {
@@ -190,7 +268,7 @@ impl Source for Enum {
if config.language == Language::C && size.is_none() {
out.close_brace(false);
write!(out, " {};", self.name);
write!(out, " {};", enum_name);
} else {
out.close_brace(true);
}
@@ -198,7 +276,42 @@ impl Source for Enum {
if config.language == Language::C {
if let Some(prim) = size {
out.new_line();
write!(out, "typedef {} {};", prim, self.name);
write!(out, "typedef {} {};", prim, enum_name);
}
}
if is_tagged {
for value in &self.values {
if let Some(ref body) = value.2 {
out.new_line();
out.new_line();
body.write(config, out);
}
}
out.new_line();
out.new_line();
if config.language == Language::C {
out.write("typedef union");
out.open_brace();
}
write!(out, "{} tag;", enum_name);
for value in &self.values {
if let Some(ref body) = value.2 {
out.new_line();
write!(out, "{} {};", body.name, value.0);
}
}
if config.language == Language::C {
out.close_brace(false);
write!(out, " {};", self.name);
} else {
out.close_brace(true);
}
}
+17 -22
View File
@@ -22,6 +22,7 @@ pub struct Struct {
pub name: String,
pub generic_params: GenericParams,
pub fields: Vec<(String, Type, Documentation)>,
pub is_variant: bool,
pub tuple_struct: bool,
pub cfg: Option<Cfg>,
pub annotations: AnnotationSet,
@@ -63,6 +64,7 @@ impl Struct {
name: name,
generic_params: GenericParams::new(generics),
fields: fields,
is_variant: false,
tuple_struct: tuple_struct,
cfg: Cfg::append(mod_cfg, Cfg::load(attrs)),
annotations: AnnotationSet::load(attrs)?,
@@ -131,33 +133,25 @@ impl Item for Struct {
config.structure.rename_fields,
];
if let Some(o) = self.annotations.list("field-names") {
let mut overriden_fields = Vec::new();
let mut names = self.fields.iter_mut().map(|field| &mut field.0);
for (i, &(ref name, ref ty, ref doc)) in self.fields.iter().enumerate() {
if i >= o.len() {
overriden_fields.push((name.clone(), ty.clone(), doc.clone()));
} else {
overriden_fields.push((o[i].clone(), ty.clone(), doc.clone()));
}
if let Some(o) = self.annotations.list("field-names") {
for (dest, src) in names.zip(o) {
*dest = src;
}
} else if let Some(r) = find_first_some(&field_rules) {
for name in names {
*name = r.apply_to_snake_case(name, IdentifierType::StructMember);
}
} else if self.tuple_struct {
// If there is a tag field, skip it
if self.is_variant {
names.next();
}
self.fields = overriden_fields;
} else if let Some(r) = find_first_some(&field_rules) {
self.fields = self.fields
.iter()
.map(|x| {
(
r.apply_to_snake_case(&x.0, IdentifierType::StructMember),
x.1.clone(),
x.2.clone(),
)
})
.collect();
} else if self.tuple_struct {
// If we don't have any rules for a tuple struct, prefix them with
// an underscore so it still compiles
for &mut (ref mut name, ..) in &mut self.fields {
for name in names {
name.insert(0, '_');
}
}
@@ -189,6 +183,7 @@ impl Item for Struct {
.iter()
.map(|x| (x.0.clone(), x.1.specialize(&mappings), x.2.clone()))
.collect(),
is_variant: self.is_variant,
tuple_struct: self.tuple_struct,
cfg: self.cfg.clone(),
annotations: self.annotations.clone(),
+25 -1
View File
@@ -42,6 +42,30 @@ enum E {
};
typedef intptr_t E;
enum F_Tag {
Foo = 0,
Bar = 1,
Baz = 2,
};
typedef uint8_t F_Tag;
typedef struct {
F_Tag tag;
int16_t _0;
} Foo_Body;
typedef struct {
F_Tag tag;
uint8_t x;
int16_t y;
} Bar_Body;
typedef union {
F_Tag tag;
Foo_Body Foo;
Bar_Body Bar;
} F;
typedef struct Opaque Opaque;
void root(Opaque *o, A a, B b, C c, D d, E e);
void root(Opaque *o, A a, B b, C c, D d, E e, F f);
+24 -1
View File
@@ -36,10 +36,33 @@ enum class E : intptr_t {
e4 = 5,
};
union F {
enum class Tag : uint8_t {
Foo = 0,
Bar = 1,
Baz = 2,
};
struct Foo_Body {
Tag tag;
int16_t _0;
};
struct Bar_Body {
Tag tag;
uint8_t x;
int16_t y;
};
Tag tag;
Foo_Body Foo;
Bar_Body Bar;
};
struct Opaque;
extern "C" {
void root(Opaque *o, A a, B b, C c, D d, E e);
void root(Opaque *o, A a, B b, C c, D d, E e, F f);
} // extern "C"
+9 -1
View File
@@ -43,6 +43,13 @@ enum E {
e4 = 5,
}
#[repr(u8)]
enum F {
Foo(i16),
Bar { x: u8, y: i16 },
Baz
}
#[no_mangle]
pub extern "C" fn root(
o: *mut Opaque,
@@ -50,5 +57,6 @@ pub extern "C" fn root(
b: B,
c: C,
d: D,
e: E
e: E,
f: F
) { }