diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 49 | ||||
-rw-r--r-- | src/ir/analysis/derive_debug.rs | 317 | ||||
-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.rs | 89 | ||||
-rw-r--r-- | src/ir/context.rs | 181 | ||||
-rw-r--r-- | src/ir/derive.rs | 17 | ||||
-rw-r--r-- | src/ir/function.rs | 63 | ||||
-rw-r--r-- | src/ir/item.rs | 47 | ||||
-rw-r--r-- | src/ir/layout.rs | 6 | ||||
-rw-r--r-- | src/ir/mod.rs | 1 | ||||
-rw-r--r-- | src/ir/template.rs | 28 | ||||
-rw-r--r-- | src/ir/traversal.rs | 59 | ||||
-rw-r--r-- | src/ir/ty.rs | 29 | ||||
-rw-r--r-- | src/uses.rs | 2 |
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. |