Description
When generating C bindings for Rust code that uses procedural macros to create VTable structs with associated types, cbindgen incorrectly resolves the associated type names. Instead of using the concrete VTable type name (e.g., SupertraitVTable), it generates a generic VTable type, which is not defined in the output header.
This issue occurs when the Rust code contains associated types like <(dyn Trait + 'static) as VTableRepr>::VTable that should resolve to a specific VTable struct name.
Expected Behavior
The generated C header should contain the correct concrete VTable type names:
struct SupertraitVTable {
void (*__drop)(void*);
MemoryLayout __layout;
void (*super_method)(DynSelf);
};
struct SubtraitVTable {
void (*__drop)(void*);
MemoryLayout __layout;
SupertraitVTable __vtable_Supertrait; // Correct: concrete type
void (*sub_method)(DynSelf);
};
struct DoubleSubtraitVTable {
void (*__drop)(void*);
MemoryLayout __layout;
SubtraitVTable __vtable_Subtrait; // Correct: concrete type
void (*double_sub_method)(DynSelf);
};
Actual Behavior
The generated header incorrectly uses a generic VTable type:
struct SupertraitVTable {
void (*__drop)(void*);
MemoryLayout __layout;
void (*super_method)(DynSelf);
};
struct SubtraitVTable {
void (*__drop)(void*);
MemoryLayout __layout;
VTable __vtable_Supertrait; // Incorrect: generic type, not defined
void (*sub_method)(DynSelf);
};
struct DoubleSubtraitVTable {
void (*__drop)(void*);
MemoryLayout __layout;
VTable __vtable_Subtrait; // Incorrect: generic type, not defined
void (*double_sub_method)(DynSelf);
};
Root Cause Analysis
In the expanded Rust code (generated by the dyntable procedural macro), the VTable structs contain fields with associated types:
// From src/lib.expand.rs:129-137
pub struct SubtraitVTable {
pub __drop: unsafe extern "C" fn(*mut ::core::ffi::c_void),
pub __layout: ::dyntable::alloc::MemoryLayout,
__vtable_Supertrait: <(dyn Supertrait + 'static) as ::dyntable::VTableRepr>::VTable, // Associated type
pub sub_method: extern "C" fn(::dyntable::DynSelf),
pub __generics: ::core::marker::PhantomData<()>,
}
The associated type <(dyn Supertrait + 'static) as ::dyntable::VTableRepr>::VTable should resolve to SupertraitVTable based on the trait implementation:
// From src/lib.expand.rs:38-40
impl ::dyntable::VTableRepr for dyn Supertrait {
type VTable = SupertraitVTable;
}
However, cbindgen fails to resolve this associated type correctly and instead generates a generic VTable placeholder.
Reproduction Steps
- Clone the reproduction repository: https://github.com/stevefan1999-personal/dyntable-fun
- Run
cargo build to generate the bindings
- Examine
bindings.h - the VTable fields will show VTable instead of the concrete type names
Environment
- cbindgen version: 0.29.0
- Rust version: (check Cargo.toml)
- Target: C/C++ bindings generation
- Configuration: See
cbindgen.toml in the repo
Additional Context
- The issue occurs with procedural macro-generated code from the
dyntable crate
- The expanded code in
src/lib.expand.rs shows the correct associated type resolution
- This affects the usability of the generated C bindings, as
VTable is not a defined type in the header
- The problem appears to be in cbindgen's handling of associated type resolution for complex generic paths
Potential Fix Location
Based on the open tabs, the issue likely resides in cbindgen's type resolution logic, possibly in:
src/bindgen/ir/generic_path.rs - handling of generic paths and associated types
- Type resolution for associated types in struct fields
Impact
This bug prevents the generation of correct C bindings for Rust code using dynamic trait objects with inheritance hierarchies, making FFI integration unreliable.
Description
When generating C bindings for Rust code that uses procedural macros to create VTable structs with associated types, cbindgen incorrectly resolves the associated type names. Instead of using the concrete VTable type name (e.g.,
SupertraitVTable), it generates a genericVTabletype, which is not defined in the output header.This issue occurs when the Rust code contains associated types like
<(dyn Trait + 'static) as VTableRepr>::VTablethat should resolve to a specific VTable struct name.Expected Behavior
The generated C header should contain the correct concrete VTable type names:
Actual Behavior
The generated header incorrectly uses a generic
VTabletype:Root Cause Analysis
In the expanded Rust code (generated by the
dyntableprocedural macro), the VTable structs contain fields with associated types:The associated type
<(dyn Supertrait + 'static) as ::dyntable::VTableRepr>::VTableshould resolve toSupertraitVTablebased on the trait implementation:However, cbindgen fails to resolve this associated type correctly and instead generates a generic
VTableplaceholder.Reproduction Steps
cargo buildto generate the bindingsbindings.h- the VTable fields will showVTableinstead of the concrete type namesEnvironment
cbindgen.tomlin the repoAdditional Context
dyntablecratesrc/lib.expand.rsshows the correct associated type resolutionVTableis not a defined type in the headerPotential Fix Location
Based on the open tabs, the issue likely resides in cbindgen's type resolution logic, possibly in:
src/bindgen/ir/generic_path.rs- handling of generic paths and associated typesImpact
This bug prevents the generation of correct C bindings for Rust code using dynamic trait objects with inheritance hierarchies, making FFI integration unreliable.