Skip to content

Commit 4ed28b0

Browse files
committed
codegen: Respect original repr for bitfield-like enums, add a constifying variant.
1 parent 615130d commit 4ed28b0

File tree

6 files changed

+130
-21
lines changed

6 files changed

+130
-21
lines changed

src/codegen/mod.rs

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,28 +1464,33 @@ enum EnumBuilder<'a> {
14641464
canonical_name: &'a str,
14651465
aster: P<ast::Item>,
14661466
},
1467+
Consts { aster: P<ast::Item>, }
14671468
}
14681469

14691470
impl<'a> EnumBuilder<'a> {
14701471
/// Create a new enum given an item builder, a canonical name, a name for
14711472
/// the representation, and whether it should be represented as a rust enum.
14721473
fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>,
14731474
name: &'a str,
1474-
repr_name: &str,
1475-
is_rust: bool)
1475+
repr: P<ast::Ty>,
1476+
bitfield_like: bool,
1477+
constify: bool)
14761478
-> Self {
1477-
if is_rust {
1478-
EnumBuilder::Rust(aster.enum_(name))
1479-
} else {
1479+
if bitfield_like {
14801480
EnumBuilder::Bitfield {
14811481
canonical_name: name,
14821482
aster: aster.tuple_struct(name)
14831483
.field()
14841484
.pub_()
1485-
.ty()
1486-
.id(repr_name)
1485+
.build_ty(repr)
14871486
.build(),
14881487
}
1488+
} else if constify {
1489+
EnumBuilder::Consts {
1490+
aster: aster.type_(name).build_ty(repr),
1491+
}
1492+
} else {
1493+
EnumBuilder::Rust(aster.enum_(name))
14891494
}
14901495
}
14911496

@@ -1535,6 +1540,25 @@ impl<'a> EnumBuilder<'a> {
15351540
result.push(constant);
15361541
self
15371542
}
1543+
EnumBuilder::Consts { .. } => {
1544+
let constant_name = match mangling_prefix {
1545+
Some(prefix) => {
1546+
Cow::Owned(format!("{}_{}", prefix, variant_name))
1547+
}
1548+
None => variant_name,
1549+
};
1550+
1551+
let constant = aster::AstBuilder::new()
1552+
.item()
1553+
.pub_()
1554+
.const_(&*constant_name)
1555+
.expr()
1556+
.build(expr)
1557+
.build(rust_ty);
1558+
1559+
result.push(constant);
1560+
self
1561+
}
15381562
}
15391563
}
15401564

