Skip to content

Commit 202325f

Browse files
committed
Add an option to generate bitfield-like typesafe enums.
1 parent 8fa42ef commit 202325f

File tree

3 files changed

+146
-22
lines changed

3 files changed

+146
-22
lines changed

src/bin/bindgen.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Usage:
2828
[--whitelist-type=<type>...] \
2929
[--whitelist-function=<name>...] \
3030
[--whitelist-var=<name>...] \
31+
[--bitfield-enum=<name>...] \
3132
<input-header> \
3233
[-- <clang-args>...]
3334
@@ -91,6 +92,10 @@ Options:
9192
matching <regex>. Same behavior on emptyness
9293
than the type whitelisting.
9394
95+
--bitfield-enum=<regex> Mark a bitfield as being used as an enum.
96+
This makes bindgen don't generate a rust enum
97+
for it, but a bitfield-like wrapper.
98+
9499
--dummy-uses=<path> For testing purposes, generate a C/C++ file
95100
containing dummy uses of all types defined in
96101
the input header.
@@ -163,6 +168,11 @@ fn parse_args_or_exit(args: Vec<String>) -> (BindgenOptions, Box<io::Write>) {
163168
.expect("--whitelist-var expects a pattern");
164169
options.whitelisted_vars.insert(&var_pat);
165170
}
171+
"--bitfield-enum" => {
172+
let enum_pat = iter.next()
173+
.expect("--bitfield-enum expects a pattern");
174+
options.bitfield_enums.insert(&enum_pat);
175+
}
166176
"--" => {
167177
while let Some(clang_arg) = iter.next() {
168178
options.clang_args.push(clang_arg);

src/codegen/mod.rs

Lines changed: 123 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,6 +1224,79 @@ impl MethodCodegen for Method {
12241224
}
12251225
}
12261226

1227+
enum EnumBuilder<'a> {
1228+
Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>),
1229+
Bitfield {
1230+
canonical_name: &'a str,
1231+
aster: P<ast::Item>,
1232+
},
1233+
}
1234+
1235+
impl<'a> EnumBuilder<'a> {
1236+
fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>,
1237+
name: &'a str,
1238+
repr_name: &str,
1239+
is_rust: bool)
1240+
-> Self {
1241+
if is_rust {
1242+
EnumBuilder::Rust(aster.enum_(name))
1243+
} else {
1244+
EnumBuilder::Bitfield {
1245+
canonical_name: name,
1246+
aster: aster.tuple_struct(name)
1247+
.field()
1248+
.pub_()
1249+
.ty()
1250+
.id(repr_name)
1251+
.build(),
1252+
}
1253+
}
1254+
}
1255+
1256+
fn with_variant_(self,
1257+
ctx: &BindgenContext,
1258+
name: &str,
1259+
expr: P<ast::Expr>,
1260+
rust_ty: P<ast::Ty>,
1261+
result: &mut CodegenResult)
1262+
-> Self {
1263+
match self {
1264+
EnumBuilder::Rust(b) => {
1265+
EnumBuilder::Rust(b.with_variant_(ast::Variant_ {
1266+
name: ctx.rust_ident(name),
1267+
attrs: vec![],
1268+
data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
1269+
disr_expr: Some(expr),
1270+
}))
1271+
}
1272+
EnumBuilder::Bitfield { canonical_name, .. } => {
1273+
// FIXME: Probably if unnamed we should be smarter! Easy to
1274+
// improve.
1275+
let constant_name = format!("{}_{}", canonical_name, name);
1276+
let constant = aster::AstBuilder::new()
1277+
.item()
1278+
.const_(constant_name)
1279+
.expr()
1280+
.call()
1281+
.id(canonical_name)
1282+
.arg()
1283+
.build(expr)
1284+
.build()
1285+
.build(rust_ty);
1286+
result.push(constant);
1287+
self
1288+
}
1289+
}
1290+
}
1291+
1292+
fn build(self) -> P<ast::Item> {
1293+
match self {
1294+
EnumBuilder::Rust(b) => b.build(),
1295+
EnumBuilder::Bitfield { aster, .. } => aster,
1296+
}
1297+
}
1298+
}
1299+
12271300
impl CodeGenerator for Enum {
12281301
type Extra = Item;
12291302

@@ -1234,7 +1307,8 @@ impl CodeGenerator for Enum {
12341307
use ir::enum_ty::EnumVariantValue;
12351308

12361309
let name = item.canonical_name(ctx);
1237-
let layout = item.expect_type().layout(ctx);
1310+
let enum_ty = item.expect_type();
1311+
let layout = enum_ty.layout(ctx);
12381312

12391313
let repr = self.repr().map(|repr| ctx.resolve_type(repr));
12401314
let repr = match repr {
@@ -1270,10 +1344,24 @@ impl CodeGenerator for Enum {
12701344

12711345
let mut builder = aster::AstBuilder::new().item().pub_();
12721346

1347+
let is_bitfield = {
1348+
ctx.options().bitfield_enums.matches(&name) ||
1349+
(enum_ty.name().is_none() &&
1350+
self.variants()
1351+
.iter()
1352+
.any(|v| ctx.options().bitfield_enums.matches(&v.name())))
1353+
};
1354+
1355+
let is_rust_enum = !is_bitfield;
1356+
12731357
// FIXME: Rust forbids repr with empty enums. Remove this condition when
12741358
// this is allowed.
1275-
if !self.variants().is_empty() {
1276-
builder = builder.with_attr(attributes::repr(repr_name));
1359+
if is_rust_enum {
1360+
if !self.variants().is_empty() {
1361+
builder = builder.with_attr(attributes::repr(repr_name));
1362+
}
1363+
} else {
1364+
builder = builder.with_attr(attributes::repr("C"));
12771365
}
12781366

12791367
if let Some(comment) = item.comment() {
@@ -1289,8 +1377,6 @@ impl CodeGenerator for Enum {
12891377

12901378
builder = builder.with_attr(derives);
12911379

1292-
let mut builder = builder.enum_(&name);
1293-
12941380
fn add_constant(enum_: &Type,
12951381
// Only to avoid recomputing every time.
12961382
enum_canonical_name: &str,
@@ -1321,21 +1407,38 @@ impl CodeGenerator for Enum {
13211407
// Used to mangle the constants we generate in the unnamed-enum case.
13221408
let mut parent_canonical_name = None;
13231409

1410+
let mut builder =
1411+
EnumBuilder::new(builder, &name, repr_name, is_rust_enum);
1412+
13241413
// A map where we keep a value -> variant relation.
13251414
let mut seen_values = HashMap::<_, String>::new();
1326-
let enum_ty = item.expect_type();
13271415
let enum_rust_ty = item.to_rust_ty(ctx);
13281416
for variant in self.variants().iter() {
13291417
match seen_values.entry(variant.val()) {
13301418
Entry::Occupied(ref entry) => {
1331-
let existing_variant_name = entry.get();
1332-
let variant_name = ctx.rust_mangle(variant.name());
1333-
add_constant(enum_ty,
1334-
&name,
1335-
&*variant_name,
1336-
existing_variant_name,
1337-
enum_rust_ty.clone(),
1338-
result);
1419+
if is_rust_enum {
1420+
let existing_variant_name = entry.get();
1421+
let variant_name = ctx.rust_mangle(variant.name());
1422+
add_constant(enum_ty,
1423+
&name,
1424+
&*variant_name,
1425+
existing_variant_name,
1426+
enum_rust_ty.clone(),
1427+
result);
1428+
} else {
1429+
// FIXME: Don't repeat this block below.
1430+
let expr = aster::AstBuilder::new().expr();
1431+
let expr = match variant.val() {
1432+
EnumVariantValue::Signed(val) => expr.int(val),
1433+
EnumVariantValue::Unsigned(val) => expr.uint(val),
1434+
};
1435+
let variant_name = ctx.rust_mangle(variant.name());
1436+
builder = builder.with_variant_(ctx,
1437+
&*variant_name,
1438+
expr,
1439+
enum_rust_ty.clone(),
1440+
result);
1441+
}
13391442
}
13401443
Entry::Vacant(entry) => {
13411444
let expr = aster::AstBuilder::new().expr();
@@ -1344,16 +1447,15 @@ impl CodeGenerator for Enum {
13441447
EnumVariantValue::Unsigned(val) => expr.uint(val),
13451448
};
13461449
let variant_name = ctx.rust_mangle(variant.name());
1347-
builder = builder.with_variant_(ast::Variant_ {
1348-
name: ctx.rust_ident(&*variant_name),
1349-
attrs: vec![],
1350-
data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
1351-
disr_expr: Some(expr),
1352-
});
1450+
builder = builder.with_variant_(ctx,
1451+
&*variant_name,
1452+
expr,
1453+
enum_rust_ty.clone(),
1454+
result);
13531455

13541456
// If it's an unnamed enum, we also generate a constant so
13551457
// it can be properly accessed.
1356-
if enum_ty.name().is_none() {
1458+
if is_rust_enum && enum_ty.name().is_none() {
13571459
// NB: if we want to do this for other kind of nested
13581460
// enums we can probably mangle the name.
13591461
let mangled_name = if item.is_toplevel(ctx) {
@@ -1384,7 +1486,6 @@ impl CodeGenerator for Enum {
13841486
}
13851487
}
13861488

1387-
13881489
result.push(builder.build());
13891490
}
13901491
}

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ impl Builder {
169169
self
170170
}
171171

172+
/// Mark the given enum (or set of enums, if using a pattern) as being
173+
/// bitfield-like.
174+
///
175+
/// This makes bindgen to generate a type for it that isn't a rust `enum`.
176+
pub fn bitfield_enum<T: Borrow<str>>(mut self, arg: T) -> Builder {
177+
self.options.bitfield_enums.insert(&arg);
178+
self
179+
}
180+
172181
/// Add a string to prepend to the generated bindings. The string is passed
173182
/// through without any modification.
174183
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
@@ -272,6 +281,9 @@ pub struct BindgenOptions {
272281
/// Whitelisted variables. See docs for `whitelisted_types` for more.
273282
pub whitelisted_vars: RegexSet,
274283

284+
/// The enum patterns to mark an enum as bitfield.
285+
pub bitfield_enums: RegexSet,
286+
275287
/// Whether we should generate builtins or not.
276288
pub builtins: bool,
277289

@@ -343,6 +355,7 @@ impl Default for BindgenOptions {
343355
whitelisted_types: Default::default(),
344356
whitelisted_functions: Default::default(),
345357
whitelisted_vars: Default::default(),
358+
bitfield_enums: Default::default(),
346359
builtins: false,
347360
links: vec![],
348361
emit_ast: false,

0 commit comments

Comments
 (0)