Skip to content

Commit

Permalink
enum: Support inlined definitions for tuple variants with a single field
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov authored and emilio committed Dec 9, 2020
1 parent eaf3e57 commit b82e375
Show file tree
Hide file tree
Showing 113 changed files with 1,241 additions and 2,092 deletions.
81 changes: 65 additions & 16 deletions src/bindgen/ir/enumeration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ pub enum VariantBody {
name: String,
/// The struct with all the items.
body: Struct,
/// A separate named struct is not created for this variant,
/// an unnamed struct is inlined at the point of use instead.
/// This is a reasonable thing to do only for tuple variants with a single field.
inline: bool,
/// Generated cast methods return the variant's only field instead of the variant itself.
/// For backward compatibility casts are inlined in a slightly
/// larger set of cases than whole variants.
inline_casts: bool,
},
}
Expand Down Expand Up @@ -62,10 +68,12 @@ impl VariantBody {
Self::Body {
ref name,
ref body,
inline,
inline_casts,
} => Self::Body {
name: name.clone(),
body: body.specialize(generic_values, mappings, config),
inline,
inline_casts,
},
}
Expand All @@ -90,6 +98,7 @@ impl EnumVariant {
mod_cfg: Option<&Cfg>,
self_path: &Path,
enum_annotations: &AnnotationSet,
config: &Config,
) -> Result<Self, String> {
let discriminant = match variant.discriminant {
Some((_, ref expr)) => Some(Literal::load(expr)?),
Expand All @@ -100,12 +109,13 @@ impl EnumVariant {
inline_tag_field: bool,
fields: &syn::punctuated::Punctuated<syn::Field, syn::token::Comma>,
self_path: &Path,
inline_name: Option<&str>,
) -> Result<Vec<Field>, String> {
let mut res = Vec::new();

if inline_tag_field {
res.push(Field::from_name_and_type(
"tag".to_string(),
inline_name.map_or_else(|| "tag".to_string(), |name| format!("{}_tag", name)),
Type::Path(GenericPath::new(Path::new("Tag"), vec![])),
));
}
Expand All @@ -114,10 +124,13 @@ impl EnumVariant {
if let Some(mut ty) = Type::load(&field.ty)? {
ty.replace_self_with(self_path);
res.push(Field {
name: match field.ident {
Some(ref ident) => ident.to_string(),
None => i.to_string(),
},
name: inline_name.map_or_else(
|| match field.ident {
Some(ref ident) => ident.to_string(),
None => i.to_string(),
},
|name| name.to_string(),
),
ty,
cfg: Cfg::load(&field.attrs),
annotations: AnnotationSet::load(&field.attrs)?,
Expand All @@ -142,11 +155,10 @@ impl EnumVariant {
.apply(&variant.ident.to_string(), IdentifierType::StructMember)
.into_owned();
VariantBody::Body {
name,
body: Struct::new(
path,
generic_params,
parse_fields(inline_tag_field, &fields.named, self_path)?,
parse_fields(inline_tag_field, &fields.named, self_path, None)?,
inline_tag_field,
true,
None,
Expand All @@ -155,6 +167,8 @@ impl EnumVariant {
annotations,
Documentation::none(),
),
name,
inline: false,
inline_casts: false,
}
}
Expand All @@ -163,12 +177,19 @@ impl EnumVariant {
let name = RenameRule::SnakeCase
.apply(&variant.ident.to_string(), IdentifierType::StructMember)
.into_owned();
let inline_casts = fields.unnamed.len() == 1;
// In C++ types with destructors cannot be put into unnamed structs like the
// inlining requires, and it's hard to detect such types.
// Besides that for C++ we generate casts/getters that can be used instead of
// direct field accesses and also have a benefit of being checked.
// As a result we don't currently inline variant definitions in C++ mode at all.
let inline = inline_casts && config.language != Language::Cxx;
let inline_name = if inline { Some(&*name) } else { None };
VariantBody::Body {
name,
body: Struct::new(
path,
generic_params,
parse_fields(inline_tag_field, &fields.unnamed, self_path)?,
parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?,
inline_tag_field,
true,
None,
Expand All @@ -177,7 +198,9 @@ impl EnumVariant {
annotations,
Documentation::none(),
),
inline_casts: fields.unnamed.len() == 1,
name,
inline,
inline_casts,
}
}
};
Expand Down Expand Up @@ -355,6 +378,7 @@ impl Enum {
mod_cfg,
&path,
&annotations,
config,
)?;
has_data = has_data || !variant.body.is_empty();
variants.push(variant);
Expand Down Expand Up @@ -531,10 +555,12 @@ impl Item for Enum {
VariantBody::Body {
ref name,
ref body,
inline,
inline_casts,
} => VariantBody::Body {
name: r.apply(&name, IdentifierType::StructMember).into_owned(),
body: body.clone(),
inline,
inline_casts,
},
},
Expand Down Expand Up @@ -836,7 +862,12 @@ impl Enum {
/// Emit struct definitions for variants having data.
fn write_variant_defs<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
for variant in &self.variants {
if let VariantBody::Body { ref body, .. } = variant.body {
if let VariantBody::Body {
ref body,
inline: false,
..
} = variant.body
{
out.new_line();
out.new_line();
let condition = variant.cfg.to_condition(config);
Expand Down Expand Up @@ -888,7 +919,10 @@ impl Enum {
fn write_variant_fields<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let mut first = true;
for variant in &self.variants {
if let VariantBody::Body { name, body, .. } = &variant.body {
if let VariantBody::Body {
name, body, inline, ..
} = &variant.body
{
if !first {
out.new_line();
}
Expand All @@ -898,7 +932,21 @@ impl Enum {
if config.language != Language::Cython {
condition.write_before(config, out);
}
if config.style.generate_typedef() || config.language == Language::Cython {
if *inline {
// Write definition of an inlined variant with data.
// Cython extern declarations don't manage layouts, layouts are defined entierly
// by the corresponding C code. So we can inline the unnamed struct and get the
// same observable result. Moreother we have to do it because Cython doesn't
// support unnamed structs.
if config.language != Language::Cython {
out.write("struct");
out.open_brace();
}
out.write_vertical_source_list(&body.fields, ListType::Cap(";"));
if config.language != Language::Cython {
out.close_brace(true);
}
} else if config.style.generate_typedef() || config.language == Language::Cython {
write!(out, "{} {};", body.export_name(), name);
} else {
write!(out, "struct {} {};", body.export_name(), name);
Expand Down Expand Up @@ -1176,6 +1224,7 @@ impl Enum {
ref name,
ref body,
inline_casts,
..
} => (name, body, inline_casts),
VariantBody::Empty(..) => return,
};
Expand Down Expand Up @@ -1217,11 +1266,11 @@ impl Enum {
out.open_brace();
write!(out, "{}(Is{}());", assert_name, variant.export_name);
out.new_line();
write!(out, "return {}", member_name);
if inline_casts {
write!(out, "return {}._0;", member_name);
} else {
write!(out, "return {};", member_name);
write!(out, "._0");
}
write!(out, ";");
out.close_brace(false);
};

Expand Down
18 changes: 7 additions & 11 deletions tests/expectations/annotation.both.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ enum F_Tag {
};
typedef uint8_t F_Tag;

typedef struct Foo_Body {
F_Tag tag;
int16_t _0;
} Foo_Body;

typedef struct Bar_Body {
F_Tag tag;
uint8_t x;
Expand All @@ -38,7 +33,10 @@ typedef struct Bar_Body {

typedef union F {
F_Tag tag;
Foo_Body foo;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;

Expand All @@ -49,10 +47,6 @@ enum H_Tag {
};
typedef uint8_t H_Tag;

typedef struct Hello_Body {
int16_t _0;
} Hello_Body;

typedef struct There_Body {
uint8_t x;
int16_t y;
Expand All @@ -61,7 +55,9 @@ typedef struct There_Body {
typedef struct H {
H_Tag tag;
union {
Hello_Body hello;
struct {
int16_t hello;
};
There_Body there;
};
} H;
Expand Down
18 changes: 7 additions & 11 deletions tests/expectations/annotation.both.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ enum F_Tag
typedef uint8_t F_Tag;
#endif // __cplusplus

typedef struct Foo_Body {
F_Tag tag;
int16_t _0;
} Foo_Body;

typedef struct Bar_Body {
F_Tag tag;
uint8_t x;
Expand All @@ -50,7 +45,10 @@ typedef struct Bar_Body {

typedef union F {
F_Tag tag;
Foo_Body foo;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;

Expand All @@ -67,10 +65,6 @@ enum H_Tag
typedef uint8_t H_Tag;
#endif // __cplusplus

typedef struct Hello_Body {
int16_t _0;
} Hello_Body;

typedef struct There_Body {
uint8_t x;
int16_t y;
Expand All @@ -79,7 +73,9 @@ typedef struct There_Body {
typedef struct H {
H_Tag tag;
union {
Hello_Body hello;
struct {
int16_t hello;
};
There_Body there;
};
} H;
Expand Down
18 changes: 7 additions & 11 deletions tests/expectations/annotation.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ enum F_Tag {
};
typedef uint8_t F_Tag;

typedef struct {
F_Tag tag;
int16_t _0;
} Foo_Body;

typedef struct {
F_Tag tag;
uint8_t x;
Expand All @@ -38,7 +33,10 @@ typedef struct {

typedef union {
F_Tag tag;
Foo_Body foo;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;

Expand All @@ -49,10 +47,6 @@ enum H_Tag {
};
typedef uint8_t H_Tag;

typedef struct {
int16_t _0;
} Hello_Body;

typedef struct {
uint8_t x;
int16_t y;
Expand All @@ -61,7 +55,9 @@ typedef struct {
typedef struct {
H_Tag tag;
union {
Hello_Body hello;
struct {
int16_t hello;
};
There_Body there;
};
} H;
Expand Down
18 changes: 7 additions & 11 deletions tests/expectations/annotation.compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,6 @@ enum F_Tag
typedef uint8_t F_Tag;
#endif // __cplusplus

typedef struct {
F_Tag tag;
int16_t _0;
} Foo_Body;

typedef struct {
F_Tag tag;
uint8_t x;
Expand All @@ -50,7 +45,10 @@ typedef struct {

typedef union {
F_Tag tag;
Foo_Body foo;
struct {
F_Tag foo_tag;
int16_t foo;
};
Bar_Body bar;
} F;

Expand All @@ -67,10 +65,6 @@ enum H_Tag
typedef uint8_t H_Tag;
#endif // __cplusplus

typedef struct {
int16_t _0;
} Hello_Body;

typedef struct {
uint8_t x;
int16_t y;
Expand All @@ -79,7 +73,9 @@ typedef struct {
typedef struct {
H_Tag tag;
union {
Hello_Body hello;
struct {
int16_t hello;
};
There_Body there;
};
} H;
Expand Down
Loading

0 comments on commit b82e375

Please sign in to comment.