Pre-RFC: #[cfg(...)] syntax for size and alignment of FFI types


#1

When trying to define stand-in types for C unions and structures containing bit fields, the main challenge is matching the size and the alignment of the C definitions, which are dependent on the size and alignment of basic C types on the target. it would be nice to be able to select implementations via conditional compilation testing the size and alignment parameters of the target. The idea is similar to target_pointer_size:

    #[cfg(size_of(c_uint = "4", pointer = "8"))]
    #[repr(C)]
    pub struct UnionTwoUIntsOrPointer {
        // Should align like a pointer
        dummy: *mut libc::c_void
    }

    #[cfg(any(size_of(c_uint = "4", pointer = "4"),
              size_of(c_uint = "8", pointer = "8")))]
    #[repr(C)]
    pub struct UnionTwoUIntsOrPointer {
        // The largest variant on 32-bit or ILP64
        // is two uints, alignment also fits
        dummy: [libc::c_uint; 2]
    }

Core team meeting 2015-11-03 (1.5 release during work week; cargo check; const patterns; std::time; ffi)
#2

Given that we’ve already got #[repr©, I think the #[repr(union)] idea is probably a better approach - C ABI rules are awkward and subtle enough, varying with platform, that we really want them implemented in a single place…

That said, bitfields are a really thorny issue, and I’m not sure if it’s even sanely possible to implement bitfields with the #repr approach…

I don’t think moving all that knowledge to a library is a bad idea, but #[cfg()] strikes me as splitting that knowledge between the compiler and the library in the worst possible way.

As an aside though - I’m trying to find the code in the compiler that actually implements #[repr©], and I’m not having any luck - could someone point me in the right direction?


#3

The current issues with bitfields and unions aside, it may be generally useful to conditionalize code on the size/alignment of C types. C does not enjoy the neat determinism we have in Rust (and people had to fight to get it here), but we need to interface with C APIs without silly structure copying and excessive typecasts. There apparently was demand for target_pointer_size, which is the same idea limited to the pointer types.

Bitfields are going to be a marginal concept in Rust, so I’d argue #[repr] is where they belong.

I’m thinking of something along the lines of:

#[repr(C; bitfields(foo: 2, bar: 1))]
struct A {
    foo: c_uint,
    bar: c_uint
}

As FFI should be the only application for bit fields in Rust, setting them could be made unsafe, to punt on the implications of exceeding the range.


#4

Bitfields are a really easy thing to do. You can use a simple macro to handle them without much issue as I do in winapi. It adds some simple foo and set_foo methods and everything works out fine.

What is difficult is unions because the size/alignment of the union has to be the max of any variant and there’s really no sane way to do that with macros.


#5

That’s nice when you have only one ABI to deal with (as Win64 uses LLP64 it’s effectively the same for bitfield purposes), but in general the layout of bit fields is implementation-defined.


#6

If such a feature is implemented anyway, I don’t see why it couldn’t be useful for pure Rust code wishing to save memory. The semantics of exceeding the range should be the same as for other types of integer overflow.


#7

I have submitted the proposal as an RFC PR.