diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/helpers.rs | 50 | ||||
-rw-r--r-- | src/codegen/mod.rs | 20 | ||||
-rw-r--r-- | src/codegen/struct_layout.rs | 6 | ||||
-rw-r--r-- | src/ir/analysis/derive_copy.rs | 2 | ||||
-rw-r--r-- | src/ir/analysis/has_destructor.rs | 179 | ||||
-rw-r--r-- | src/ir/analysis/mod.rs | 2 | ||||
-rw-r--r-- | src/ir/comp.rs | 51 | ||||
-rw-r--r-- | src/ir/context.rs | 28 | ||||
-rw-r--r-- | src/ir/template.rs | 8 | ||||
-rw-r--r-- | src/ir/ty.rs | 16 |
10 files changed, 245 insertions, 117 deletions
diff --git a/src/codegen/helpers.rs b/src/codegen/helpers.rs index 2bc3ad44..ed165aa9 100644 --- a/src/codegen/helpers.rs +++ b/src/codegen/helpers.rs @@ -60,40 +60,28 @@ pub mod attributes { /// Generates a proper type for a field or type with a given `Layout`, that is, /// a type with the correct size and alignment restrictions. -pub struct BlobTyBuilder { - layout: Layout, -} - -impl BlobTyBuilder { - pub fn new(layout: Layout) -> Self { - BlobTyBuilder { - layout: layout, +pub fn blob(layout: Layout) -> P<ast::Ty> { + let opaque = layout.opaque(); + + // FIXME(emilio, #412): We fall back to byte alignment, but there are + // some things that legitimately are more than 8-byte aligned. + // + // Eventually we should be able to `unwrap` here, but... + let ty_name = match opaque.known_rust_type_for_array() { + Some(ty) => ty, + None => { + warn!("Found unknown alignment on code generation!"); + "u8" } - } + }; - pub fn build(self) -> P<ast::Ty> { - let opaque = self.layout.opaque(); + let data_len = opaque.array_size().unwrap_or(layout.size); - // FIXME(emilio, #412): We fall back to byte alignment, but there are - // some things that legitimately are more than 8-byte aligned. - // - // Eventually we should be able to `unwrap` here, but... - let ty_name = match opaque.known_rust_type_for_array() { - Some(ty) => ty, - None => { - warn!("Found unknown alignment on code generation!"); - "u8" - } - }; - - let data_len = opaque.array_size().unwrap_or(self.layout.size); - - let inner_ty = aster::AstBuilder::new().ty().path().id(ty_name).build(); - if data_len == 1 { - inner_ty - } else { - aster::ty::TyBuilder::new().array(data_len).build(inner_ty) - } + let inner_ty = aster::AstBuilder::new().ty().path().id(ty_name).build(); + if data_len == 1 { + inner_ty + } else { + aster::ty::TyBuilder::new().array(data_len).build(inner_ty) } } diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 1df6f123..553dd24f 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -3,7 +3,7 @@ mod error; mod helpers; pub mod struct_layout; -use self::helpers::{BlobTyBuilder, attributes}; +use self::helpers::attributes; use self::struct_layout::StructLayoutTracker; use aster; @@ -1155,7 +1155,7 @@ impl Bitfield { let bitfield_ty_layout = bitfield_ty.layout(ctx).expect( "Bitfield without layout? Gah!", ); - let bitfield_int_ty = BlobTyBuilder::new(bitfield_ty_layout).build(); + let bitfield_int_ty = helpers::blob(bitfield_ty_layout); let bitfield_ty = bitfield_ty.to_rust_ty_or_opaque(ctx, bitfield_ty_item); @@ -1205,7 +1205,7 @@ impl<'a> FieldCodegen<'a> for BitfieldUnit { F: Extend<ast::StructField>, M: Extend<ast::ImplItem>, { - let field_ty = BlobTyBuilder::new(self.layout()).build(); + let field_ty = helpers::blob(self.layout()); let unit_field_name = format!("_bitfield_{}", self.nth()); let field = StructFieldBuilder::named(&unit_field_name) @@ -1355,7 +1355,7 @@ impl<'a> FieldCodegen<'a> for Bitfield { let bitfield_ty_layout = bitfield_ty.layout(ctx).expect( "Bitfield without layout? Gah!", ); - let bitfield_int_ty = BlobTyBuilder::new(bitfield_ty_layout).build(); + let bitfield_int_ty = helpers::blob(bitfield_ty_layout); let bitfield_ty = bitfield_ty.to_rust_ty_or_opaque(ctx, bitfield_ty_item); @@ -1643,7 +1643,7 @@ impl CodeGenerator for CompInfo { let layout = item.kind().expect_type().layout(ctx); if is_union && !is_opaque { let layout = layout.expect("Unable to get layout information?"); - let ty = BlobTyBuilder::new(layout).build(); + let ty = helpers::blob(layout); let field = if self.can_be_rust_union(ctx) { StructFieldBuilder::named("_bindgen_union_align").build_ty(ty) @@ -1665,7 +1665,7 @@ impl CodeGenerator for CompInfo { match layout { Some(l) => { - let ty = BlobTyBuilder::new(l).build(); + let ty = helpers::blob(l); let field = StructFieldBuilder::named( "_bindgen_opaque_blob", ).pub_() @@ -1710,7 +1710,7 @@ impl CodeGenerator for CompInfo { }; if has_address { - let ty = BlobTyBuilder::new(Layout::new(1, 1)).build(); + let ty = helpers::blob(Layout::new(1, 1)); let field = StructFieldBuilder::named("_address").pub_().build_ty(ty); fields.push(field); @@ -2731,7 +2731,7 @@ trait TryToOpaque { extra: &Self::Extra, ) -> error::Result<P<ast::Ty>> { self.try_get_layout(ctx, extra).map(|layout| { - BlobTyBuilder::new(layout).build() + helpers::blob(layout) }) } } @@ -2759,7 +2759,7 @@ trait ToOpaque: TryToOpaque { extra: &Self::Extra, ) -> P<ast::Ty> { let layout = self.get_layout(ctx, extra); - BlobTyBuilder::new(layout).build() + helpers::blob(layout) } } @@ -2817,7 +2817,7 @@ where |_| if let Ok(layout) = self.try_get_layout(ctx, extra) { - Ok(BlobTyBuilder::new(layout).build()) + Ok(helpers::blob(layout)) } else { Err(error::Error::NoLayoutForOpaqueBlob) }, diff --git a/src/codegen/struct_layout.rs b/src/codegen/struct_layout.rs index cd2d62f4..956a1f44 100644 --- a/src/codegen/struct_layout.rs +++ b/src/codegen/struct_layout.rs @@ -1,6 +1,6 @@ //! Helpers for code generation that need struct layout -use super::helpers::BlobTyBuilder; +use super::helpers; use aster::struct_field::StructFieldBuilder; @@ -295,7 +295,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { if self.max_field_align < layout.align && layout.align <= mem::size_of::<*mut ()>() { - let ty = BlobTyBuilder::new(Layout::new(0, layout.align)).build(); + let ty = helpers::blob(Layout::new(0, layout.align)); Some( StructFieldBuilder::named("__bindgen_align") @@ -312,7 +312,7 @@ impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> { } fn padding_field(&mut self, layout: Layout) -> ast::StructField { - let ty = BlobTyBuilder::new(layout).build(); + let ty = helpers::blob(layout); let padding_count = self.padding_count; self.padding_count += 1; diff --git a/src/ir/analysis/derive_copy.rs b/src/ir/analysis/derive_copy.rs index de06f81c..c108a961 100644 --- a/src/ir/analysis/derive_copy.rs +++ b/src/ir/analysis/derive_copy.rs @@ -208,7 +208,7 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveCopy<'ctx, 'gen> { // NOTE: Take into account that while unions in C and C++ are copied by // default, the may have an explicit destructor in C++, so we can't // defer this check just for the union case. - if info.has_destructor(self.ctx) { + if self.ctx.lookup_item_id_has_destructor(&id) { trace!(" comp has destructor which cannot derive copy"); return self.insert(id); } diff --git a/src/ir/analysis/has_destructor.rs b/src/ir/analysis/has_destructor.rs new file mode 100644 index 00000000..b37dbaaa --- /dev/null +++ b/src/ir/analysis/has_destructor.rs @@ -0,0 +1,179 @@ +//! Determining which types have destructors + +use super::{ConstrainResult, MonotoneFramework, generate_dependencies}; +use ir::context::{BindgenContext, ItemId}; +use ir::traversal::EdgeKind; +use ir::comp::{CompKind, Field, FieldMethods}; +use ir::ty::TypeKind; +use std::collections::HashMap; +use std::collections::HashSet; + +/// An analysis that finds for each IR item whether it has a destructor or not +/// +/// We use the monotone function `has destructor`, defined as follows: +/// +/// * If T is a type alias, a templated alias, or an indirection to another type, +/// T has a destructor if the type T refers to has a destructor. +/// * If T is a compound type, T has a destructor if we saw a destructor when parsing it, +/// or if it's a struct, T has a destructor if any of its base members has a destructor, +/// or if any of its fields have a destructor. +/// * If T is an instantiation of an abstract template definition, T has +/// a destructor if its template definition has a destructor, +/// or if any of the template arguments has a destructor. +/// * If T is the type of a field, that field has a destructor if it's not a bitfield, +/// and if T has a destructor. +#[derive(Debug, Clone)] +pub struct HasDestructorAnalysis<'ctx, 'gen> +where + 'gen: 'ctx, +{ + ctx: &'ctx BindgenContext<'gen>, + + // The incremental result of this analysis's computation. Everything in this + // set definitely has a destructor. + have_destructor: HashSet<ItemId>, + + // Dependencies saying that if a key ItemId has been inserted into the + // `have_destructor` set, then each of the ids in Vec<ItemId> need to be + // considered again. + // + // This is a subset of the natural IR graph with reversed edges, where we + // only include the edges from the IR graph that can affect whether a type + // has a destructor or not. + dependencies: HashMap<ItemId, Vec<ItemId>>, +} + +impl<'ctx, 'gen> HasDestructorAnalysis<'ctx, 'gen> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + // These are the only edges that can affect whether a type has a + // destructor or not. + EdgeKind::TypeReference | + EdgeKind::BaseMember | + EdgeKind::Field | + EdgeKind::TemplateArgument | + EdgeKind::TemplateDeclaration => true, + _ => false, + } + } + + fn insert(&mut self, id: ItemId) -> ConstrainResult { + let was_not_already_in_set = self.have_destructor.insert(id); + assert!( + was_not_already_in_set, + "We shouldn't try and insert {:?} twice because if it was \ + already in the set, `constrain` should have exited early.", + id + ); + ConstrainResult::Changed + } +} + +impl<'ctx, 'gen> MonotoneFramework for HasDestructorAnalysis<'ctx, 'gen> { + type Node = ItemId; + type Extra = &'ctx BindgenContext<'gen>; + type Output = HashSet<ItemId>; + + fn new(ctx: &'ctx BindgenContext<'gen>) -> Self { + let have_destructor = HashSet::new(); + let dependencies = generate_dependencies(ctx, Self::consider_edge); + + HasDestructorAnalysis { + ctx, + have_destructor, + dependencies, + } + } + + fn initial_worklist(&self) -> Vec<ItemId> { + self.ctx.whitelisted_items().iter().cloned().collect() + } + + fn constrain(&mut self, id: ItemId) -> ConstrainResult { + if self.have_destructor.contains(&id) { + // We've already computed that this type has a destructor and that can't + // change. + return ConstrainResult::Same; + } + + let item = self.ctx.resolve_item(id); + let ty = match item.as_type() { + None => return ConstrainResult::Same, + Some(ty) => ty, + }; + + match *ty.kind() { + TypeKind::TemplateAlias(t, _) | + TypeKind::Alias(t) | + TypeKind::ResolvedTypeRef(t) => { + if self.have_destructor.contains(&t) { + self.insert(id) + } else { + ConstrainResult::Same + } + } + + TypeKind::Comp(ref info) => { + if info.has_own_destructor() { + return self.insert(id); + } + + match info.kind() { + CompKind::Union => ConstrainResult::Same, + CompKind::Struct => { + let base_or_field_destructor = + info.base_members().iter().any(|base| { + self.have_destructor.contains(&base.ty) + }) || + info.fields().iter().any(|field| { + match *field { + Field::DataMember(ref data) => + self.have_destructor.contains(&data.ty()), + Field::Bitfields(_) => false + } + }); + if base_or_field_destructor { + self.insert(id) + } else { + ConstrainResult::Same + } + } + } + } + + TypeKind::TemplateInstantiation(ref inst) => { + let definition_or_arg_destructor = + self.have_destructor.contains(&inst.template_definition()) + || + inst.template_arguments().iter().any(|arg| { + self.have_destructor.contains(arg) + }); + if definition_or_arg_destructor { + self.insert(id) + } else { + ConstrainResult::Same + } + } + + _ => ConstrainResult::Same, + } + } + + fn each_depending_on<F>(&self, id: ItemId, mut f: F) + where + F: FnMut(ItemId), + { + if let Some(edges) = self.dependencies.get(&id) { + for item in edges { + trace!("enqueue {:?} into worklist", item); + f(*item); + } + } + } +} + +impl<'ctx, 'gen> From<HasDestructorAnalysis<'ctx, 'gen>> for HashSet<ItemId> { + fn from(analysis: HasDestructorAnalysis<'ctx, 'gen>) -> Self { + analysis.have_destructor + } +} diff --git a/src/ir/analysis/mod.rs b/src/ir/analysis/mod.rs index 42aa4a8f..ab19cb4f 100644 --- a/src/ir/analysis/mod.rs +++ b/src/ir/analysis/mod.rs @@ -45,6 +45,8 @@ pub use self::derive_debug::CannotDeriveDebug; mod has_vtable; pub use self::has_vtable::HasVtable; pub use self::has_vtable::HasVtableAnalysis; +mod has_destructor; +pub use self::has_destructor::HasDestructorAnalysis; mod derive_default; pub use self::derive_default::CannotDeriveDefault; mod derive_copy; diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 1a02feb5..bbccd06a 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -13,7 +13,6 @@ use codegen::struct_layout::{align_to, bytes_from_bits_pow2}; use ir::derive::CanDeriveCopy; use parse::{ClangItemParser, ParseError}; use peeking_take_while::PeekableExt; -use std::cell::Cell; use std::cmp; use std::io; use std::mem; @@ -170,18 +169,6 @@ pub enum Field { } impl Field { - fn has_destructor(&self, ctx: &BindgenContext) -> bool { - match *self { - Field::DataMember(ref data) => { - ctx.resolve_type(data.ty).has_destructor(ctx) - } - // Bitfields may not be of a type that has a destructor. - Field::Bitfields(BitfieldUnit { - .. - }) => false, - } - } - /// Get this field's layout. pub fn layout(&self, ctx: &BindgenContext) -> Option<Layout> { match *self { @@ -865,10 +852,6 @@ pub struct CompInfo { /// and pray, or behave as an opaque type. found_unknown_attr: bool, - /// Used to detect if we've run in a has_destructor cycle while cycling - /// around the template arguments. - detect_has_destructor_cycle: Cell<bool>, - /// Used to indicate when a struct has been forward declared. Usually used /// in headers so that APIs can't modify them directly. is_forward_declaration: bool, @@ -893,7 +876,6 @@ impl CompInfo { has_non_type_template_params: false, packed: false, found_unknown_attr: false, - detect_has_destructor_cycle: Cell::new(false), is_forward_declaration: false, } } @@ -909,34 +891,6 @@ impl CompInfo { }) } - /// Does this compound type have a destructor? - pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { - if self.detect_has_destructor_cycle.get() { - warn!("Cycle detected looking for destructors"); - // Assume no destructor, since we don't have an explicit one. - return false; - } - - self.detect_has_destructor_cycle.set(true); - - let has_destructor = self.has_destructor || - match self.kind { - CompKind::Union => false, - CompKind::Struct => { - self.base_members.iter().any(|base| { - ctx.resolve_type(base.ty).has_destructor(ctx) - }) || - self.fields().iter().any( - |field| field.has_destructor(ctx), - ) - } - }; - - self.detect_has_destructor_cycle.set(false); - - has_destructor - } - /// Compute the layout of this type. /// /// This is called as a fallback under some circumstances where LLVM doesn't @@ -989,6 +943,11 @@ impl CompInfo { return self.has_own_virtual_method; } + /// Did we see a destructor when parsing this type? + pub fn has_own_destructor(&self) -> bool { + self.has_destructor + } + /// Get this type's set of methods. pub fn methods(&self) -> &[Method] { &self.methods diff --git a/src/ir/context.rs b/src/ir/context.rs index 67245c29..3f503fdd 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -3,8 +3,8 @@ use super::analysis::{CannotDeriveCopy, CannotDeriveDebug, CannotDeriveDefault, CannotDeriveHash, CannotDerivePartialEq, HasTypeParameterInArray, - HasVtableAnalysis, UsedTemplateParameters, HasFloat, - analyze}; + HasVtableAnalysis, HasDestructorAnalysis, UsedTemplateParameters, + HasFloat, analyze}; use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash, CanDerivePartialEq, CanDeriveEq}; use super::int::IntKind; @@ -239,6 +239,12 @@ pub struct BindgenContext<'ctx> { /// before that and `Some` after. have_vtable: Option<HashSet<ItemId>>, + /// The set of (`ItemId's of`) types that has destructor. + /// + /// Populated when we enter codegen by `compute_has_destructor`; always `None` + /// before that and `Some` after. + have_destructor: Option<HashSet<ItemId>>, + /// The set of (`ItemId's of`) types that has array. /// /// Populated when we enter codegen by `compute_has_type_param_in_array`; always `None` @@ -390,6 +396,7 @@ impl<'ctx> BindgenContext<'ctx> { cannot_derive_hash: None, cannot_derive_partialeq: None, have_vtable: None, + have_destructor: None, has_type_param_in_array: None, has_float: None, }; @@ -901,6 +908,7 @@ impl<'ctx> BindgenContext<'ctx> { self.assert_every_item_in_a_module(); self.compute_has_vtable(); + self.compute_has_destructor(); self.find_used_template_parameters(); self.compute_cannot_derive_debug(); self.compute_cannot_derive_default(); @@ -1001,6 +1009,22 @@ impl<'ctx> BindgenContext<'ctx> { self.have_vtable.as_ref().unwrap().contains(id) } + /// Compute whether the type has a destructor. + fn compute_has_destructor(&mut self) { + assert!(self.have_destructor.is_none()); + self.have_destructor = Some(analyze::<HasDestructorAnalysis>(self)); + } + + /// Look up whether the item with `id` has a destructor. + pub fn lookup_item_id_has_destructor(&self, id: &ItemId) -> bool { + assert!( + self.in_codegen_phase(), + "We only compute destructors when we enter codegen" + ); + + self.have_destructor.as_ref().unwrap().contains(id) + } + fn find_used_template_parameters(&mut self) { if self.options.whitelist_recursively { let used_params = analyze::<UsedTemplateParameters>(self); diff --git a/src/ir/template.rs b/src/ir/template.rs index 8fe4c704..c1650abc 100644 --- a/src/ir/template.rs +++ b/src/ir/template.rs @@ -309,14 +309,6 @@ impl TemplateInstantiation { template_args, )) } - - /// Does this instantiation have a destructor? - pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { - ctx.resolve_type(self.definition).has_destructor(ctx) || - self.args.iter().any(|arg| { - ctx.resolve_type(*arg).has_destructor(ctx) - }) - } } impl IsOpaque for TemplateInstantiation { diff --git a/src/ir/ty.rs b/src/ir/ty.rs index bd2a54ef..da1cc6d7 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -237,22 +237,6 @@ impl Type { }) } - /// Returns whether this type has a destructor. - pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { - match self.kind { - TypeKind::TemplateAlias(t, _) | - TypeKind::Alias(t) | - TypeKind::ResolvedTypeRef(t) => { - ctx.resolve_type(t).has_destructor(ctx) - } - TypeKind::TemplateInstantiation(ref inst) => { - inst.has_destructor(ctx) - } - TypeKind::Comp(ref info) => info.has_destructor(ctx), - _ => false, - } - } - /// Whether this named type is an invalid C++ identifier. This is done to /// avoid generating invalid code with some cases we can't handle, see: /// |