Skip to content

cbindgen fails to resolve associated type names in VTable structs generated by procedural macros #1106

@stevefan1999-personal

Description

@stevefan1999-personal

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

  1. Clone the reproduction repository: https://github.com/stevefan1999-personal/dyntable-fun
  2. Run cargo build to generate the bindings
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions