Skip to content

_BitInt types lose their signedness and get widened to the next storage size #3398

Description

@lwz23

Input C/C++ Header

_BitInt(24) f(_BitInt(24) v);

Bindgen Invocation

$ bindgen input.h

Actual Results

extern "C" {
    pub fn f(v: u32) -> u32;
}

Expected Results

A signed type, or an error.

_BitInt looks unhandled: libclang reports it as CXType_Unexposed, so it ends up in the opaque blob fallback, which picks a type by size from a table that only has unsigned integers in it. So every signed _BitInt(N) comes out unsigned, and widths that aren't a whole storage unit get widened — _BitInt(7)u8, _BitInt(24)u32, _BitInt(33)u64. An extern _BitInt(24) x; declaration is dropped from the output entirely.

The widening isn't only cosmetic, because a sub-storage-width _BitInt carries an extension contract on x86-64. clang emits

declare signext i24 @f(i24 noundef signext)

while the binding lowers to a plain i32, so Rust reads bits that the ABI defines as sign extension. Calling f(0x7FFFFF) (increment the largest positive 24-bit value) returns -8388608 from C and 4286578688 through the binding. Nothing is printed on the way.

One thing worth not changing: unsigned _BitInt(128) currently maps to __BindgenOpaqueArray<u64, 2> and that's already correct — x86-64 gives _BitInt(128) alignment 8 rather than __int128's 16, and rust-lang/rust#137306 says i128 won't be guaranteed to match it.

$ bindgen --version
bindgen 0.72.1
$ clang --version
Ubuntu clang version 15.0.7
$ rustc --version
rustc 1.75.0

Target is x86_64-unknown-linux-gnu.

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