Skip to content

Commit 0a78cde

Browse files
authored
Fix name collision between C enum and typedef (#2326)
Fixes #2008. Example: ```c enum Enum { Variant }; typedef int16_t Enum; ``` This is valid and idiomatic C (though not valid C++). `cbindgen` uses this idiom as the default C translation of Rust enums, the equivalent of what would be `enum Enum : int16_t { Variant };` in C++. `bindgen header.h` before: ```rust pub const Enum_Variant: Enum = 0; pub type Enum = ::std::os::raw::c_uint; pub type Enum = i16; ``` ```console error[E0428]: the name `Enum` is defined multiple times --> generated.rs:3:1 | 2 | pub type Enum = ::std::os::raw::c_uint; | --------------------------------------- previous definition of the type `Enum` here 3 | pub type Enum = i16; | ^^^^^^^^^^^^^^^^^^^^ `Enum` redefined here | = note: `Enum` must be defined only once in the type namespace of this module ``` After: ```rust pub const Enum_Variant: Enum = 0; pub type Enum = i16; ```
1 parent 95fd17b commit 0a78cde

File tree

5 files changed

+126
-5
lines changed

5 files changed

+126
-5
lines changed

bindgen-tests/tests/expectations/tests/enum-typedef.rs

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
typedef short int16_t;
2+
3+
// `cbindgen` emits this C idiom as the translation of:
4+
//
5+
// #[repr(i16)]
6+
// pub enum Enum {
7+
// Variant,
8+
// }
9+
enum Enum {
10+
Variant,
11+
};
12+
typedef int16_t Enum;
13+
14+
// C is also fine with the typedef coming before the enum.
15+
typedef int16_t TypedefFirst;
16+
enum TypedefFirst {
17+
Variant2,
18+
};

bindgen/codegen/mod.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -2709,6 +2709,7 @@ impl<'a> EnumBuilder<'a> {
27092709
mut attrs: Vec<proc_macro2::TokenStream>,
27102710
repr: proc_macro2::TokenStream,
27112711
enum_variation: EnumVariation,
2712+
has_typedef: bool,
27122713
) -> Self {
27132714
let ident = Ident::new(name, Span::call_site());
27142715

@@ -2741,10 +2742,12 @@ impl<'a> EnumBuilder<'a> {
27412742
EnumVariation::Consts => {
27422743
let mut variants = Vec::new();
27432744

2744-
variants.push(quote! {
2745-
#( #attrs )*
2746-
pub type #ident = #repr;
2747-
});
2745+
if !has_typedef {
2746+
variants.push(quote! {
2747+
#( #attrs )*
2748+
pub type #ident = #repr;
2749+
});
2750+
}
27482751

27492752
EnumBuilder::Consts { variants }
27502753
}
@@ -3157,8 +3160,10 @@ impl CodeGenerator for Enum {
31573160
}
31583161

31593162
let repr = repr.to_rust_ty_or_opaque(ctx, item);
3163+
let has_typedef = ctx.is_enum_typedef_combo(item.id());
31603164

3161-
let mut builder = EnumBuilder::new(&name, attrs, repr, variation);
3165+
let mut builder =
3166+
EnumBuilder::new(&name, attrs, repr, variation, has_typedef);
31623167

31633168
// A map where we keep a value -> variant relation.
31643169
let mut seen_values = HashMap::<_, Ident>::default();

bindgen/ir/context.rs

+82
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,22 @@ pub struct BindgenContext {
398398
/// bitfield allocation units computed. Drained in `compute_bitfield_units`.
399399
need_bitfield_allocation: Vec<ItemId>,
400400

401+
/// The set of enums that are defined by a pair of `enum` and `typedef`,
402+
/// which is legal in C (but not C++).
403+
///
404+
/// ```c++
405+
/// // in either order
406+
/// enum Enum { Variants... };
407+
/// typedef int16_t Enum;
408+
/// ```
409+
///
410+
/// The stored `ItemId` is that of the `TypeKind::Enum`, not of the
411+
/// `TypeKind::Alias`.
412+
///
413+
/// This is populated when we enter codegen by `compute_enum_typedef_combos`
414+
/// and is always `None` before that and `Some` after.
415+
enum_typedef_combos: Option<HashSet<ItemId>>,
416+
401417
/// The set of (`ItemId`s of) types that can't derive debug.
402418
///
403419
/// 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!"
565581
codegen_items: None,
566582
used_template_parameters: None,
567583
need_bitfield_allocation: Default::default(),
584+
enum_typedef_combos: None,
568585
cannot_derive_debug: None,
569586
cannot_derive_default: None,
570587
cannot_derive_copy: None,
@@ -1157,6 +1174,7 @@ If you encounter an error missing from this list, please file an issue or a PR!"
11571174
self.compute_sizedness();
11581175
self.compute_has_destructor();
11591176
self.find_used_template_parameters();
1177+
self.compute_enum_typedef_combos();
11601178
self.compute_cannot_derive_debug();
11611179
self.compute_cannot_derive_default();
11621180
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!"
24762494
self.generated_bindgen_complex.get()
24772495
}
24782496

2497+
/// Compute which `enum`s have an associated `typedef` definition.
2498+
fn compute_enum_typedef_combos(&mut self) {
2499+
let _t = self.timer("compute_enum_typedef_combos");
2500+
assert!(self.enum_typedef_combos.is_none());
2501+
2502+
let mut enum_typedef_combos = HashSet::default();
2503+
for item in &self.items {
2504+
if let Some(ItemKind::Module(module)) =
2505+
item.as_ref().map(Item::kind)
2506+
{
2507+
// Find typedefs in this module, and build set of their names.
2508+
let mut names_of_typedefs = HashSet::default();
2509+
for child_id in module.children() {
2510+
if let Some(ItemKind::Type(ty)) =
2511+
self.items[child_id.0].as_ref().map(Item::kind)
2512+
{
2513+
if let (Some(name), TypeKind::Alias(type_id)) =
2514+
(ty.name(), ty.kind())
2515+
{
2516+
// We disregard aliases that refer to the enum
2517+
// itself, such as in `typedef enum { ... } Enum;`.
2518+
if type_id
2519+
.into_resolver()
2520+
.through_type_refs()
2521+
.through_type_aliases()
2522+
.resolve(self)
2523+
.expect_type()
2524+
.is_int()
2525+
{
2526+
names_of_typedefs.insert(name);
2527+
}
2528+
}
2529+
}
2530+
}
2531+
2532+
// Find enums in this module, and record the id of each one that
2533+
// has a typedef.
2534+
for child_id in module.children() {
2535+
if let Some(ItemKind::Type(ty)) =
2536+
self.items[child_id.0].as_ref().map(Item::kind)
2537+
{
2538+
if let (Some(name), true) = (ty.name(), ty.is_enum()) {
2539+
if names_of_typedefs.contains(name) {
2540+
enum_typedef_combos.insert(*child_id);
2541+
}
2542+
}
2543+
}
2544+
}
2545+
}
2546+
}
2547+
2548+
self.enum_typedef_combos = Some(enum_typedef_combos);
2549+
}
2550+
2551+
/// Look up whether `id` refers to an `enum` whose underlying type is
2552+
/// defined by a `typedef`.
2553+
pub fn is_enum_typedef_combo(&self, id: ItemId) -> bool {
2554+
assert!(
2555+
self.in_codegen_phase(),
2556+
"We only compute enum_typedef_combos when we enter codegen",
2557+
);
2558+
self.enum_typedef_combos.as_ref().unwrap().contains(&id)
2559+
}
2560+
24792561
/// Compute whether we can derive debug.
24802562
fn compute_cannot_derive_debug(&mut self) {
24812563
let _t = self.timer("compute_cannot_derive_debug");

bindgen/ir/ty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ impl Type {
9595
matches!(self.kind, TypeKind::BlockPointer(..))
9696
}
9797

98+
/// Is this an integer type, including `bool` or `char`?
99+
pub fn is_int(&self) -> bool {
100+
matches!(self.kind, TypeKind::Int(_))
101+
}
102+
98103
/// Is this a compound type?
99104
pub fn is_comp(&self) -> bool {
100105
matches!(self.kind, TypeKind::Comp(..))

0 commit comments

Comments
 (0)