Skip to content

C++ empty class by-value arguments are passed even though clang omits them #3439

Description

@lwz23

Input C/C++ Header

struct E {};
int take(E e, int x);
int take(E e, int x) { (void)e; return x + 1; }

Bindgen Invocation

$ bindgen input.h --enable-cxx-namespaces --output bindings.rs -- -x c++ -std=c++20

Layout tests are left on (the default).

Actual Results

sizeof(E) is 1 in both clang++ and the generated Rust, so the layout tests compile.

clang++ does not pass the empty class at all:

define i32 @_Z4take1Ei(i32 noundef %0)
; caller:
call i32 @_Z4take1Ei(i32 noundef 10)

bindgen still emits a 1-byte argument:

pub struct E { pub _address: u8 }
pub fn take(e: root::E, x: c_int) -> c_int;

rustc therefore calls with two arguments:

declare i32 @"\01__Z4take1Ei"(i64, i32)
call i32 @"\01__Z4take1Ei"(i64 %1, i32 10)

Runtime, take(E{}, 10):

C++:  11
Rust: 1

bindgen does not panic. rustc accepts the bindings, including the layout tests.

Expected Results

The generated signature should match the C++ ABI used by clang: the empty class parameter is not passed, so take is effectively fn(c_int) -> c_int (or whatever equivalent rustc will lower the same way). Passing a dummy E by value makes rustc pass an extra integer and shifts the real arguments.

Environment

bindgen:  0.72.1
clang++:  Apple clang 21.0.0
rustc:    1.97.1
target:   aarch64-apple-darwin

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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