Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failed to generate Default impl for arrays larger than 32 entries #2803

Open
wmmc88 opened this issue Apr 9, 2024 · 7 comments
Open

Failed to generate Default impl for arrays larger than 32 entries #2803

wmmc88 opened this issue Apr 9, 2024 · 7 comments

Comments

@wmmc88
Copy link

wmmc88 commented Apr 9, 2024

Input C/C++ Header

#define CCHDEVICENAME 32
typedef long LONG;
typedef char CHAR;
typedef unsigned long DWORD;

typedef struct tagRECT
{
    LONG    left;
    LONG    top;
    LONG    right;
    LONG    bottom;
} RECT;
typedef struct tagMONITORINFO
{
    DWORD   cbSize;
    RECT    rcMonitor;
    RECT    rcWork;
    DWORD   dwFlags;
} MONITORINFO, *LPMONITORINFO;
typedef struct tagMONITORINFOEXA
{
    MONITORINFO;
    CHAR        szDevice[CCHDEVICENAME];
} MONITORINFOEXA, *LPMONITORINFOEXA;
        

Bindgen Invocation

use std::env;
use std::path::PathBuf;

fn main() {

    let bindings = bindgen::Builder::default()
        .header_contents(
            "input.h",
            r#"
#define CCHDEVICENAME 32
typedef long LONG;
typedef char CHAR;
typedef unsigned long DWORD;

typedef struct tagRECT
{
    LONG    left;
    LONG    top;
    LONG    right;
    LONG    bottom;
} RECT;
typedef struct tagMONITORINFO
{
    DWORD   cbSize;
    RECT    rcMonitor;
    RECT    rcWork;
    DWORD   dwFlags;
} MONITORINFO, *LPMONITORINFO;
typedef struct tagMONITORINFOEXA
{
    MONITORINFO;
    CHAR        szDevice[CCHDEVICENAME];
} MONITORINFOEXA, *LPMONITORINFOEXA;"#,
        )
        .derive_default(true)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .generate()
        .expect("Unable to generate bindings");

    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("bindings.rs"))
        .expect("Couldn't write bindings!");
}

Actual Results

error[E0277]: the trait bound `[u8; 40]: Default` is not satisfied
   --> D:\git-repos\bindgen-test\target\debug\build\bindgen-test-39d5729a71a2ce1c\out/bindings.rs:140:5
    |
138 | #[derive(Debug, Default, Copy, Clone)]
    |                 ------- in this derive macro expansion
