rust-lang / rust-lang/rust-bindgen
`[[no_unique_address]]` on an empty member is laid out as a 1-byte field, disagreeing with clang
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 5.3k
- Forks
- 829
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 15
Description
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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the input.h example with bindgen using C++20, then compare the generated S layout with clang's sizeof and offsetof results. Investigate how empty bases and empty members are represented, and consider the expected result achieved by skipping or otherwise representing the empty member; done means the generated Rust layout matches clang and the C++ get_x call reads the Rust-assigned value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, rust
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100