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):
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
Input C/C++ Header
Bindgen Invocation
$ bindgen input.h --enable-cxx-namespaces --output bindings.rs -- -x c++ -std=c++20Layout 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:
bindgen still emits a 1-byte argument:
rustc therefore calls with two arguments:
Runtime,
take(E{}, 10):bindgen does not panic.
rustcaccepts 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
takeis effectivelyfn(c_int) -> c_int(or whatever equivalent rustc will lower the same way). Passing a dummyEby value makes rustc pass an extra integer and shifts the real arguments.Environment