diff --git a/bindgen-tests/tests/expectations/tests/bitfield-accessor-name-conflict.rs b/bindgen-tests/tests/expectations/tests/bitfield-accessor-name-conflict.rs new file mode 100644 index 0000000000..2825c21bc8 --- /dev/null +++ b/bindgen-tests/tests/expectations/tests/bitfield-accessor-name-conflict.rs @@ -0,0 +1,665 @@ +#![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)) / 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)) / 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)) / 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)) / 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)) / 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; + 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)) / 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; + 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 = ((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)) / 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; + 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)) / 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; + 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 = ((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; + } + } + } +} +/** Bitfield accessor name conflicts: + - `set_x` getter collides with `x` setter + - `set_x_bindgen_bitfield` tests the collision chain (the suffix + used for deduplication itself collides with a real field name)*/ +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +pub struct test { + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize]>, +} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of test"][::std::mem::size_of::() - 1usize]; + ["Alignment of test"][::std::mem::align_of::() - 1usize]; +}; +impl test { + #[inline] + pub fn set_x(&self) -> ::std::os::raw::c_char { + unsafe { + ::std::mem::transmute(self._bitfield_1.get_const::<0usize, 1u8>() as u8) + } + } + #[inline] + pub fn set_set_x(&mut self, val: ::std::os::raw::c_char) { + unsafe { + let val: u8 = val as _; + self._bitfield_1.set_const::<0usize, 1u8>(val as u64) + } + } + #[inline] + pub unsafe fn set_x_raw(this: *const Self) -> ::std::os::raw::c_char { + unsafe { + ::std::mem::transmute( + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_get_const::< + 0usize, + 1u8, + >(::std::ptr::addr_of!((*this)._bitfield_1)) as u8, + ) + } + } + #[inline] + pub unsafe fn set_set_x_raw(this: *mut Self, val: ::std::os::raw::c_char) { + unsafe { + let val: u8 = val as _; + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_set_const::< + 0usize, + 1u8, + >(::std::ptr::addr_of_mut!((*this)._bitfield_1), val as u64) + } + } + #[inline] + pub fn x_bindgen_bitfield(&self) -> ::std::os::raw::c_char { + unsafe { + ::std::mem::transmute(self._bitfield_1.get_const::<1usize, 1u8>() as u8) + } + } + #[inline] + pub fn set_x_bindgen_bitfield(&mut self, val: ::std::os::raw::c_char) { + unsafe { + let val: u8 = val as _; + self._bitfield_1.set_const::<1usize, 1u8>(val as u64) + } + } + #[inline] + pub unsafe fn x_bindgen_bitfield_raw(this: *const Self) -> ::std::os::raw::c_char { + unsafe { + ::std::mem::transmute( + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_get_const::< + 1usize, + 1u8, + >(::std::ptr::addr_of!((*this)._bitfield_1)) as u8, + ) + } + } + #[inline] + pub unsafe fn set_x_bindgen_bitfield_raw( + this: *mut Self, + val: ::std::os::raw::c_char, + ) { + unsafe { + let val: u8 = val as _; + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_set_const::< + 1usize, + 1u8, + >(::std::ptr::addr_of_mut!((*this)._bitfield_1), val as u64) + } + } + #[inline] + pub fn set_x_bindgen_bitfield_bindgen_bitfield(&self) -> ::std::os::raw::c_char { + unsafe { + ::std::mem::transmute(self._bitfield_1.get_const::<2usize, 1u8>() as u8) + } + } + #[inline] + pub fn set_set_x_bindgen_bitfield_bindgen_bitfield( + &mut self, + val: ::std::os::raw::c_char, + ) { + unsafe { + let val: u8 = val as _; + self._bitfield_1.set_const::<2usize, 1u8>(val as u64) + } + } + #[inline] + pub unsafe fn set_x_bindgen_bitfield_bindgen_bitfield_raw( + this: *const Self, + ) -> ::std::os::raw::c_char { + unsafe { + ::std::mem::transmute( + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_get_const::< + 2usize, + 1u8, + >(::std::ptr::addr_of!((*this)._bitfield_1)) as u8, + ) + } + } + #[inline] + pub unsafe fn set_set_x_bindgen_bitfield_bindgen_bitfield_raw( + this: *mut Self, + val: ::std::os::raw::c_char, + ) { + unsafe { + let val: u8 = val as _; + <__BindgenBitfieldUnit< + [u8; 1usize], + >>::raw_set_const::< + 2usize, + 1u8, + >(::std::ptr::addr_of_mut!((*this)._bitfield_1), val as u64) + } + } + #[inline] + pub fn new_bitfield_1( + set_x: ::std::os::raw::c_char, + x_bindgen_bitfield: ::std::os::raw::c_char, + set_x_bindgen_bitfield_bindgen_bitfield: ::std::os::raw::c_char, + ) -> __BindgenBitfieldUnit<[u8; 1usize]> { + let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default(); + __bindgen_bitfield_unit + .set_const::< + 0usize, + 1u8, + >({ + let set_x: u8 = set_x as _; + set_x as u64 + }); + __bindgen_bitfield_unit + .set_const::< + 1usize, + 1u8, + >({ + let x_bindgen_bitfield: u8 = x_bindgen_bitfield as _; + x_bindgen_bitfield as u64 + }); + __bindgen_bitfield_unit + .set_const::< + 2usize, + 1u8, + >({ + let set_x_bindgen_bitfield_bindgen_bitfield: u8 = set_x_bindgen_bitfield_bindgen_bitfield + as _; + set_x_bindgen_bitfield_bindgen_bitfield as u64 + }); + __bindgen_bitfield_unit + } +} diff --git a/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs b/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs index 928e626ba7..77dad62ac3 100644 --- a/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs +++ b/bindgen-tests/tests/expectations/tests/bitfield-method-same-name.rs @@ -512,22 +512,20 @@ unsafe extern "C" { } impl Foo { #[inline] - pub fn type__bindgen_bitfield(&self) -> ::std::os::raw::c_char { + pub fn type_(&self) -> ::std::os::raw::c_char { unsafe { ::std::mem::transmute(self._bitfield_1.get_const::<0usize, 3u8>() as u8) } } #[inline] - pub fn set_type__bindgen_bitfield(&mut self, val: ::std::os::raw::c_char) { + pub fn set_type_(&mut self, val: ::std::os::raw::c_char) { unsafe { let val: u8 = val as _; self._bitfield_1.set_const::<0usize, 3u8>(val as u64) } } #[inline] - pub unsafe fn type__bindgen_bitfield_raw( - this: *const Self, - ) -> ::std::os::raw::c_char { + pub unsafe fn type__raw(this: *const Self) -> ::std::os::raw::c_char { unsafe { ::std::mem::transmute( <__BindgenBitfieldUnit< @@ -540,10 +538,7 @@ impl Foo { } } #[inline] - pub unsafe fn set_type__bindgen_bitfield_raw( - this: *mut Self, - val: ::std::os::raw::c_char, - ) { + pub unsafe fn set_type__raw(this: *mut Self, val: ::std::os::raw::c_char) { unsafe { let val: u8 = val as _; <__BindgenBitfieldUnit< @@ -556,7 +551,7 @@ impl Foo { } #[inline] pub fn new_bitfield_1( - type__bindgen_bitfield: ::std::os::raw::c_char, + type_: ::std::os::raw::c_char, ) -> __BindgenBitfieldUnit<[u8; 1usize]> { let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize]> = Default::default(); __bindgen_bitfield_unit @@ -564,17 +559,17 @@ impl Foo { 0usize, 3u8, >({ - let type__bindgen_bitfield: u8 = type__bindgen_bitfield as _; - type__bindgen_bitfield as u64 + let type_: u8 = type_ as _; + type_ as u64 }); __bindgen_bitfield_unit } #[inline] - pub unsafe fn type_(&mut self) -> ::std::os::raw::c_char { + pub unsafe fn type_1(&mut self) -> ::std::os::raw::c_char { Foo_type(self) } #[inline] - pub unsafe fn set_type_(&mut self, c: ::std::os::raw::c_char) { + pub unsafe fn set_type_1(&mut self, c: ::std::os::raw::c_char) { Foo_set_type_(self, c) } #[inline] diff --git a/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs b/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs index a0d18252ab..484957c3b3 100644 --- a/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs +++ b/bindgen-tests/tests/expectations/tests/derive-bitfield-method-same-name.rs @@ -527,22 +527,20 @@ impl Default for Foo { } impl Foo { #[inline] - pub fn type__bindgen_bitfield(&self) -> ::std::os::raw::c_char { + pub fn type_(&self) -> ::std::os::raw::c_char { unsafe { ::std::mem::transmute(self._bitfield_1.get_const::<0usize, 3u8>() as u8) } } #[inline] - pub fn set_type__bindgen_bitfield(&mut self, val: ::std::os::raw::c_char) { + pub fn set_type_(&mut self, val: ::std::os::raw::c_char) { unsafe { let val: u8 = val as _; self._bitfield_1.set_const::<0usize, 3u8>(val as u64) } } #[inline] - pub unsafe fn type__bindgen_bitfield_raw( - this: *const Self, - ) -> ::std::os::raw::c_char { + pub unsafe fn type__raw(this: *const Self) -> ::std::os::raw::c_char { unsafe { ::std::mem::transmute( <__BindgenBitfieldUnit< @@ -555,10 +553,7 @@ impl Foo { } } #[inline] - pub unsafe fn set_type__bindgen_bitfield_raw( - this: *mut Self, - val: ::std::os::raw::c_char, - ) { + pub unsafe fn set_type__raw(this: *mut Self, val: ::std::os::raw::c_char) { unsafe { let val: u8 = val as _; <__BindgenBitfieldUnit< @@ -571,7 +566,7 @@ impl Foo { } #[inline] pub fn new_bitfield_1( - type__bindgen_bitfield: ::std::os::raw::c_char, + type_: ::std::os::raw::c_char, ) -> __BindgenBitfieldUnit<[u8; 2usize]> { let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 2usize]> = Default::default(); __bindgen_bitfield_unit @@ -579,17 +574,17 @@ impl Foo { 0usize, 3u8, >({ - let type__bindgen_bitfield: u8 = type__bindgen_bitfield as _; - type__bindgen_bitfield as u64 + let type_: u8 = type_ as _; + type_ as u64 }); __bindgen_bitfield_unit } #[inline] - pub unsafe fn type_(&mut self) -> ::std::os::raw::c_char { + pub unsafe fn type_1(&mut self) -> ::std::os::raw::c_char { Foo_type(self) } #[inline] - pub unsafe fn set_type_(&mut self, c: ::std::os::raw::c_char) { + pub unsafe fn set_type_1(&mut self, c: ::std::os::raw::c_char) { Foo_set_type_(self, c) } #[inline] diff --git a/bindgen-tests/tests/headers/bitfield-accessor-name-conflict.h b/bindgen-tests/tests/headers/bitfield-accessor-name-conflict.h new file mode 100644 index 0000000000..1e18ebffd2 --- /dev/null +++ b/bindgen-tests/tests/headers/bitfield-accessor-name-conflict.h @@ -0,0 +1,9 @@ +/// Bitfield accessor name conflicts: +/// - `set_x` getter collides with `x` setter +/// - `set_x_bindgen_bitfield` tests the collision chain (the suffix +/// used for deduplication itself collides with a real field name) +struct test { + char set_x: 1; + char x: 1; + char set_x_bindgen_bitfield: 1; +}; diff --git a/bindgen-tests/tests/tests.rs b/bindgen-tests/tests/tests.rs index 6e3c358d3e..672b51ed88 100644 --- a/bindgen-tests/tests/tests.rs +++ b/bindgen-tests/tests/tests.rs @@ -563,6 +563,131 @@ fn test_mixed_header_and_header_contents() { } } +#[test] +fn test_bitfield_accessor_name_conflict_torture() { + // This deliberately mixes first-order accessor collisions, repeated + // suffix chains, raw-name collisions, reversed declaration order, + // and C++ methods whose names only collide after suffixing. + let header = r" +struct BasicPair { + char set_x : 1; + char x : 1; +}; + +struct GetterSetterChain { + char x : 1; + char set_x : 1; + char set_x_bindgen_bitfield : 1; +}; + +struct RawGetterSetterChain { + char x : 1; + char set_x_raw : 1; + char set_x_raw_bindgen_bitfield : 1; +}; + +struct ReverseGetterSetterChain { + char set_x_bindgen_bitfield : 1; + char set_x : 1; + char x : 1; +}; + +struct ReverseRawGetterSetterChain { + char set_x_raw_bindgen_bitfield : 1; + char set_x_raw : 1; + char x : 1; +}; + +struct MethodAfterAccessorCollision { + char x : 1; + char set_x : 1; + void set_x_bindgen_bitfield(); + void set_set_x_bindgen_bitfield(char c); +}; + +struct MethodAfterRawCollision { + char x : 1; + char set_x_raw : 1; + void set_x_raw_bindgen_bitfield(); + void set_set_x_raw_bindgen_bitfield(char c); +}; + +struct MethodCollisionChain { + char type_ : 1; + char set_type : 1; + char set_type_bindgen_bitfield : 1; + char type(); + void set_type_(char c); + void set_set_type_bindgen_bitfield(char c); +}; + +struct OverloadedMethodConflict { + char foo1 : 1; + char bar : 1; + void foo(); + void foo(int); +}; + +struct DtorConflict { + char destruct : 1; + ~DtorConflict(); +}; + +struct DtorShiftConflict { + char destruct1 : 1; + void destruct(); + ~DtorShiftConflict(); +}; + +struct CtorConflict { + char new_ : 1; + CtorConflict(); + CtorConflict(int); +}; + +struct CtorShiftConflict { + char new1 : 1; + CtorShiftConflict(); + CtorShiftConflict(int); +}; +"; + + let bindings = builder() + .disable_header_comment() + .header_contents("bitfield_accessor_name_conflict_torture.hpp", header) + .clang_arg("-xc++") + .clang_arg("--target=x86_64-unknown-linux") + .generate() + .unwrap() + .to_string(); + + let tmpdir = tempfile::tempdir().unwrap(); + let source = tmpdir.path().join("bindings.rs"); + let output = tmpdir.path().join("libbindings.rlib"); + fs::write(&source, &bindings).unwrap(); + + let rustc = env::var_os("RUSTC") + .unwrap_or_else(|| std::ffi::OsString::from("rustc")); + // Duplicate accessors still parse as Rust, so compile the generated + // bindings instead of only formatting/parsing them. + let compile = std::process::Command::new(rustc) + .arg("--crate-type=lib") + .arg("--edition=2021") + .arg(&source) + .arg("-o") + .arg(&output) + .output() + .unwrap(); + + assert!( + compile.status.success(), + "Generated bindings did not compile.\nstdout:\n{}\nstderr:\n{}\nbindings:\n{}", + String::from_utf8_lossy(&compile.stdout), + String::from_utf8_lossy(&compile.stderr), + bindings, + ); +} + #[test] fn test_macro_fallback_non_system_dir() { let actual = builder() diff --git a/bindgen/codegen/mod.rs b/bindgen/codegen/mod.rs index 7a998c8fac..7c2a1949df 100644 --- a/bindgen/codegen/mod.rs +++ b/bindgen/codegen/mod.rs @@ -2783,7 +2783,26 @@ impl CodeGenerator for CompInfo { } } - let mut method_names = Default::default(); + // Seed method_names with bitfield accessor names so C++ + // methods, constructors, and destructors that collide with + // them get renamed instead of producing duplicates. + let mut method_names: HashSet = self + .fields() + .iter() + .filter_map(|f| match f { + Field::Bitfields(ref bu) => Some(bu.bitfields().iter()), + Field::DataMember(_) => None, + }) + .flatten() + .filter(|bf| bf.name().is_some()) + .flat_map(|bf| { + let g = bf.getter_name().to_owned(); + let s = bf.setter_name().to_owned(); + let gr = format!("{g}_raw"); + let sr = format!("{s}_raw"); + [g, s, gr, sr] + }) + .collect(); let discovered_id = DiscoveredItemId::new(item.id().as_usize()); if ctx.options().codegen_config.methods() { for method in self.methods() { @@ -3126,6 +3145,12 @@ impl Method { return; } + // Mangle the name before dedup so we compare the actual Rust + // identifier (e.g. C++ `type` → Rust `type_`) against + // method_names, which contains bitfield accessor names that + // are already in their Rust-mangled form. + name = ctx.rust_mangle(&name).to_string(); + if method_names.contains(&name) { let mut count = 1; let mut new_name; diff --git a/bindgen/ir/comp.rs b/bindgen/ir/comp.rs index a41fa6f0b2..7ac1ae14b7 100644 --- a/bindgen/ir/comp.rs +++ b/bindgen/ir/comp.rs @@ -15,8 +15,8 @@ use crate::clang; use crate::codegen::struct_layout::align_to; use crate::ir::derive::CanDeriveCopy; use crate::parse::ParseError; -use crate::HashMap; use crate::NonCopyUnionStyle; +use crate::{HashMap, HashSet}; use std::cmp; use std::io; use std::mem; @@ -703,7 +703,7 @@ impl CompFields { } } - fn deanonymize_fields(&mut self, ctx: &BindgenContext, methods: &[Method]) { + fn deanonymize_fields(&mut self, ctx: &BindgenContext) { let fields = match *self { CompFields::After { ref mut fields, .. } => fields, // Nothing to do here. @@ -713,51 +713,69 @@ impl CompFields { } }; - fn has_method( - methods: &[Method], - ctx: &BindgenContext, - name: &str, - ) -> bool { - methods.iter().any(|method| { - let method_name = ctx.resolve_func(method.signature()).name(); - method_name == name || ctx.rust_mangle(method_name) == name - }) - } - struct AccessorNamesPair { getter: String, setter: String, } - let mut accessor_names: HashMap = fields + // Build accessor names for each bitfield, resolving + // inter-bitfield collisions. Method/constructor/destructor + // collisions are handled in codegen by seeding method_names + // with bitfield accessor names before emitting C++ methods. + // + // Names are suffixed with `_bindgen_bitfield` repeatedly until + // unique, handling chains like `x` / `set_x` / + // `set_x_bindgen_bitfield`. + let mut accessor_names: HashMap = + Default::default(); + let mut used_names: HashSet = Default::default(); + + let bitfield_names: Vec = fields .iter() .flat_map(|field| match *field { Field::Bitfields(ref bu) => &*bu.bitfields, Field::DataMember(_) => &[], }) .filter_map(|bitfield| bitfield.name()) - .map(|bitfield_name| { - let bitfield_name = bitfield_name.to_string(); - let getter = { - let mut getter = - ctx.rust_mangle(&bitfield_name).to_string(); - if has_method(methods, ctx, &getter) { - getter.push_str("_bindgen_bitfield"); - } - getter - }; - let setter = { - let setter = format!("set_{bitfield_name}"); - let mut setter = ctx.rust_mangle(&setter).to_string(); - if has_method(methods, ctx, &setter) { - setter.push_str("_bindgen_bitfield"); - } - setter - }; - (bitfield_name, AccessorNamesPair { getter, setter }) - }) + .map(|n| n.to_string()) .collect(); + for bitfield_name in &bitfield_names { + let mut getter = ctx.rust_mangle(bitfield_name).to_string(); + let mut setter = { + let s = format!("set_{bitfield_name}"); + ctx.rust_mangle(&s).to_string() + }; + + // Resolve collisions against previously assigned bitfield + // accessor names. Check all four variants (getter, setter, + // and their _raw forms) and keep suffixing until unique. + loop { + let all_unique = [ + &getter, + &setter, + &format!("{getter}_raw"), + &format!("{setter}_raw"), + ] + .iter() + .all(|n| !used_names.contains(n.as_str())); + if all_unique { + break; + } + getter.push_str("_bindgen_bitfield"); + setter.push_str("_bindgen_bitfield"); + } + + used_names.insert(getter.clone()); + used_names.insert(setter.clone()); + used_names.insert(format!("{getter}_raw")); + used_names.insert(format!("{setter}_raw")); + accessor_names.insert( + bitfield_name.clone(), + AccessorNamesPair { getter, setter }, + ); + } + let mut anon_field_counter = 0; for field in fields.iter_mut() { match *field { @@ -1701,7 +1719,7 @@ impl CompInfo { /// Assign for each anonymous field a generated name. pub(crate) fn deanonymize_fields(&mut self, ctx: &BindgenContext) { - self.fields.deanonymize_fields(ctx, &self.methods); + self.fields.deanonymize_fields(ctx); } /// Returns whether the current union can be represented as a Rust `union` diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs index 8e4163df5e..e1992da46a 100644 --- a/bindgen/ir/context.rs +++ b/bindgen/ir/context.rs @@ -11,7 +11,6 @@ use super::derive::{ CanDerive, CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveEq, CanDeriveHash, CanDeriveOrd, CanDerivePartialEq, CanDerivePartialOrd, }; -use super::function::Function; use super::int::IntKind; use super::item::{IsOpaque, Item, ItemAncestors, ItemSet}; use super::item_kind::ItemKind; @@ -1456,14 +1455,6 @@ If you encounter an error missing from this list, please file an issue or a PR!" self.resolve_item(type_id).kind().expect_type() } - /// Resolve a function with the given ID. - /// - /// Panics if there is no item for the given `FunctionId` or if the resolved - /// item is not a `Function`. - pub(crate) fn resolve_func(&self, func_id: FunctionId) -> &Function { - self.resolve_item(func_id).kind().expect_function() - } - /// Resolve the given `ItemId` as a type, or `None` if there is no item with /// the given ID. ///