Skip to content

bindgen produces an E0588 when dealing with union and packed struct #1896

Open
@himself65

Description

See #1896 (comment) for minimal repro, OP below:

Input C/C++ Header

typedef unsigned int UINT32;
typedef unsigned short UINT16;
typedef union {
  struct {
    UINT32 Revision: 8;
    UINT32 ShiftPressed: 1;
    UINT32 ControlPressed: 1;
    UINT32 AltPressed: 1;
    UINT32 LogoPressed: 1;
    UINT32 MenuPressed: 1;
    UINT32 SysReqPressed: 1;
    UINT32 Reserved: 16;
    UINT32 InputKeyCount: 2;
  } Options;
  UINT32 PackedValue;
} EFI_BOOT_KEY_DATA;

#pragma pack(1)
typedef struct {
  EFI_BOOT_KEY_DATA KeyData;
  UINT32 BootOptionCrc;
  UINT16 BootOption;
} EFI_KEY_OPTION;
#pragma pack()

Bindgen Invocation

    let bindings = bindgen::Builder::default()
        .header("input.h")
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to generate bindings");
    let out_path = PathBuf::from(env::var("OUTPUT_DIR").unwrap());
    bindings
        .write_to_file(out_path.join("binding.rs"))
        .expect("Couldn't write bindings!");

Actual Results

> cargo build
   Compiling bread-os-lib v0.0.1 (/home/himself65/Desktop/github/bread-os)
error[E0588]: packed type cannot transitively contain a `#[repr(align)]` type
   --> src/../out/binding.rs:303:1
    |
303 | / pub struct EFI_KEY_OPTION {
304 | |     #[doc = ""]
305 | |     #[doc = " Specifies options about how the key will be processed."]
306 | |     #[doc = ""]
...   |
318 | |     pub BootOption: UINT16,
319 | | }
    | |_^
    |
note: `EFI_BOOT_KEY_DATA__bindgen_ty_1` has a `#[repr(align)]` attribute
   --> src/../out/binding.rs:99:1
    |
99  | / pub struct EFI_BOOT_KEY_DATA__bindgen_ty_1 {
100 | |     pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize], u16>,
101 | | }
    | |_^
note: `EFI_KEY_OPTION` contains a field of type `EFI_BOOT_KEY_DATA`
   --> src/../out/binding.rs:307:9
    |
307 |     pub KeyData: EFI_BOOT_KEY_DATA,
    |         ^^^^^^^
note: ...which contains a field of type `EFI_BOOT_KEY_DATA__bindgen_ty_1`
   --> src/../out/binding.rs:92:9
    |
