rust-lang / rust-bindgen

Automatically generates Rust FFI bindings to C (and some C++) libraries.
https://rust-lang.github.io/rust-bindgen/
BSD 3-Clause "New" or "Revised" License
4.39k stars 691 forks source link

`__IncompleteArrayField` does not `impl ::std::marker::Copy` #2791

Closed VariableExp0rt closed 6 months ago

VariableExp0rt commented 6 months ago

Input C/C++ Header

/*
 * IO completion data structure (Completion Queue Entry)
 */
struct io_uring_cqe {
    __u64   user_data;  /* sqe->data submission passed back */
    __s32   res;        /* result code for this event */
    __u32   flags;

    /*
     * If the ring is initialized with IORING_SETUP_CQE32, then this field
     * contains 16-bytes of padding, doubling the size of the CQE.
     */
    __u64 big_cqe[];
};

Bindgen Invocation

bindgen::Builder::default()
        .header("wrapper.h")
        .allowlist_type("(io_uring_(sqe|cqe)|io_(sq|cq)ring_offsets|io_uring_params)")
        .blocklist_type("__(u8|u16|s32|u32|u64|kernel_rwf_t).*")
        .derive_debug(true)
        .derive_copy(true)
        .formatter(bindgen::Formatter::Rustfmt)
        .impl_debug(true)
        .layout_tests(false)
        .parse_callbacks(Box::new(CallBack))
        .generate()
        .expect("Unable to generate bindings");

Actual Results

/* automatically generated by rust-bindgen 0.69.4 */

#[repr(C)]
#[derive(Default)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
impl<T> __IncompleteArrayField<T> {
    #[inline]
    pub const fn new() -> Self {
        __IncompleteArrayField(::std::marker::PhantomData, [])
    }
    #[inline]
    pub fn as_ptr(&self) -> *const T {
        self as *const _ as *const T
    }
    #[inline]
    pub fn as_mut_ptr(&mut self) -> *mut T {
        self as *mut _ as *mut T
    }
    #[inline]
    pub unsafe fn as_slice(&self, len: usize) -> &[T] {
        ::std::slice::from_raw_parts(self.as_ptr(), len)
    }
    #[inline]
    pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
        ::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
    }
}
impl<T> ::std::fmt::Debug for __IncompleteArrayField<T> {
    fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
        fmt.write_str("__IncompleteArrayField")
    }
}

Expected Results

::std::marker::Copy is implemented for __IncompleteArrayField<__u64>

It seems that a few of the other examples I can find related to this field imply that it can be Copy, but perhaps I am missing some constraints about why it's omitted here (I am new to bindgen and Rust). Should I be doing this manually because PhantomData<T> cannot be sure that T is actually Copy? For instance the below works but I'm unsure it's "sound":

impl<T> ::std::clone::Clone for __IncompleteArrayField<T> {
    #[inline]
    fn clone(&self) -> Self {
        Self::new()
    }
}
impl<T: std::marker::Copy> ::std::marker::Copy for __IncompleteArrayField<T> {}

I should also mention I'm using blocklisted_item_implements_trait in ParseCallback to get the behaviour I need too (so that the io_uring types can implement the necessary traits).

Thanks!

emilio commented 6 months ago

That's intended, since we don't know how much memory we'd need to copy?

emilio commented 6 months ago

In general trailing incomplete array members are used to implement dynamically sized data. That can't implement Copy in rust, because it can't be allocated on the stack (easily at least), and bindgen doesn't know the length of the array per se.

Closing because I don't think generating Copy there is reasonable, but lmk if I'm missing something.