Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix name collision between C enum and typedef #2326

Merged
merged 2 commits into from Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions bindgen-tests/tests/expectations/tests/enum-typedef.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions 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,
};
15 changes: 10 additions & 5 deletions bindgen/codegen/mod.rs
Expand Up @@ -2709,6 +2709,7 @@ impl<'a> EnumBuilder<'a> {
mut attrs: Vec<proc_macro2::TokenStream>,
repr: proc_macro2::TokenStream,
enum_variation: EnumVariation,
has_typedef: bool,
) -> Self {
let ident = Ident::new(name, Span::call_site());

Expand Down Expand Up @@ -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 }
}
Expand Down Expand Up @@ -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();
Expand Down
82 changes: 82 additions & 0 deletions bindgen/ir/context.rs
Expand Up @@ -398,6 +398,22 @@ pub struct BindgenContext {
/// bitfield allocation units computed. Drained in `compute_bitfield_units`.
need_bitfield_allocation: Vec<ItemId>,

/// 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<HashSet<ItemId>>,

/// The set of (`ItemId`s of) types that can't derive debug.
///
/// This is populated when we enter codegen by `compute_cannot_derive_debug`
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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");
Expand Down
5 changes: 5 additions & 0 deletions bindgen/ir/ty.rs
Expand Up @@ -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(..))
Expand Down