Skip to content

Commit

Permalink
Add option to translate enum integer types to native Rust integer types
Browse files Browse the repository at this point in the history
Fixes #430
  • Loading branch information
Jethro Beekman committed Mar 11, 2021
1 parent 84c7020 commit b9dbb39
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 42 deletions.
102 changes: 60 additions & 42 deletions src/codegen/mod.rs
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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,
Expand Down
18 changes: 18 additions & 0 deletions src/lib.rs
Expand Up @@ -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());
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/options.rs
Expand Up @@ -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);

Expand Down Expand Up @@ -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))
Expand Down
15 changes: 15 additions & 0 deletions 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;
14 changes: 14 additions & 0 deletions tests/headers/enum-translate-type.hpp
@@ -0,0 +1,14 @@
// bindgen-flags: --translate-enum-integer-types -- -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,
};

0 comments on commit b9dbb39

Please sign in to comment.