diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 170 | ||||
-rw-r--r-- | src/codegen/struct_layout.rs | 306 | ||||
-rw-r--r-- | src/ir/comp.rs | 72 | ||||
-rw-r--r-- | src/ir/enum_ty.rs | 17 | ||||
-rw-r--r-- | src/ir/layout.rs | 25 | ||||
-rw-r--r-- | src/ir/ty.rs | 35 |
6 files changed, 343 insertions, 282 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index ad6736b0..77f654e6 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -2,7 +2,8 @@ mod helpers; mod struct_layout; use self::helpers::{BlobTyBuilder, attributes}; -use self::struct_layout::StructLayoutTracker; +use self::struct_layout::{align_to, bytes_from_bits}; +use self::struct_layout::{bytes_from_bits_pow2, StructLayoutTracker}; use aster; use ir::annotations::FieldAccessorKind; @@ -363,8 +364,7 @@ impl CodeGenerator for Module { } if item.id() == ctx.root_module() { - let saw_union = result.saw_union; - if saw_union && !ctx.options().unstable_rust { + if result.saw_union && !ctx.options().unstable_rust { utils::prepend_union_types(ctx, &mut *result); } if result.saw_incomplete_array { @@ -717,12 +717,12 @@ impl<'a> ItemToRustTy for Vtable<'a> { } struct Bitfield<'a> { - index: usize, + index: &'a mut usize, fields: Vec<&'a Field>, } impl<'a> Bitfield<'a> { - fn new(index: usize, fields: Vec<&'a Field>) -> Self { + fn new(index: &'a mut usize, fields: Vec<&'a Field>) -> Self { Bitfield { index: index, fields: fields, @@ -732,89 +732,96 @@ impl<'a> Bitfield<'a> { fn codegen_fields(self, ctx: &BindgenContext, fields: &mut Vec<ast::StructField>, - methods: &mut Vec<ast::ImplItem>) + _methods: &mut Vec<ast::ImplItem>) -> Layout { use aster::struct_field::StructFieldBuilder; - let mut total_width = self.fields - .iter() - .fold(0u32, |acc, f| acc + f.bitfield().unwrap()); - - if !total_width.is_power_of_two() || total_width < 8 { - total_width = cmp::max(8, total_width.next_power_of_two()); - } - debug_assert_eq!(total_width % 8, 0); - let total_width_in_bytes = total_width as usize / 8; - - let bitfield_layout = Layout::new(total_width_in_bytes, - total_width_in_bytes); - let bitfield_type = BlobTyBuilder::new(bitfield_layout).build(); - let field_name = format!("_bitfield_{}", self.index); - let field_ident = ctx.ext_cx().ident_of(&field_name); - let field = StructFieldBuilder::named(&field_name) - .pub_() - .build_ty(bitfield_type.clone()); - fields.push(field); + // NOTE: What follows is reverse-engineered from LLVM's + // lib/AST/RecordLayoutBuilder.cpp + // + // FIXME(emilio): There are some differences between Microsoft and the + // Itanium ABI, but we'll ignore those and stick to Itanium for now. + // + // Also, we need to handle packed bitfields and stuff. + // TODO(emilio): Take into account C++'s wide bitfields, and + // packing, sigh. + let mut total_size_in_bits = 0; + let mut max_align = 0; + let mut unfilled_bits_in_last_unit = 0; + let mut field_size_in_bits = 0; + *self.index += 1; + let mut last_field_name = format!("_bitfield_{}", self.index); + let mut last_field_align = 0; - let mut offset = 0; for field in self.fields { let width = field.bitfield().unwrap(); - let field_name = field.name() - .map(ToOwned::to_owned) - .unwrap_or_else(|| format!("at_offset_{}", offset)); - let field_item = ctx.resolve_item(field.ty()); let field_ty_layout = field_item.kind() .expect_type() .layout(ctx) .expect("Bitfield without layout? Gah!"); - let field_type = field_item.to_rust_ty(ctx); - let int_type = BlobTyBuilder::new(field_ty_layout).build(); + let field_align = field_ty_layout.align; - let getter_name = ctx.rust_ident(&field_name); - let setter_name = ctx.ext_cx() - .ident_of(&format!("set_{}", &field_name)); - let mask = ((1usize << width) - 1) << offset; - let prefix = ctx.trait_prefix(); - // The transmute is unfortunate, but it's needed for enums in - // bitfields. - let item = quote_item!(ctx.ext_cx(), - impl X { - #[inline] - pub fn $getter_name(&self) -> $field_type { - unsafe { - ::$prefix::mem::transmute( - ( - (self.$field_ident & - ($mask as $bitfield_type)) - >> $offset - ) as $int_type - ) - } - } + if field_size_in_bits != 0 && + (width == 0 || width as usize > unfilled_bits_in_last_unit) { + field_size_in_bits = align_to(field_size_in_bits, field_align); + // Push the new field. + let ty = + BlobTyBuilder::new(Layout::new(bytes_from_bits_pow2(field_size_in_bits), + bytes_from_bits_pow2(last_field_align))) + .build(); - #[inline] - pub fn $setter_name(&mut self, val: $field_type) { - self.$field_ident &= !($mask as $bitfield_type); - self.$field_ident |= - (val as $int_type as $bitfield_type << $offset) & - ($mask as $bitfield_type); - } - } - ) - .unwrap(); + let field = StructFieldBuilder::named(&last_field_name) + .pub_() + .build_ty(ty); + fields.push(field); - let items = match item.unwrap().node { - ast::ItemKind::Impl(_, _, _, _, _, items) => items, - _ => unreachable!(), - }; + // TODO(emilio): dedup this. + *self.index += 1; + last_field_name = format!("_bitfield_{}", self.index); + + // Now reset the size and the rest of stuff. + // unfilled_bits_in_last_unit = 0; + field_size_in_bits = 0; + last_field_align = 0; + } + + // TODO(emilio): Create the accessors. Problem here is that we still + // don't know which one is going to be the final alignment of the + // bitfield, and whether we have to index in it. Thus, we don't know + // which integer type do we need. + // + // We could push them to a Vec or something, but given how buggy + // they where maybe it's not a great idea? + field_size_in_bits += width as usize; + total_size_in_bits += width as usize; + + + let data_size = align_to(field_size_in_bits, field_align * 8); + + max_align = cmp::max(max_align, field_align); + + // NB: The width here is completely, absolutely intentional. + last_field_align = cmp::max(last_field_align, width as usize); + + unfilled_bits_in_last_unit = data_size - field_size_in_bits; + } + + if field_size_in_bits != 0 { + // Push the last field. + let ty = + BlobTyBuilder::new(Layout::new(bytes_from_bits_pow2(field_size_in_bits), + bytes_from_bits_pow2(last_field_align))) + .build(); - methods.extend(items.into_iter()); - offset += width; + let field = StructFieldBuilder::named(&last_field_name) + .pub_() + .build_ty(ty); + fields.push(field); } - bitfield_layout + Layout::new(bytes_from_bits(total_size_in_bits), max_align) } } @@ -1062,12 +1069,10 @@ impl CodeGenerator for CompInfo { debug_assert!(!current_bitfield_fields.is_empty()); let bitfield_fields = mem::replace(&mut current_bitfield_fields, vec![]); - bitfield_count += 1; - let bitfield_layout = Bitfield::new(bitfield_count, + let bitfield_layout = Bitfield::new(&mut bitfield_count, bitfield_fields) .codegen_fields(ctx, &mut fields, &mut methods); - - struct_layout.saw_bitfield(bitfield_layout); + struct_layout.saw_bitfield_batch(bitfield_layout); current_bitfield_width = None; current_bitfield_layout = None; @@ -1099,8 +1104,7 @@ impl CodeGenerator for CompInfo { } else { quote_ty!(ctx.ext_cx(), __BindgenUnionField<$ty>) } - } else if let Some(item) = - field_ty.is_incomplete_array(ctx) { + } else if let Some(item) = field_ty.is_incomplete_array(ctx) { result.saw_incomplete_array(); let inner = item.to_rust_ty(ctx); @@ -1224,12 +1228,10 @@ impl CodeGenerator for CompInfo { debug_assert!(!current_bitfield_fields.is_empty()); let bitfield_fields = mem::replace(&mut current_bitfield_fields, vec![]); - bitfield_count += 1; - let bitfield_layout = Bitfield::new(bitfield_count, + let bitfield_layout = Bitfield::new(&mut bitfield_count, bitfield_fields) .codegen_fields(ctx, &mut fields, &mut methods); - - struct_layout.saw_bitfield(bitfield_layout); + struct_layout.saw_bitfield_batch(bitfield_layout); } debug_assert!(current_bitfield_fields.is_empty()); @@ -1268,7 +1270,7 @@ impl CodeGenerator for CompInfo { } } else if !is_union && !self.is_unsized(ctx) { if let Some(padding_field) = - layout.and_then(|layout| struct_layout.pad_struct(layout)) { + layout.and_then(|layout| struct_layout.pad_struct(&canonical_name, layout)) { fields.push(padding_field); } @@ -2174,8 +2176,8 @@ impl ToRustTy for Type { quote_ty!(ctx.ext_cx(), ::$prefix::option::Option<$ty>) } TypeKind::Array(item, len) => { - let inner = item.to_rust_ty(ctx); - aster::ty::TyBuilder::new().array(len).build(inner) + let ty = item.to_rust_ty(ctx); + aster::ty::TyBuilder::new().array(len).build(ty) } TypeKind::Enum(..) => { let path = item.namespace_aware_canonical_path(ctx); @@ -2190,7 +2192,7 @@ impl ToRustTy for Type { .map(|arg| arg.to_rust_ty(ctx)) .collect::<Vec<_>>(); - path.segments.last_mut().unwrap().parameters = if + path.segments.last_mut().unwrap().parameters = if template_args.is_empty() { None } else { diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs index f8a88bc2..24938c16 100644 --- a/src/codegen/struct_layout.rs +++ b/src/codegen/struct_layout.rs @@ -7,7 +7,7 @@ use aster::struct_field::StructFieldBuilder; use ir::comp::CompInfo; use ir::context::BindgenContext; use ir::layout::Layout; -use ir::ty::Type; +use ir::ty::{Type, TypeKind}; use std::cmp; use std::mem; @@ -21,6 +21,84 @@ pub struct StructLayoutTracker<'a, 'ctx: 'a> { padding_count: usize, latest_field_layout: Option<Layout>, max_field_align: usize, + last_field_was_bitfield: bool, +} + +/// Returns a size aligned to a given value. +pub fn align_to(size: usize, align: usize) -> usize { + if align == 0 { + return size; + } + + let rem = size % align; + if rem == 0 { + return size; + } + + size + align - rem +} + +/// Returns the amount of bytes from a given amount of bytes, rounding up. +pub fn bytes_from_bits(n: usize) -> usize { + if n % 8 == 0 { + return n / 8; + } + + n / 8 + 1 +} + +/// Returns the lower power of two byte count that can hold at most n bits. +pub fn bytes_from_bits_pow2(mut n: usize) -> usize { + if n == 0 { + return 0; + } + + if n <= 8 { + return 1; + } + + if !n.is_power_of_two() { + n = n.next_power_of_two(); + } + + n / 8 +} + +#[test] +fn test_align_to() { + assert_eq!(align_to(1, 1), 1); + assert_eq!(align_to(1, 2), 2); + assert_eq!(align_to(1, 4), 4); + assert_eq!(align_to(5, 1), 5); + assert_eq!(align_to(17, 4), 20); +} + +#[test] +fn test_bytes_from_bits_pow2() { + assert_eq!(bytes_from_bits_pow2(0), 0); + for i in 1..9 { + assert_eq!(bytes_from_bits_pow2(i), 1); + } + for i in 9..17 { + assert_eq!(bytes_from_bits_pow2(i), 2); + } + for i in 17..33 { + assert_eq!(bytes_from_bits_pow2(i), 4); + } +} + +#[test] +fn test_bytes_from_bits() { + assert_eq!(bytes_from_bits(0), 0); + for i in 1..9 { + assert_eq!(bytes_from_bits(i), 1); + } + for i in 9..17 { + assert_eq!(bytes_from_bits(i), 2); + } + for i in 17..25 { + assert_eq!(bytes_from_bits(i), 3); + } } impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { @@ -32,6 +110,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { padding_count: 0, latest_field_layout: None, max_field_align: 0, + last_field_was_bitfield: false, } } @@ -43,112 +122,149 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { } pub fn saw_base(&mut self, base_ty: &Type) { - self.align_to_latest_field(); - if let Some(layout) = base_ty.layout(self.ctx) { + self.align_to_latest_field(layout); + self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); self.max_field_align = cmp::max(self.max_field_align, layout.align); } } - pub fn saw_bitfield(&mut self, layout: Layout) { - self.align_to_latest_field(); + pub fn saw_bitfield_batch(&mut self, layout: Layout) { + self.align_to_latest_field(layout); + + self.latest_offset += layout.size; + + debug!("Offset: <bitfield>: {} -> {}", + self.latest_offset - layout.size, + self.latest_offset); - self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); - self.max_field_align = cmp::max(self.max_field_align, layout.align); + self.last_field_was_bitfield = true; + // NB: We intentionally don't update the max_field_align here, since our + // bitfields code doesn't necessarily guarantee it, so we need to + // actually generate the dummy alignment. } pub fn saw_union(&mut self, layout: Layout) { - self.align_to_latest_field(); + self.align_to_latest_field(layout); self.latest_offset += self.padding_bytes(layout) + layout.size; self.latest_field_layout = Some(layout); self.max_field_align = cmp::max(self.max_field_align, layout.align); } + /// Add a padding field if necessary for a given new field _before_ adding + /// that field. pub fn pad_field(&mut self, field_name: &str, field_ty: &Type, field_offset: Option<usize>) -> Option<ast::StructField> { - field_ty.layout(self.ctx).and_then(|field_layout| { - self.align_to_latest_field(); + let mut field_layout = match field_ty.layout(self.ctx) { + Some(l) => l, + None => return None, + }; + + if let TypeKind::Array(inner, len) = *field_ty.canonical_type(self.ctx).kind() { + // FIXME(emilio): As an _ultra_ hack, we correct the layout returned + // by arrays of structs that have a bigger alignment than what we + // can support. + // + // This means that the structs in the array are super-unsafe to + // access, since they won't be properly aligned, but *shrug*. + if let Some(layout) = self.ctx.resolve_type(inner).layout(self.ctx) { + if layout.align > mem::size_of::<*mut ()>() { + field_layout.size = + align_to(layout.size, layout.align) * len; + field_layout.align = mem::size_of::<*mut ()>(); + } + } + } - let padding_layout = if self.comp.packed() { - None - } else { - let calculated_layout = field_ty.as_comp() - .and_then(|comp| comp.calc_layout(self.ctx)) - .unwrap_or(field_layout); - - let align = cmp::min(calculated_layout.align, mem::size_of::<*mut ()>()); - - let (padding_bytes, need_padding) = match field_offset { - Some(offset) if offset / 8 > self.latest_offset => { - (offset / 8 - self.latest_offset, true) - } - _ if field_layout.align != 0 => { - (self.padding_bytes(field_layout), (self.latest_offset % field_layout.align) != 0) - } - _ => { - (0, false) - } - }; - - self.latest_offset += padding_bytes; - - debug!("align field {} to {}/{} with {} padding bytes {:?}, calculated {:?}", - field_name, - self.latest_offset, - field_offset.unwrap_or(0) / 8, - padding_bytes, - field_layout, - calculated_layout); - - if need_padding && - (padding_bytes > calculated_layout.align || - field_layout.align > mem::size_of::<*mut ()>()) { - Some(Layout::new(padding_bytes, align)) - } else { - None + let will_merge_with_bitfield = self.align_to_latest_field(field_layout); + + let padding_layout = if self.comp.packed() { + None + } else { + let padding_bytes = match field_offset { + Some(offset) if offset / 8 > self.latest_offset => { + offset / 8 - self.latest_offset } + _ if will_merge_with_bitfield || field_layout.align == 0 => 0, + _ => self.padding_bytes(field_layout), }; - self.latest_offset += field_ty.calc_size(self.ctx).unwrap_or(field_layout.size); + // Otherwise the padding is useless. + let need_padding = padding_bytes >= field_layout.align; - self.latest_field_layout = Some(field_layout); - self.max_field_align = cmp::max(self.max_field_align, field_layout.align); + self.latest_offset += padding_bytes; - padding_layout.map(|layout| self.padding_field(layout)) - }) - } + debug!("Offset: <padding>: {} -> {}", + self.latest_offset - padding_bytes, + self.latest_offset); - pub fn pad_struct(&mut self, layout: Layout) -> Option<ast::StructField> { - if layout.size < self.latest_offset { - warn!("calculate struct layout incorrect, too more {} bytes", - self.latest_offset - layout.size); + debug!("align field {} to {}/{} with {} padding bytes {:?}", + field_name, + self.latest_offset, + field_offset.unwrap_or(0) / 8, + padding_bytes, + field_layout); - None - } else { - let padding_bytes = layout.size - self.latest_offset; - let struct_align = cmp::min(layout.align, - mem::size_of::<*mut ()>()); - - if padding_bytes > struct_align || - (layout.align > mem::size_of::<*mut ()>() && padding_bytes > 0) { - let padding_align = if self.comp.packed() { - 1 - } else { - cmp::min(1 << padding_bytes.trailing_zeros(), - mem::size_of::<*mut ()>()) - }; - - Some(self.padding_field(Layout::new(padding_bytes, padding_align))) + if need_padding && padding_bytes != 0 { + Some(Layout::new(padding_bytes, field_layout.align)) } else { None } + }; + + self.latest_offset += field_layout.size; + self.latest_field_layout = Some(field_layout); + self.max_field_align = cmp::max(self.max_field_align, field_layout.align); + self.last_field_was_bitfield = false; + + debug!("Offset: {}: {} -> {}", + field_name, + self.latest_offset - field_layout.size, + self.latest_offset); + + padding_layout.map(|layout| self.padding_field(layout)) + } + + pub fn pad_struct(&mut self, name: &str, layout: Layout) -> Option<ast::StructField> { + if layout.size < self.latest_offset { + error!("Calculated wrong layout for {}, too more {} bytes", + name, self.latest_offset - layout.size); + return None + } + + let padding_bytes = layout.size - self.latest_offset; + + // We always pad to get to the correct size if the struct is one of + // those we can't align properly. + // + // Note that if the last field we saw was a bitfield, we may need to pad + // regardless, because bitfields don't respect alignment as strictly as + // other fields. + if padding_bytes > 0 && + (padding_bytes >= layout.align || + (self.last_field_was_bitfield && + padding_bytes >= self.latest_field_layout.unwrap().align) || + layout.align > mem::size_of::<*mut ()>()) { + let layout = if self.comp.packed() { + Layout::new(padding_bytes, 1) + } else if self.last_field_was_bitfield || + layout.align > mem::size_of::<*mut ()>() { + // We've already given up on alignment here. + Layout::for_size(padding_bytes) + } else { + Layout::new(padding_bytes, layout.align) + }; + + Some(self.padding_field(layout)) + } else { + None } } @@ -166,15 +282,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { } fn padding_bytes(&self, layout: Layout) -> usize { - if layout.align == 0 { - warn!("try to padding bytes without layout"); - - 0 - } else if self.latest_offset % layout.align == 0 { - 0 - } else { - layout.align - (self.latest_offset % layout.align) - } + align_to(self.latest_offset, layout.align) - self.latest_offset } fn padding_field(&mut self, layout: Layout) -> ast::StructField { @@ -190,11 +298,37 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { StructFieldBuilder::named(padding_field_name).pub_().build_ty(ty) } - fn align_to_latest_field(&mut self) { + /// Returns whether the new field is known to merge with a bitfield. + /// + /// This is just to avoid doing the same check also in pad_field. + fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool { if self.comp.packed() { - // skip to align field when packed - } else if let Some(layout) = self.latest_field_layout { - self.latest_offset += self.padding_bytes(layout); + // Skip to align fields when packed. + return false; + } + + let layout = match self.latest_field_layout { + Some(l) => l, + None => return false, + }; + + // If it was, we may or may not need to align, depending on what the + // current field alignment and the bitfield size and alignment are. + debug!("align_to_bitfield? {}: {:?} {:?}", self.last_field_was_bitfield, + layout, new_field_layout); + + if self.last_field_was_bitfield && + new_field_layout.align <= layout.size % layout.align && + new_field_layout.size <= layout.size % layout.align { + // The new field will be coalesced into some of the remaining bits. + // + // FIXME(emilio): I think this may not catch everything? + debug!("Will merge with bitfield"); + return true; } + + // Else, just align the obvious way. + self.latest_offset += self.padding_bytes(layout); + return false; } } diff --git a/src/ir/comp.rs b/src/ir/comp.rs index fff4655c..ce6ec25d 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -408,73 +408,25 @@ impl CompInfo { /// members. This is not ideal, but clang fails to report the size for these /// kind of unions, see test/headers/template_union.hpp pub fn layout(&self, ctx: &BindgenContext) -> Option<Layout> { + use std::cmp; // We can't do better than clang here, sorry. if self.kind == CompKind::Struct { - None - } else { - self.calc_layout(ctx) + return None } - } - - /// Compute the layout of this type. - pub fn calc_layout(&self, ctx: &BindgenContext) -> Option<Layout> { - use std::cmp; - use std::mem; - - if self.kind == CompKind::Struct { - let mut latest_offset_in_bits = 0; - let mut max_align = 0; - - if self.needs_explicit_vtable(ctx) { - latest_offset_in_bits += mem::size_of::<*mut ()>() * 8; - max_align = mem::size_of::<*mut ()>(); - } - - for field in &self.fields { - if let Some(bits) = field.bitfield() { - latest_offset_in_bits += bits as usize; - } else { - let field_ty = ctx.resolve_type(field.ty); - if let Some(field_layout) = - field_ty.as_comp() - .and_then(|comp| comp.calc_layout(ctx)) - .or_else(|| field_ty.layout(ctx)) { + let mut max_size = 0; + let mut max_align = 0; + for field in &self.fields { + let field_layout = ctx.resolve_type(field.ty) + .layout(ctx); - let n = (latest_offset_in_bits / 8) % - field_layout.align; - - if !self.packed && n != 0 { - latest_offset_in_bits += (field_layout.align - n) * - 8; - } - - latest_offset_in_bits += field_layout.size * 8; - max_align = cmp::max(max_align, field_layout.align); - } - } - } - - if latest_offset_in_bits == 0 && max_align == 0 { - None - } else { - Some(Layout::new((latest_offset_in_bits + 7) / 8, max_align)) - } - } else { - let mut max_size = 0; - let mut max_align = 0; - for field in &self.fields { - let field_layout = ctx.resolve_type(field.ty) - .layout(ctx); - - if let Some(layout) = field_layout { - max_size = cmp::max(max_size, layout.size); - max_align = cmp::max(max_align, layout.align); - } + if let Some(layout) = field_layout { + max_size = cmp::max(max_size, layout.size); + max_align = cmp::max(max_align, layout.align); } - - Some(Layout::new(max_size, max_align)) } + + Some(Layout::new(max_size, max_align)) } /// Get this type's set of fields. diff --git a/src/ir/enum_ty.rs b/src/ir/enum_ty.rs index 0a85577e..3470e033 100644 --- a/src/ir/enum_ty.rs +++ b/src/ir/enum_ty.rs @@ -5,8 +5,6 @@ use super::item::Item; use super::ty::TypeKind; use clang; use ir::annotations::Annotations; -use ir::int::IntKind; -use ir::layout::Layout; use parse::{ClangItemParser, ParseError}; /// An enum representing custom handling that can be given to a variant. @@ -51,19 +49,6 @@ impl Enum { &self.variants } - /// Compute the layout of this type. - pub fn calc_layout(&self, ctx: &BindgenContext) -> Option<Layout> { - self.repr - .map(|repr| ctx.resolve_type(repr)) - .and_then(|repr| match *repr.canonical_type(ctx).kind() { - TypeKind::Int(int_kind) => Some(int_kind), - _ => None, - }) - .unwrap_or(IntKind::Int) - .known_size() - .map(|size| Layout::new(size, size)) - } - /// Construct an enumeration from the given Clang type. pub fn from_ty(ty: &clang::Type, ctx: &mut BindgenContext) @@ -114,7 +99,7 @@ impl Enum { Annotations::new(&cursor) .and_then(|anno| if anno.hide() { Some(EnumVariantCustomBehavior::Hide) - } else if + } else if anno.constify_enum_variant() { Some(EnumVariantCustomBehavior::Constify) } else { diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 03d43b51..38379261 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -3,10 +3,10 @@ use super::context::BindgenContext; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; use super::ty::RUST_DERIVE_IN_ARRAY_LIMIT; -use std::cmp; +use std::{cmp, mem}; /// A type that represents the struct layout of a type. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Layout { /// The size (in bytes) of this layout. pub size: usize, @@ -16,6 +16,13 @@ pub struct Layout { pub packed: bool, } +#[test] +fn test_layout_for_size() { + let ptr_size = mem::size_of::<*mut ()>(); + assert_eq!(Layout::for_size(ptr_size), Layout::new(ptr_size, ptr_size)); + assert_eq!(Layout::for_size(3 * ptr_size), Layout::new(3 * ptr_size, ptr_size)); +} + impl Layout { /// Construct a new `Layout` with the given `size` and `align`. It is not /// packed. @@ -27,6 +34,20 @@ impl Layout { } } + /// Creates a non-packed layout for a given size, trying to use the maximum + /// alignment possible. + pub fn for_size(size: usize) -> Self { + let mut next_align = 2; + while size % next_align == 0 && next_align <= 2 * mem::size_of::<*mut ()>() { + next_align *= 2; + } + Layout { + size: size, + align: next_align / 2, + packed: false, + } + } + /// Is this a zero-sized layout? pub fn is_zero(&self) -> bool { self.size == 0 && self.align == 0 diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 332ad236..c3ec4039 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -382,41 +382,8 @@ impl Type { _ => false, } } - - /// If this type has a known size, return it (in bytes). - pub fn calc_size(&self, ctx: &BindgenContext) -> Option<usize> { - match self.kind { - TypeKind::Comp(ref ci) => { - ci.calc_layout(ctx).map(|layout| layout.size) - } - TypeKind::Enum(ref enum_ty) => { - enum_ty.calc_layout(ctx).map(|layout| layout.size) - } - TypeKind::Int(int_kind) => int_kind.known_size(), - TypeKind::Float(float_kind) => Some(float_kind.known_size()), - TypeKind::Complex(float_kind) => Some(float_kind.known_size() * 2), - TypeKind::Reference(..) | - TypeKind::NullPtr | - TypeKind::Pointer(..) | - TypeKind::BlockPointer | - TypeKind::Function(..) | - TypeKind::ObjCInterface(..) => Some(mem::size_of::<*mut ()>()), - TypeKind::ResolvedTypeRef(inner) | - TypeKind::Alias(inner) | - TypeKind::TemplateAlias(inner, _) | - TypeKind::TemplateInstantiation(inner, _) => { - ctx.resolve_type(inner).calc_size(ctx) - } - TypeKind::Array(inner, len) => { - ctx.resolve_type(inner) - .layout(ctx) - .map(|layout| layout.size * len) - } - TypeKind::Void | TypeKind::Named => None, - TypeKind::UnresolvedTypeRef(..) => unreachable!(), - } - } } + #[test] fn is_invalid_named_type_valid() { let ty = Type::new(Some("foo".into()), None, TypeKind::Named, false); |