139 | pub struct tagMONITORINFOEXA {
140 |     pub __bindgen_padding_0: [u8; 40usize],
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[u8; 40]`
    |
    = help: the following other types implement trait `Default`:
              [T; 0]
              [T; 1]
              [T; 2]
              [T; 3]
              [T; 4]
              [T; 5]
              [T; 6]
              [T; 7]
            and 27 others
    = note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
error: could not compile `bindgen-test` (lib) due to 1 previous error

Caused by:
  process didn't exit successfully: `sccache rustc --crate-name bindgen_test --edition=2021 src\lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=191 --crate-type lib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 -C metadata=dfb5b3ab9206c6f2 -C extra-filename=-dfb5b3ab9206c6f2 --out-dir D:\git-repos\bindgen-test\target\debug\deps -C incremental=D:\git-repos\bindgen-test\target\debug\incremental -L dependency=D:\git-repos\bindgen-test\target\debug\deps` (exit code: 1)

and/or

/* automatically generated by rust-bindgen 0.69.4 */

pub const CCHDEVICENAME: u32 = 32;
pub type LONG = ::std::os::raw::c_long;
pub type CHAR = ::std::os::raw::c_char;
pub type DWORD = ::std::os::raw::c_ulong;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct tagRECT {
    pub left: LONG,
    pub top: LONG,
    pub right: LONG,
    pub bottom: LONG,
}
#[test]
fn bindgen_test_layout_tagRECT() {
    const UNINIT: ::std::mem::MaybeUninit<tagRECT> = ::std::mem::MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    assert_eq!(
        ::std::mem::size_of::<tagRECT>(),
        16usize,
        concat!("Size of: ", stringify!(tagRECT))
    );
    assert_eq!(
        ::std::mem::align_of::<tagRECT>(),
        4usize,
        concat!("Alignment of ", stringify!(tagRECT))
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).left) as usize - ptr as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(tagRECT),
            "::",
            stringify!(left)
        )
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).top) as usize - ptr as usize },
        4usize,
        concat!(
            "Offset of field: ",
            stringify!(tagRECT),
            "::",
            stringify!(top)
        )
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).right) as usize - ptr as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(tagRECT),
            "::",
            stringify!(right)
        )
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).bottom) as usize - ptr as usize },
        12usize,
        concat!(
            "Offset of field: ",
            stringify!(tagRECT),
            "::",
            stringify!(bottom)
        )
    );
}
pub type RECT = tagRECT;
#[repr(C)]
#[derive(Debug, Default, Copy, Clone)]
pub struct tagMONITORINFO {
    pub cbSize: DWORD,
    pub rcMonitor: RECT,
    pub rcWork: RECT,
    pub dwFlags: DWORD,
}
#[test]
fn bindgen_test_layout_tagMONITORINFO() {
    const UNINIT: ::std::mem::MaybeUninit<tagMONITORINFO> = ::std::mem::MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    assert_eq!(
        ::std::mem::size_of::<tagMONITORINFO>(),
        40usize,
        concat!("Size of: ", stringify!(tagMONITORINFO))
    );
    assert_eq!(
        ::std::mem::align_of::<tagMONITORINFO>(),
        4usize,
        concat!("Alignment of ", stringify!(tagMONITORINFO))
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).cbSize) as usize - ptr as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(tagMONITORINFO),
            "::",
            stringify!(cbSize)
        )
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).rcMonitor) as usize - ptr as usize },
        4usize,
        concat!(
            "Offset of field: ",
            stringify!(tagMONITORINFO),
            "::",
            stringify!(rcMonitor)
        )
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).rcWork) as usize - ptr as usize },
        20usize,
        concat!(
            "Offset of field: ",
            stringify!(tagMONITORINFO),
            "::",
            stringify!(rcWork)
        )
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).dwFlags) as usize - ptr as usize },
        36usize,
        concat!(
            "Offset of field: ",
            stringify!(tagMONITORINFO),
            "::",
            stringify!(dwFlags)
        )
    );
}
pub type MONITORINFO = tagMONITORINFO;
pub type LPMONITORINFO = *mut tagMONITORINFO;
#[repr(C)]
#[repr(align(4))]
#[derive(Debug, Default, Copy, Clone)]
pub struct tagMONITORINFOEXA {
    pub __bindgen_padding_0: [u8; 40usize],
    pub szDevice: [CHAR; 32usize],
}
#[test]
fn bindgen_test_layout_tagMONITORINFOEXA() {
    const UNINIT: ::std::mem::MaybeUninit<tagMONITORINFOEXA> = ::std::mem::MaybeUninit::uninit();
    let ptr = UNINIT.as_ptr();
    assert_eq!(
        ::std::mem::size_of::<tagMONITORINFOEXA>(),
        72usize,
        concat!("Size of: ", stringify!(tagMONITORINFOEXA))
    );
    assert_eq!(
        ::std::mem::align_of::<tagMONITORINFOEXA>(),
        4usize,
        concat!("Alignment of ", stringify!(tagMONITORINFOEXA))
    );
    assert_eq!(
        unsafe { ::std::ptr::addr_of!((*ptr).szDevice) as usize - ptr as usize },
        40usize,
        concat!(
            "Offset of field: ",
            stringify!(tagMONITORINFOEXA),
            "::",
            stringify!(szDevice)
        )
    );
}
pub type MONITORINFOEXA = tagMONITORINFOEXA;
pub type LPMONITORINFOEXA = *mut tagMONITORINFOEXA;

Expected Results

I expected it to generate a default implementation successfully. This seems to be another regression of #1718/#1719 and #2082, where it fails to generate a valid Default when the array has more than the 32 entries (where rust itself generates a Default for <=32 entries).

@emilio
Copy link
Contributor

emilio commented Apr 19, 2024

Which rust version are you using? We have code to deal with this, see a380678

@emilio
Copy link
Contributor

emilio commented Apr 19, 2024

Ah, so I couldn't repro this, but that's because for some reason --target=x86_64-pc-win32 is needed to repro:

./target/debug/bindgen --with-derive-default t.h -- --target=x86_64-pc-win32

Is the command I used to reproduce. It seems the MONITORINFO; line is kind of like a member, but it's unnamed and clang doesn't expose it as such... o.O

@emilio
Copy link
Contributor

emilio commented Apr 19, 2024

And somehow that only happens when targeting windows

@emilio
Copy link
Contributor

emilio commented Apr 19, 2024

I think that limitation in rustc is mostly historical? rust-lang/rust#124163 removes it.

emilio added a commit to emilio/rust that referenced this issue Apr 19, 2024
…r than 32 elements.

Much like rust-lang#74060 did for every other trait.

This avoids tools like rust-bindgen having to painfully work around this
limitations, sometimes incorrectly like in
rust-lang/rust-bindgen#2803
@wmmc88
Copy link
Author

wmmc88 commented Apr 19, 2024

@emilio

Which rust version are you using? We have code to deal with this, see a380678

I'm able to repro on latest stable(stable-x86_64-pc-windows-msvc unchanged - rustc 1.77.2 (25ef9e3d8 2024-04-09)), beta(beta-x86_64-pc-windows-msvc updated - rustc 1.78.0-beta.7 (6fd191292 2024-04-12) (from rustc 1.78.0-beta.5 (9eff51035 2024-04-06))), and nightly(nightly-x86_64-pc-windows-msvc updated - rustc 1.79.0-nightly (e3181b091 2024-04-18) (from rustc 1.79.0-nightly (8b2459c1f 2024-04-09))). I'm assuming this is some sort of regression.

Ah, so I couldn't repro this, but that's because for some reason --target=x86_64-pc-win32 is needed to repro:

I am using x64 windows w/ msvc toolchain, so my native target is x86_64-pc-windows-msvc

I've also tried enabling clang's msvc compat, and it doesn't alter behavior here:

.clang_args([
            "-fms-compatibility",
            "-fms-extensions",
            "-fdelayed-template-parsing",
        ])

@wmmc88
Copy link
Author

wmmc88 commented Apr 24, 2024

@emilio looks like your proposed patch to rust was rejected. Can this continue to be handled by bindgen?

@pvdrz
Copy link
Contributor

pvdrz commented Apr 24, 2024

I think this issue is a bit deeper than just a missing Default implementation. If we had something like this:

// hello.c
typedef struct bar {
  int *a[17];
} bar;

typedef struct foo {
  bar;
  int b;
} foo;

and ran bindgen hello.c --with-derive-default --no-layout-tests -- --target=x86_64-pc-windows-mvsc -fms-extensions you'd get:

/* automatically generated by rust-bindgen 0.69.4 */

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct bar {
    pub a: [*mut ::std::os::raw::c_int; 17usize],
}
impl Default for bar {
    fn default() -> Self {
        let mut s = ::std::mem::MaybeUninit::<Self>::uninit();
        unsafe {
            ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1);
            s.assume_init()
        }
    }
}
#[repr(C)]
#[repr(align(8))]
#[derive(Debug, Default, Copy, Clone)]
pub struct foo {
    pub __bindgen_padding_0: [u32; 34usize],
    pub b: ::std::os::raw::c_int,
}

implementing Default for foo is most likely wrong as you'd end up with an array of null pointers. I guess this is just a symptom of clang exposing the anonymous struct inside foo as an opaque type instead of giving the proper information.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants