summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/codegen/mod.rs49
-rw-r--r--src/ir/analysis/derive_debug.rs317
-rw-r--r--src/ir/analysis/mod.rs (renamed from src/ir/analysis.rs)75
-rw-r--r--src/ir/analysis/template_params.rs (renamed from src/ir/named.rs)90
-rw-r--r--src/ir/comp.rs89
-rw-r--r--src/ir/context.rs181
-rw-r--r--src/ir/derive.rs17
-rw-r--r--src/ir/function.rs63
-rw-r--r--src/ir/item.rs47
-rw-r--r--src/ir/layout.rs6
-rw-r--r--src/ir/mod.rs1
-rw-r--r--src/ir/template.rs28
-rw-r--r--src/ir/traversal.rs59
-rw-r--r--src/ir/ty.rs29
-rw-r--r--src/uses.rs2
15 files changed, 738 insertions, 315 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 422ad418..0076f1df 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -294,6 +294,10 @@ impl CodeGenerator for Item {
result: &mut CodegenResult<'a>,
whitelisted_items: &ItemSet,
_extra: &()) {
+ if !self.is_enabled_for_codegen(ctx) {
+ return;
+ }
+
if self.is_hidden(ctx) || result.seen(self.id()) {
debug!("<Item as CodeGenerator>::codegen: Ignoring hidden or seen: \
self = {:?}",
@@ -316,19 +320,13 @@ impl CodeGenerator for Item {
module.codegen(ctx, result, whitelisted_items, self);
}
ItemKind::Function(ref fun) => {
- if ctx.options().codegen_config.functions {
- fun.codegen(ctx, result, whitelisted_items, self);
- }
+ fun.codegen(ctx, result, whitelisted_items, self);
}
ItemKind::Var(ref var) => {
- if ctx.options().codegen_config.vars {
- var.codegen(ctx, result, whitelisted_items, self);
- }
+ var.codegen(ctx, result, whitelisted_items, self);
}
ItemKind::Type(ref ty) => {
- if ctx.options().codegen_config.types {
- ty.codegen(ctx, result, whitelisted_items, self);
- }
+ ty.codegen(ctx, result, whitelisted_items, self);
}
}
}
@@ -419,6 +417,7 @@ impl CodeGenerator for Var {
item: &Item) {
use ir::var::VarType;
debug!("<Var as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
let canonical_name = item.canonical_name(ctx);
@@ -522,6 +521,7 @@ impl CodeGenerator for Type {
whitelisted_items: &ItemSet,
item: &Item) {
debug!("<Type as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
match *self.kind() {
TypeKind::Void |
@@ -705,6 +705,8 @@ impl<'a> CodeGenerator for Vtable<'a> {
_whitelisted_items: &ItemSet,
item: &Item) {
assert_eq!(item.id(), self.item_id);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
+
// For now, generate an empty struct, later we should generate function
// pointers and whatnot.
let attributes = vec![attributes::repr("C")];
@@ -745,6 +747,8 @@ impl CodeGenerator for TemplateInstantiation {
result: &mut CodegenResult<'a>,
_whitelisted_items: &ItemSet,
item: &Item) {
+ debug_assert!(item.is_enabled_for_codegen(ctx));
+
// Although uses of instantiations don't need code generation, and are
// just converted to rust types in fields, vars, etc, we take this
// opportunity to generate tests for their layout here. If the
@@ -1376,6 +1380,7 @@ impl CodeGenerator for CompInfo {
whitelisted_items: &ItemSet,
item: &Item) {
debug!("<CompInfo as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
// Don't output classes with template parameters that aren't types, and
// also don't output template specializations, neither total or partial.
@@ -1897,6 +1902,18 @@ impl MethodCodegen for Method {
result: &mut CodegenResult<'a>,
whitelisted_items: &ItemSet,
_parent: &CompInfo) {
+ assert!({
+ let cc = &ctx.options().codegen_config;
+ match self.kind() {
+ MethodKind::Constructor => cc.constructors,
+ MethodKind::Destructor => cc.destructors,
+ MethodKind::VirtualDestructor => cc.destructors,
+ MethodKind::Static |
+ MethodKind::Normal |
+ MethodKind::Virtual => cc.methods,
+ }
+ });
+
if self.is_virtual() {
return; // FIXME
}
@@ -2287,6 +2304,7 @@ impl CodeGenerator for Enum {
_whitelisted_items: &ItemSet,
item: &Item) {
debug!("<Enum as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
let name = item.canonical_name(ctx);
let enum_ty = item.expect_type();
@@ -3026,6 +3044,7 @@ impl CodeGenerator for Function {
_whitelisted_items: &ItemSet,
item: &Item) {
debug!("<Function as CodeGenerator>::codegen: item = {:?}", item);
+ debug_assert!(item.is_enabled_for_codegen(ctx));
// Similar to static member variables in a class template, we can't
// generate bindings to template functions, because the set of
@@ -3202,7 +3221,9 @@ impl CodeGenerator for ObjCInterface {
ctx: &BindgenContext,
result: &mut CodegenResult<'a>,
_whitelisted_items: &ItemSet,
- _: &Item) {
+ item: &Item) {
+ debug_assert!(item.is_enabled_for_codegen(ctx));
+
let mut impl_items = vec![];
let mut trait_items = vec![];
@@ -3254,8 +3275,6 @@ impl CodeGenerator for ObjCInterface {
}
}
-
-
pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
context.gen(|context| {
let counter = Cell::new(0);
@@ -3263,10 +3282,10 @@ pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
debug!("codegen: {:?}", context.options());
- let whitelisted_items: ItemSet = context.whitelisted_items().collect();
+ let whitelisted_items = context.whitelisted_items();
if context.options().emit_ir {
- for &id in whitelisted_items.iter() {
+ for &id in whitelisted_items {
let item = context.resolve_item(id);
println!("ir: {:?} = {:#?}", id, item);
}
@@ -3280,7 +3299,7 @@ pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> {
}
context.resolve_item(context.root_module())
- .codegen(context, &mut result, &whitelisted_items, &());
+ .codegen(context, &mut result, whitelisted_items, &());
result.items
})
diff --git a/src/ir/analysis/derive_debug.rs b/src/ir/analysis/derive_debug.rs
new file mode 100644
index 00000000..6e72da1f
--- /dev/null
+++ b/src/ir/analysis/derive_debug.rs
@@ -0,0 +1,317 @@
+//! Determining which types for which we can emit `#[derive(Debug)]`.
+
+use super::{ConstrainResult, MonotoneFramework};
+use std::collections::HashSet;
+use std::collections::HashMap;
+use ir::context::{BindgenContext, ItemId};
+use ir::traversal::EdgeKind;
+use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
+use ir::ty::TypeKind;
+use ir::comp::Field;
+use ir::traversal::Trace;
+use ir::comp::FieldMethods;
+use ir::layout::Layout;
+use ir::derive::CanTriviallyDeriveDebug;
+use ir::comp::CompKind;
+
+/// An analysis that finds for each IR item whether debug cannot be derived.
+///
+/// We use the monotone constraint function `cannot_derive_debug`, defined as
+/// follows:
+///
+/// * If T is Opaque and layout of the type is known, get this layout as opaque
+/// type and check whether it can be derived using trivial checks.
+/// * If T is Array type, debug cannot be derived if the length of the array is
+/// larger than the limit or the type of data the array contains cannot derive
+/// debug.
+/// * If T is a type alias, a templated alias or an indirection to another type,
+/// debug cannot be derived if the type T refers to cannot be derived debug.
+/// * If T is a compound type, debug cannot be derived if any of its base member
+/// or field cannot be derived debug.
+/// * If T is a pointer, T cannot be derived debug if T is a function pointer
+/// and the function signature cannot be derived debug.
+/// * If T is an instantiation of an abstract template definition, T cannot be
+/// derived debug if any of the template arguments or template definition
+/// cannot derive debug.
+#[derive(Debug, Clone)]
+pub struct CannotDeriveDebug<'ctx, 'gen>
+ where 'gen: 'ctx
+{
+ ctx: &'ctx BindgenContext<'gen>,
+
+ // The incremental result of this analysis's computation. Everything in this
+ // set cannot derive debug.
+ cannot_derive_debug: HashSet<ItemId>,
+
+ // Dependencies saying that if a key ItemId has been inserted into the
+ // `cannot_derive_debug` 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
+ // can derive debug or not.
+ dependencies: HashMap<ItemId, Vec<ItemId>>,
+}
+
+impl<'ctx, 'gen> CannotDeriveDebug<'ctx, 'gen> {
+ fn consider_edge(kind: EdgeKind) -> bool {
+ match kind {
+ // These are the only edges that can affect whether a type can derive
+ // debug or not.
+ EdgeKind::BaseMember |
+ EdgeKind::Field |
+ EdgeKind::TypeReference |
+ EdgeKind::VarType |
+ EdgeKind::TemplateArgument |
+ EdgeKind::TemplateDeclaration |
+ EdgeKind::TemplateParameterDefinition => true,
+
+ EdgeKind::Constructor |
+ EdgeKind::Destructor |
+ EdgeKind::FunctionReturn |
+ EdgeKind::FunctionParameter |
+ EdgeKind::InnerType |
+ EdgeKind::InnerVar |
+ EdgeKind::Method => false,
+ EdgeKind::Generic => false,
+ }
+ }
+
+ fn insert(&mut self, id: ItemId) -> ConstrainResult {
+ let was_not_already_in_set = self.cannot_derive_debug.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 CannotDeriveDebug<'ctx, 'gen> {
+ type Node = ItemId;
+ type Extra = &'ctx BindgenContext<'gen>;
+ type Output = HashSet<ItemId>;
+
+ fn new(ctx: &'ctx BindgenContext<'gen>) -> CannotDeriveDebug<'ctx, 'gen> {
+ let cannot_derive_debug = HashSet::new();
+ let mut dependencies = HashMap::new();
+
+ for &item in ctx.whitelisted_items() {
+ dependencies.entry(item).or_insert(vec![]);
+
+ {
+ // We reverse our natural IR graph edges to find dependencies
+ // between nodes.
+ item.trace(ctx, &mut |sub_item: ItemId, edge_kind| {
+ if ctx.whitelisted_items().contains(&sub_item) &&
+ Self::consider_edge(edge_kind) {
+ dependencies.entry(sub_item)
+ .or_insert(vec![])
+ .push(item);
+ }
+ }, &());
+ }
+ }
+
+ CannotDeriveDebug {
+ ctx,
+ cannot_derive_debug,
+ dependencies,
+ }
+ }
+
+ fn initial_worklist(&self) -> Vec<ItemId> {
+ self.ctx.whitelisted_items().iter().cloned().collect()
+ }
+
+ fn constrain(&mut self, id: ItemId) -> ConstrainResult {
+ if self.cannot_derive_debug.contains(&id) {
+ 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() {
+ // Handle the simple cases. These can derive debug without further
+ // information.
+ TypeKind::Void |
+ TypeKind::NullPtr |
+ TypeKind::Int(..) |
+ TypeKind::Float(..) |
+ TypeKind::Complex(..) |
+ TypeKind::Function(..) |
+ TypeKind::Enum(..) |
+ TypeKind::Reference(..) |
+ TypeKind::BlockPointer |
+ TypeKind::Named |
+ TypeKind::UnresolvedTypeRef(..) |
+ TypeKind::ObjCInterface(..) |
+ TypeKind::ObjCId |
+ TypeKind::ObjCSel => {
+ ConstrainResult::Same
+ },
+
+ TypeKind::Opaque => {
+ if ty.layout(self.ctx)
+ .map_or(true, |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
+ ConstrainResult::Same
+ } else {
+ self.insert(id)
+ }
+ },
+
+ TypeKind::Array(t, len) => {
+ if self.cannot_derive_debug.contains(&t) {
+ return self.insert(id);
+ }
+
+ if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
+ ConstrainResult::Same
+ } else {
+ self.insert(id)
+ }
+ },
+
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::TemplateAlias(t, _) |
+ TypeKind::Alias(t) => {
+ if self.cannot_derive_debug.contains(&t) {
+ self.insert(id)
+ } else {
+ ConstrainResult::Same
+ }
+ },
+
+ TypeKind::Comp(ref info) => {
+ if info.has_non_type_template_params() {
+ if ty.layout(self.ctx)
+ .map_or(true,
+ |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
+ return ConstrainResult::Same;
+ } else {
+ return self.insert(id);
+ }
+ }
+
+ if info.kind() == CompKind::Union {
+ if self.ctx.options().unstable_rust {
+ return self.insert(id);
+ }
+
+ if ty.layout(self.ctx)
+ .map_or(true,
+ |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
+ return ConstrainResult::Same;
+ } else {
+ return self.insert(id);
+ }
+ }
+
+ let bases_cannot_derive = info.base_members()
+ .iter()
+ .any(|base| self.cannot_derive_debug.contains(&base.ty));
+ if bases_cannot_derive {
+ return self.insert(id);
+ }
+
+ let fields_cannot_derive = info.fields()
+ .iter()
+ .any(|f| {
+ match *f {
+ Field::DataMember(ref data) => {
+ self.cannot_derive_debug.contains(&data.ty())
+ }
+ Field::Bitfields(ref bfu) => {
+ bfu.bitfields()
+ .iter().any(|b| {
+ self.cannot_derive_debug.contains(&b.ty())
+ })
+ }
+ }
+ });
+ if fields_cannot_derive {
+ return self.insert(id);
+ }
+
+ ConstrainResult::Same
+ },
+
+ TypeKind::Pointer(inner) => {
+ let inner_type = self.ctx.resolve_type(inner).canonical_type(self.ctx);
+ if let TypeKind::Function(ref sig) = *inner_type.kind() {
+ if !sig.can_trivially_derive_debug(&self.ctx, ()) {
+ return self.insert(id);
+ }
+ }
+ ConstrainResult::Same
+ },
+
+ TypeKind::TemplateInstantiation(ref template) => {
+ let args_cannot_derive = template.template_arguments()
+ .iter()
+ .any(|arg| self.cannot_derive_debug.contains(&arg));
+ if args_cannot_derive {
+ return self.insert(id);
+ }
+
+ let ty_cannot_derive = template.template_definition()
+ .into_resolver()
+ .through_type_refs()
+ .through_type_aliases()
+ .resolve(self.ctx)
+ .as_type()
+ .expect("Instantiations of a non-type?")
+ .as_comp()
+ .and_then(|c| {
+ // For non-type template parameters, we generate an opaque
+ // blob, and in this case the instantiation has a better
+ // idea of the layout than the definition does.
+ if c.has_non_type_template_params() {
+ let opaque = ty.layout(self.ctx)
+ .or_else(|| {
+ self.ctx
+ .resolve_type(template.template_definition())
+ .layout(self.ctx)
+ })
+ .unwrap_or(Layout::zero())
+ .opaque();
+ Some(!opaque.can_trivially_derive_debug(&self.ctx, ()))
+ } else {
+ None
+ }
+ })
+ .unwrap_or_else(|| {
+ self.cannot_derive_debug.contains(&template.template_definition())
+ });
+ if ty_cannot_derive {
+ return self.insert(id);
+ }
+
+ 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<CannotDeriveDebug<'ctx, 'gen>> for HashSet<ItemId> {
+ fn from(analysis: CannotDeriveDebug<'ctx, 'gen>) -> Self {
+ analysis.cannot_derive_debug
+ }
+}
diff --git a/src/ir/analysis.rs b/src/ir/analysis/mod.rs
index 28425a2c..5fe6785b 100644
--- a/src/ir/analysis.rs
+++ b/src/ir/analysis/mod.rs
@@ -1,4 +1,48 @@
-//! Fix-point analyses on the IR using the monotone framework.
+//! Fix-point analyses on the IR using the "monotone framework".
+//!
+//! A lattice is a set with a partial ordering between elements, where there is
+//! a single least upper bound and a single greatest least bound for every
+//! subset. We are dealing with finite lattices, which means that it has a
+//! finite number of elements, and it follows that there exists a single top and
+//! a single bottom member of the lattice. For example, the power set of a
+//! finite set forms a finite lattice where partial ordering is defined by set
+//! inclusion, that is `a <= b` if `a` is a subset of `b`. Here is the finite
+//! lattice constructed from the set {0,1,2}:
+//!
+//! ```text
+//! .----- Top = {0,1,2} -----.
+//! / | \
+//! / | \
+//! / | \
+//! {0,1} -------. {0,2} .--------- {1,2}
+//! | \ / \ / |
+//! | / \ |
+//! | / \ / \ |
+//! {0} --------' {1} `---------- {2}
+//! \ | /
+//! \ | /
+//! \ | /
+//! `------ Bottom = {} ------'
+//! ```
+//!
+//! A monotone function `f` is a function where if `x <= y`, then it holds that
+//! `f(x) <= f(y)`. It should be clear that running a monotone function to a
+//! fix-point on a finite lattice will always terminate: `f` can only "move"
+//! along the lattice in a single direction, and therefore can only either find
+//! a fix-point in the middle of the lattice or continue to the top or bottom
+//! depending if it is ascending or descending the lattice respectively.
+//!
+//! For a deeper introduction to the general form of this kind of analysis, see
+//! [Static Program Analysis by Anders Møller and Michael I. Schwartzbach][spa].
+//!
+//! [spa]: https://cs.au.dk/~amoeller/spa/spa.pdf
+
+// Re-export individual analyses.
+mod template_params;
+pub use self::template_params::UsedTemplateParameters;
+mod derive_debug;
+pub use self::derive_debug::CannotDeriveDebug;
+
use std::fmt;
/// An analysis in the monotone framework.
@@ -45,10 +89,10 @@ pub trait MonotoneFramework: Sized + fmt::Debug {
///
/// If this results in changing our internal state (ie, we discovered that
/// we have not reached a fix-point and iteration should continue), return
- /// `true`. Otherwise, return `false`. When `constrain` returns false for
- /// all nodes in the set, we have reached a fix-point and the analysis is
- /// complete.
- fn constrain(&mut self, node: Self::Node) -> bool;
+ /// `ConstrainResult::Changed`. Otherwise, return `ConstrainResult::Same`.
+ /// When `constrain` returns `ConstrainResult::Same` for all nodes in the
+ /// set, we have reached a fix-point and the analysis is complete.
+ fn constrain(&mut self, node: Self::Node) -> ConstrainResult;
/// For each node `d` that depends on the given `node`'s current answer when
/// running `constrain(d)`, call `f(d)`. This informs us which new nodes to
@@ -58,6 +102,17 @@ pub trait MonotoneFramework: Sized + fmt::Debug {
where F: FnMut(Self::Node);
}
+/// Whether an analysis's `constrain` function modified the incremental results
+/// or not.
+pub enum ConstrainResult {
+ /// The incremental results were updated, and the fix-point computation
+ /// should continue.
+ Changed,
+
+ /// The incremental results were not updated.
+ Same,
+}
+
/// Run an analysis in the monotone framework.
pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output
where Analysis: MonotoneFramework,
@@ -66,7 +121,7 @@ pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output
let mut worklist = analysis.initial_worklist();
while let Some(node) = worklist.pop() {
- if analysis.constrain(node) {
+ if let ConstrainResult::Changed = analysis.constrain(node) {
analysis.each_depending_on(node, |needs_work| {
worklist.push(needs_work);
});
@@ -183,7 +238,7 @@ mod tests {
self.graph.0.keys().cloned().collect()
}
- fn constrain(&mut self, node: Node) -> bool {
+ fn constrain(&mut self, node: Node) -> ConstrainResult {
// The set of nodes reachable from a node `x` is
//
// reachable(x) = s_0 U s_1 U ... U reachable(s_0) U reachable(s_1) U ...
@@ -210,7 +265,11 @@ mod tests {
}
let new_size = self.reachable[&node].len();
- original_size != new_size
+ if original_size != new_size {
+ ConstrainResult::Changed
+ } else {
+ ConstrainResult::Same
+ }
}
fn each_depending_on<F>(&self, node: Node, mut f: F)
diff --git a/src/ir/named.rs b/src/ir/analysis/template_params.rs
index a36ded64..44878d9e 100644
--- a/src/ir/named.rs
+++ b/src/ir/analysis/template_params.rs
@@ -75,63 +75,25 @@
//! union of its sub-item's used template parameters) and iterate to a
//! fixed-point.
//!
-//! We use the "monotone framework" for this fix-point analysis where our
-//! lattice is the mapping from each IR item to the powerset of the template
-//! parameters that appear in the input C++ header, our join function is set
-//! union, and we use the `ir::traversal::Trace` trait to implement the
-//! work-list optimization so we don't have to revisit every node in the graph
-//! when for every iteration towards the fix-point.
+//! We use the `ir::analysis::MonotoneFramework` infrastructure for this
+//! fix-point analysis, where our lattice is the mapping from each IR item to
+//! the powerset of the template parameters that appear in the input C++ header,
+//! our join function is set union. The set of template parameters appearing in
+//! the program is finite, as is the number of IR items. We start at our
+//! lattice's bottom element: every item mapping to an empty set of template
+//! parameters. Our analysis only adds members to each item's set of used
+//! template parameters, never removes them, so it is monotone. Because our
+//! lattice is finite and our constraint function is monotone, iteration to a
+//! fix-point will terminate.
//!
-//! A lattice is a set with a partial ordering between elements, where there is
-//! a single least upper bound and a single greatest least bound for every
-//! subset. We are dealing with finite lattices, which means that it has a
-//! finite number of elements, and it follows that there exists a single top and
-//! a single bottom member of the lattice. For example, the power set of a
-//! finite set forms a finite lattice where partial ordering is defined by set
-//! inclusion, that is `a <= b` if `a` is a subset of `b`. Here is the finite
-//! lattice constructed from the set {0,1,2}:
-//!
-//! ```text
-//! .----- Top = {0,1,2} -----.
-//! / | \
-//! / | \
-//! / | \
-//! {0,1} -------. {0,2} .--------- {1,2}
-//! | \ / \ / |
-//! | / \ |
-//! | / \ / \ |
-//! {0} --------' {1} `---------- {2}
-//! \ | /
-//! \ | /
-//! \ | /
-//! `------ Bottom = {} ------'
-//! ```
-//!
-//! A monotone function `f` is a function where if `x <= y`, then it holds that
-//! `f(x) <= f(y)`. It should be clear that running a monotone function to a
-//! fix-point on a finite lattice will always terminate: `f` can only "move"
-//! along the lattice in a single direction, and therefore can only either find
-//! a fix-point in the middle of the lattice or continue to the top or bottom
-//! depending if it is ascending or descending the lattice respectively.
-//!
-//! For our analysis, we are collecting the set of template parameters used by
-//! any given IR node. The set of template parameters appearing in the program
-//! is finite. Our lattice is their powerset. We start at the bottom element,
-//! the empty set. Our analysis only adds members to the set of used template
-//! parameters, never removes them, so it is monotone, and therefore iteration
-//! to a fix-point will terminate.
-//!
-//! For a deeper introduction to the general form of this kind of analysis, see
-//! [Static Program Analysis by Anders Møller and Michael I. Schwartzbach][spa].
-//!
-//! [spa]: https://cs.au.dk/~amoeller/spa/spa.pdf
-
-use super::analysis::MonotoneFramework;
-use super::context::{BindgenContext, ItemId};
-use super::item::{Item, ItemSet};
-use super::template::{TemplateInstantiation, TemplateParameters};
-use super::traversal::{EdgeKind, Trace};
-use super::ty::TypeKind;
+//! See `src/ir/analysis.rs` for more.
+
+use super::{ConstrainResult, MonotoneFramework};
+use ir::context::{BindgenContext, ItemId};
+use ir::item::{Item, ItemSet};
+use ir::template::{TemplateInstantiation, TemplateParameters};
+use ir::traversal::{EdgeKind, Trace};
+use ir::ty::TypeKind;
use std::collections::{HashMap, HashSet};
/// An analysis that finds for each IR item its set of template parameters that
@@ -211,6 +173,7 @@ impl<'ctx, 'gen> UsedTemplateParameters<'ctx, 'gen> {
EdgeKind::BaseMember |
EdgeKind::Field |
EdgeKind::Constructor |
+ EdgeKind::Destructor |
EdgeKind::VarType |
EdgeKind::FunctionReturn |
EdgeKind::FunctionParameter |
@@ -387,7 +350,10 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
-> UsedTemplateParameters<'ctx, 'gen> {
let mut used = HashMap::new();
let mut dependencies = HashMap::new();
- let whitelisted_items: HashSet<_> = ctx.whitelisted_items().collect();
+ let whitelisted_items: HashSet<_> = ctx.whitelisted_items()
+ .iter()
+ .cloned()
+ .collect();
let whitelisted_and_blacklisted_items: ItemSet = whitelisted_items.iter()
.cloned()
@@ -490,6 +456,8 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
// blacklisted items.
self.ctx
.whitelisted_items()
+ .iter()
+ .cloned()
.flat_map(|i| {
let mut reachable = vec![i];
i.trace(self.ctx, &mut |s, _| {
@@ -500,7 +468,7 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
.collect()
}
- fn constrain(&mut self, id: ItemId) -> bool {
+ fn constrain(&mut self, id: ItemId) -> ConstrainResult {
// Invariant: all hash map entries' values are `Some` upon entering and
// exiting this method.
extra_assert!(self.used.values().all(|v| v.is_some()));
@@ -552,7 +520,11 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
self.used.insert(id, Some(used_by_this_id));
extra_assert!(self.used.values().all(|v| v.is_some()));
- new_len != original_len
+ if new_len != original_len {
+ ConstrainResult::Changed
+ } else {
+ ConstrainResult::Same
+ }
}
fn each_depending_on<F>(&self, item: ItemId, mut f: F)
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index bc2d675d..0960ee11 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -2,7 +2,7 @@
use super::annotations::Annotations;
use super::context::{BindgenContext, ItemId};
-use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
+use super::derive::{CanDeriveCopy, CanDeriveDefault};
use super::dot::DotAttributes;
use super::item::{IsOpaque, Item};
use super::layout::Layout;
@@ -701,19 +701,6 @@ impl FieldMethods for FieldData {
}
}
-impl CanDeriveDebug for Field {
- type Extra = ();
-
- fn can_derive_debug(&self, ctx: &BindgenContext, _: ()) -> bool {
- match *self {
- Field::DataMember(ref data) => data.ty.can_derive_debug(ctx, ()),
- Field::Bitfields(BitfieldUnit { ref bitfields, .. }) => bitfields.iter().all(|b| {
- b.ty().can_derive_debug(ctx, ())
- }),
- }
- }
-}
-
impl CanDeriveDefault for Field {
type Extra = ();
@@ -857,10 +844,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 can_derive_debug cycle while cycling
- /// around the template arguments.
- detect_derive_debug_cycle: Cell<bool>,
-
/// Used to detect if we've run in a can_derive_default cycle while cycling
/// around the template arguments.
detect_derive_default_cycle: Cell<bool>,
@@ -893,7 +876,6 @@ impl CompInfo {
has_non_type_template_params: false,
packed: false,
found_unknown_attr: false,
- detect_derive_debug_cycle: Cell::new(false),
detect_derive_default_cycle: Cell::new(false),
detect_has_destructor_cycle: Cell::new(false),
is_forward_declaration: false,
@@ -1422,6 +1404,14 @@ impl DotAttributes for CompInfo {
}
}
+impl IsOpaque for CompInfo {
+ type Extra = ();
+
+ fn is_opaque(&self, _: &BindgenContext, _: &()) -> bool {
+ self.has_non_type_template_params
+ }
+}
+
impl TemplateParameters for CompInfo {
fn self_template_params(&self,
_ctx: &BindgenContext)
@@ -1434,51 +1424,6 @@ impl TemplateParameters for CompInfo {
}
}
-impl CanDeriveDebug for CompInfo {
- type Extra = Option<Layout>;
-
- fn can_derive_debug(&self,
- ctx: &BindgenContext,
- layout: Option<Layout>)
- -> bool {
- if self.has_non_type_template_params() {
- return layout.map_or(false, |l| l.opaque().can_derive_debug(ctx, ()));
- }
-
- // We can reach here recursively via template parameters of a member,
- // for example.
- if self.detect_derive_debug_cycle.get() {
- warn!("Derive debug cycle detected!");
- return true;
- }
-
- if self.kind == CompKind::Union {
- if ctx.options().unstable_rust {
- return false;
- }
-
- return layout.unwrap_or_else(Layout::zero)
- .opaque()
- .can_derive_debug(ctx, ());
- }
-
- self.detect_derive_debug_cycle.set(true);
-
- let can_derive_debug = {
- self.base_members
- .iter()
- .all(|base| base.ty.can_derive_debug(ctx, ())) &&
- self.fields()
- .iter()
- .all(|f| f.can_derive_debug(ctx, ()))
- };
-
- self.detect_derive_debug_cycle.set(false);
-
- can_derive_debug
- }
-}
-
impl CanDeriveDefault for CompInfo {
type Extra = Option<Layout>;
@@ -1498,9 +1443,11 @@ impl CanDeriveDefault for CompInfo {
return false;
}
- return layout.unwrap_or_else(Layout::zero)
- .opaque()
- .can_derive_default(ctx, ());
+ return layout.map_or(true, |l| l.opaque().can_derive_default(ctx, ()));
+ }
+
+ if self.has_non_type_template_params {
+ return layout.map_or(true, |l| l.opaque().can_derive_default(ctx, ()));
}
self.detect_derive_default_cycle.set(true);
@@ -1528,7 +1475,7 @@ impl<'a> CanDeriveCopy<'a> for CompInfo {
(item, layout): (&Item, Option<Layout>))
-> bool {
if self.has_non_type_template_params() {
- return layout.map_or(false, |l| l.opaque().can_derive_copy(ctx, ()));
+ return layout.map_or(true, |l| l.opaque().can_derive_copy(ctx, ()));
}
// NOTE: Take into account that while unions in C and C++ are copied by
@@ -1587,7 +1534,11 @@ impl Trace for CompInfo {
}
for method in self.methods() {
- tracer.visit_kind(method.signature, EdgeKind::Method);
+ if method.is_destructor() {
+ tracer.visit_kind(method.signature, EdgeKind::Destructor);
+ } else {
+ tracer.visit_kind(method.signature, EdgeKind::Method);
+ }
}
for &ctor in self.constructors() {
diff --git a/src/ir/context.rs b/src/ir/context.rs
index c3a0dd5a..ec79577f 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -5,8 +5,7 @@ use super::int::IntKind;
use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalPath, ItemSet};
use super::item_kind::ItemKind;
use super::module::{Module, ModuleKind};
-use super::named::UsedTemplateParameters;
-use super::analysis::analyze;
+use super::analysis::{analyze, UsedTemplateParameters, CannotDeriveDebug};
use super::template::{TemplateInstantiation, TemplateParameters};
use super::traversal::{self, Edge, ItemTraversal};
use super::ty::{FloatKind, Type, TypeKind};
@@ -18,7 +17,7 @@ use clang_sys;
use parse::ClangItemParser;
use std::borrow::Cow;
use std::cell::Cell;
-use std::collections::{HashMap, hash_map};
+use std::collections::{HashMap, hash_map, HashSet};
use std::collections::btree_map::{self, BTreeMap};
use std::fmt;
use std::iter::IntoIterator;
@@ -158,6 +157,11 @@ pub struct BindgenContext<'ctx> {
/// Whether a bindgen complex was generated
generated_bindegen_complex: Cell<bool>,
+ /// The set of `ItemId`s that are whitelisted for code generation. This the
+ /// very first thing computed after parsing our IR, and before running any
+ /// of our analyses.
+ whitelisted: Option<ItemSet>,
+
/// Map from an item's id to the set of template parameter items that it
/// uses. See `ir::named` for more details. Always `Some` during the codegen
/// phase.
@@ -169,6 +173,12 @@ pub struct BindgenContext<'ctx> {
/// Whether we need the mangling hack which removes the prefixing underscore.
needs_mangling_hack: bool,
+
+ /// The set of (`ItemId`s of) types that can't derive debug.
+ ///
+ /// This is populated when we enter codegen by `compute_can_derive_debug`
+ /// and is always `None` before that and `Some` after.
+ cant_derive_debug: Option<HashSet<ItemId>>,
}
/// A traversal of whitelisted items.
@@ -180,7 +190,7 @@ pub struct WhitelistedItems<'ctx, 'gen>
'gen,
ItemSet,
Vec<ItemId>,
- fn(Edge) -> bool>,
+ for<'a> fn(&'a BindgenContext, Edge) -> bool>,
}
impl<'ctx, 'gen> Iterator for WhitelistedItems<'ctx, 'gen>
@@ -209,7 +219,7 @@ impl<'ctx, 'gen> WhitelistedItems<'ctx, 'gen>
where R: IntoIterator<Item = ItemId>,
{
let predicate = if ctx.options().whitelist_recursively {
- traversal::all_edges
+ traversal::codegen_edges
} else {
traversal::no_edges
};
@@ -292,9 +302,11 @@ impl<'ctx> BindgenContext<'ctx> {
translation_unit: translation_unit,
options: options,
generated_bindegen_complex: Cell::new(false),
+ whitelisted: None,
used_template_parameters: None,
need_bitfield_allocation: Default::default(),
needs_mangling_hack: needs_mangling_hack,
+ cant_derive_debug: None,
};
me.add_item(root_module, None, None);
@@ -751,12 +763,22 @@ impl<'ctx> BindgenContext<'ctx> {
self.process_replacements();
}
+ // And assert once again, because resolving type refs and processing
+ // replacements both mutate the IR graph.
+ self.assert_no_dangling_references();
+
+ // Compute the whitelisted set after processing replacements and
+ // resolving type refs, as those are the final mutations of the IR
+ // graph, and their completion means that the IR graph is now frozen.
+ self.compute_whitelisted_items();
+
// Make sure to do this after processing replacements, since that messes
// with the parentage and module children, and we want to assert that it
// messes with them correctly.
self.assert_every_item_in_a_module();
self.find_used_template_parameters();
+ self.compute_cant_derive_debug();
let ret = cb(self);
self.gen_ctx = None;
@@ -830,7 +852,7 @@ impl<'ctx> BindgenContext<'ctx> {
// If you aren't recursively whitelisting, then we can't really make
// any sense of template parameter usage, and you're on your own.
let mut used_params = HashMap::new();
- for id in self.whitelisted_items() {
+ for &id in self.whitelisted_items() {
used_params.entry(id)
.or_insert(id.self_template_params(self)
.map_or(Default::default(),
@@ -1560,77 +1582,91 @@ impl<'ctx> BindgenContext<'ctx> {
///
/// If no items are explicitly whitelisted, then all items are considered
/// whitelisted.
- pub fn whitelisted_items<'me>(&'me self) -> WhitelistedItems<'me, 'ctx> {
+ pub fn whitelisted_items(&self) -> &ItemSet {
assert!(self.in_codegen_phase());
assert!(self.current_module == self.root_module);
- let roots = self.items()
- .filter(|&(_, item)| {
- // If nothing is explicitly whitelisted, then everything is fair
- // game.
- if self.options().whitelisted_types.is_empty() &&
- self.options().whitelisted_functions.is_empty() &&
- self.options().whitelisted_vars.is_empty() {
- return true;
- }
+ self.whitelisted.as_ref().unwrap()
+ }
- // If this is a type that explicitly replaces another, we assume
- // you know what you're doing.
- if item.annotations().use_instead_of().is_some() {
- return true;
- }
+ /// Compute the whitelisted items set and populate `self.whitelisted`.
+ fn compute_whitelisted_items(&mut self) {
+ assert!(self.in_codegen_phase());
+ assert!(self.current_module == self.root_module);
+ assert!(self.whitelisted.is_none());
+
+ self.whitelisted = Some({
+ let roots = self.items()
+ // Only consider items that are enabled for codegen.
+ .filter(|&(_, item)| item.is_enabled_for_codegen(self))
+ .filter(|&(_, item)| {
+ // If nothing is explicitly whitelisted, then everything is fair
+ // game.
+ if self.options().whitelisted_types.is_empty() &&
+ self.options().whitelisted_functions.is_empty() &&
+ self.options().whitelisted_vars.is_empty() {
+ return true;
+ }
- let name = item.canonical_path(self)[1..].join("::");
- debug!("whitelisted_items: testing {:?}", name);
- match *item.kind() {
- ItemKind::Module(..) => true,
- ItemKind::Function(_) => {
- self.options().whitelisted_functions.matches(&name)
- }
- ItemKind::Var(_) => {
- self.options().whitelisted_vars.matches(&name)
+ // If this is a type that explicitly replaces another, we assume
+ // you know what you're doing.
+ if item.annotations().use_instead_of().is_some() {
+ return true;
}
- ItemKind::Type(ref ty) => {
- if self.options().whitelisted_types.matches(&name) {
- return true;
+
+ let name = item.canonical_path(self)[1..].join("::");
+ debug!("whitelisted_items: testing {:?}", name);
+ match *item.kind() {
+ ItemKind::Module(..) => true,
+ ItemKind::Function(_) => {
+ self.options().whitelisted_functions.matches(&name)
}
+ ItemKind::Var(_) => {
+ self.options().whitelisted_vars.matches(&name)
+ }
+ ItemKind::Type(ref ty) => {
+ if self.options().whitelisted_types.matches(&name) {
+ return true;
+ }
- let parent = self.resolve_item(item.parent_id());
- if parent.is_module() {
- let mut prefix_path = parent.canonical_path(self);
-
- // Unnamed top-level enums are special and we
- // whitelist them via the `whitelisted_vars` filter,
- // since they're effectively top-level constants,
- // and there's no way for them to be referenced
- // consistently.
- if let TypeKind::Enum(ref enum_) = *ty.kind() {
- if ty.name().is_none() &&
- enum_.variants().iter().any(|variant| {
- prefix_path.push(variant.name().into());
- let name = prefix_path[1..].join("::");
- prefix_path.pop().unwrap();
- self.options()
- .whitelisted_vars
- .matches(&name)
- }) {
- return true;
+ let parent = self.resolve_item(item.parent_id());
+ if parent.is_module() {
+ let mut prefix_path = parent.canonical_path(self);
+
+ // Unnamed top-level enums are special and we
+ // whitelist them via the `whitelisted_vars` filter,
+ // since they're effectively top-level constants,
+ // and there's no way for them to be referenced
+ // consistently.
+ if let TypeKind::Enum(ref enum_) = *ty.kind() {
+ if ty.name().is_none() &&
+ enum_.variants().iter().any(|variant| {
+ prefix_path.push(variant.name().into());
+ let name = prefix_path[1..].join("::");
+ prefix_path.pop().unwrap();
+ self.options()
+ .whitelisted_vars
+ .matches(&name)
+ }) {
+ return true;
+ }
}
}
- }
- false
+ false
+ }
}
- }
- })
- .map(|(&id, _)| id);
+ })
+ .map(|(&id, _)| id);
+
+ // The reversal preserves the expected ordering of traversal, resulting
+ // in more stable-ish bindgen-generated names for anonymous types (like
+ // unions).
+ let mut roots: Vec<_> = roots.collect();
+ roots.reverse();
- // The reversal preserves the expected ordering of traversal, resulting
- // in more stable-ish bindgen-generated names for anonymous types (like
- // unions).
- let mut roots: Vec<_> = roots.collect();
- roots.reverse();
- WhitelistedItems::new(self, roots)
+ WhitelistedItems::new(self, roots).collect()
+ });
}
/// Convenient method for getting the prefix to use for most traits in
@@ -1652,6 +1688,23 @@ impl<'ctx> BindgenContext<'ctx> {
pub fn need_bindegen_complex_type(&self) -> bool {
self.generated_bindegen_complex.get()
}
+
+ /// Compute whether we can derive debug.
+ fn compute_cant_derive_debug(&mut self) {
+ assert!(self.cant_derive_debug.is_none());
+ self.cant_derive_debug = Some(analyze::<CannotDeriveDebug>(self));
+ }
+
+ /// Look up whether the item with `id` can
+ /// derive debug or not.
+ pub fn lookup_item_id_can_derive_debug(&self, id: ItemId) -> bool {
+ assert!(self.in_codegen_phase(),
+ "We only compute can_derive_debug when we enter codegen");
+
+ // Look up the computed value for whether the item with `id` can
+ // derive debug or not.
+ !self.cant_derive_debug.as_ref().unwrap().contains(&id)
+ }
}
/// A builder struct for configuring item resolution options.
diff --git a/src/ir/derive.rs b/src/ir/derive.rs
index 0fc8debf..59548163 100644
--- a/src/ir/derive.rs
+++ b/src/ir/derive.rs
@@ -23,6 +23,23 @@ pub trait CanDeriveDebug {
-> bool;
}
+/// A trait that encapsulates the logic for whether or not we can derive `Debug`.
+/// The difference between this trait and the CanDeriveDebug is that the type
+/// implementing this trait cannot use recursion or lookup result from fix point
+/// analysis. It's a helper trait for fix point analysis.
+pub trait CanTriviallyDeriveDebug {
+
+ /// Serve the same purpose as the Extra in CanDeriveDebug.
+ type Extra;
+
+ /// Return `true` if `Debug` can be derived for this thing, `false`
+ /// otherwise.
+ fn can_trivially_derive_debug(&self,
+ ctx: &BindgenContext,
+ extra: Self::Extra)
+ -> bool;
+}
+
/// A trait that encapsulates the logic for whether or not we can derive `Copy`
/// for a given thing.
pub trait CanDeriveCopy<'a> {
diff --git a/src/ir/function.rs b/src/ir/function.rs
index 5fa95203..663bb8e3 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -1,17 +1,47 @@
//! Intermediate representation for C/C++ functions and methods.
+use super::comp::MethodKind;
use super::context::{BindgenContext, ItemId};
use super::dot::DotAttributes;
use super::item::Item;
use super::traversal::{EdgeKind, Trace, Tracer};
use super::ty::TypeKind;
use clang;
-use clang_sys::CXCallingConv;
-use ir::derive::CanDeriveDebug;
+use clang_sys::{self, CXCallingConv};
+use ir::derive::CanTriviallyDeriveDebug;
use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult};
use std::io;
use syntax::abi;
+/// What kind of a function are we looking at?
+#[derive(Debug, Copy, Clone, PartialEq)]
+pub enum FunctionKind {
+ /// A plain, free function.
+ Function,
+ /// A method of some kind.
+ Method(MethodKind),
+}
+
+impl FunctionKind {
+ fn from_cursor(cursor: &clang::Cursor) -> Option<FunctionKind> {
+ Some(match cursor.kind() {
+ clang_sys::CXCursor_FunctionDecl => FunctionKind::Function,
+ clang_sys::CXCursor_Constructor => FunctionKind::Method(MethodKind::Constructor),
+ clang_sys::CXCursor_Destructor => FunctionKind::Method(MethodKind::Destructor),
+ clang_sys::CXCursor_CXXMethod => {
+ if cursor.method_is_virtual() {
+ FunctionKind::Method(MethodKind::Virtual)
+ } else if cursor.method_is_static() {
+ FunctionKind::Method(MethodKind::Static)
+ } else {
+ FunctionKind::Method(MethodKind::Normal)
+ }
+ }
+ _ => return None,
+ })
+ }
+}
+
/// A function declaration, with a signature, arguments, and argument names.
///
/// The argument names vector must be the same length as the ones in the
@@ -29,6 +59,9 @@ pub struct Function {
/// The doc comment on the function, if any.
comment: Option<String>,
+
+ /// The kind of function this is.
+ kind: FunctionKind,
}
impl Function {
@@ -36,13 +69,15 @@ impl Function {
pub fn new(name: String,
mangled_name: Option<String>,
sig: ItemId,
- comment: Option<String>)
+ comment: Option<String>,
+ kind: FunctionKind)
-> Self {
Function {
name: name,
mangled_name: mangled_name,
signature: sig,
comment: comment,
+ kind: kind,
}
}
@@ -60,6 +95,11 @@ impl Function {
pub fn signature(&self) -> ItemId {
self.signature
}
+
+ /// Get this function's kind.
+ pub fn kind(&self) -> FunctionKind {
+ self.kind
+ }
}
impl DotAttributes for Function {
@@ -70,6 +110,7 @@ impl DotAttributes for Function {
where W: io::Write,
{
if let Some(ref mangled) = self.mangled_name {
+ let mangled: String = mangled.chars().flat_map(|c| c.escape_default()).collect();
try!(writeln!(out,
"<tr><td>mangled name</td><td>{}</td></tr>",
mangled));
@@ -356,12 +397,10 @@ impl ClangSubItemParser for Function {
context: &mut BindgenContext)
-> Result<ParseResult<Self>, ParseError> {
use clang_sys::*;
- match cursor.kind() {
- CXCursor_FunctionDecl |
- CXCursor_Constructor |
- CXCursor_Destructor |
- CXCursor_CXXMethod => {}
- _ => return Err(ParseError::Continue),
+
+ let kind = match FunctionKind::from_cursor(&cursor) {
+ None => return Err(ParseError::Continue),
+ Some(k) => k,
};
debug!("Function::parse({:?}, {:?})", cursor, cursor.cur_type());
@@ -414,7 +453,7 @@ impl ClangSubItemParser for Function {
let comment = cursor.raw_comment();
- let function = Self::new(name, mangled_name, sig, comment);
+ let function = Self::new(name, mangled_name, sig, comment, kind);
Ok(ParseResult::New(function, Some(cursor)))
}
}
@@ -440,10 +479,10 @@ impl Trace for FunctionSig {
// and https://github.com/rust-lang/rust/issues/40158
//
// Note that copy is always derived, so we don't need to implement it.
-impl CanDeriveDebug for FunctionSig {
+impl CanTriviallyDeriveDebug for FunctionSig {
type Extra = ();
- fn can_derive_debug(&self, _ctx: &BindgenContext, _: ()) -> bool {
+ fn can_trivially_derive_debug(&self, _ctx: &BindgenContext, _: ()) -> bool {
const RUST_DERIVE_FUNPTR_LIMIT: usize = 12;
if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
return false;
diff --git a/src/ir/item.rs b/src/ir/item.rs
index aa1b216a..7f3afefb 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -3,10 +3,11 @@
use super::super::codegen::CONSTIFIED_ENUM_MODULE_REPR_NAME;
use super::annotations::Annotations;
use super::comment;
+use super::comp::MethodKind;
use super::context::{BindgenContext, ItemId, PartialType};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
use super::dot::DotAttributes;
-use super::function::Function;
+use super::function::{Function, FunctionKind};
use super::item_kind::ItemKind;
use super::layout::Opaque;
use super::module::Module;
@@ -272,28 +273,7 @@ impl CanDeriveDebug for Item {
type Extra = ();
fn can_derive_debug(&self, ctx: &BindgenContext, _: ()) -> bool {
- if self.detect_derive_debug_cycle.get() {
- return true;
- }
-
- self.detect_derive_debug_cycle.set(true);
-
- let result = ctx.options().derive_debug &&
- match self.kind {
- ItemKind::Type(ref ty) => {
- if self.is_opaque(ctx, &()) {
- ty.layout(ctx)
- .map_or(true, |l| l.opaque().can_derive_debug(ctx, ()))
- } else {
- ty.can_derive_debug(ctx, ())
- }
- }
- _ => false,
- };
-
- self.detect_derive_debug_cycle.set(false);
-
- result
+ ctx.options().derive_debug && ctx.lookup_item_id_can_derive_debug(self.id())
}
}
@@ -922,6 +902,27 @@ impl Item {
_ => false,
}
}
+
+ /// Is this item of a kind that is enabled for code generation?
+ pub fn is_enabled_for_codegen(&self, ctx: &BindgenContext) -> bool {
+ let cc = &ctx.options().codegen_config;
+ match *self.kind() {
+ ItemKind::Module(..) => true,
+ ItemKind::Var(_) => cc.vars,
+ ItemKind::Type(_) => cc.types,
+ ItemKind::Function(ref f) => {
+ match f.kind() {
+ FunctionKind::Function => cc.functions,
+ FunctionKind::Method(MethodKind::Constructor) => cc.constructors,
+ FunctionKind::Method(MethodKind::Destructor) |
+ FunctionKind::Method(MethodKind::VirtualDestructor) => cc.destructors,
+ FunctionKind::Method(MethodKind::Static) |
+ FunctionKind::Method(MethodKind::Normal) |
+ FunctionKind::Method(MethodKind::Virtual) => cc.methods,
+ }
+ }
+ }
+ }
}
impl IsOpaque for ItemId {
diff --git a/src/ir/layout.rs b/src/ir/layout.rs
index 21382b2d..91dd9d6c 100644
--- a/src/ir/layout.rs
+++ b/src/ir/layout.rs
@@ -1,7 +1,7 @@
//! Intermediate representation for the physical layout of some type.
use super::context::BindgenContext;
-use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
+use super::derive::{CanDeriveCopy, CanTriviallyDeriveDebug, CanDeriveDefault};
use super::ty::{RUST_DERIVE_IN_ARRAY_LIMIT, Type, TypeKind};
use clang;
use std::{cmp, mem};
@@ -102,10 +102,10 @@ impl Opaque {
}
}
-impl CanDeriveDebug for Opaque {
+impl CanTriviallyDeriveDebug for Opaque {
type Extra = ();
- fn can_derive_debug(&self, _: &BindgenContext, _: ()) -> bool {
+ fn can_trivially_derive_debug(&self, _: &BindgenContext, _: ()) -> bool {
self.array_size()
.map_or(false, |size| size <= RUST_DERIVE_IN_ARRAY_LIMIT)
}
diff --git a/src/ir/mod.rs b/src/ir/mod.rs
index 6d4d8a23..93894f94 100644
--- a/src/ir/mod.rs
+++ b/src/ir/mod.rs
@@ -17,7 +17,6 @@ pub mod item;
pub mod item_kind;
pub mod layout;
pub mod module;
-pub mod named;
pub mod template;
pub mod traversal;
pub mod ty;
diff --git a/src/ir/template.rs b/src/ir/template.rs
index fcba40e1..25d1d438 100644
--- a/src/ir/template.rs
+++ b/src/ir/template.rs
@@ -28,9 +28,8 @@
//! ```
use super::context::{BindgenContext, ItemId};
-use super::derive::{CanDeriveCopy, CanDeriveDebug};
+use super::derive::{CanDeriveCopy};
use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalPath};
-use super::layout::Layout;
use super::traversal::{EdgeKind, Trace, Tracer};
use clang;
use parse::ClangItemParser;
@@ -353,31 +352,6 @@ impl<'a> CanDeriveCopy<'a> for TemplateInstantiation {
}
}
-impl CanDeriveDebug for TemplateInstantiation {
- type Extra = Option<Layout>;
-
- fn can_derive_debug(&self,
- ctx: &BindgenContext,
- layout: Option<Layout>)
- -> bool {
- self.args.iter().all(|arg| arg.can_derive_debug(ctx, ())) &&
- ctx.resolve_type(self.definition)
- .as_comp()
- .and_then(|c| {
- // For non-type template parameters, we generate an opaque
- // blob, and in this case the instantiation has a better
- // idea of the layout than the definition does.
- if c.has_non_type_template_params() {
- let opaque = layout.unwrap_or(Layout::zero()).opaque();
- Some(opaque.can_derive_debug(ctx, ()))
- } else {
- None
- }
- })
- .unwrap_or_else(|| self.definition.can_derive_debug(ctx, ()))
- }
-}
-
impl Trace for TemplateInstantiation {
type Extra = ();
diff --git a/src/ir/traversal.rs b/src/ir/traversal.rs
index f0b7159c..d4b081f5 100644
--- a/src/ir/traversal.rs
+++ b/src/ir/traversal.rs
@@ -58,7 +58,7 @@ pub enum EdgeKind {
/// template<typename T>
/// class Foo { };
///
- /// using Bar = Foo<int>;
+ /// using Bar = Foo<ant>;
/// ```
TemplateDeclaration,
@@ -139,6 +139,19 @@ pub enum EdgeKind {
/// ```
Constructor,
+ /// An edge from a class or struct type to its destructor function. For
+ /// example, the edge from `Doggo` to `Doggo::~Doggo()`:
+ ///
+ /// ```C++
+ /// struct Doggo {
+ /// char* wow;
+ ///
+ /// public:
+ /// ~Doggo();
+ /// };
+ /// ```
+ Destructor,
+
/// An edge from a function declaration to its return type. For example, the
/// edge from `foo` to `int`:
///
@@ -172,26 +185,54 @@ pub enum EdgeKind {
pub trait TraversalPredicate {
/// Should the traversal follow this edge, and visit everything that is
/// reachable through it?
- fn should_follow(&self, edge: Edge) -> bool;
+ fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool;
}
-impl TraversalPredicate for fn(Edge) -> bool {
- fn should_follow(&self, edge: Edge) -> bool {
- (*self)(edge)
+impl TraversalPredicate for for<'a> fn(&'a BindgenContext, Edge) -> bool {
+ fn should_follow(&self, ctx: &BindgenContext, edge: Edge) -> bool {
+ (*self)(ctx, edge)
}
}
/// A `TraversalPredicate` implementation that follows all edges, and therefore
/// traversals using this predicate will see the whole IR graph reachable from
/// the traversal's roots.
-pub fn all_edges(_: Edge) -> bool {
+pub fn all_edges(_: &BindgenContext, _: Edge) -> bool {
true
}
+/// A `TraversalPredicate` implementation that only follows edges to items that
+/// are enabled for code generation. This lets us skip considering items for
+/// which we won't generate any bindings to.
+pub fn codegen_edges(ctx: &BindgenContext, edge: Edge) -> bool {
+ let cc = &ctx.options().codegen_config;
+ match edge.kind {
+ EdgeKind::Generic => ctx.resolve_item(edge.to).is_enabled_for_codegen(ctx),
+
+ // We statically know the kind of item that non-generic edges can point
+ // to, so we don't need to actually resolve the item and check
+ // `Item::is_enabled_for_codegen`.
+ EdgeKind::TemplateParameterDefinition |
+ EdgeKind::TemplateArgument |
+ EdgeKind::TemplateDeclaration |
+ EdgeKind::BaseMember |
+ EdgeKind::Field |
+ EdgeKind::InnerType |
+ EdgeKind::FunctionReturn |
+ EdgeKind::FunctionParameter |
+ EdgeKind::VarType |
+ EdgeKind::TypeReference => cc.types,
+ EdgeKind::InnerVar => cc.vars,
+ EdgeKind::Method => cc.methods,
+ EdgeKind::Constructor => cc.constructors,
+ EdgeKind::Destructor => cc.destructors,
+ }
+}
+
/// A `TraversalPredicate` implementation that never follows any edges, and
/// therefore traversals using this predicate will only visit the traversal's
/// roots.
-pub fn no_edges(_: Edge) -> bool {
+pub fn no_edges(_: &BindgenContext, _: Edge) -> bool {
false
}
@@ -401,7 +442,7 @@ impl<'ctx, 'gen, Storage, Queue, Predicate> Tracer
{
fn visit_kind(&mut self, item: ItemId, kind: EdgeKind) {
let edge = Edge::new(item, kind);
- if !self.predicate.should_follow(edge) {
+ if !self.predicate.should_follow(self.ctx, edge) {
return;
}
@@ -451,7 +492,7 @@ pub type AssertNoDanglingItemsTraversal<'ctx, 'gen> =
'gen,
Paths<'ctx, 'gen>,
VecDeque<ItemId>,
- fn(Edge) -> bool>;
+ for<'a> fn(&'a BindgenContext, Edge) -> bool>;
#[cfg(test)]
mod tests {
diff --git a/src/ir/ty.rs b/src/ir/ty.rs
index 5ab7cf08..78274d94 100644
--- a/src/ir/ty.rs
+++ b/src/ir/ty.rs
@@ -358,6 +358,7 @@ impl IsOpaque for Type {
match self.kind {
TypeKind::Opaque => true,
TypeKind::TemplateInstantiation(ref inst) => inst.is_opaque(ctx, item),
+ TypeKind::Comp(ref comp) => comp.is_opaque(ctx, &()),
_ => false,
}
}
@@ -538,32 +539,10 @@ impl TemplateParameters for TypeKind {
}
impl CanDeriveDebug for Type {
- type Extra = ();
+ type Extra = Item;
- fn can_derive_debug(&self, ctx: &BindgenContext, _: ()) -> bool {
- match self.kind {
- TypeKind::Array(t, len) => {
- len <= RUST_DERIVE_IN_ARRAY_LIMIT && t.can_derive_debug(ctx, ())
- }
- TypeKind::ResolvedTypeRef(t) |
- TypeKind::TemplateAlias(t, _) |
- TypeKind::Alias(t) => t.can_derive_debug(ctx, ()),
- TypeKind::Comp(ref info) => {
- info.can_derive_debug(ctx, self.layout(ctx))
- }
- TypeKind::Pointer(inner) => {
- let inner = ctx.resolve_type(inner);
- if let TypeKind::Function(ref sig) =
- *inner.canonical_type(ctx).kind() {
- return sig.can_derive_debug(ctx, ());
- }
- return true;
- }
- TypeKind::TemplateInstantiation(ref inst) => {
- inst.can_derive_debug(ctx, self.layout(ctx))
- }
- _ => true,
- }
+ fn can_derive_debug(&self, ctx: &BindgenContext, item: Item) -> bool {
+ ctx.lookup_item_id_can_derive_debug(item.id())
}
}
diff --git a/src/uses.rs b/src/uses.rs
index fee2be24..fc7c126e 100644
--- a/src/uses.rs
+++ b/src/uses.rs
@@ -72,6 +72,8 @@ pub fn generate_dummy_uses<W>(ctx: &mut BindgenContext,
try!(writeln!(dest, ""));
let type_items = ctx.whitelisted_items()
+ .iter()
+ .cloned()
.map(|id| ctx.resolve_item(id))
.filter(|item| {
// We only want type items.