Add option to translate enum integer types to native Rust integer types

Fixes #430
This commit is contained in:
Jethro Beekman 2021-03-10 13:25:53 +01:00 committed by Emilio Cobos Álvarez
parent fb931bd6c1
commit dedbea5bc0
5 changed files with 113 additions and 41 deletions

View File

@ -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 signed = repr.is_signed();
let size = layout
.map(|l| l.size)
.or_else(|| repr.known_size())
.unwrap_or(0);
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 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"
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
}
};
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,

View File

@ -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,
}
}
}

View File

@ -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))

View File

@ -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;

View File

@ -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,
};