diff options
author | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-11-08 20:45:04 +0100 |
---|---|---|
committer | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-11-08 20:46:43 +0100 |
commit | 4062713e0fc3dbfc7c6329252a814579a4702677 (patch) | |
tree | b1e2f29b291452eaa95e83f70b382c51f5eaad8a | |
parent | 19db528515e5e099cecb3fe7bf89209482046bd6 (diff) |
codegen: Simplify bitfield logic a bit, make it work for unnamed enums, generate
default BitOr implementation.
-rwxr-xr-x | src/bin/bindgen.rs | 6 | ||||
-rwxr-xr-x | src/codegen/mod.rs | 131 | ||||
-rwxr-xr-x | src/lib.rs | 2 | ||||
-rw-r--r-- | tests/expectations/tests/bitfield-enum-basic.rs | 73 | ||||
-rw-r--r-- | tests/headers/bitfield-enum-basic.h | 14 |
5 files changed, 164 insertions, 62 deletions
diff --git a/src/bin/bindgen.rs b/src/bin/bindgen.rs index b9e56b08..bdb3b292 100755 --- a/src/bin/bindgen.rs +++ b/src/bin/bindgen.rs @@ -92,9 +92,9 @@ 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. + --bitfield-enum=<regex> Mark any enum whose name matches <regex> as a + set of bitfield flags instead of an + enumeration. --dummy-uses=<path> For testing purposes, generate a C/C++ file containing dummy uses of all types defined in diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 02051f27..20f377e1 100755 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -6,7 +6,7 @@ use aster; use ir::annotations::FieldAccessorKind; use ir::comp::{CompInfo, CompKind, Field, Method}; use ir::context::BindgenContext; -use ir::enum_ty::Enum; +use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Function, FunctionSig}; use ir::int::IntKind; use ir::item::{Item, ItemCanonicalName, ItemCanonicalPath, ItemId}; @@ -1224,6 +1224,7 @@ impl MethodCodegen for Method { } } +/// A helper type to construct enums, either bitfield ones or rust-style ones. enum EnumBuilder<'a> { Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>), Bitfield { @@ -1233,6 +1234,8 @@ enum EnumBuilder<'a> { } impl<'a> EnumBuilder<'a> { + /// Create a new enum given an item builder, a canonical name, a name for + /// the representation, and whether it should be represented as a rust enum. fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>, name: &'a str, repr_name: &str, @@ -1253,29 +1256,42 @@ impl<'a> EnumBuilder<'a> { } } - fn with_variant_(self, - ctx: &BindgenContext, - name: &str, - expr: P<ast::Expr>, - rust_ty: P<ast::Ty>, - result: &mut CodegenResult) - -> Self { + /// Add a variant to this enum. + fn with_variant(self, + ctx: &BindgenContext, + variant: &EnumVariant, + mangling_prefix: Option<&String>, + rust_ty: P<ast::Ty>, + result: &mut CodegenResult) + -> Self { + let variant_name = ctx.rust_mangle(variant.name()); + let expr = aster::AstBuilder::new().expr(); + let expr = match variant.val() { + EnumVariantValue::Signed(v) => expr.int(v), + EnumVariantValue::Unsigned(v) => expr.uint(v), + }; + match self { EnumBuilder::Rust(b) => { EnumBuilder::Rust(b.with_variant_(ast::Variant_ { - name: ctx.rust_ident(name), + name: ctx.rust_ident(&*variant_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_name = match mangling_prefix { + Some(prefix) => { + Cow::Owned(format!("{}_{}", prefix, variant_name)) + } + None => variant_name, + }; + let constant = aster::AstBuilder::new() .item() - .const_(constant_name) + .pub_() + .const_(&*constant_name) .expr() .call() .id(canonical_name) @@ -1289,10 +1305,32 @@ impl<'a> EnumBuilder<'a> { } } - fn build(self) -> P<ast::Item> { + fn build(self, + ctx: &BindgenContext, + rust_ty: P<ast::Ty>, + result: &mut CodegenResult) + -> P<ast::Item> { match self { EnumBuilder::Rust(b) => b.build(), - EnumBuilder::Bitfield { aster, .. } => aster, + EnumBuilder::Bitfield { canonical_name, aster } => { + let rust_ty_name = ctx.rust_ident_raw(canonical_name); + let prefix = ctx.trait_prefix(); + + let impl_ = quote_item!(ctx.ext_cx(), + impl ::$prefix::ops::BitOr<$rust_ty> for $rust_ty { + type Output = Self; + + #[inline] + fn bitor(self, other: Self) -> Self { + $rust_ty_name(self.0 | other.0) + } + } + ) + .unwrap(); + + result.push(impl_); + aster + } } } } @@ -1304,8 +1342,6 @@ impl CodeGenerator for Enum { ctx: &BindgenContext, result: &mut CodegenResult, item: &Item) { - use ir::enum_ty::EnumVariantValue; - let name = item.canonical_name(ctx); let enum_ty = item.expect_type(); let layout = enum_ty.layout(ctx); @@ -1404,15 +1440,27 @@ impl CodeGenerator for Enum { result.push(constant); } - // 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_rust_ty = item.to_rust_ty(ctx); + let is_toplevel = item.is_toplevel(ctx); + + // Used to mangle the constants we generate in the unnamed-enum case. + let parent_canonical_name = if is_toplevel { + None + } else { + Some(item.parent_id().canonical_name(ctx)) + }; + + let constant_mangling_prefix = if enum_ty.name().is_none() { + parent_canonical_name.as_ref().map(|n| &*n) + } else { + Some(&name) + }; + for variant in self.variants().iter() { match seen_values.entry(variant.val()) { Entry::Occupied(ref entry) => { @@ -1426,46 +1474,30 @@ impl CodeGenerator for Enum { 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); + builder = builder.with_variant(ctx, + variant, + constant_mangling_prefix, + enum_rust_ty.clone(), + result); } } Entry::Vacant(entry) => { - let expr = aster::AstBuilder::new().expr(); - let expr = match variant.val() { - EnumVariantValue::Signed(val) => expr.int(val), - EnumVariantValue::Unsigned(val) => expr.uint(val), - }; + builder = builder.with_variant(ctx, + variant, + constant_mangling_prefix, + enum_rust_ty.clone(), + result); + let variant_name = ctx.rust_mangle(variant.name()); - 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 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) { + let mangled_name = if is_toplevel { variant_name.clone() } else { - if parent_canonical_name.is_none() { - parent_canonical_name = Some(item.parent_id() - .canonical_name(ctx)); - } - let parent_name = parent_canonical_name.as_ref() .unwrap(); @@ -1486,7 +1518,8 @@ impl CodeGenerator for Enum { } } - result.push(builder.build()); + let enum_ = builder.build(ctx, enum_rust_ty, result); + result.push(enum_); } } @@ -169,7 +169,7 @@ impl Builder { /// 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`. + /// This makes bindgen generate a type 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 diff --git a/tests/expectations/tests/bitfield-enum-basic.rs b/tests/expectations/tests/bitfield-enum-basic.rs index c287d9eb..03e07de6 100644 --- a/tests/expectations/tests/bitfield-enum-basic.rs +++ b/tests/expectations/tests/bitfield-enum-basic.rs @@ -4,17 +4,74 @@ #![allow(non_snake_case)] -const Foo_Bar: Foo = Foo(2); -const Foo_Baz: Foo = Foo(4); -const Foo_Duplicated: Foo = Foo(4); -const Foo_Negative: Foo = Foo(-3); +pub const Foo_Bar: Foo = Foo(2); +pub const Foo_Baz: Foo = Foo(4); +pub const Foo_Duplicated: Foo = Foo(4); +pub const Foo_Negative: Foo = Foo(-3); +impl ::std::ops::BitOr<Foo> for Foo { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { Foo(self.0 | other.0) } +} #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Foo(pub i32); -const Buz_Bar: Buz = Buz(2); -const Buz_Baz: Buz = Buz(4); -const Buz_Duplicated: Buz = Buz(4); -const Buz_Negative: Buz = Buz(-3); +pub const Buz_Bar: Buz = Buz(2); +pub const Buz_Baz: Buz = Buz(4); +pub const Buz_Duplicated: Buz = Buz(4); +pub const Buz_Negative: Buz = Buz(-3); +impl ::std::ops::BitOr<Buz> for Buz { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { Buz(self.0 | other.0) } +} #[repr(C)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Buz(pub i8); +pub const NS_FOO: _bindgen_ty_1 = _bindgen_ty_1(1); +pub const NS_BAR: _bindgen_ty_1 = _bindgen_ty_1(2); +impl ::std::ops::BitOr<_bindgen_ty_1> for _bindgen_ty_1 { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { _bindgen_ty_1(self.0 | other.0) } +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct _bindgen_ty_1(pub u32); +#[repr(C)] +#[derive(Debug, Copy)] +pub struct Dummy { + pub _address: u8, +} +pub const Dummy_DUMMY_FOO: Dummy__bindgen_ty_1 = Dummy__bindgen_ty_1(1); +pub const Dummy_DUMMY_BAR: Dummy__bindgen_ty_1 = Dummy__bindgen_ty_1(2); +impl ::std::ops::BitOr<Dummy__bindgen_ty_1> for Dummy__bindgen_ty_1 { + type + Output + = + Self; + #[inline] + fn bitor(self, other: Self) -> Self { + Dummy__bindgen_ty_1(self.0 | other.0) + } +} +#[repr(C)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct Dummy__bindgen_ty_1(pub u32); +#[test] +fn bindgen_test_layout_Dummy() { + assert_eq!(::std::mem::size_of::<Dummy>() , 1usize); + assert_eq!(::std::mem::align_of::<Dummy>() , 1usize); +} +impl Clone for Dummy { + fn clone(&self) -> Self { *self } +} diff --git a/tests/headers/bitfield-enum-basic.h b/tests/headers/bitfield-enum-basic.h index 890735e9..364bebf2 100644 --- a/tests/headers/bitfield-enum-basic.h +++ b/tests/headers/bitfield-enum-basic.h @@ -1,4 +1,4 @@ -// bindgen-flags: --bitfield-enum ".*" -- -std=c++11 +// bindgen-flags: --bitfield-enum "Foo|Buz|NS_.*|DUMMY_.*" -- -std=c++11 enum Foo { Bar = 1 << 1, @@ -13,3 +13,15 @@ enum class Buz : signed char { Duplicated = 1 << 2, Negative = -3, }; + +enum { + NS_FOO = 1 << 0, + NS_BAR = 1 << 1, +}; + +class Dummy { + enum { + DUMMY_FOO = 1 << 0, + DUMMY_BAR = 1 << 1, + }; +}; |