Skip to content

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

Open
@himself65

Description

@himself65

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

Metadata

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