diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 5ac8dc11c9..ab62b13577 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -1310,23 +1310,26 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { F: Extend, M: Extend, { + use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; + result.saw_bitfield_unit(); + let layout = self.layout(); + let unit_field_ty = helpers::bitfield_unit(ctx, layout); let field_ty = { - let ty = helpers::bitfield_unit(ctx, self.layout()); if parent.is_union() && !parent.can_be_rust_union(ctx) { result.saw_bindgen_union(); if ctx.options().enable_cxx_namespaces { quote! { - root::__BindgenUnionField<#ty> + root::__BindgenUnionField<#unit_field_ty> } } else { quote! { - __BindgenUnionField<#ty> + __BindgenUnionField<#unit_field_ty> } } } else { - ty + unit_field_ty.clone() } }; @@ -1338,12 +1341,13 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { }; fields.extend(Some(field)); - let unit_field_ty = helpers::bitfield_unit(ctx, self.layout()); - let ctor_name = self.ctor_name(); let mut ctor_params = vec![]; let mut ctor_impl = quote! {}; - let mut generate_ctor = true; + + // We cannot generate any constructor if the underlying storage can't + // implement AsRef<[u8]> / AsMut<[u8]> / etc. + let mut generate_ctor = layout.size <= RUST_DERIVE_IN_ARRAY_LIMIT; for bf in self.bitfields() { // Codegen not allowed for anonymous bitfields @@ -1351,6 +1355,10 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { continue; } + if layout.size > RUST_DERIVE_IN_ARRAY_LIMIT { + continue; + } + let mut bitfield_representable_as_int = true; bf.codegen( @@ -1395,7 +1403,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { })); } - struct_layout.saw_bitfield_unit(self.layout()); + struct_layout.saw_bitfield_unit(layout); } } diff --git a/src/ir/analysis/derive.rs b/src/ir/analysis/derive.rs index e07f6bc2c0..f9cc404c03 100644 --- a/src/ir/analysis/derive.rs +++ b/src/ir/analysis/derive.rs @@ -240,25 +240,24 @@ impl<'ctx> CannotDerive<'ctx> { self.derive_trait ); return CanDerive::No; - } else { - if self.derive_trait.can_derive_large_array() { - trace!(" array can derive {}", self.derive_trait); - return CanDerive::Yes; - } else { - if len <= RUST_DERIVE_IN_ARRAY_LIMIT { - trace!( - " array is small enough to derive {}", - self.derive_trait - ); - return CanDerive::Yes; - } else { - trace!( - " array is too large to derive {}, but it may be implemented", self.derive_trait - ); - return CanDerive::Manually; - } - } } + + if self.derive_trait.can_derive_large_array() { + trace!(" array can derive {}", self.derive_trait); + return CanDerive::Yes; + } + + if len > RUST_DERIVE_IN_ARRAY_LIMIT { + trace!( + " array is too large to derive {}, but it may be implemented", self.derive_trait + ); + return CanDerive::Manually; + } + trace!( + " array is small enough to derive {}", + self.derive_trait + ); + return CanDerive::Yes; } TypeKind::Vector(t, len) => { let inner_type = @@ -362,6 +361,20 @@ impl<'ctx> CannotDerive<'ctx> { return CanDerive::No; } + // Bitfield units are always represented as arrays of u8, but + // they're not traced as arrays, so we need to check here + // instead. + if !self.derive_trait.can_derive_large_array() && + info.has_too_large_bitfield_unit() && + !item.is_opaque(self.ctx, &()) + { + trace!( + " cannot derive {} for comp with too large bitfield unit", + self.derive_trait + ); + return CanDerive::No; + } + let pred = self.derive_trait.consider_edge_comp(); return self.constrain_join(item, pred); } diff --git a/src/ir/comp.rs b/src/ir/comp.rs index cc58e910f8..23120020ce 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -6,9 +6,9 @@ use super::context::{BindgenContext, FunctionId, ItemId, TypeId, VarId}; use super::dot::DotAttributes; use super::item::{IsOpaque, Item}; use super::layout::Layout; -// use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT; use super::template::TemplateParameters; use super::traversal::{EdgeKind, Trace, Tracer}; +use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT; use clang; use codegen::struct_layout::{align_to, bytes_from_bits_pow2}; use ir::derive::CanDeriveCopy; @@ -497,7 +497,7 @@ fn raw_fields_to_fields_and_bitfield_units( ctx: &BindgenContext, raw_fields: I, packed: bool, -) -> Result, ()> +) -> Result<(Vec, bool), ()> where I: IntoIterator, { @@ -543,7 +543,7 @@ where "The above loop should consume all items in `raw_fields`" ); - Ok(fields) + Ok((fields, bitfield_unit_count != 0)) } /// Given a set of contiguous raw bitfields, group and allocate them into @@ -707,7 +707,10 @@ where #[derive(Debug)] enum CompFields { BeforeComputingBitfieldUnits(Vec), - AfterComputingBitfieldUnits(Vec), + AfterComputingBitfieldUnits { + fields: Vec, + has_bitfield_units: bool, + }, ErrorComputingBitfieldUnits, } @@ -744,10 +747,13 @@ impl CompFields { let result = raw_fields_to_fields_and_bitfield_units(ctx, raws, packed); match result { - Ok(fields_and_units) => { + Ok((fields, has_bitfield_units)) => { mem::replace( self, - CompFields::AfterComputingBitfieldUnits(fields_and_units), + CompFields::AfterComputingBitfieldUnits { + fields, + has_bitfield_units, + }, ); } Err(()) => { @@ -758,11 +764,11 @@ impl CompFields { fn deanonymize_fields(&mut self, ctx: &BindgenContext, methods: &[Method]) { let fields = match *self { - CompFields::AfterComputingBitfieldUnits(ref mut fields) => fields, - CompFields::ErrorComputingBitfieldUnits => { - // Nothing to do here. - return; - } + CompFields::AfterComputingBitfieldUnits { + ref mut fields, .. + } => fields, + // Nothing to do here. + CompFields::ErrorComputingBitfieldUnits => return, CompFields::BeforeComputingBitfieldUnits(_) => { panic!("Not yet computed bitfield units."); } @@ -859,7 +865,7 @@ impl Trace for CompFields { tracer.visit_kind(f.ty().into(), EdgeKind::Field); } } - CompFields::AfterComputingBitfieldUnits(ref fields) => { + CompFields::AfterComputingBitfieldUnits { ref fields, .. } => { for f in fields { f.trace(context, tracer, &()); } @@ -1061,7 +1067,7 @@ impl CompInfo { /// Construct a new compound type. pub fn new(kind: CompKind) -> Self { CompInfo { - kind: kind, + kind, fields: CompFields::default(), template_params: vec![], methods: vec![], @@ -1124,13 +1130,43 @@ impl CompInfo { pub fn fields(&self) -> &[Field] { match self.fields { CompFields::ErrorComputingBitfieldUnits => &[], - CompFields::AfterComputingBitfieldUnits(ref fields) => fields, + CompFields::AfterComputingBitfieldUnits { ref fields, .. } => { + fields + } CompFields::BeforeComputingBitfieldUnits(_) => { panic!("Should always have computed bitfield units first"); } } } + fn has_bitfields(&self) -> bool { + match self.fields { + CompFields::ErrorComputingBitfieldUnits => false, + CompFields::AfterComputingBitfieldUnits { + has_bitfield_units, + .. + } => has_bitfield_units, + CompFields::BeforeComputingBitfieldUnits(_) => { + panic!("Should always have computed bitfield units first"); + } + } + } + + /// Returns whether we have a too large bitfield unit, in which case we may + /// not be able to derive some of the things we should be able to normally + /// derive. + pub fn has_too_large_bitfield_unit(&self) -> bool { + if !self.has_bitfields() { + return false; + } + self.fields().iter().any(|field| match *field { + Field::DataMember(..) => false, + Field::Bitfields(ref unit) => { + unit.layout.size > RUST_DERIVE_IN_ARRAY_LIMIT + } + }) + } + /// Does this type have any template parameters that aren't types /// (e.g. int)? pub fn has_non_type_template_params(&self) -> bool { diff --git a/tests/expectations/tests/timex.rs b/tests/expectations/tests/timex.rs new file mode 100644 index 0000000000..19543173f9 --- /dev/null +++ b/tests/expectations/tests/timex.rs @@ -0,0 +1,166 @@ +/* automatically generated by rust-bindgen */ + +#![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, + align: [Align; 0], +} +impl __BindgenBitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { + Self { storage, align: [] } + } +} +impl __BindgenBitfieldUnit +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); + } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct timex { + pub tai: ::std::os::raw::c_int, + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 44usize], u8>, +} +#[test] +fn bindgen_test_layout_timex() { + assert_eq!( + ::std::mem::size_of::(), + 48usize, + concat!("Size of: ", stringify!(timex)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(timex)) + ); + assert_eq!( + unsafe { &(*(::std::ptr::null::())).tai as *const _ as usize }, + 0usize, + concat!( + "Offset of field: ", + stringify!(timex), + "::", + stringify!(tai) + ) + ); +} +impl Default for timex { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} +#[repr(C)] +#[derive(Copy, Clone)] +pub struct timex_named { + pub tai: ::std::os::raw::c_int, + pub _bitfield_1: __BindgenBitfieldUnit<[u8; 44usize], u32>, +} +#[test] +fn bindgen_test_layout_timex_named() { + assert_eq!( + ::std::mem::size_of::(), + 48usize, + concat!("Size of: ", stringify!(timex_named)) + ); + assert_eq!( + ::std::mem::align_of::(), + 4usize, + concat!("Alignment of ", stringify!(timex_named)) + ); + assert_eq!( + unsafe { + &(*(::std::ptr::null::())).tai as *const _ as usize + }, + 0usize, + concat!( + "Offset of field: ", + stringify!(timex_named), + "::", + stringify!(tai) + ) + ); +} +impl Default for timex_named { + fn default() -> Self { + unsafe { ::std::mem::zeroed() } + } +} diff --git a/tests/headers/timex.h b/tests/headers/timex.h new file mode 100644 index 0000000000..1add26cabe --- /dev/null +++ b/tests/headers/timex.h @@ -0,0 +1,15 @@ +struct timex { + int tai; + + int :32; int :32; int :32; int :32; + int :32; int :32; int :32; int :32; + int :32; int :32; int :32; +}; + +struct timex_named { + int tai; + + int a:32; int b:32; int c:32; int d:32; + int e:32; int f:32; int g:32; int h:32; + int i:32; int j:32; int k:32; +};