diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 62271acf30..1fba83167e 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, + mut attrs: Vec, 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 0716b42f47..3eeb0735b4 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 2289ad09bd..4f75424adf 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 0000000000..89e6003e44 --- /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 0000000000..11e08e85b8 --- /dev/null +++ b/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, +};