Skip to content

Commit c9eccea

Browse files
author
bors-servo
authored
Auto merge of #226 - emilio:bitfields, r=fitzgen
Generate and add options for bitfield-like enums. r? @fitzgen or @Manishearth cc @bholley @upsuper @heycam Followups: * Reduce duplication a bit in that code (probably E-easy) * Generate an impl of `BitOr` for bitfield-like enums (E-easy/E-less-easy).
2 parents 7fe40e0 + 4062713 commit c9eccea

File tree

6 files changed

+308
-48
lines changed

6 files changed

+308
-48
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 any enum whose name matches <regex> as a
96+
set of bitfield flags instead of an
97+
enumeration.
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/clang.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,9 @@ impl Cursor {
102102
/// lexical parents.
103103
pub fn fallible_semantic_parent(&self) -> Option<Cursor> {
104104
let sp = unsafe {
105-
Cursor { x: clang_getCursorSemanticParent(self.x) }
105+
Cursor {
106+
x: clang_getCursorSemanticParent(self.x),
107+
}
106108
};
107109
if sp == *self || !sp.is_valid() {
108110
return None;
@@ -161,7 +163,8 @@ impl Cursor {
161163
(semantic_parent.unwrap().kind() == CXCursor_Namespace ||
162164
semantic_parent.unwrap().kind() == CXCursor_NamespaceAlias ||
163165
semantic_parent.unwrap().kind() == CXCursor_NamespaceRef) {
164-
semantic_parent = semantic_parent.unwrap().fallible_semantic_parent();
166+
semantic_parent = semantic_parent.unwrap()
167+
.fallible_semantic_parent();
165168
}
166169

167170
let tu = self.translation_unit();
@@ -363,9 +366,9 @@ impl Cursor {
363366
///
364367
/// Returns None if the cursor's referent is not an enum variant.
365368
pub fn enum_val_unsigned(&self) -> Option<u64> {
366-
unsafe {
369+
unsafe {
367370
if self.kind() == CXCursor_EnumConstantDecl {
368-
Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64)
371+
Some(clang_getEnumConstantDeclUnsignedValue(self.x) as u64)
369372
} else {
370373
None
371374
}
@@ -691,11 +694,7 @@ impl Type {
691694
let rt = Type {
692695
x: unsafe { clang_getResultType(self.x) },
693696
};
694-
if rt.is_valid() {
695-
Some(rt)
696-
} else {
697-
None
698-
}
697+
if rt.is_valid() { Some(rt) } else { None }
699698
}
700699

701700
/// Given that this type is a function type, get its calling convention. If

src/codegen/mod.rs

Lines changed: 173 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use aster;
66
use ir::annotations::FieldAccessorKind;
77
use ir::comp::{CompInfo, CompKind, Field, Method};
88
use ir::context::BindgenContext;
9-
use ir::enum_ty::Enum;
9+
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
1010
use ir::function::{Function, FunctionSig};
1111
use ir::int::IntKind;
1212
use ir::item::{Item, ItemCanonicalName, ItemCanonicalPath, ItemId};
@@ -1224,17 +1224,127 @@ impl MethodCodegen for Method {
12241224
}
12251225
}
12261226

1227+
/// A helper type to construct enums, either bitfield ones or rust-style ones.
1228+
enum EnumBuilder<'a> {
1229+
Rust(aster::item::ItemEnumBuilder<aster::invoke::Identity>),
1230+
Bitfield {
1231+
canonical_name: &'a str,
1232+
aster: P<ast::Item>,
1233+
},
1234+
}
1235+
1236+
impl<'a> EnumBuilder<'a> {
1237+
/// Create a new enum given an item builder, a canonical name, a name for
1238+
/// the representation, and whether it should be represented as a rust enum.
1239+
fn new(aster: aster::item::ItemBuilder<aster::invoke::Identity>,
1240+
name: &'a str,
1241+
repr_name: &str,
1242+
is_rust: bool)
1243+
-> Self {
1244+
if is_rust {
1245+
EnumBuilder::Rust(aster.enum_(name))
1246+
} else {
1247+
EnumBuilder::Bitfield {
1248+
canonical_name: name,
1249+
aster: aster.tuple_struct(name)
1250+
.field()
1251+
.pub_()
1252+
.ty()
1253+
.id(repr_name)
1254+
.build(),
1255+
}
1256+
}
1257+
}
1258+
1259+
/// Add a variant to this enum.
1260+
fn with_variant(self,
1261+
ctx: &BindgenContext,
1262+
variant: &EnumVariant,
1263+
mangling_prefix: Option<&String>,
1264+
rust_ty: P<ast::Ty>,
1265+
result: &mut CodegenResult)
1266+
-> Self {
1267+
let variant_name = ctx.rust_mangle(variant.name());
1268+
let expr = aster::AstBuilder::new().expr();
1269+
let expr = match variant.val() {
1270+
EnumVariantValue::Signed(v) => expr.int(v),
1271+
EnumVariantValue::Unsigned(v) => expr.uint(v),
1272+
};
1273+
1274+
match self {
1275+
EnumBuilder::Rust(b) => {
1276+
EnumBuilder::Rust(b.with_variant_(ast::Variant_ {
1277+
name: ctx.rust_ident(&*variant_name),
1278+
attrs: vec![],
1279+
data: ast::VariantData::Unit(ast::DUMMY_NODE_ID),
1280+
disr_expr: Some(expr),
1281+
}))
1282+
}
1283+
EnumBuilder::Bitfield { canonical_name, .. } => {
1284+
let constant_name = match mangling_prefix {
1285+
Some(prefix) => {
1286+
Cow::Owned(format!("{}_{}", prefix, variant_name))
1287+
}
1288+
None => variant_name,
1289+
};
1290+
1291+
let constant = aster::AstBuilder::new()
1292+
.item()
1293+
.pub_()
1294+
.const_(&*constant_name)
1295+
.expr()
1296+
.call()
1297+
.id(canonical_name)
1298+
.arg()
1299+
.build(expr)
1300+
.build()
1301+
.build(rust_ty);
1302+
result.push(constant);
1303+
self
1304+
}
1305+
}
1306+
}
1307+
1308+
fn build(self,
1309+
ctx: &BindgenContext,
1310+
rust_ty: P<ast::Ty>,
1311+
result: &mut CodegenResult)
1312+
-> P<ast::Item> {
1313+
match self {
1314+
EnumBuilder::Rust(b) => b.build(),
1315+
EnumBuilder::Bitfield { canonical_name, aster } => {
1316+
let rust_ty_name = ctx.rust_ident_raw(canonical_name);
1317+
let prefix = ctx.trait_prefix();
1318+
1319+
let impl_ = quote_item!(ctx.ext_cx(),
1320+
impl ::$prefix::ops::BitOr<$rust_ty> for $rust_ty {
1321+
type Output = Self;
1322+
1323+
#[inline]
1324+
fn bitor(self, other: Self) -> Self {
1325+
$rust_ty_name(self.0 | other.0)
1326+
}
1327+
}
1328+
)
1329+
.unwrap();
1330+
1331+
result.push(impl_);
1332+
aster
1333+
}
1334+
}
1335+
}
1336+
}
1337+
12271338
impl CodeGenerator for Enum {
12281339
type Extra = Item;
12291340

12301341
fn codegen(&self,
12311342
ctx: &BindgenContext,
12321343
result: &mut CodegenResult,
12331344
item: &Item) {
1234-
use ir::enum_ty::EnumVariantValue;
1235-
12361345
let name = item.canonical_name(ctx);
1237-
let layout = item.expect_type().layout(ctx);
1346+
let enum_ty = item.expect_type();
1347+
let layout = enum_ty.layout(ctx);
12381348

12391349
let repr = self.repr().map(|repr| ctx.resolve_type(repr));
12401350
let repr = match repr {
@@ -1270,10 +1380,24 @@ impl CodeGenerator for Enum {
12701380

12711381
let mut builder = aster::AstBuilder::new().item().pub_();
12721382

1383+
let is_bitfield = {
1384+
ctx.options().bitfield_enums.matches(&name) ||
1385+
(enum_ty.name().is_none() &&
1386+
self.variants()
1387+
.iter()
1388+
.any(|v| ctx.options().bitfield_enums.matches(&v.name())))
1389+
};
1390+
1391+
let is_rust_enum = !is_bitfield;
1392+
12731393
// FIXME: Rust forbids repr with empty enums. Remove this condition when
12741394
// this is allowed.
1275-
if !self.variants().is_empty() {
1276-
builder = builder.with_attr(attributes::repr(repr_name));
1395+
if is_rust_enum {
1396+
if !self.variants().is_empty() {
1397+
builder = builder.with_attr(attributes::repr(repr_name));
1398+
}
1399+
} else {
1400+
builder = builder.with_attr(attributes::repr("C"));
12771401
}
12781402

12791403
if let Some(comment) = item.comment() {
@@ -1289,8 +1413,6 @@ impl CodeGenerator for Enum {
12891413

12901414
builder = builder.with_attr(derives);
12911415

1292-
let mut builder = builder.enum_(&name);
1293-
12941416
fn add_constant(enum_: &Type,
12951417
// Only to avoid recomputing every time.
12961418
enum_canonical_name: &str,
@@ -1318,52 +1440,64 @@ impl CodeGenerator for Enum {
13181440
result.push(constant);
13191441
}
13201442

1321-
// Used to mangle the constants we generate in the unnamed-enum case.
1322-
let mut parent_canonical_name = None;
1443+
let mut builder =
1444+
EnumBuilder::new(builder, &name, repr_name, is_rust_enum);
13231445

13241446
// A map where we keep a value -> variant relation.
13251447
let mut seen_values = HashMap::<_, String>::new();
1326-
let enum_ty = item.expect_type();
13271448
let enum_rust_ty = item.to_rust_ty(ctx);
1449+
let is_toplevel = item.is_toplevel(ctx);
1450+
1451+
// Used to mangle the constants we generate in the unnamed-enum case.
1452+
let parent_canonical_name = if is_toplevel {
1453+
None
1454+
} else {
1455+
Some(item.parent_id().canonical_name(ctx))
1456+
};
1457+
1458+
let constant_mangling_prefix = if enum_ty.name().is_none() {
1459+
parent_canonical_name.as_ref().map(|n| &*n)
1460+
} else {
1461+
Some(&name)
1462+
};
1463+
13281464
for variant in self.variants().iter() {
13291465
match seen_values.entry(variant.val()) {
13301466
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);
1467+
if is_rust_enum {
1468+
let existing_variant_name = entry.get();
1469+
let variant_name = ctx.rust_mangle(variant.name());
1470+
add_constant(enum_ty,
1471+
&name,
1472+
&*variant_name,
1473+
existing_variant_name,
1474+
enum_rust_ty.clone(),
1475+
result);
1476+
} else {
1477+
builder = builder.with_variant(ctx,
1478+
variant,
1479+
constant_mangling_prefix,
1480+
enum_rust_ty.clone(),
1481+
result);
1482+
}
13391483
}
13401484
Entry::Vacant(entry) => {
1341-
let expr = aster::AstBuilder::new().expr();
1342-
let expr = match variant.val() {
1343-
EnumVariantValue::Signed(val) => expr.int(val),
1344-
EnumVariantValue::Unsigned(val) => expr.uint(val),
1345-
};
1485+
builder = builder.with_variant(ctx,
1486+
variant,
1487+
constant_mangling_prefix,
1488+
enum_rust_ty.clone(),
1489+
result);
1490+
13461491
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-
});
13531492

13541493
// If it's an unnamed enum, we also generate a constant so
13551494
// it can be properly accessed.
1356-
if enum_ty.name().is_none() {
1495+
if is_rust_enum && enum_ty.name().is_none() {
13571496
// NB: if we want to do this for other kind of nested
13581497
// enums we can probably mangle the name.
1359-
let mangled_name = if item.is_toplevel(ctx) {
1498+
let mangled_name = if is_toplevel {
13601499
variant_name.clone()
13611500
} else {
1362-
if parent_canonical_name.is_none() {
1363-
parent_canonical_name = Some(item.parent_id()
1364-
.canonical_name(ctx));
1365-
}
1366-
13671501
let parent_name = parent_canonical_name.as_ref()
13681502
.unwrap();
13691503

@@ -1384,8 +1518,8 @@ impl CodeGenerator for Enum {
13841518
}
13851519
}
13861520

1387-
1388-
result.push(builder.build());
1521+
let enum_ = builder.build(ctx, enum_rust_ty, result);
1522+
result.push(enum_);
13891523
}
13901524
}
13911525

src/lib.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ impl Builder {
166166
self
167167
}
168168

169+
/// Mark the given enum (or set of enums, if using a pattern) as being
170+
/// bitfield-like.
171+
///
172+
/// This makes bindgen generate a type that isn't a rust `enum`.
173+
pub fn bitfield_enum<T: Borrow<str>>(mut self, arg: T) -> Builder {
174+
self.options.bitfield_enums.insert(&arg);
175+
self
176+
}
177+
169178
/// Add a string to prepend to the generated bindings. The string is passed
170179
/// through without any modification.
171180
pub fn raw_line<T: Into<String>>(mut self, arg: T) -> Builder {
@@ -262,6 +271,9 @@ pub struct BindgenOptions {
262271
/// Whitelisted variables. See docs for `whitelisted_types` for more.
263272
pub whitelisted_vars: RegexSet,
264273

274+
/// The enum patterns to mark an enum as bitfield.
275+
pub bitfield_enums: RegexSet,
276+
265277
/// Whether we should generate builtins or not.
266278
pub builtins: bool,
267279

@@ -329,6 +341,7 @@ impl Default for BindgenOptions {
329341
whitelisted_types: Default::default(),
330342
whitelisted_functions: Default::default(),
331343
whitelisted_vars: Default::default(),
344+
bitfield_enums: Default::default(),
332345
builtins: false,
333346
links: vec![],
334347
emit_ast: false,

0 commit comments

Comments
 (0)