summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/codegen/helpers.rs50
-rw-r--r--src/codegen/mod.rs20
-rw-r--r--src/codegen/struct_layout.rs6
-rw-r--r--src/ir/analysis/derive_copy.rs2
-rw-r--r--src/ir/analysis/has_destructor.rs179
-rw-r--r--src/ir/analysis/mod.rs2
-rw-r--r--src/ir/comp.rs51
-rw-r--r--src/ir/context.rs28
-rw-r--r--src/ir/template.rs8
-rw-r--r--src/ir/ty.rs16
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:
///