Skip to content

Commit b9dbb39

Browse files
author
Jethro Beekman
committed
Add option to translate enum integer types to native Rust integer types
Fixes rust-lang#430
1 parent 84c7020 commit b9dbb39

File tree

5 files changed

+114
-42
lines changed

5 files changed

+114
-42
lines changed

src/codegen/mod.rs

+60-42
Original file line numberDiff line numberDiff line change
@@ -2524,7 +2524,7 @@ impl<'a> EnumBuilder<'a> {
25242524
/// the representation, and which variation it should be generated as.
25252525
fn new(
25262526
name: &'a str,
2527-
attrs: Vec<proc_macro2::TokenStream>,
2527+
mut attrs: Vec<proc_macro2::TokenStream>,
25282528
repr: proc_macro2::TokenStream,
25292529
enum_variation: EnumVariation,
25302530
enum_codegen_depth: usize,
@@ -2543,6 +2543,8 @@ impl<'a> EnumBuilder<'a> {
25432543
},
25442544

25452545
EnumVariation::Rust { .. } => {
2546+
// `repr` is guaranteed to be Rustified in Enum::codegen
2547+
attrs.insert(0, quote! { #[repr( #repr )] });
25462548
let tokens = quote!();
25472549
EnumBuilder::Rust {
25482550
codegen_depth: enum_codegen_depth + 1,
@@ -2820,51 +2822,73 @@ impl CodeGenerator for Enum {
28202822
let ident = ctx.rust_ident(&name);
28212823
let enum_ty = item.expect_type();
28222824
let layout = enum_ty.layout(ctx);
2825+
let variation = self.computed_enum_variation(ctx, item);
28232826

2824-
let repr = self.repr().map(|repr| ctx.resolve_type(repr));
2825-
let repr = match repr {
2826-
Some(repr) => match *repr.canonical_type(ctx).kind() {
2827-
TypeKind::Int(int_kind) => int_kind,
2828-
_ => panic!("Unexpected type as enum repr"),
2829-
},
2830-
None => {
2831-
warn!(
2832-
"Guessing type of enum! Forward declarations of enums \
2833-
shouldn't be legal!"
2834-
);
2835-
IntKind::Int
2827+
let repr_translated;
2828+
let repr = match self.repr().map(|repr| ctx.resolve_type(repr)) {
2829+
Some(repr)
2830+
if !ctx.options().translate_enum_integer_types &&
2831+
!variation.is_rust() =>
2832+
{
2833+
repr
28362834
}
2837-
};
2835+
repr => {
2836+
// An enum's integer type is translated to a native Rust
2837+
// integer type in 3 cases:
2838+
// * the enum is Rustified and we need a translated type for
2839+
// the repr attribute
2840+
// * the representation couldn't be determined from the C source
2841+
// * it was explicitly requested as a bindgen option
2842+
2843+
let kind = match repr {
2844+
Some(repr) => match *repr.canonical_type(ctx).kind() {
2845+
TypeKind::Int(int_kind) => int_kind,
2846+
_ => panic!("Unexpected type as enum repr"),
2847+
},
2848+
None => {
2849+
warn!(
2850+
"Guessing type of enum! Forward declarations of enums \
2851+
shouldn't be legal!"
2852+
);
2853+
IntKind::Int
2854+
}
2855+
};
2856+
2857+
let signed = kind.is_signed();
2858+
let size = layout
2859+
.map(|l| l.size)
2860+
.or_else(|| kind.known_size())
2861+
.unwrap_or(0);
2862+
2863+
let translated = match (signed, size) {
2864+
(true, 1) => IntKind::I8,
2865+
(false, 1) => IntKind::U8,
2866+
(true, 2) => IntKind::I16,
2867+
(false, 2) => IntKind::U16,
2868+
(true, 4) => IntKind::I32,
2869+
(false, 4) => IntKind::U32,
2870+
(true, 8) => IntKind::I64,
2871+
(false, 8) => IntKind::U64,
2872+
_ => {
2873+
warn!(
2874+
"invalid enum decl: signed: {}, size: {}",
2875+
signed, size
2876+
);
2877+
IntKind::I32
2878+
}
2879+
};
28382880

2839-
let signed = repr.is_signed();
2840-
let size = layout
2841-
.map(|l| l.size)
2842-
.or_else(|| repr.known_size())
2843-
.unwrap_or(0);
2844-
2845-
let repr_name = match (signed, size) {
2846-
(true, 1) => "i8",
2847-
(false, 1) => "u8",
2848-
(true, 2) => "i16",
2849-
(false, 2) => "u16",
2850-
(true, 4) => "i32",
2851-
(false, 4) => "u32",
2852-
(true, 8) => "i64",
2853-
(false, 8) => "u64",
2854-
_ => {
2855-
warn!("invalid enum decl: signed: {}, size: {}", signed, size);
2856-
"i32"
2881+
repr_translated =
2882+
Type::new(None, None, TypeKind::Int(translated), false);
2883+
&repr_translated
28572884
}
28582885
};
28592886

28602887
let mut attrs = vec![];
28612888

2862-
let variation = self.computed_enum_variation(ctx, item);
2863-
28642889
// TODO(emilio): Delegate this to the builders?
28652890
match variation {
28662891
EnumVariation::Rust { non_exhaustive } => {
2867-
attrs.push(attributes::repr(repr_name));
28682892
if non_exhaustive &&
28692893
ctx.options().rust_features().non_exhaustive
28702894
{
@@ -2934,13 +2958,7 @@ impl CodeGenerator for Enum {
29342958
});
29352959
}
29362960

2937-
let repr = match self.repr() {
2938-
Some(ty) => ty.to_rust_ty_or_opaque(ctx, &()),
2939-
None => {
2940-
let repr_name = ctx.rust_ident_raw(repr_name);
2941-
quote! { #repr_name }
2942-
}
2943-
};
2961+
let repr = repr.to_rust_ty_or_opaque(ctx, item);
29442962

29452963
let mut builder = EnumBuilder::new(
29462964
&name,

src/lib.rs

+18
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,10 @@ impl Builder {
549549
output_vector.push("--respect-cxx-access-specs".into());
550550
}
551551

552+
if self.options.translate_enum_integer_types {
553+
output_vector.push("--translate-enum-integer-types".into());
554+
}
555+
552556
// Add clang arguments
553557

554558
output_vector.push("--".into());
@@ -1568,6 +1572,16 @@ impl Builder {
15681572
self.options.respect_cxx_access_specs = doit;
15691573
self
15701574
}
1575+
1576+
/// Always translate enum integer types to native Rust integer types.
1577+
///
1578+
/// This will result in enums having types such as `u32` and `i16` instead
1579+
/// of `c_uint` and `c_short`. Types for Rustified enums are always
1580+
/// translated.
1581+
pub fn translate_enum_integer_types(mut self, doit: bool) -> Self {
1582+
self.options.translate_enum_integer_types = doit;
1583+
self
1584+
}
15711585
}
15721586

15731587
/// Configuration options for generated bindings.
@@ -1859,6 +1873,9 @@ struct BindgenOptions {
18591873
/// Only make generated bindings `pub` if the items would be publically accessible
18601874
/// by C++.
18611875
respect_cxx_access_specs: bool,
1876+
1877+
/// Always translate enum integer types to native Rust integer types.
1878+
translate_enum_integer_types: bool,
18621879
}
18631880

18641881
/// TODO(emilio): This is sort of a lie (see the error message that results from
@@ -1996,6 +2013,7 @@ impl Default for BindgenOptions {
19962013
wasm_import_module_name: None,
19972014
dynamic_library_name: None,
19982015
respect_cxx_access_specs: false,
2016+
translate_enum_integer_types: false,
19992017
}
20002018
}
20012019
}

src/options.rs

+7
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ where
503503
Arg::with_name("respect-cxx-access-specs")
504504
.long("respect-cxx-access-specs")
505505
.help("Makes generated bindings `pub` only for items if the items are publically accessible in C++."),
506+
Arg::with_name("translate-enum-integer-types")
507+
.long("translate-enum-integer-types")
508+
.help("Always translate enum integer types to native Rust integer types."),
506509
]) // .args()
507510
.get_matches_from(args);
508511

@@ -929,6 +932,10 @@ where
929932
builder = builder.respect_cxx_access_specs(true);
930933
}
931934

935+
if matches.is_present("translate-enum-integer-types") {
936+
builder = builder.translate_enum_integer_types(true);
937+
}
938+
932939
let verbose = matches.is_present("verbose");
933940

934941
Ok((builder, output, verbose))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![allow(
2+
dead_code,
3+
non_snake_case,
4+
non_camel_case_types,
5+
non_upper_case_globals
6+
)]
7+
8+
pub const my_enum1_A: my_enum1 = 0;
9+
pub type my_enum1 = u32;
10+
pub const my_enum2_B: my_enum2 = -1;
11+
pub type my_enum2 = i32;
12+
pub const my_enum3_C: my_enum3 = 0;
13+
pub type my_enum3 = i16;
14+
pub const my_enum4_D: my_enum4 = 255;
15+
pub type my_enum4 = u8;

tests/headers/enum-translate-type.hpp

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// bindgen-flags: --translate-enum-integer-types -- -Wno-narrowing
2+
3+
enum my_enum1 {
4+
A = 0,
5+
};
6+
enum my_enum2 {
7+
B = -1,
8+
};
9+
enum my_enum3: short {
10+
C = 0,
11+
};
12+
enum my_enum4: unsigned char {
13+
D = -1,
14+
};

0 commit comments

Comments
 (0)