Input C/C++ Header
struct Empty {};
struct S {
[[no_unique_address]] Empty e;
int x;
};
int get_x(const S *s);
int get_x(const S *s) { return s->x; }
Bindgen Invocation
$ bindgen input.h --enable-cxx-namespaces --output bindings.rs -- -x c++ -std=c++20
and the same with --no-layout-tests.
Actual Results
clang++ layout (same compiler bindgen uses):
sizeof(S)=4 offsetof(S, x)=0 sizeof(Empty)=1
Setting s.x = 7 and calling get_x(&s) from C++ returns 7.
bindgen emits Empty as a 1-byte struct and then a normal repr(C) pair of fields:
pub struct Empty { pub _address: u8 }
pub struct S {
pub e: root::Empty,
pub x: ::std::os::raw::c_int,
}
rustc lays that out as sizeof=8, offsetof(x)=4. The generated layout tests still assert clang's numbers (sizeof == 4, offsetof(x) == 0), so the default output fails to compile:
error[E0080]: index out of bounds: the length is 1 but the index is 4
["Size of S"][size_of::<S>() - 4usize]
With --no-layout-tests the bindings compile. A Rust caller that does s.x = 7 and calls get_x(&s) (the C++ function) gets 0, because C++ reads offset 0 and the int in Rust lives at offset 4.
Expected Results
The generated Rust type should have the same size and x offset as clang (4 and 0), so that field access and FFI agree. Empty bases are already omitted in similar cases; [[no_unique_address]] on an empty member is not.
If overlapping fields cannot be expressed in repr(C), skipping the empty member (or making S a newtype over c_int) would match clang better than a 1-byte field plus padding.
Environment
bindgen: 0.72.1
clang++: Apple clang 21.0.0
rustc: 1.97.1
target: aarch64-apple-darwin
Reproduced with -std=c++20. The mismatch is the generated field list vs clang's layout, not a target-specific calling convention.
Input C/C++ Header
Bindgen Invocation
$ bindgen input.h --enable-cxx-namespaces --output bindings.rs -- -x c++ -std=c++20and the same with
--no-layout-tests.Actual Results
clang++ layout (same compiler bindgen uses):
Setting
s.x = 7and callingget_x(&s)from C++ returns7.bindgen emits
Emptyas a 1-byte struct and then a normalrepr(C)pair of fields:rustc lays that out as
sizeof=8,offsetof(x)=4. The generated layout tests still assert clang's numbers (sizeof == 4,offsetof(x) == 0), so the default output fails to compile:With
--no-layout-teststhe bindings compile. A Rust caller that doess.x = 7and callsget_x(&s)(the C++ function) gets0, because C++ reads offset 0 and theintin Rust lives at offset 4.Expected Results
The generated Rust type should have the same size and
xoffset as clang (4and0), so that field access and FFI agree. Empty bases are already omitted in similar cases;[[no_unique_address]]on an empty member is not.If overlapping fields cannot be expressed in
repr(C), skipping the empty member (or makingSa newtype overc_int) would match clang better than a 1-byte field plus padding.Environment
Reproduced with
-std=c++20. The mismatch is the generated field list vs clang's layout, not a target-specific calling convention.