summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJethro Beekman <jethro@fortanix.com>2021-03-10 13:25:53 +0100
committerEmilio Cobos Álvarez <emilio@crisal.io>2021-03-14 09:37:26 +0100
commitdedbea5bc0317be4e7fd47a49392a6b080f47ac8 (patch)
tree5c1f4175310fb2d5b4c3524b8e32e05aeb760a83
parentfb931bd6c1a91908fa34dab7bc76059f33131663 (diff)
Add option to translate enum integer types to native Rust integer types
Fixes #430
-rw-r--r--src/codegen/mod.rs102
-rw-r--r--src/lib.rs18
-rw-r--r--src/options.rs7
-rw-r--r--tests/expectations/tests/enum-translate-type.rs15
-rw-r--r--tests/headers/enum-translate-type.hpp14
5 files changed, 114 insertions, 42 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 62271acf..1fba8316 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -2524,7 +2524,7 @@ impl<'a> EnumBuilder<'a> {
/// the representation, and which variation it should be generated as.
fn new(
name: &'a str,
- attrs: Vec<proc_macro2::TokenStream>,
+ mut attrs: Vec<proc_macro2::TokenStream>,
repr: proc_macro2::TokenStream,
enum_variation: EnumVariation,
enum_codegen_depth: usize,
@@ -2543,6 +2543,8 @@ impl<'a> EnumBuilder<'a> {
},
EnumVariation::Rust { .. } => {
+ // `repr` is guaranteed to be Rustified in Enum::codegen
+ attrs.insert(0, quote! { #[repr( #repr )] });
let tokens = quote!();
EnumBuilder::Rust {
codegen_depth: enum_codegen_depth + 1,
@@ -2820,51 +2822,73 @@ impl CodeGenerator for Enum {
let ident = ctx.rust_ident(&name);
let enum_ty = item.expect_type();
let layout = enum_ty.layout(ctx);
+ let variation = self.computed_enum_variation(ctx, item);
- let repr = self.repr().map(|repr| ctx.resolve_type(repr));
- let repr = match repr {
- Some(repr) => match *repr.canonical_type(ctx).kind() {
- TypeKind::Int(int_kind) => int_kind,
- _ => panic!("Unexpected type as enum repr"),
- },
- None => {
- warn!(
- "Guessing type of enum! Forward declarations of enums \
- shouldn't be legal!"
- );
- IntKind::Int
+ let repr_translated;
+ let repr = match self.repr().map(|repr| ctx.resolve_type(repr)) {
+ Some(repr)
+ if !ctx.options().translate_enum_integer_types &&
+ !variation.is_rust() =>
+ {
+ repr
}
- };
+ repr => {
+ // An enum's integer type is translated to a native Rust
+ // integer type in 3 cases:
+ // * the enum is Rustified and we need a translated type for
+ // the repr attribute
+ // * the representation couldn't be determined from the C source
+ // * it was explicitly requested as a bindgen option
+
+ let kind = match repr {
+ Some(repr) => match *repr.canonical_type(ctx).kind() {
+ TypeKind::Int(int_kind) => int_kind,
+ _ => panic!("Unexpected type as enum repr"),
+ },
+ None => {
+ warn!(
+ "Guessing type of enum! Forward declarations of enums \
+ shouldn't be legal!"
+ );
+ IntKind::Int
+ }
+ };
+
+ let signed = kind.is_signed();
+ let size = layout
+ .map(|l| l.size)
+ .or_else(|| kind.known_size())
+ .unwrap_or(0);
+
+ let translated = match (signed, size) {
+ (true, 1) => IntKind::I8,
+ (false, 1) => IntKind::U8,
+ (true, 2) => IntKind::I16,
+ (false, 2) => IntKind::U16,
+ (true, 4) => IntKind::I32,
+ (false, 4) => IntKind::U32,
+ (true, 8) => IntKind::I64,
+ (false, 8) => IntKind::U64,
+ _ => {
+ warn!(
+ "invalid enum decl: signed: {}, size: {}",
+ signed, size
+ );
+ IntKind::I32
+ }
+ };
- let signed = repr.is_signed();
- let size = layout
- .map(|l| l.size)
- .or_else(|| repr.known_size())
- .unwrap_or(0);
-
- let repr_name = match (signed, size) {
- (true, 1) => "i8",
- (false, 1) => "u8",
- (true, 2) => "i16",
- (false, 2) => "u16",
- (true, 4) => "i32",
- (false, 4) => "u32",
- (true, 8) => "i64",
- (false, 8) => "u64",
- _ => {
- warn!("invalid enum decl: signed: {}, size: {}", signed, size);
- "i32"
+ repr_translated =
+ Type::new(None, None, TypeKind::Int(translated), false);
+ &repr_translated
}
};
let mut attrs = vec![];
- let variation = self.computed_enum_variation(ctx, item);
-
// TODO(emilio): Delegate this to the builders?
match variation {
EnumVariation::Rust { non_exhaustive } => {
- attrs.push(attributes::repr(repr_name));
if non_exhaustive &&
ctx.options().rust_features().non_exhaustive
{
@@ -2934,13 +2958,7 @@ impl CodeGenerator for Enum {
});
}
- let repr = match self.repr() {
- Some(ty) => ty.to_rust_ty_or_opaque(ctx, &()),
- None => {
- let repr_name = ctx.rust_ident_raw(repr_name);
- quote! { #repr_name }
- }
- };
+ let repr = repr.to_rust_ty_or_opaque(ctx, item);
let mut builder = EnumBuilder::new(
&name,
diff --git a/src/lib.rs b/src/lib.rs
index 0716b42f..3eeb0735 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -549,6 +549,10 @@ impl Builder {
output_vector.push("--respect-cxx-access-specs".into());
}
+ if self.options.translate_enum_integer_types {
+ output_vector.push("--translate-enum-integer-types".into());
+ }
+
// Add clang arguments
output_vector.push("--".into());
@@ -1568,6 +1572,16 @@ impl Builder {
self.options.respect_cxx_access_specs = doit;
self
}
+
+ /// Always translate enum integer types to native Rust integer types.
+ ///
+ /// This will result in enums having types such as `u32` and `i16` instead
+ /// of `c_uint` and `c_short`. Types for Rustified enums are always
+ /// translated.
+ pub fn translate_enum_integer_types(mut self, doit: bool) -> Self {
+ self.options.translate_enum_integer_types = doit;
+ self
+ }
}
/// Configuration options for generated bindings.
@@ -1859,6 +1873,9 @@ struct BindgenOptions {
/// Only make generated bindings `pub` if the items would be publically accessible
/// by C++.
respect_cxx_access_specs: bool,
+
+ /// Always translate enum integer types to native Rust integer types.
+ translate_enum_integer_types: bool,
}
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1996,6 +2013,7 @@ impl Default for BindgenOptions {
wasm_import_module_name: None,
dynamic_library_name: None,
respect_cxx_access_specs: false,
+ translate_enum_integer_types: false,
}
}
}
diff --git a/src/options.rs b/src/options.rs
index 2289ad09..4f75424a 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -503,6 +503,9 @@ where
Arg::with_name("respect-cxx-access-specs")
.long("respect-cxx-access-specs")
.help("Makes generated bindings `pub` only for items if the items are publically accessible in C++."),
+ Arg::with_name("translate-enum-integer-types")
+ .long("translate-enum-integer-types")
+ .help("Always translate enum integer types to native Rust integer types."),
]) // .args()
.get_matches_from(args);
@@ -929,6 +932,10 @@ where
builder = builder.respect_cxx_access_specs(true);
}
+ if matches.is_present("translate-enum-integer-types") {
+ builder = builder.translate_enum_integer_types(true);
+ }
+
let verbose = matches.is_present("verbose");
Ok((builder, output, verbose))
diff --git a/tests/expectations/tests/enum-translate-type.rs b/tests/expectations/tests/enum-translate-type.rs
new file mode 100644
index 00000000..89e6003e
--- /dev/null
+++ b/tests/expectations/tests/enum-translate-type.rs
@@ -0,0 +1,15 @@
+#![allow(
+ dead_code,
+ non_snake_case,
+ non_camel_case_types,
+ non_upper_case_globals
+)]
+
+pub const my_enum1_A: my_enum1 = 0;
+pub type my_enum1 = u32;
+pub const my_enum2_B: my_enum2 = -1;
+pub type my_enum2 = i32;
+pub const my_enum3_C: my_enum3 = 0;
+pub type my_enum3 = i16;
+pub const my_enum4_D: my_enum4 = 255;
+pub type my_enum4 = u8;
diff --git a/tests/headers/enum-translate-type.hpp b/tests/headers/enum-translate-type.hpp
new file mode 100644
index 00000000..8d856261
--- /dev/null
+++ b/tests/headers/enum-translate-type.hpp
@@ -0,0 +1,14 @@
+// bindgen-flags: --translate-enum-integer-types -- -std=c++11 -Wno-narrowing
+
+enum my_enum1 {
+ A = 0,
+};
+enum my_enum2 {
+ B = -1,
+};
+enum my_enum3: short {
+ C = 0,
+};
+enum my_enum4: unsigned char {
+ D = -1,
+};