From 20c9e80d6db24e256dfbca084b74cf9baa86b769 Mon Sep 17 00:00:00 2001 From: Guflly <145608489+Guflly@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:47:20 -0700 Subject: [PATCH] Fix generic enum variant body collisions --- src/bindgen/ir/enumeration.rs | 56 +++++++++-------- src/bindgen/ir/structure.rs | 8 ++- .../generic_enum_variant_collision.c.sym | 4 ++ .../generic_enum_variant_collision.c | 46 ++++++++++++++ .../generic_enum_variant_collision.compat.c | 54 ++++++++++++++++ .../generic_enum_variant_collision.cpp | 61 +++++++++++++++++++ .../generic_enum_variant_collision.pyx | 32 ++++++++++ .../generic_enum_variant_collision_both.c | 46 ++++++++++++++ ...neric_enum_variant_collision_both.compat.c | 54 ++++++++++++++++ .../generic_enum_variant_collision_tag.c | 46 ++++++++++++++ ...eneric_enum_variant_collision_tag.compat.c | 54 ++++++++++++++++ .../generic_enum_variant_collision_tag.pyx | 32 ++++++++++ tests/rust/generic_enum_variant_collision.rs | 24 ++++++++ .../rust/generic_enum_variant_collision.toml | 2 + 14 files changed, 491 insertions(+), 28 deletions(-) create mode 100644 tests/expectations-symbols/generic_enum_variant_collision.c.sym create mode 100644 tests/expectations/generic_enum_variant_collision.c create mode 100644 tests/expectations/generic_enum_variant_collision.compat.c create mode 100644 tests/expectations/generic_enum_variant_collision.cpp create mode 100644 tests/expectations/generic_enum_variant_collision.pyx create mode 100644 tests/expectations/generic_enum_variant_collision_both.c create mode 100644 tests/expectations/generic_enum_variant_collision_both.compat.c create mode 100644 tests/expectations/generic_enum_variant_collision_tag.c create mode 100644 tests/expectations/generic_enum_variant_collision_tag.compat.c create mode 100644 tests/expectations/generic_enum_variant_collision_tag.pyx create mode 100644 tests/rust/generic_enum_variant_collision.rs create mode 100644 tests/rust/generic_enum_variant_collision.toml diff --git a/src/bindgen/ir/enumeration.rs b/src/bindgen/ir/enumeration.rs index aa83a780..e5171cb4 100644 --- a/src/bindgen/ir/enumeration.rs +++ b/src/bindgen/ir/enumeration.rs @@ -164,33 +164,35 @@ impl EnumVariant { let body = match variant.fields { syn::Fields::Unit => VariantBody::Empty(annotations), syn::Fields::Named(ref fields) => { - let path = Path::new(format!("{}_Body", variant.ident)); + let path = Path::new(format!("{}_{}_Body", self_path.name(), variant.ident)); let name = body_rule .apply( &variant.ident.unraw().to_string(), IdentifierType::StructMember, ) .into_owned(); + let mut body = Struct::new( + path, + generic_params, + parse_fields(inline_tag_field, &fields.named, self_path, None)?, + inline_tag_field, + true, + None, + false, + None, + annotations, + Documentation::none(), + ); + body.export_name = format!("{}_Body", variant.ident); VariantBody::Body { - body: Struct::new( - path, - generic_params, - parse_fields(inline_tag_field, &fields.named, self_path, None)?, - inline_tag_field, - true, - None, - false, - None, - annotations, - Documentation::none(), - ), + body, name, inline: false, inline_casts: false, } } syn::Fields::Unnamed(ref fields) => { - let path = Path::new(format!("{}_Body", variant.ident)); + let path = Path::new(format!("{}_{}_Body", self_path.name(), variant.ident)); let name = body_rule .apply( &variant.ident.unraw().to_string(), @@ -205,19 +207,21 @@ impl EnumVariant { // As a result we don't currently inline variant definitions in C++ mode at all. let inline = inline_casts && config.language != Language::Cxx; let inline_name = if inline { Some(&*name) } else { None }; + let mut body = Struct::new( + path, + generic_params, + parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?, + inline_tag_field, + true, + None, + false, + None, + annotations, + Documentation::none(), + ); + body.export_name = format!("{}_Body", variant.ident); VariantBody::Body { - body: Struct::new( - path, - generic_params, - parse_fields(inline_tag_field, &fields.unnamed, self_path, inline_name)?, - inline_tag_field, - true, - None, - false, - None, - annotations, - Documentation::none(), - ), + body, name, inline, inline_casts, diff --git a/src/bindgen/ir/structure.rs b/src/bindgen/ir/structure.rs index b4810962..2f649c0f 100644 --- a/src/bindgen/ir/structure.rs +++ b/src/bindgen/ir/structure.rs @@ -199,7 +199,9 @@ impl Struct { config: &Config, ) -> Self { let mangled_path = mangle::mangle_path(&self.path, generic_values, &config.export.mangle); - Struct::new( + let mangled_export_name = + mangle::mangle_name(&self.export_name, generic_values, &config.export.mangle); + let mut specialized = Struct::new( mangled_path, GenericParams::default(), self.fields @@ -219,7 +221,9 @@ impl Struct { self.cfg.clone(), self.annotations.clone(), self.documentation.clone(), - ) + ); + specialized.export_name = mangled_export_name; + specialized } pub(crate) fn emit_bitflags_binop( diff --git a/tests/expectations-symbols/generic_enum_variant_collision.c.sym b/tests/expectations-symbols/generic_enum_variant_collision.c.sym new file mode 100644 index 00000000..d8de6239 --- /dev/null +++ b/tests/expectations-symbols/generic_enum_variant_collision.c.sym @@ -0,0 +1,4 @@ +{ +use_a; +use_b; +}; \ No newline at end of file diff --git a/tests/expectations/generic_enum_variant_collision.c b/tests/expectations/generic_enum_variant_collision.c new file mode 100644 index 00000000..4c281304 --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +typedef struct { + int32_t x; +} Payload; + +typedef enum { + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, +} ResultA_Payload_Tag; + +typedef struct { + ResultA_Payload_Tag tag; + union { + struct { + Payload ok; + }; + struct { + void *err; + }; + }; +} ResultA_Payload; + +typedef enum { + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, +} ResultB_Payload_Tag; + +typedef struct { + ResultB_Payload_Tag tag; + union { + struct { + Payload ok; + }; + struct { + void *err; + }; + }; +} ResultB_Payload; + +void use_a(ResultA_Payload _a); + +void use_b(ResultB_Payload _b); diff --git a/tests/expectations/generic_enum_variant_collision.compat.c b/tests/expectations/generic_enum_variant_collision.compat.c new file mode 100644 index 00000000..1c2756de --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision.compat.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +typedef struct { + int32_t x; +} Payload; + +typedef enum { + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, +} ResultA_Payload_Tag; + +typedef struct { + ResultA_Payload_Tag tag; + union { + struct { + Payload ok; + }; + struct { + void *err; + }; + }; +} ResultA_Payload; + +typedef enum { + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, +} ResultB_Payload_Tag; + +typedef struct { + ResultB_Payload_Tag tag; + union { + struct { + Payload ok; + }; + struct { + void *err; + }; + }; +} ResultB_Payload; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void use_a(ResultA_Payload _a); + +void use_b(ResultB_Payload _b); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/generic_enum_variant_collision.cpp b/tests/expectations/generic_enum_variant_collision.cpp new file mode 100644 index 00000000..aa29811c --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +struct Payload { + int32_t x; +}; + +template +struct ResultA { + enum class Tag { + ResultA_Ok, + ResultA_Err, + }; + + struct ResultA_Ok_Body { + T _0; + }; + + struct ResultA_Err_Body { + void *_0; + }; + + Tag tag; + union { + ResultA_Ok_Body ok; + ResultA_Err_Body err; + }; +}; + +template +struct ResultB { + enum class Tag { + ResultB_Ok, + ResultB_Err, + }; + + struct ResultB_Ok_Body { + T _0; + }; + + struct ResultB_Err_Body { + void *_0; + }; + + Tag tag; + union { + ResultB_Ok_Body ok; + ResultB_Err_Body err; + }; +}; + +extern "C" { + +void use_a(ResultA _a); + +void use_b(ResultB _b); + +} // extern "C" diff --git a/tests/expectations/generic_enum_variant_collision.pyx b/tests/expectations/generic_enum_variant_collision.pyx new file mode 100644 index 00000000..35fab0a5 --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision.pyx @@ -0,0 +1,32 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + ctypedef struct Payload: + int32_t x; + + ctypedef enum ResultA_Payload_Tag: + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, + + ctypedef struct ResultA_Payload: + ResultA_Payload_Tag tag; + Payload ok; + void *err; + + ctypedef enum ResultB_Payload_Tag: + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, + + ctypedef struct ResultB_Payload: + ResultB_Payload_Tag tag; + Payload ok; + void *err; + + void use_a(ResultA_Payload _a); + + void use_b(ResultB_Payload _b); diff --git a/tests/expectations/generic_enum_variant_collision_both.c b/tests/expectations/generic_enum_variant_collision_both.c new file mode 100644 index 00000000..a9ed1398 --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision_both.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +typedef struct Payload { + int32_t x; +} Payload; + +typedef enum ResultA_Payload_Tag { + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, +} ResultA_Payload_Tag; + +typedef struct ResultA_Payload { + ResultA_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +} ResultA_Payload; + +typedef enum ResultB_Payload_Tag { + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, +} ResultB_Payload_Tag; + +typedef struct ResultB_Payload { + ResultB_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +} ResultB_Payload; + +void use_a(struct ResultA_Payload _a); + +void use_b(struct ResultB_Payload _b); diff --git a/tests/expectations/generic_enum_variant_collision_both.compat.c b/tests/expectations/generic_enum_variant_collision_both.compat.c new file mode 100644 index 00000000..8b485b94 --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision_both.compat.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +typedef struct Payload { + int32_t x; +} Payload; + +typedef enum ResultA_Payload_Tag { + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, +} ResultA_Payload_Tag; + +typedef struct ResultA_Payload { + ResultA_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +} ResultA_Payload; + +typedef enum ResultB_Payload_Tag { + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, +} ResultB_Payload_Tag; + +typedef struct ResultB_Payload { + ResultB_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +} ResultB_Payload; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void use_a(struct ResultA_Payload _a); + +void use_b(struct ResultB_Payload _b); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/generic_enum_variant_collision_tag.c b/tests/expectations/generic_enum_variant_collision_tag.c new file mode 100644 index 00000000..6c30d00e --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision_tag.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include + +struct Payload { + int32_t x; +}; + +enum ResultA_Payload_Tag { + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, +}; + +struct ResultA_Payload { + enum ResultA_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +}; + +enum ResultB_Payload_Tag { + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, +}; + +struct ResultB_Payload { + enum ResultB_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +}; + +void use_a(struct ResultA_Payload _a); + +void use_b(struct ResultB_Payload _b); diff --git a/tests/expectations/generic_enum_variant_collision_tag.compat.c b/tests/expectations/generic_enum_variant_collision_tag.compat.c new file mode 100644 index 00000000..05867be2 --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision_tag.compat.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +struct Payload { + int32_t x; +}; + +enum ResultA_Payload_Tag { + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, +}; + +struct ResultA_Payload { + enum ResultA_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +}; + +enum ResultB_Payload_Tag { + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, +}; + +struct ResultB_Payload { + enum ResultB_Payload_Tag tag; + union { + struct { + struct Payload ok; + }; + struct { + void *err; + }; + }; +}; + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +void use_a(struct ResultA_Payload _a); + +void use_b(struct ResultB_Payload _b); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus diff --git a/tests/expectations/generic_enum_variant_collision_tag.pyx b/tests/expectations/generic_enum_variant_collision_tag.pyx new file mode 100644 index 00000000..d1a6966f --- /dev/null +++ b/tests/expectations/generic_enum_variant_collision_tag.pyx @@ -0,0 +1,32 @@ +from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t +from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t +cdef extern from *: + ctypedef bint bool + ctypedef struct va_list + +cdef extern from *: + + cdef struct Payload: + int32_t x; + + cdef enum ResultA_Payload_Tag: + ResultA_Payload_Ok_Payload, + ResultA_Payload_Err_Payload, + + cdef struct ResultA_Payload: + ResultA_Payload_Tag tag; + Payload ok; + void *err; + + cdef enum ResultB_Payload_Tag: + ResultB_Payload_Ok_Payload, + ResultB_Payload_Err_Payload, + + cdef struct ResultB_Payload: + ResultB_Payload_Tag tag; + Payload ok; + void *err; + + void use_a(ResultA_Payload _a); + + void use_b(ResultB_Payload _b); diff --git a/tests/rust/generic_enum_variant_collision.rs b/tests/rust/generic_enum_variant_collision.rs new file mode 100644 index 00000000..e1417706 --- /dev/null +++ b/tests/rust/generic_enum_variant_collision.rs @@ -0,0 +1,24 @@ +use std::os::raw::c_void; + +#[repr(C)] +pub enum ResultA { + Ok(T), + Err(*mut c_void), +} + +#[repr(C)] +pub enum ResultB { + Ok(T), + Err(*mut c_void), +} + +#[repr(C)] +pub struct Payload { + pub x: i32, +} + +#[no_mangle] +pub extern "C" fn use_a(_a: ResultA) {} + +#[no_mangle] +pub extern "C" fn use_b(_b: ResultB) {} diff --git a/tests/rust/generic_enum_variant_collision.toml b/tests/rust/generic_enum_variant_collision.toml new file mode 100644 index 00000000..6c945d43 --- /dev/null +++ b/tests/rust/generic_enum_variant_collision.toml @@ -0,0 +1,2 @@ +[enum] +prefix_with_name = true