-
Notifications
You must be signed in to change notification settings - Fork 364
Description
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 buildto generate the bindings - Examine
bindings.h- the VTable fields will showVTableinstead of the concrete type names
Environment
- cbindgen version: 0.29.0
- Rust version: (check Cargo.toml)
- Target: C/C++ bindings generation
- Configuration: See
cbindgen.tomlin the repo
Additional Context
- The issue occurs with procedural macro-generated code from the
dyntablecrate - The expanded code in
src/lib.expand.rsshows the correct associated type resolution - This affects the usability of the generated C bindings, as
VTableis 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.