From c6d783a7c8ad95922dddbd1df201e68dc163891d Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 24 Jul 2026 13:30:09 +0000 Subject: [PATCH] Fix panic on allowlisted class with bitfield and base class --- .../tests/bitfield-unparsed-method.rs | 591 ++++++++++++++++++ .../tests/non-recursive-array-layout.rs | 55 ++ .../tests/non-recursive-field-layout.rs | 23 + .../headers/bitfield-unparsed-method.hpp | 11 + .../headers/non-recursive-array-layout.hpp | 11 + .../headers/non-recursive-field-layout.hpp | 11 + bindgen/ir/comp.rs | 15 +- bindgen/ir/context.rs | 13 + bindgen/ir/ty.rs | 4 +- 9 files changed, 727 insertions(+), 7 deletions(-) create mode 100644 bindgen-tests/tests/expectations/tests/bitfield-unparsed-method.rs create mode 100644 bindgen-tests/tests/expectations/tests/non-recursive-array-layout.rs create mode 100644 bindgen-tests/tests/expectations/tests/non-recursive-field-layout.rs create mode 100644 bindgen-tests/tests/headers/bitfield-unparsed-method.hpp create mode 100644 bindgen-tests/tests/headers/non-recursive-array-layout.hpp create mode 100644 bindgen-tests/tests/headers/non-recursive-field-layout.hpp diff --git a/bindgen-tests/tests/expectations/tests/bitfield-unparsed-method.rs b/bindgen-tests/tests/expectations/tests/bitfield-unparsed-method.rs new file mode 100644 index 0000000000..06828f6e2b --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/bitfield-unparsed-method.rs @@ -0,0 +1,591 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct __BindgenBitfieldUnit { + storage: Storage, +} +impl __BindgenBitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { + Self { storage } + } +} +impl __BindgenBitfieldUnit +where + Storage: AsRef<[u8]> + AsMut<[u8]>, +{ + #[inline] + fn extract_bit(byte: u8, index: usize) -> bool { + 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 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]; + Self::extract_bit(byte, index) + } + #[inline] + pub unsafe fn raw_get_bit(this: *const Self, index: usize) -> bool { + debug_assert!(index / 8 < core::mem::size_of::()); + let byte_index = index / 8; + let byte = unsafe { + *(core::ptr::addr_of!((*this).storage) as *const u8) + .offset(byte_index as isize) + }; + Self::extract_bit(byte, index) + } + #[inline] + fn change_bit(byte: u8, index: usize, val: bool) -> u8 { + 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 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]; + *byte = Self::change_bit(*byte, index, val); + } + #[inline] + pub unsafe fn raw_set_bit(this: *mut Self, index: usize, val: bool) { + debug_assert!(index / 8 < core::mem::size_of::()); + let byte_index = index / 8; + let byte = unsafe { + (core::ptr::addr_of_mut!((*this).storage) as *mut u8) + .offset(byte_index as isize) + }; + unsafe { *byte = Self::change_bit(*byte, index, val) }; + } + #[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) + 7) / 8 <= self.storage.as_ref().len(), + ); + if bit_width == 0 { + return 0; + } + let mut val = 0u64; + let storage = self.storage.as_ref(); + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + if cfg!(target_endian = "big") { + for i in 0..bytes_needed { + val |= (storage[start_byte + i].reverse_bits() as u64) << (i * 8); + } + } else { + for i in 0..bytes_needed { + val |= (storage[start_byte + i] as u64) << (i * 8); + } + } + val >>= bit_shift; + if bit_width < 64 { + val &= (1u64 << bit_width) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - bit_width as usize); + } + val + } + #[inline] + pub unsafe fn raw_get(this: *const Self, bit_offset: usize, bit_width: u8) -> u64 { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < core::mem::size_of::()); + debug_assert!( + (bit_offset + (bit_width as usize) + 7) / 8 + <= core::mem::size_of::(), + ); + if bit_width == 0 { + return 0; + } + let mut val = 0u64; + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; + if cfg!(target_endian = "big") { + for i in 0..bytes_needed { + let byte = unsafe { *storage_ptr.add(start_byte + i) }; + val |= (byte.reverse_bits() as u64) << (i * 8); + } + } else { + for i in 0..bytes_needed { + let byte = unsafe { *storage_ptr.add(start_byte + i) }; + val |= (byte as u64) << (i * 8); + } + } + val >>= bit_shift; + if bit_width < 64 { + val &= (1u64 << bit_width) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - bit_width as usize); + } + 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) + 7) / 8 <= self.storage.as_ref().len(), + ); + if bit_width == 0 { + return; + } + let mut val = val; + if bit_width < 64 { + val &= (1u64 << bit_width) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - bit_width as usize); + } + let storage = self.storage.as_mut(); + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + val <<= bit_shift; + let field_mask = if bit_width as usize + bit_shift >= 64 { + !0u64 << bit_shift + } else { + ((1u64 << bit_width) - 1) << bit_shift + }; + for i in 0..bytes_needed { + let byte_val = (val >> (i * 8)) as u8; + let byte_mask = (field_mask >> (i * 8)) as u8; + if cfg!(target_endian = "big") { + let byte = storage[start_byte + i].reverse_bits(); + let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); + storage[start_byte + i] = new_byte.reverse_bits(); + } else { + storage[start_byte + i] = (storage[start_byte + i] & !byte_mask) + | (byte_val & byte_mask); + } + } + } + #[inline] + pub unsafe fn raw_set(this: *mut Self, bit_offset: usize, bit_width: u8, val: u64) { + debug_assert!(bit_width <= 64); + debug_assert!(bit_offset / 8 < core::mem::size_of::()); + debug_assert!( + (bit_offset + (bit_width as usize) + 7) / 8 + <= core::mem::size_of::(), + ); + if bit_width == 0 { + return; + } + let mut val = val; + if bit_width < 64 { + val &= (1u64 << bit_width) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - bit_width as usize); + } + let start_byte = bit_offset / 8; + let bit_shift = bit_offset % 8; + let bytes_needed = (bit_width as usize + bit_shift + 7) / 8; + val <<= bit_shift; + let field_mask = if bit_width as usize + bit_shift >= 64 { + !0u64 << bit_shift + } else { + ((1u64 << bit_width) - 1) << bit_shift + }; + let storage_ptr = unsafe { core::ptr::addr_of_mut!((*this).storage) as *mut u8 }; + for i in 0..bytes_needed { + let byte_val = (val >> (i * 8)) as u8; + let byte_mask = (field_mask >> (i * 8)) as u8; + let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; + if cfg!(target_endian = "big") { + let byte = unsafe { (*byte_ptr).reverse_bits() }; + let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); + unsafe { *byte_ptr = new_byte.reverse_bits() }; + } else { + unsafe { *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) }; + } + } + } +} +/// Const-generic methods for efficient bitfield access when offset and width +/// are known at compile time. +impl __BindgenBitfieldUnit<[u8; N]> { + /// Get a field using const generics for compile-time optimization. + /// Uses native word size operations when the field fits in usize. + #[inline] + pub const fn get_const(&self) -> u64 { + debug_assert!(BIT_WIDTH <= 64); + debug_assert!(BIT_OFFSET / 8 < N); + debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); + if BIT_WIDTH == 0 { + return 0; + } + let start_byte = BIT_OFFSET / 8; + let bit_shift = BIT_OFFSET % 8; + let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; + if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { + let mut val = 0usize; + if cfg!(target_endian = "big") { + let mut i = 0; + while i < bytes_needed { + val + |= (self.storage[start_byte + i].reverse_bits() as usize) + << (i * 8); + i += 1; + } + } else { + let mut i = 0; + while i < bytes_needed { + val |= (self.storage[start_byte + i] as usize) << (i * 8); + i += 1; + } + } + val >>= bit_shift; + if (BIT_WIDTH as u32) < usize::BITS { + val &= (1usize << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); + } + val as u64 + } else { + let mut val = 0u64; + if cfg!(target_endian = "big") { + let mut i = 0; + while i < bytes_needed { + val + |= (self.storage[start_byte + i].reverse_bits() as u64) + << (i * 8); + i += 1; + } + } else { + let mut i = 0; + while i < bytes_needed { + val |= (self.storage[start_byte + i] as u64) << (i * 8); + i += 1; + } + } + val >>= bit_shift; + if BIT_WIDTH < 64 { + val &= (1u64 << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); + } + val + } + } + /// Set a field using const generics for compile-time optimization. + /// Uses native word size operations when the field fits in usize. + #[inline] + pub fn set_const(&mut self, val: u64) { + debug_assert!(BIT_WIDTH <= 64); + debug_assert!(BIT_OFFSET / 8 < N); + debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); + if BIT_WIDTH == 0 { + return; + } + let start_byte = BIT_OFFSET / 8; + let bit_shift = BIT_OFFSET % 8; + let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; + if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { + let mut val = val as usize; + if (BIT_WIDTH as u32) < usize::BITS { + val &= (1usize << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); + } + val <<= bit_shift; + let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { + !0usize << bit_shift + } else { + ((1usize << BIT_WIDTH) - 1) << bit_shift + }; + let mut i = 0; + while i < bytes_needed { + let byte_val = (val >> (i * 8)) as u8; + let byte_mask = (field_mask >> (i * 8)) as u8; + if cfg!(target_endian = "big") { + let byte = self.storage[start_byte + i].reverse_bits(); + let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); + self.storage[start_byte + i] = new_byte.reverse_bits(); + } else { + self.storage[start_byte + i] = (self.storage[start_byte + i] + & !byte_mask) | (byte_val & byte_mask); + } + i += 1; + } + } else { + let mut val = val; + if BIT_WIDTH < 64 { + val &= (1u64 << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); + } + val <<= bit_shift; + let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { + !0u64 << bit_shift + } else { + ((1u64 << BIT_WIDTH) - 1) << bit_shift + }; + let mut i = 0; + while i < bytes_needed { + let byte_val = (val >> (i * 8)) as u8; + let byte_mask = (field_mask >> (i * 8)) as u8; + if cfg!(target_endian = "big") { + let byte = self.storage[start_byte + i].reverse_bits(); + let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); + self.storage[start_byte + i] = new_byte.reverse_bits(); + } else { + self.storage[start_byte + i] = (self.storage[start_byte + i] + & !byte_mask) | (byte_val & byte_mask); + } + i += 1; + } + } + } + /// Raw pointer get using const generics for compile-time optimization. + /// Uses native word size operations when the field fits in usize. + #[inline] + pub const unsafe fn raw_get_const( + this: *const Self, + ) -> u64 { + debug_assert!(BIT_WIDTH <= 64); + debug_assert!(BIT_OFFSET / 8 < N); + debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); + if BIT_WIDTH == 0 { + return 0; + } + let start_byte = BIT_OFFSET / 8; + let bit_shift = BIT_OFFSET % 8; + let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; + let storage_ptr = unsafe { core::ptr::addr_of!((*this).storage) as *const u8 }; + if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { + let mut val = 0usize; + if cfg!(target_endian = "big") { + let mut i = 0; + while i < bytes_needed { + let byte = unsafe { *storage_ptr.add(start_byte + i) }; + val |= (byte.reverse_bits() as usize) << (i * 8); + i += 1; + } + } else { + let mut i = 0; + while i < bytes_needed { + let byte = unsafe { *storage_ptr.add(start_byte + i) }; + val |= (byte as usize) << (i * 8); + i += 1; + } + } + val >>= bit_shift; + if (BIT_WIDTH as u32) < usize::BITS { + val &= (1usize << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); + } + val as u64 + } else { + let mut val = 0u64; + if cfg!(target_endian = "big") { + let mut i = 0; + while i < bytes_needed { + let byte = unsafe { *storage_ptr.add(start_byte + i) }; + val |= (byte.reverse_bits() as u64) << (i * 8); + i += 1; + } + } else { + let mut i = 0; + while i < bytes_needed { + let byte = unsafe { *storage_ptr.add(start_byte + i) }; + val |= (byte as u64) << (i * 8); + i += 1; + } + } + val >>= bit_shift; + if BIT_WIDTH < 64 { + val &= (1u64 << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); + } + val + } + } + /// Raw pointer set using const generics for compile-time optimization. + /// Uses native word size operations when the field fits in usize. + #[inline] + pub unsafe fn raw_set_const( + this: *mut Self, + val: u64, + ) { + debug_assert!(BIT_WIDTH <= 64); + debug_assert!(BIT_OFFSET / 8 < N); + debug_assert!((BIT_OFFSET + (BIT_WIDTH as usize) + 7) / 8 <= N); + if BIT_WIDTH == 0 { + return; + } + let start_byte = BIT_OFFSET / 8; + let bit_shift = BIT_OFFSET % 8; + let bytes_needed = (BIT_WIDTH as usize + bit_shift + 7) / 8; + let storage_ptr = this.cast::<[u8; N]>().cast::(); + if BIT_WIDTH as usize + bit_shift <= usize::BITS as usize { + let mut val = val as usize; + if (BIT_WIDTH as u32) < usize::BITS { + val &= (1usize << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (usize::BITS as usize - BIT_WIDTH as usize); + } + val <<= bit_shift; + let field_mask = if BIT_WIDTH as usize + bit_shift >= usize::BITS as usize { + !0usize << bit_shift + } else { + ((1usize << BIT_WIDTH) - 1) << bit_shift + }; + let mut i = 0; + while i < bytes_needed { + let byte_val = (val >> (i * 8)) as u8; + let byte_mask = (field_mask >> (i * 8)) as u8; + let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; + if cfg!(target_endian = "big") { + let byte = unsafe { (*byte_ptr).reverse_bits() }; + let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); + unsafe { *byte_ptr = new_byte.reverse_bits() }; + } else { + unsafe { + *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) + }; + } + i += 1; + } + } else { + let mut val = val; + if BIT_WIDTH < 64 { + val &= (1u64 << BIT_WIDTH) - 1; + } + if cfg!(target_endian = "big") { + val = val.reverse_bits() >> (64 - BIT_WIDTH as usize); + } + val <<= bit_shift; + let field_mask = if BIT_WIDTH as usize + bit_shift >= 64 { + !0u64 << bit_shift + } else { + ((1u64 << BIT_WIDTH) - 1) << bit_shift + }; + let mut i = 0; + while i < bytes_needed { + let byte_val = (val >> (i * 8)) as u8; + let byte_mask = (field_mask >> (i * 8)) as u8; + let byte_ptr = unsafe { storage_ptr.add(start_byte + i) }; + if cfg!(target_endian = "big") { + let byte = unsafe { (*byte_ptr).reverse_bits() }; + let new_byte = (byte & !byte_mask) | (byte_val & byte_mask); + unsafe { *byte_ptr = new_byte.reverse_bits() }; + } else { + unsafe { + *byte_ptr = (*byte_ptr & !byte_mask) | (byte_val & byte_mask) + }; + } + i += 1; + } + } + } +} +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct Base { + pub _address: u8, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of Base"][::std::mem::size_of::() - 1usize]; + ["Alignment of Base"][::std::mem::align_of::() - 1usize]; +}; +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct StructWithBitfieldAndMethod { + pub _bindgen_align: [u32; 0], + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>, + pub __bindgen_padding_0: [u8; 3usize], +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + [ + "Size of StructWithBitfieldAndMethod", + ][::std::mem::size_of::() - 4usize]; + [ + "Alignment of StructWithBitfieldAndMethod", + ][::std::mem::align_of::() - 4usize]; +}; +unsafe extern "C" { + #[link_name = "\u{1}_ZN27StructWithBitfieldAndMethod14regular_methodEv"] + pub fn StructWithBitfieldAndMethod_regular_method( + this: *mut StructWithBitfieldAndMethod, + ); +} +impl StructWithBitfieldAndMethod { + #[inline] + pub fn field(&self) -> ::std::os::raw::c_int { + self._bitfield_1.get_const::<0usize, 1u8>() as u32 as _ + } + #[inline] + pub fn set_field(&mut self, val: ::std::os::raw::c_int) { + let val: u32 = val as _; + self._bitfield_1.set_const::<0usize, 1u8>(val as u64) + } + #[inline] + pub unsafe fn field_raw(this: *const Self) -> ::std::os::raw::c_int { + unsafe { + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_get_const::<0usize, 1u8>(::std::ptr::addr_of!((*this)._bitfield_1)) + as u32 as _ + } + } + #[inline] + pub unsafe fn set_field_raw(this: *mut Self, val: ::std::os::raw::c_int) { + unsafe { + let val: u32 = val as _; + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_set_const::< + 0usize, + 1u8, + >(::std::ptr::addr_of_mut!((*this)._bitfield_1), val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + field: ::std::os::raw::c_int, + ) -> __BindgenBitfieldUnit<[u8; 1usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default(); + __bindgen_bitfield_unit + .set_const::< + 0usize, + 1u8, + >({ + let field: u32 = field as _; + field as u64 + }); + __bindgen_bitfield_unit + } + #[inline] + pub unsafe fn regular_method(&mut self) { + StructWithBitfieldAndMethod_regular_method(self) + } +} diff --git a/bindgen-tests/tests/expectations/tests/non-recursive-array-layout.rs b/bindgen-tests/tests/expectations/tests/non-recursive-array-layout.rs new file mode 100644 index 0000000000..36fad5f306 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/non-recursive-array-layout.rs @@ -0,0 +1,55 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +pub struct UnparsedArrayElem(pub u32); +#[repr(C)] +#[derive(Default)] +pub struct __IncompleteArrayField(::std::marker::PhantomData, [T; 0]); +impl __IncompleteArrayField { + #[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 ::std::fmt::Debug for __IncompleteArrayField { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + fmt.write_str("__IncompleteArrayField") + } +} +#[repr(C)] +pub struct OuterArrayStruct { + pub flex_array: __IncompleteArrayField, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of OuterArrayStruct"][::std::mem::size_of::() - 0usize]; + [ + "Alignment of OuterArrayStruct", + ][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: OuterArrayStruct::flex_array", + ][::std::mem::offset_of!(OuterArrayStruct, flex_array) - 0usize]; +}; +impl Default for OuterArrayStruct { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} diff --git a/bindgen-tests/tests/expectations/tests/non-recursive-field-layout.rs b/bindgen-tests/tests/expectations/tests/non-recursive-field-layout.rs new file mode 100644 index 0000000000..3286321329 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/non-recursive-field-layout.rs @@ -0,0 +1,23 @@ +#![allow(dead_code, non_snake_case, non_camel_case_types, non_upper_case_globals)] +pub struct UnparsedTemplate(pub u32); +#[repr(C)] +pub struct OuterStruct { + pub field: UnparsedTemplate, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of OuterStruct"][::std::mem::size_of::() - 4usize]; + ["Alignment of OuterStruct"][::std::mem::align_of::() - 4usize]; + [ + "Offset of field: OuterStruct::field", + ][::std::mem::offset_of!(OuterStruct, field) - 0usize]; +}; +impl Default for OuterStruct { + fn default() -> Self { + let mut s = ::std::mem::MaybeUninit::::uninit(); + unsafe { + ::std::ptr::write_bytes(s.as_mut_ptr(), 0, 1); + s.assume_init() + } + } +} diff --git a/bindgen-tests/tests/headers/bitfield-unparsed-method.hpp b/bindgen-tests/tests/headers/bitfield-unparsed-method.hpp new file mode 100644 index 0000000000..e3608a7fe2 --- /dev/null +++ b/bindgen-tests/tests/headers/bitfield-unparsed-method.hpp @@ -0,0 +1,11 @@ +// bindgen-flags: --allowlist-type StructWithBitfieldAndMethod -- -std=c++20 + +struct Base { + template void t_func(T t); +}; + +struct StructWithBitfieldAndMethod : Base { + int field : 1; + template void t_method(T t); + void regular_method(); +}; diff --git a/bindgen-tests/tests/headers/non-recursive-array-layout.hpp b/bindgen-tests/tests/headers/non-recursive-array-layout.hpp new file mode 100644 index 0000000000..327cc7465f --- /dev/null +++ b/bindgen-tests/tests/headers/non-recursive-array-layout.hpp @@ -0,0 +1,11 @@ +// bindgen-flags: --allowlist-type OuterArrayStruct --no-recursive-allowlist --raw-line "pub struct UnparsedArrayElem(pub u32);" + +template +struct UnparsedArrayElem { + T val; + template void unparsed_method(U u); +}; + +struct OuterArrayStruct { + UnparsedArrayElem flex_array[0]; +}; diff --git a/bindgen-tests/tests/headers/non-recursive-field-layout.hpp b/bindgen-tests/tests/headers/non-recursive-field-layout.hpp new file mode 100644 index 0000000000..99f74156a2 --- /dev/null +++ b/bindgen-tests/tests/headers/non-recursive-field-layout.hpp @@ -0,0 +1,11 @@ +// bindgen-flags: --allowlist-type OuterStruct --no-recursive-allowlist --raw-line "pub struct UnparsedTemplate(pub u32);" + +template +struct UnparsedTemplate { + T val; + template void unparsed_method(U u); +}; + +struct OuterStruct { + UnparsedTemplate field; +}; diff --git a/bindgen/ir/comp.rs b/bindgen/ir/comp.rs index 92e69a2fe2..ac97a623da 100644 --- a/bindgen/ir/comp.rs +++ b/bindgen/ir/comp.rs @@ -205,7 +205,7 @@ impl Field { match *self { Field::Bitfields(BitfieldUnit { layout, .. }) => Some(layout), Field::DataMember(ref data) => { - ctx.resolve_type(data.ty).layout(ctx) + ctx.safe_resolve_type(data.ty)?.layout(ctx) } } } @@ -765,7 +765,11 @@ impl CompFields { name: &str, ) -> bool { methods.iter().any(|method| { - let method_name = ctx.resolve_func(method.signature()).name(); + let Some(func) = ctx.resolve_func_fallible(method.signature()) + else { + return false; + }; + let method_name = func.name(); method_name == name || ctx.rust_mangle(method_name) == name }) } @@ -1214,9 +1218,10 @@ impl CompInfo { } CompFields::Before(ref raw_fields) => { for field in raw_fields { - let field_ty = ctx.resolve_type(field.0.ty); - if let Some(layout) = field_ty.layout(ctx) { - callback(layout); + if let Some(field_ty) = ctx.safe_resolve_type(field.0.ty) { + if let Some(layout) = field_ty.layout(ctx) { + callback(layout); + } } } } diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index b5b6b4a000..4ae33a3096 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -1463,6 +1463,7 @@ If you encounter an error missing from this list, please file an issue or a PR!" /// /// Panics if there is no item for the given `FunctionId` or if the resolved /// item is not a `Function`. + #[allow(dead_code)] pub(crate) fn resolve_func(&self, func_id: FunctionId) -> &Function { self.resolve_item(func_id).kind().expect_function() } @@ -1476,6 +1477,18 @@ If you encounter an error missing from this list, please file an issue or a PR!" .map(|t| t.kind().expect_type()) } + /// Resolve the given `ItemId` as a function, or `None` if there is no item + /// with the given ID. + /// + /// Panics if the ID resolves to an item that is not a function. + pub(crate) fn resolve_func_fallible( + &self, + func_id: FunctionId, + ) -> Option<&Function> { + self.resolve_item_fallible(func_id) + .map(|t| t.kind().expect_function()) + } + /// Resolve the given `ItemId` into an `Item`, or `None` if no such item /// exists. pub(crate) fn resolve_item_fallible>( diff --git a/bindgen/ir/ty.rs b/bindgen/ir/ty.rs index 805138b7a9..d9af0adb3b 100644 --- a/bindgen/ir/ty.rs +++ b/bindgen/ir/ty.rs @@ -223,7 +223,7 @@ impl Type { TypeKind::Comp(ref ci) => ci.layout(ctx), TypeKind::Array(inner, 0) => Some(Layout::new( 0, - ctx.resolve_type(inner).layout(ctx)?.align, + ctx.safe_resolve_type(inner)?.layout(ctx)?.align, )), // FIXME(emilio): This is a hack for anonymous union templates. // Use the actual pointer size! @@ -232,7 +232,7 @@ impl Type { ctx.target_pointer_size(), )), TypeKind::ResolvedTypeRef(inner) => { - ctx.resolve_type(inner).layout(ctx) + ctx.safe_resolve_type(inner)?.layout(ctx) } _ => None, }