92  |     pub Options: EFI_BOOT_KEY_DATA__bindgen_ty_1,
    |         ^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0588`.
error: could not compile `bread-os-lib`.

To learn more, run the command again with --verbose.

rust-bindgen code

/* automatically generated by rust-bindgen 0.55.1 */

#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct __BindgenBitfieldUnit<Storage, Align> {
    storage: Storage,
    align: [Align; 0],
}
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align> {
    #[inline]
    pub const fn new(storage: Storage) -> Self {
        Self { storage, align: [] }
    }
}
impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
where
    Storage: AsRef<[u8]> + AsMut<[u8]>,
{
    #[inline]
    pub fn get_bit(&self, index: usize) -> bool {
        debug_assert!(index / 8 < self.storage.as_ref().len());
        let byte_index = index / 8;
        let byte = self.storage.as_ref()[byte_index];
        let bit_index = if cfg!(target_endian = "big") {
            7 - (index % 8)
        } else {
            index % 8
        };
        let mask = 1 << bit_index;
        byte & mask == mask
    }
    #[inline]
    pub fn set_bit(&mut self, index: usize, val: bool) {
        debug_assert!(index / 8 < self.storage.as_ref().len());
        let byte_index = index / 8;
        let byte = &mut self.storage.as_mut()[byte_index];
        let bit_index = if cfg!(target_endian = "big") {
            7 - (index % 8)
        } else {
            index % 8
        };
        let mask = 1 << bit_index;
        if val {
            *byte |= mask;
        } else {
            *byte &= !mask;
        }
    }
    #[inline]
    pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
        let mut val = 0;
        for i in 0..(bit_width as usize) {
            if self.get_bit(i + bit_offset) {
                let index = if cfg!(target_endian = "big") {
                    bit_width as usize - 1 - i
                } else {
                    i
                };
                val |= 1 << index;
            }
        }
        val
    }
    #[inline]
    pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
        debug_assert!(bit_width <= 64);
        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
        for i in 0..(bit_width as usize) {
            let mask = 1 << i;
            let val_bit_is_set = val & mask == mask;
            let index = if cfg!(target_endian = "big") {
                bit_width as usize - 1 - i
            } else {
                i
            };
            self.set_bit(index + bit_offset, val_bit_is_set);
        }
    }
}
pub type UINT32 = ::std::os::raw::c_uint;
pub type UINT16 = ::std::os::raw::c_ushort;
#[doc = ""]
#[doc = " EFI Boot Key Data"]
#[doc = ""]
#[repr(C)]
#[derive(Copy, Clone)]
pub union EFI_BOOT_KEY_DATA {
    pub Options: EFI_BOOT_KEY_DATA__bindgen_ty_1,
    pub PackedValue: UINT32,
    _bindgen_union_align: u32,
}
#[repr(C)]
#[repr(align(4))]
#[derive(Debug, Copy, Clone)]
pub struct EFI_BOOT_KEY_DATA__bindgen_ty_1 {
    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 4usize], u16>,
}
#[test]
fn bindgen_test_layout_EFI_BOOT_KEY_DATA__bindgen_ty_1() {
    assert_eq!(
        ::std::mem::size_of::<EFI_BOOT_KEY_DATA__bindgen_ty_1>(),
        4usize,
        concat!("Size of: ", stringify!(EFI_BOOT_KEY_DATA__bindgen_ty_1))
    );
    assert_eq!(
        ::std::mem::align_of::<EFI_BOOT_KEY_DATA__bindgen_ty_1>(),
        4usize,
        concat!("Alignment of ", stringify!(EFI_BOOT_KEY_DATA__bindgen_ty_1))
    );
}
impl EFI_BOOT_KEY_DATA__bindgen_ty_1 {
    #[inline]
    pub fn Revision(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 8u8) as u32) }
    }
    #[inline]
    pub fn set_Revision(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(0usize, 8u8, val as u64)
        }
    }
    #[inline]
    pub fn ShiftPressed(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(8usize, 1u8) as u32) }
    }
    #[inline]
    pub fn set_ShiftPressed(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(8usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn ControlPressed(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(9usize, 1u8) as u32) }
    }
    #[inline]
    pub fn set_ControlPressed(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(9usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn AltPressed(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(10usize, 1u8) as u32) }
    }
    #[inline]
    pub fn set_AltPressed(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(10usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn LogoPressed(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(11usize, 1u8) as u32) }
    }
    #[inline]
    pub fn set_LogoPressed(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(11usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn MenuPressed(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(12usize, 1u8) as u32) }
    }
    #[inline]
    pub fn set_MenuPressed(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(12usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn SysReqPressed(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(13usize, 1u8) as u32) }
    }
    #[inline]
    pub fn set_SysReqPressed(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(13usize, 1u8, val as u64)
        }
    }
    #[inline]
    pub fn Reserved(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(14usize, 16u8) as u32) }
    }
    #[inline]
    pub fn set_Reserved(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(14usize, 16u8, val as u64)
        }
    }
    #[inline]
    pub fn InputKeyCount(&self) -> UINT32 {
        unsafe { ::std::mem::transmute(self._bitfield_1.get(30usize, 2u8) as u32) }
    }
    #[inline]
    pub fn set_InputKeyCount(&mut self, val: UINT32) {
        unsafe {
            let val: u32 = ::std::mem::transmute(val);
            self._bitfield_1.set(30usize, 2u8, val as u64)
        }
    }
    #[inline]
    pub fn new_bitfield_1(
        Revision: UINT32,
        ShiftPressed: UINT32,
        ControlPressed: UINT32,
        AltPressed: UINT32,
        LogoPressed: UINT32,
        MenuPressed: UINT32,
        SysReqPressed: UINT32,
        Reserved: UINT32,
        InputKeyCount: UINT32,
    ) -> __BindgenBitfieldUnit<[u8; 4usize], u16> {
        let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 4usize], u16> =
            Default::default();
        __bindgen_bitfield_unit.set(0usize, 8u8, {
            let Revision: u32 = unsafe { ::std::mem::transmute(Revision) };
            Revision as u64
        });
        __bindgen_bitfield_unit.set(8usize, 1u8, {
            let ShiftPressed: u32 = unsafe { ::std::mem::transmute(ShiftPressed) };
            ShiftPressed as u64
        });
        __bindgen_bitfield_unit.set(9usize, 1u8, {
            let ControlPressed: u32 = unsafe { ::std::mem::transmute(ControlPressed) };
            ControlPressed as u64
        });
        __bindgen_bitfield_unit.set(10usize, 1u8, {
            let AltPressed: u32 = unsafe { ::std::mem::transmute(AltPressed) };
            AltPressed as u64
        });
        __bindgen_bitfield_unit.set(11usize, 1u8, {
            let LogoPressed: u32 = unsafe { ::std::mem::transmute(LogoPressed) };
            LogoPressed as u64
        });
        __bindgen_bitfield_unit.set(12usize, 1u8, {
            let MenuPressed: u32 = unsafe { ::std::mem::transmute(MenuPressed) };
            MenuPressed as u64
        });
        __bindgen_bitfield_unit.set(13usize, 1u8, {
            let SysReqPressed: u32 = unsafe { ::std::mem::transmute(SysReqPressed) };
            SysReqPressed as u64
        });
        __bindgen_bitfield_unit.set(14usize, 16u8, {
            let Reserved: u32 = unsafe { ::std::mem::transmute(Reserved) };
            Reserved as u64
        });
        __bindgen_bitfield_unit.set(30usize, 2u8, {
            let InputKeyCount: u32 = unsafe { ::std::mem::transmute(InputKeyCount) };
            InputKeyCount as u64
        });
        __bindgen_bitfield_unit
    }
}
#[test]
fn bindgen_test_layout_EFI_BOOT_KEY_DATA() {
    assert_eq!(
        ::std::mem::size_of::<EFI_BOOT_KEY_DATA>(),
        4usize,
        concat!("Size of: ", stringify!(EFI_BOOT_KEY_DATA))
    );
    assert_eq!(
        ::std::mem::align_of::<EFI_BOOT_KEY_DATA>(),
        4usize,
        concat!("Alignment of ", stringify!(EFI_BOOT_KEY_DATA))
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<EFI_BOOT_KEY_DATA>())).Options as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(EFI_BOOT_KEY_DATA),
            "::",
            stringify!(Options)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<EFI_BOOT_KEY_DATA>())).PackedValue as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(EFI_BOOT_KEY_DATA),
            "::",
            stringify!(PackedValue)
        )
    );
}
#[repr(C, packed)]
#[derive(Copy, Clone)]
pub struct EFI_KEY_OPTION {
    #[doc = ""]
    #[doc = " Specifies options about how the key will be processed."]
    #[doc = ""]
    pub KeyData: EFI_BOOT_KEY_DATA,
    #[doc = ""]
    #[doc = " The CRC-32 which should match the CRC-32 of the entire EFI_LOAD_OPTION to"]
    #[doc = " which BootOption refers. If the CRC-32s do not match this value, then this key"]
    #[doc = " option is ignored."]
    #[doc = ""]
    pub BootOptionCrc: UINT32,
    #[doc = ""]
    #[doc = " The Boot#### option which will be invoked if this key is pressed and the boot option"]
    #[doc = " is active (LOAD_OPTION_ACTIVE is set)."]
    #[doc = ""]
    pub BootOption: UINT16,
}
#[test]
fn bindgen_test_layout_EFI_KEY_OPTION() {
    assert_eq!(
        ::std::mem::size_of::<EFI_KEY_OPTION>(),
        10usize,
        concat!("Size of: ", stringify!(EFI_KEY_OPTION))
    );
    assert_eq!(
        ::std::mem::align_of::<EFI_KEY_OPTION>(),
        1usize,
        concat!("Alignment of ", stringify!(EFI_KEY_OPTION))
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<EFI_KEY_OPTION>())).KeyData as *const _ as usize },
        0usize,
        concat!(
            "Offset of field: ",
            stringify!(EFI_KEY_OPTION),
            "::",
            stringify!(KeyData)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<EFI_KEY_OPTION>())).BootOptionCrc as *const _ as usize },
        4usize,
        concat!(
            "Offset of field: ",
            stringify!(EFI_KEY_OPTION),
            "::",
            stringify!(BootOptionCrc)
        )
    );
    assert_eq!(
        unsafe { &(*(::std::ptr::null::<EFI_KEY_OPTION>())).BootOption as *const _ as usize },
        8usize,
        concat!(
            "Offset of field: ",
            stringify!(EFI_KEY_OPTION),
            "::",
            stringify!(BootOption)
        )
    );
}

Expected Results

Build correctly

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions