From fad1b508934a25ff9351f92b98f53a6c848b13d6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 26 Oct 2022 15:07:07 -0700 Subject: [PATCH 1/2] Add test of enum and typedef with same name error[E0428]: the name `Enum` is defined multiple times --> bindgen-tests/tests/expectations/tests/enum-typedef.rs:10:1 | 9 | pub type Enum = ::std::os::raw::c_uint; | --------------------------------------- previous definition of the type `Enum` here 10 | pub type Enum = i16; | ^^^^^^^^^^^^^^^^^^^^ `Enum` redefined here | = note: `Enum` must be defined only once in the type namespace of this module error[E0428]: the name `TypedefFirst` is defined multiple times --> bindgen-tests/tests/expectations/tests/enum-typedef.rs:13:1 | 11 | pub type TypedefFirst = i16; | ---------------------------- previous definition of the type `TypedefFirst` here 12 | pub const TypedefFirst_Variant2: TypedefFirst = 0; 13 | pub type TypedefFirst = ::std::os::raw::c_uint; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `TypedefFirst` redefined here | = note: `TypedefFirst` must be defined only once in the type namespace of this module --- .../tests/expectations/tests/enum-typedef.rs | 13 +++++++++++++ bindgen-tests/tests/headers/enum-typedef.h | 18 ++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 bindgen-tests/tests/expectations/tests/enum-typedef.rs create mode 100644 bindgen-tests/tests/headers/enum-typedef.h diff --git a/bindgen-tests/tests/expectations/tests/enum-typedef.rs b/bindgen-tests/tests/expectations/tests/enum-typedef.rs new file mode 100644 index 0000000000..fd9558b3e1 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/enum-typedef.rs @@ -0,0 +1,13 @@ +#![allow( + dead_code, + non_snake_case, + non_camel_case_types, + non_upper_case_globals +)] + +pub const Enum_Variant: Enum = 0; +pub type Enum = ::std::os::raw::c_uint; +pub type Enum = i16; +pub type TypedefFirst = i16; +pub const TypedefFirst_Variant2: TypedefFirst = 0; +pub type TypedefFirst = ::std::os::raw::c_uint; diff --git a/bindgen-tests/tests/headers/enum-typedef.h b/bindgen-tests/tests/headers/enum-typedef.h new file mode 100644 index 0000000000..f345f4de27 --- /dev/null +++ b/bindgen-tests/tests/headers/enum-typedef.h @@ -0,0 +1,18 @@ +typedef short int16_t; + +// `cbindgen` emits this C idiom as the translation of: +// +// #[repr(i16)] +// pub enum Enum { +// Variant, +// } +enum Enum { + Variant, +}; +typedef int16_t Enum; + +// C is also fine with the typedef coming before the enum. +typedef int16_t TypedefFirst; +enum TypedefFirst { + Variant2, +}; From 0238fc49d2244d7a50cc88278ce96df331178fff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 26 Oct 2022 15:18:16 -0700 Subject: [PATCH 2/2] Fix name collision between C enum and typedef --- .../tests/expectations/tests/enum-typedef.rs | 2 - bindgen/codegen/mod.rs | 15 ++-- bindgen/ir/context.rs | 82 +++++++++++++++++++ bindgen/ir/ty.rs | 5 ++ 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/bindgen-tests/tests/expectations/tests/enum-typedef.rs b/bindgen-tests/tests/expectations/tests/enum-typedef.rs index fd9558b3e1..dc78eb11eb 100644 --- a/bindgen-tests/tests/expectations/tests/enum-typedef.rs +++ b/bindgen-tests/tests/expectations/tests/enum-typedef.rs @@ -6,8 +6,6 @@ )] pub const Enum_Variant: Enum = 0; -pub type Enum = ::std::os::raw::c_uint; pub type Enum = i16; pub type TypedefFirst = i16; pub const TypedefFirst_Variant2: TypedefFirst = 0; -pub type TypedefFirst = ::std::os::raw::c_uint; diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index b405063737..154d7fd1ab 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -2709,6 +2709,7 @@ impl<'a> EnumBuilder<'a> { mut attrs: Vec, repr: proc_macro2::TokenStream, enum_variation: EnumVariation, + has_typedef: bool, ) -> Self { let ident = Ident::new(name, Span::call_site()); @@ -2741,10 +2742,12 @@ impl<'a> EnumBuilder<'a> { EnumVariation::Consts => { let mut variants = Vec::new(); - variants.push(quote! { - #( #attrs )* - pub type #ident = #repr; - }); + if !has_typedef { + variants.push(quote! { + #( #attrs )* + pub type #ident = #repr; + }); + } EnumBuilder::Consts { variants } } @@ -3157,8 +3160,10 @@ impl CodeGenerator for Enum { } let repr = repr.to_rust_ty_or_opaque(ctx, item); + let has_typedef = ctx.is_enum_typedef_combo(item.id()); - let mut builder = EnumBuilder::new(&name, attrs, repr, variation); + let mut builder = + EnumBuilder::new(&name, attrs, repr, variation, has_typedef); // A map where we keep a value -> variant relation. let mut seen_values = HashMap::<_, Ident>::default(); diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index 3cc30f1c0b..c5df37d7e9 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -398,6 +398,22 @@ pub struct BindgenContext { /// bitfield allocation units computed. Drained in `compute_bitfield_units`. need_bitfield_allocation: Vec, + /// The set of enums that are defined by a pair of `enum` and `typedef`, + /// which is legal in C (but not C++). + /// + /// ```c++ + /// // in either order + /// enum Enum { Variants... }; + /// typedef int16_t Enum; + /// ``` + /// + /// The stored `ItemId` is that of the `TypeKind::Enum`, not of the + /// `TypeKind::Alias`. + /// + /// This is populated when we enter codegen by `compute_enum_typedef_combos` + /// and is always `None` before that and `Some` after. + enum_typedef_combos: Option>, + /// The set of (`ItemId`s of) types that can't derive debug. /// /// This is populated when we enter codegen by `compute_cannot_derive_debug` @@ -565,6 +581,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" codegen_items: None, used_template_parameters: None, need_bitfield_allocation: Default::default(), + enum_typedef_combos: None, cannot_derive_debug: None, cannot_derive_default: None, cannot_derive_copy: None, @@ -1157,6 +1174,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.compute_sizedness(); self.compute_has_destructor(); self.find_used_template_parameters(); + self.compute_enum_typedef_combos(); self.compute_cannot_derive_debug(); self.compute_cannot_derive_default(); self.compute_cannot_derive_copy(); @@ -2476,6 +2494,70 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.generated_bindgen_complex.get() } + /// Compute which `enum`s have an associated `typedef` definition. + fn compute_enum_typedef_combos(&mut self) { + let _t = self.timer("compute_enum_typedef_combos"); + assert!(self.enum_typedef_combos.is_none()); + + let mut enum_typedef_combos = HashSet::default(); + for item in &self.items { + if let Some(ItemKind::Module(module)) = + item.as_ref().map(Item::kind) + { + // Find typedefs in this module, and build set of their names. + let mut names_of_typedefs = HashSet::default(); + for child_id in module.children() { + if let Some(ItemKind::Type(ty)) = + self.items[child_id.0].as_ref().map(Item::kind) + { + if let (Some(name), TypeKind::Alias(type_id)) = + (ty.name(), ty.kind()) + { + // We disregard aliases that refer to the enum + // itself, such as in `typedef enum { ... } Enum;`. + if type_id + .into_resolver() + .through_type_refs() + .through_type_aliases() + .resolve(self) + .expect_type() + .is_int() + { + names_of_typedefs.insert(name); + } + } + } + } + + // Find enums in this module, and record the id of each one that + // has a typedef. + for child_id in module.children() { + if let Some(ItemKind::Type(ty)) = + self.items[child_id.0].as_ref().map(Item::kind) + { + if let (Some(name), true) = (ty.name(), ty.is_enum()) { + if names_of_typedefs.contains(name) { + enum_typedef_combos.insert(*child_id); + } + } + } + } + } + } + + self.enum_typedef_combos = Some(enum_typedef_combos); + } + + /// Look up whether `id` refers to an `enum` whose underlying type is + /// defined by a `typedef`. + pub fn is_enum_typedef_combo(&self, id: ItemId) -> bool { + assert!( + self.in_codegen_phase(), + "We only compute enum_typedef_combos when we enter codegen", + ); + self.enum_typedef_combos.as_ref().unwrap().contains(&id) + } + /// Compute whether we can derive debug. fn compute_cannot_derive_debug(&mut self) { let _t = self.timer("compute_cannot_derive_debug"); diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs index ed3331adb2..fef340dee9 100644 --- a/bindgen/ir/ty.rs +++ b/bindgen/ir/ty.rs @@ -95,6 +95,11 @@ impl Type { matches!(self.kind, TypeKind::BlockPointer(..)) } + /// Is this an integer type, including `bool` or `char`? + pub fn is_int(&self) -> bool { + matches!(self.kind, TypeKind::Int(_)) + } + /// Is this a compound type? pub fn is_comp(&self) -> bool { matches!(self.kind, TypeKind::Comp(..))