@@ -1564,6 +1588,7 @@ impl<'a> EnumBuilder<'a> {
15641588
result.push(impl_);
15651589
aster
15661590
}
1591+
EnumBuilder::Consts { aster, .. } => aster,
15671592
}
15681593
}
15691594
}
@@ -1619,6 +1644,8 @@ impl CodeGenerator for Enum {
16191644

16201645
let mut builder = aster::AstBuilder::new().item().pub_();
16211646

1647+
// FIXME(emilio): These should probably use the path so it can
1648+
// disambiguate between namespaces, just like is_opaque etc.
16221649
let is_bitfield = {
16231650
ctx.options().bitfield_enums.matches(&name) ||
16241651
(enum_ty.name().is_none() &&
@@ -1627,30 +1654,42 @@ impl CodeGenerator for Enum {
16271654
.any(|v| ctx.options().bitfield_enums.matches(&v.name())))
16281655
};
16291656

1630-
let is_rust_enum = !is_bitfield;
1657+
let is_constified_enum = {
1658+
ctx.options().constified_enums.matches(&name) ||
1659+
(enum_ty.name().is_none() &&
1660+
self.variants()
1661+
.iter()
1662+
.any(|v| ctx.options().constified_enums.matches(&v.name())))
1663+
};
1664+
1665+
let is_rust_enum = !is_bitfield && !is_constified_enum;
16311666

16321667
// FIXME: Rust forbids repr with empty enums. Remove this condition when
16331668
// this is allowed.
1669+
//
1670+
// TODO(emilio): Delegate this to the builders?
16341671
if is_rust_enum {
16351672
if !self.variants().is_empty() {
16361673
builder = builder.with_attr(attributes::repr(repr_name));
16371674
}
1638-
} else {
1675+
} else if is_bitfield {
16391676
builder = builder.with_attr(attributes::repr("C"));
16401677
}
16411678

16421679
if let Some(comment) = item.comment() {
16431680
builder = builder.with_attr(attributes::doc(comment));
16441681
}
16451682

1646-
let derives = attributes::derives(&["Debug",
1647-
"Copy",
1648-
"Clone",
1649-
"PartialEq",
1650-
"Eq",
1651-
"Hash"]);
1683+
if !is_constified_enum {
1684+
let derives = attributes::derives(&["Debug",
1685+
"Copy",
1686+
"Clone",
1687+
"PartialEq",
1688+
"Eq",
1689+
"Hash"]);
16521690

1653-
builder = builder.with_attr(derives);
1691+
builder = builder.with_attr(derives);
1692+
}
16541693

16551694
fn add_constant<'a>(enum_: &Type,
16561695
// Only to avoid recomputing every time.
@@ -1680,8 +1719,16 @@ impl CodeGenerator for Enum {
16801719
result.push(constant);
16811720
}
16821721

1722+
let repr = self.repr()
1723+
.map(|repr| repr.to_rust_ty(ctx))
1724+
.unwrap_or_else(|| helpers::ast_ty::raw_type(ctx, repr_name));
1725+
16831726
let mut builder =
1684-
EnumBuilder::new(builder, &name, repr_name, is_rust_enum);
1727+
EnumBuilder::new(builder,
1728+
&name,
1729+
repr,
1730+
is_bitfield,
1731+
is_constified_enum);
16851732

16861733
// A map where we keep a value -> variant relation.
16871734
let mut seen_values = HashMap::<_, String>::new();

src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,15 @@ impl Builder {
224224
self
225225
}
226226

227+
/// Mark the given enum (or set of enums, if using a pattern) as being
228+
/// constant.
229+
///
230+
/// This makes bindgen generate constants instead of enums.
231+
pub fn constified_enum<T: AsRef<str>>(mut self, arg: T) -> Builder {
232+
self.options.constified_enums.insert(arg);
233+
self
234+
}
235+
227236
/// Add a string to prepend to the generated bindings. The string is passed
228237
/// through without any modification.
229238
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
@@ -418,6 +427,9 @@ pub struct BindgenOptions {
418427
/// The enum patterns to mark an enum as bitfield.
419428
pub bitfield_enums: RegexSet,
420429

430+
/// The enum patterns to mark an enum as constant.
431+
pub constified_enums: RegexSet,
432+
421433
/// Whether we should generate builtins or not.
422434
pub builtins: bool,
423435

@@ -496,6 +508,7 @@ impl BindgenOptions {
496508
self.hidden_types.build();
497509
self.opaque_types.build();
498510
self.bitfield_enums.build();
511+
self.constified_enums.build();
499512
}
500513
}
501514

@@ -508,6 +521,7 @@ impl Default for BindgenOptions {
508521
whitelisted_functions: Default::default(),
509522
whitelisted_vars: Default::default(),
510523
bitfield_enums: Default::default(),
524+
constified_enums: Default::default(),
511525
builtins: false,
512526
links: vec![],
513527
emit_ast: false,

src/options.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ pub fn builder_from_flags<I>(args: I)
2424
.takes_value(true)
2525
.multiple(true)
2626
.number_of_values(1),
27+
Arg::with_name("constified-enum")
28+
.long("constified-enum")
29+
.help("Mark any enum whose name matches <regex> as a set of \
30+
constants instead of an enumeration.")
31+
.value_name("regex")
32+
.takes_value(true)
33+
.multiple(true)
34+
.number_of_values(1),
2735
Arg::with_name("blacklist-type")
2836
.long("blacklist-type")
2937
.help("Mark a type as hidden.")
@@ -171,6 +179,12 @@ pub fn builder_from_flags<I>(args: I)
171179
}
172180
}
173181

182+
if let Some(bitfields) = matches.values_of("constified-enum") {
183+
for regex in bitfields {
184+
builder = builder.constified_enum(regex);
185+
}
186+
}
187+
174188
if let Some(hidden_types) = matches.values_of("blacklist-type") {
175189
for ty in hidden_types {
176190
builder = builder.hide_type(ty);

tests/expectations/tests/bitfield-enum-basic.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ impl ::std::ops::BitOr<Foo> for Foo {
1818
}
1919
#[repr(C)]
2020
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
21-
pub struct Foo(pub i32);
21+
pub struct Foo(pub ::std::os::raw::c_int);
2222
pub const Buz_Bar: Buz = Buz(2);
2323
pub const Buz_Baz: Buz = Buz(4);
2424
pub const Buz_Duplicated: Buz = Buz(4);
@@ -33,7 +33,7 @@ impl ::std::ops::BitOr<Buz> for Buz {
3333
}
3434
#[repr(C)]
3535
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
36-
pub struct Buz(pub i8);
36+
pub struct Buz(pub ::std::os::raw::c_char);
3737
pub const NS_FOO: _bindgen_ty_1 = _bindgen_ty_1(1);
3838
pub const NS_BAR: _bindgen_ty_1 = _bindgen_ty_1(2);
3939
impl ::std::ops::BitOr<_bindgen_ty_1> for _bindgen_ty_1 {
@@ -46,7 +46,7 @@ impl ::std::ops::BitOr<_bindgen_ty_1> for _bindgen_ty_1 {
4646
}
4747
#[repr(C)]
4848
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
49-
pub struct _bindgen_ty_1(pub u32);
49+
pub struct _bindgen_ty_1(pub ::std::os::raw::c_uint);
5050
#[repr(C)]
5151
#[derive(Debug, Copy)]
5252
pub struct Dummy {
@@ -66,7 +66,7 @@ impl ::std::ops::BitOr<Dummy__bindgen_ty_1> for Dummy__bindgen_ty_1 {
6666
}
6767
#[repr(C)]
6868
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
69-
pub struct Dummy__bindgen_ty_1(pub u32);
69+
pub struct Dummy__bindgen_ty_1(pub ::std::os::raw::c_uint);
7070
#[test]
7171
fn bindgen_test_layout_Dummy() {
7272
assert_eq!(::std::mem::size_of::<Dummy>() , 1usize);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
7+
pub const foo_THIS: foo = 0;
8+
pub const foo_SHOULD_BE: foo = 1;
9+
pub const foo_A_CONSTANT: foo = 2;
10+
pub type foo = ::std::os::raw::c_uint;
11+
#[repr(C)]
12+
#[derive(Debug, Copy)]
13+
pub struct bar {
14+
pub this_should_work: foo,
15+
}
16+
#[test]
17+
fn bindgen_test_layout_bar() {
18+
assert_eq!(::std::mem::size_of::<bar>() , 4usize);
19+
assert_eq!(::std::mem::align_of::<bar>() , 4usize);
20+
}
21+
impl Clone for bar {
22+
fn clone(&self) -> Self { *self }
23+
}

tests/headers/constify-all-enums.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// bindgen-flags: --constified-enum foo
2+
3+
enum foo {
4+
THIS,
5+
SHOULD_BE,
6+
A_CONSTANT,
7+
};
8+
9+
struct bar {
10+
enum foo this_should_work;
11+
};

0 commit comments

Comments
 (0)