summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmilio Cobos Álvarez <ecoal95@gmail.com>2016-11-08 07:31:53 +0100
committerEmilio Cobos Álvarez <ecoal95@gmail.com>2016-11-08 19:48:19 +0100
commit18ce3006a87b745fd52a1ce5b2bf7de2b5e2b4ef (patch)
tree6e47e6057ea3d111fa772851a378807448c4d41d
parent7fe40e0cf92df36219308406dcb4130a848fb6f6 (diff)
Add an option to generate bitfield-like typesafe enums.
-rwxr-xr-xsrc/bin/bindgen.rs10
-rwxr-xr-xsrc/codegen/mod.rs145
-rwxr-xr-xsrc/lib.rs13
3 files changed, 146 insertions, 22 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs
index 2dbb1690..b9e56b08 100755
--- a/src/bin/bindgen.rs
+++ b/src/bin/bindgen.rs
@@ -28,6 +28,7 @@ Usage:
[--whitelist-type=<type>...] \
[--whitelist-function=<name>...] \
[--whitelist-var=<name>...] \
+ [--bitfield-enum=<name>...] \
<input-header> \
[-- <clang-args>...]
@@ -91,6 +92,10 @@ Options:
matching <regex>. Same behavior on emptyness
than the type whitelisting.
+ --bitfield-enum=<regex> Mark a bitfield as being used as an enum.
+ This makes bindgen don't generate a rust enum
+ for it, but a bitfield-like wrapper.
+
--dummy-uses=<path> For testing purposes, generate a C/C++ file
containing dummy uses of all types defined in
the input header.
@@ -163,6 +168,11 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
.expect("--whitelist-var expects a pattern");
options.whitelisted_vars.insert(&var_pat);
}
+ "--bitfield-enum" => {
+ let enum_pat = iter.next()
+ .expect("--bitfield-enum expects a pattern");
+ options.bitfield_enums.insert(&enum_pat);
+ }
"--" => {
while let Some(clang_arg) = iter.next() {
options.clang_args.push(clang_arg);
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 7fa8b19c..02051f27 100755
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -1224,6 +1224,79 @@ impl MethodCodegen for Method {
}
}
+enum EnumBuilder<'a> {
+ Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>),
+ Bitfield {
+ canonical_name: &'a str,
+ aster: P<ast::Item>,
+ },
+}
+
+impl<'a> EnumBuilder<'a> {
+ fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>,
+ name: &'a str,
+ repr_name: &str,
+ is_rust: bool)
+ -> Self {
+ if is_rust {
+ EnumBuilder::Rust(aster.enum_(name))
+ } else {
+ EnumBuilder::Bitfield {
+ canonical_name: name,
+ aster: aster.tuple_struct(name)
+ .field()
+ .pub_()
+ .ty()
+ .id(repr_name)
+ .build(),
+ }
+ }
+ }
+
+ fn with_variant_(self,
+ ctx: &BindgenContext,
+ name: &str,
+ expr: P<ast::Expr>,
+ rust_ty: P<ast::Ty>,
+ result: &mut CodegenResult)
+ -> Self {
+ match self {
+ EnumBuilder::Rust(b) => {
+ EnumBuilder::Rust(b.with_variant_(ast::Variant_ {
+ name: ctx.rust_ident(name),
+ attrs: vec![],
+ data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
+ disr_expr: Some(expr),
+ }))
+ }
+ EnumBuilder::Bitfield { canonical_name, .. } => {
+ // FIXME: Probably if unnamed we should be smarter! Easy to
+ // improve.
+ let constant_name = format!("{}_{}", canonical_name, name);
+ let constant = aster::AstBuilder::new()
+ .item()
+ .const_(constant_name)
+ .expr()
+ .call()
+ .id(canonical_name)
+ .arg()
+ .build(expr)
+ .build()
+ .build(rust_ty);
+ result.push(constant);
+ self
+ }
+ }
+ }
+
+ fn build(self) -> P<ast::Item> {
+ match self {
+ EnumBuilder::Rust(b) => b.build(),
+ EnumBuilder::Bitfield { aster, .. } => aster,
+ }
+ }
+}
+
impl CodeGenerator for Enum {
type Extra = Item;
@@ -1234,7 +1307,8 @@ impl CodeGenerator for Enum {
use ir::enum_ty::EnumVariantValue;
let name = item.canonical_name(ctx);
- let layout = item.expect_type().layout(ctx);
+ let enum_ty = item.expect_type();
+ let layout = enum_ty.layout(ctx);
let repr = self.repr().map(|repr| ctx.resolve_type(repr));
let repr = match repr {
@@ -1270,10 +1344,24 @@ impl CodeGenerator for Enum {
let mut builder = aster::AstBuilder::new().item().pub_();
+ let is_bitfield = {
+ ctx.options().bitfield_enums.matches(&name) ||
+ (enum_ty.name().is_none() &&
+ self.variants()
+ .iter()
+ .any(|v| ctx.options().bitfield_enums.matches(&v.name())))
+ };
+
+ let is_rust_enum = !is_bitfield;
+
// FIXME: Rust forbids repr with empty enums. Remove this condition when
// this is allowed.
- if !self.variants().is_empty() {
- builder = builder.with_attr(attributes::repr(repr_name));
+ if is_rust_enum {
+ if !self.variants().is_empty() {
+ builder = builder.with_attr(attributes::repr(repr_name));
+ }
+ } else {
+ builder = builder.with_attr(attributes::repr("C"));
}
if let Some(comment) = item.comment() {
@@ -1289,8 +1377,6 @@ impl CodeGenerator for Enum {
builder = builder.with_attr(derives);
- let mut builder = builder.enum_(&name);
-
fn add_constant(enum_: &Type,
// Only to avoid recomputing every time.
enum_canonical_name: &str,
@@ -1321,21 +1407,38 @@ impl CodeGenerator for Enum {
// Used to mangle the constants we generate in the unnamed-enum case.
let mut parent_canonical_name = None;
+ let mut builder =
+ EnumBuilder::new(builder, &name, repr_name, is_rust_enum);
+
// A map where we keep a value -> variant relation.
let mut seen_values = HashMap::<_, String>::new();
- let enum_ty = item.expect_type();
let enum_rust_ty = item.to_rust_ty(ctx);
for variant in self.variants().iter() {
match seen_values.entry(variant.val()) {
Entry::Occupied(ref entry) => {
- let existing_variant_name = entry.get();
- let variant_name = ctx.rust_mangle(variant.name());
- add_constant(enum_ty,
- &name,
- &*variant_name,
- existing_variant_name,
- enum_rust_ty.clone(),
- result);
+ if is_rust_enum {
+ let existing_variant_name = entry.get();
+ let variant_name = ctx.rust_mangle(variant.name());
+ add_constant(enum_ty,
+ &name,
+ &*variant_name,
+ existing_variant_name,
+ enum_rust_ty.clone(),
+ result);
+ } else {
+ // FIXME: Don't repeat this block below.
+ let expr = aster::AstBuilder::new().expr();
+ let expr = match variant.val() {
+ EnumVariantValue::Signed(val) => expr.int(val),
+ EnumVariantValue::Unsigned(val) => expr.uint(val),
+ };
+ let variant_name = ctx.rust_mangle(variant.name());
+ builder = builder.with_variant_(ctx,
+ &*variant_name,
+ expr,
+ enum_rust_ty.clone(),
+ result);
+ }
}
Entry::Vacant(entry) => {
let expr = aster::AstBuilder::new().expr();
@@ -1344,16 +1447,15 @@ impl CodeGenerator for Enum {
EnumVariantValue::Unsigned(val) => expr.uint(val),
};
let variant_name = ctx.rust_mangle(variant.name());
- builder = builder.with_variant_(ast::Variant_ {
- name: ctx.rust_ident(&*variant_name),
- attrs: vec![],
- data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
- disr_expr: Some(expr),
- });
+ builder = builder.with_variant_(ctx,
+ &*variant_name,
+ expr,
+ enum_rust_ty.clone(),
+ result);
// If it's an unnamed enum, we also generate a constant so
// it can be properly accessed.
- if enum_ty.name().is_none() {
+ if is_rust_enum && enum_ty.name().is_none() {
// NB: if we want to do this for other kind of nested
// enums we can probably mangle the name.
let mangled_name = if item.is_toplevel(ctx) {
@@ -1384,7 +1486,6 @@ impl CodeGenerator for Enum {
}
}
-
result.push(builder.build());
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 03dac3cc..2e61840e 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -166,6 +166,15 @@ impl Builder {
self
}
+ /// Mark the given enum (or set of enums, if using a pattern) as being
+ /// bitfield-like.
+ ///
+ /// This makes bindgen to generate a type for it that isn't a rust `enum`.
+ pub fn bitfield_enum<T: Borrow<str>>(mut self, arg: T) -> Builder {
+ self.options.bitfield_enums.insert(&arg);
+ self
+ }
+
/// Add a string to prepend to the generated bindings. The string is passed
/// through without any modification.
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
@@ -262,6 +271,9 @@ pub struct BindgenOptions {
/// Whitelisted variables. See docs for `whitelisted_types` for more.
pub whitelisted_vars: RegexSet,
+ /// The enum patterns to mark an enum as bitfield.
+ pub bitfield_enums: RegexSet,
+
/// Whether we should generate builtins or not.
pub builtins: bool,
@@ -329,6 +341,7 @@ impl Default for BindgenOptions {
whitelisted_types: Default::default(),
whitelisted_functions: Default::default(),
whitelisted_vars: Default::default(),
+ bitfield_enums: Default::default(),
builtins: false,
links: vec![],
emit_ast: false,