diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 64 | ||||
-rw-r--r-- | src/ir/analysis/derive_debug.rs | 153 | ||||
-rw-r--r-- | src/ir/analysis/has_vtable.rs | 168 | ||||
-rw-r--r-- | src/ir/analysis/mod.rs | 30 | ||||
-rw-r--r-- | src/ir/comp.rs | 47 | ||||
-rw-r--r-- | src/ir/context.rs | 36 | ||||
-rw-r--r-- | src/ir/derive.rs | 2 | ||||
-rw-r--r-- | src/ir/function.rs | 4 | ||||
-rw-r--r-- | src/ir/item.rs | 21 | ||||
-rw-r--r-- | src/ir/layout.rs | 2 | ||||
-rw-r--r-- | src/ir/template.rs | 5 | ||||
-rw-r--r-- | src/ir/ty.rs | 33 | ||||
-rw-r--r-- | src/lib.rs | 6 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/options.rs | 2 |
15 files changed, 390 insertions, 189 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index a089b070..05e16a5b 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -18,7 +18,7 @@ use ir::dot; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Abi, Function, FunctionSig}; use ir::int::IntKind; -use ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath, ItemSet}; +use ir::item::{IsOpaque, Item, ItemCanonicalName, ItemCanonicalPath}; use ir::item_kind::ItemKind; use ir::layout::Layout; use ir::module::Module; @@ -282,7 +282,6 @@ trait CodeGenerator { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, extra: &Self::Extra); } @@ -292,7 +291,6 @@ impl CodeGenerator for Item { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, _extra: &()) { if !self.is_enabled_for_codegen(ctx) { return; @@ -306,7 +304,7 @@ impl CodeGenerator for Item { } debug!("<Item as CodeGenerator>::codegen: self = {:?}", self); - if !whitelisted_items.contains(&self.id()) { + if !ctx.codegen_items().contains(&self.id()) { // TODO(emilio, #453): Figure out what to do when this happens // legitimately, we could track the opaque stuff and disable the // assertion there I guess. @@ -317,16 +315,16 @@ impl CodeGenerator for Item { match *self.kind() { ItemKind::Module(ref module) => { - module.codegen(ctx, result, whitelisted_items, self); + module.codegen(ctx, result, self); } ItemKind::Function(ref fun) => { - fun.codegen(ctx, result, whitelisted_items, self); + fun.codegen(ctx, result, self); } ItemKind::Var(ref var) => { - var.codegen(ctx, result, whitelisted_items, self); + var.codegen(ctx, result, self); } ItemKind::Type(ref ty) => { - ty.codegen(ctx, result, whitelisted_items, self); + ty.codegen(ctx, result, self); } } } @@ -338,17 +336,16 @@ impl CodeGenerator for Module { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, item: &Item) { debug!("<Module as CodeGenerator>::codegen: item = {:?}", item); let codegen_self = |result: &mut CodegenResult, found_any: &mut bool| { for child in self.children() { - if whitelisted_items.contains(child) { + if ctx.codegen_items().contains(child) { *found_any = true; ctx.resolve_item(*child) - .codegen(ctx, result, whitelisted_items, &()); + .codegen(ctx, result, &()); } } @@ -413,7 +410,6 @@ impl CodeGenerator for Var { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - _whitelisted_items: &ItemSet, item: &Item) { use ir::var::VarType; debug!("<Var as CodeGenerator>::codegen: item = {:?}", item); @@ -518,7 +514,6 @@ impl CodeGenerator for Type { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, item: &Item) { debug!("<Type as CodeGenerator>::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -542,10 +537,10 @@ impl CodeGenerator for Type { return; } TypeKind::TemplateInstantiation(ref inst) => { - inst.codegen(ctx, result, whitelisted_items, item) + inst.codegen(ctx, result, item) } TypeKind::Comp(ref ci) => { - ci.codegen(ctx, result, whitelisted_items, item) + ci.codegen(ctx, result, item) } TypeKind::TemplateAlias(inner, _) | TypeKind::Alias(inner) => { @@ -660,13 +655,13 @@ impl CodeGenerator for Type { result.push(typedef) } TypeKind::Enum(ref ei) => { - ei.codegen(ctx, result, whitelisted_items, item) + ei.codegen(ctx, result, item) } TypeKind::ObjCId | TypeKind::ObjCSel => { result.saw_objc(); } TypeKind::ObjCInterface(ref interface) => { - interface.codegen(ctx, result, whitelisted_items, item) + interface.codegen(ctx, result, item) } ref u @ TypeKind::UnresolvedTypeRef(..) => { unreachable!("Should have been resolved after parsing {:?}!", u) @@ -702,7 +697,6 @@ impl<'a> CodeGenerator for Vtable<'a> { fn codegen<'b>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'b>, - _whitelisted_items: &ItemSet, item: &Item) { assert_eq!(item.id(), self.item_id); debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -745,7 +739,6 @@ impl CodeGenerator for TemplateInstantiation { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - _whitelisted_items: &ItemSet, item: &Item) { debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -1377,7 +1370,6 @@ impl CodeGenerator for CompInfo { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, item: &Item) { debug!("<CompInfo as CodeGenerator>::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -1481,10 +1473,10 @@ impl CodeGenerator for CompInfo { // the parent too. let mut fields = vec![]; let mut struct_layout = StructLayoutTracker::new(ctx, self, &canonical_name); - if self.needs_explicit_vtable(ctx) { + if self.needs_explicit_vtable(ctx, item) { let vtable = Vtable::new(item.id(), self.methods(), self.base_members()); - vtable.codegen(ctx, result, whitelisted_items, item); + vtable.codegen(ctx, result, item); let vtable_type = vtable.try_to_rust_ty(ctx, &()) .expect("vtable to Rust type conversion is infallible") @@ -1512,7 +1504,7 @@ impl CodeGenerator for CompInfo { // NB: We won't include unsized types in our base chain because they // would contribute to our size given the dummy field we insert for // unsized types. - if base_ty.is_unsized(ctx) { + if base_ty.is_unsized(ctx, &base.ty) { continue; } @@ -1591,7 +1583,7 @@ impl CodeGenerator for CompInfo { warn!("Opaque type without layout! Expect dragons!"); } } - } else if !is_union && !self.is_unsized(ctx) { + } else if !is_union && !self.is_unsized(ctx, &item.id()) { if let Some(padding_field) = layout.and_then(|layout| { struct_layout.pad_struct(layout) @@ -1609,13 +1601,13 @@ impl CodeGenerator for CompInfo { // is making the struct 1-byte sized. // // This is apparently not the case for C, see: - // https://github.com/servo/rust-bindgen/issues/551 + // https://github.com/rust-lang-nursery/rust-bindgen/issues/551 // // Just get the layout, and assume C++ if not. // // NOTE: This check is conveniently here to avoid the dummy fields we // may add for unused template parameters. - if self.is_unsized(ctx) { + if self.is_unsized(ctx, &item.id()) { let has_address = if is_opaque { // Generate the address field if it's an opaque type and // couldn't determine the layout of the blob. @@ -1668,7 +1660,7 @@ impl CodeGenerator for CompInfo { for ty in self.inner_types() { let child_item = ctx.resolve_item(*ty); // assert_eq!(child_item.parent_id(), item.id()); - child_item.codegen(ctx, result, whitelisted_items, &()); + child_item.codegen(ctx, result, &()); } // NOTE: Some unexposed attributes (like alignment attributes) may @@ -1683,7 +1675,7 @@ impl CodeGenerator for CompInfo { if !is_opaque { for var in self.inner_vars() { ctx.resolve_item(*var) - .codegen(ctx, result, whitelisted_items, &()); + .codegen(ctx, result, &()); } } @@ -1711,11 +1703,11 @@ impl CodeGenerator for CompInfo { ) }; - // FIXME when [issue #465](https://github.com/servo/rust-bindgen/issues/465) ready + // FIXME when [issue #465](https://github.com/rust-lang-nursery/rust-bindgen/issues/465) ready let too_many_base_vtables = self.base_members() .iter() .filter(|base| { - ctx.resolve_type(base.ty).has_vtable(ctx) + ctx.lookup_item_id_has_vtable(&base.ty) }) .count() > 1; @@ -1772,7 +1764,6 @@ impl CodeGenerator for CompInfo { &mut methods, &mut method_names, result, - whitelisted_items, self); } } @@ -1787,7 +1778,6 @@ impl CodeGenerator for CompInfo { &mut methods, &mut method_names, result, - whitelisted_items, self); } } @@ -1805,7 +1795,6 @@ impl CodeGenerator for CompInfo { &mut methods, &mut method_names, result, - whitelisted_items, self); } } @@ -1890,7 +1879,6 @@ trait MethodCodegen { methods: &mut Vec<ast::ImplItem>, method_names: &mut HashMap<String, usize>, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, parent: &CompInfo); } @@ -1900,7 +1888,6 @@ impl MethodCodegen for Method { methods: &mut Vec<ast::ImplItem>, method_names: &mut HashMap<String, usize>, result: &mut CodegenResult<'a>, - whitelisted_items: &ItemSet, _parent: &CompInfo) { assert!({ let cc = &ctx.options().codegen_config; @@ -1920,7 +1907,7 @@ impl MethodCodegen for Method { // First of all, output the actual function. let function_item = ctx.resolve_item(self.signature()); - function_item.codegen(ctx, result, whitelisted_items, &()); + function_item.codegen(ctx, result, &()); let function = function_item.expect_function(); let signature_item = ctx.resolve_item(function.signature()); @@ -2301,7 +2288,6 @@ impl CodeGenerator for Enum { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - _whitelisted_items: &ItemSet, item: &Item) { debug!("<Enum as CodeGenerator>::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -3041,7 +3027,6 @@ impl CodeGenerator for Function { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - _whitelisted_items: &ItemSet, item: &Item) { debug!("<Function as CodeGenerator>::codegen: item = {:?}", item); debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -3220,7 +3205,6 @@ impl CodeGenerator for ObjCInterface { fn codegen<'a>(&self, ctx: &BindgenContext, result: &mut CodegenResult<'a>, - _whitelisted_items: &ItemSet, item: &Item) { debug_assert!(item.is_enabled_for_codegen(ctx)); @@ -3298,7 +3282,7 @@ pub fn codegen(context: &mut BindgenContext) -> Vec<P<ast::Item>> { } context.resolve_item(context.root_module()) - .codegen(context, &mut result, codegen_items, &()); + .codegen(context, &mut result, &()); result.items }) diff --git a/src/ir/analysis/derive_debug.rs b/src/ir/analysis/derive_debug.rs index b9b0be10..0dc1a0c5 100644 --- a/src/ir/analysis/derive_debug.rs +++ b/src/ir/analysis/derive_debug.rs @@ -1,6 +1,6 @@ //! Determining which types for which we can emit `#[derive(Debug)]`. -use super::{ConstrainResult, MonotoneFramework}; +use super::{ConstrainResult, MonotoneFramework, generate_dependencies}; use std::collections::HashSet; use std::collections::HashMap; use ir::context::{BindgenContext, ItemId}; @@ -9,9 +9,7 @@ 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; @@ -79,6 +77,8 @@ impl<'ctx, 'gen> CannotDeriveDebug<'ctx, 'gen> { } fn insert(&mut self, id: ItemId) -> ConstrainResult { + trace!("inserting {:?} into the cannot_derive_debug set", id); + let was_not_already_in_set = self.cannot_derive_debug.insert(id); assert!( was_not_already_in_set, @@ -86,6 +86,7 @@ impl<'ctx, 'gen> CannotDeriveDebug<'ctx, 'gen> { already in the set, `constrain` should have exited early.", id ); + ConstrainResult::Changed } } @@ -97,24 +98,7 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> { 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); - } - }, &()); - } - } + let dependencies = generate_dependencies(ctx, Self::consider_edge); CannotDeriveDebug { ctx, @@ -128,16 +112,35 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> { } fn constrain(&mut self, id: ItemId) -> ConstrainResult { + trace!("constrain: {:?}", id); + if self.cannot_derive_debug.contains(&id) { + trace!(" already know it cannot derive Debug"); return ConstrainResult::Same; } let item = self.ctx.resolve_item(id); let ty = match item.as_type() { - None => return ConstrainResult::Same, - Some(ty) => ty + Some(ty) => ty, + None => { + trace!(" not a type; ignoring"); + return ConstrainResult::Same; + } }; + if ty.is_opaque(self.ctx, item) { + let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| { + l.opaque().can_trivially_derive_debug(self.ctx, ()) + }); + return if layout_can_derive { + trace!(" we can trivially derive Debug for the layout"); + ConstrainResult::Same + } else { + trace!(" we cannot derive Debug for the layout"); + self.insert(id) + }; + } + match *ty.kind() { // Handle the simple cases. These can derive debug without further // information. @@ -155,61 +158,59 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> { TypeKind::ObjCInterface(..) | TypeKind::ObjCId | TypeKind::ObjCSel => { + trace!(" simple type that can always derive Debug"); 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) { + trace!(" arrays of T for which we cannot derive Debug \ + also cannot derive Debug"); return self.insert(id); } if len <= RUST_DERIVE_IN_ARRAY_LIMIT { + trace!(" array is small enough to derive Debug"); ConstrainResult::Same } else { + trace!(" array is too large to derive Debug"); self.insert(id) } - }, + } TypeKind::ResolvedTypeRef(t) | TypeKind::TemplateAlias(t, _) | TypeKind::Alias(t) => { if self.cannot_derive_debug.contains(&t) { + trace!(" aliases and type refs to T which cannot derive \ + Debug also cannot derive Debug"); self.insert(id) } else { + trace!(" aliases and type refs to T which can derive \ + Debug can also derive Debug"); 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); - } - } + assert!( + !info.has_non_type_template_params(), + "The early ty.is_opaque check should have handled this case" + ); if info.kind() == CompKind::Union { if self.ctx.options().unstable_rust { + trace!(" cannot derive Debug for Rust unions"); return self.insert(id); } if ty.layout(self.ctx) .map_or(true, |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) { + trace!(" union layout can trivially derive Debug"); return ConstrainResult::Same; } else { + trace!(" union layout cannot derive Debug"); return self.insert(id); } } @@ -218,6 +219,8 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> { .iter() .any(|base| self.cannot_derive_debug.contains(&base.ty)); if bases_cannot_derive { + trace!(" base members cannot derive Debug, so we can't \ + either"); return self.insert(id); } @@ -237,69 +240,57 @@ impl<'ctx, 'gen> MonotoneFramework for CannotDeriveDebug<'ctx, 'gen> { } }); if fields_cannot_derive { + trace!(" fields cannot derive Debug, so we can't either"); return self.insert(id); } + trace!(" comp can derive Debug"); 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, ()) { + trace!(" function pointer that can't trivially derive Debug"); return self.insert(id); } } + trace!(" pointers can derive Debug"); 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 { + trace!(" template args cannot derive Debug, so \ + insantiation can't either"); return self.insert(id); } - let template_definition = template.template_definition() - .into_resolver() - .through_type_refs() - .through_type_aliases() - .resolve(self.ctx); - - let ty_cannot_derive = template_definition - .as_type() - .expect("Instantiations of a non-type?") - .as_comp() - .and_then(|c| { - // For non-type template parameters, or opaque template - // definitions, we generate an opaque blob, and in this - // case the instantiation has a better idea of the - // layout than the definition does. - if template_definition.is_opaque(self.ctx, &()) || - 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 { + assert!( + !template.template_definition().is_opaque(self.ctx, &()), + "The early ty.is_opaque check should have handled this case" + ); + let def_cannot_derive = self.cannot_derive_debug + .contains(&template.template_definition()); + if def_cannot_derive { + trace!(" template definition cannot derive Debug, so \ + insantiation can't either"); return self.insert(id); } + trace!(" template instantiation can derive Debug"); ConstrainResult::Same - }, + } + + TypeKind::Opaque => { + unreachable!( + "The early ty.is_opaque check should have handled this case" + ) + } } } diff --git a/src/ir/analysis/has_vtable.rs b/src/ir/analysis/has_vtable.rs new file mode 100644 index 00000000..47c9ee15 --- /dev/null +++ b/src/ir/analysis/has_vtable.rs @@ -0,0 +1,168 @@ +//! Determining which types has vtable +use super::{ConstrainResult, MonotoneFramework, generate_dependencies}; +use std::collections::HashSet; +use std::collections::HashMap; +use ir::context::{BindgenContext, ItemId}; +use ir::traversal::EdgeKind; +use ir::ty::TypeKind; + +/// An analysis that finds for each IR item whether it has vtable or not +/// +/// We use the monotone function `has vtable`, defined as follows: +/// +/// * If T is a type alias, a templated alias, an indirection to another type, +/// or a reference of a type, T has vtable if the type T refers to has vtable. +/// * If T is a compound type, T has vtable if we saw a virtual function when +/// parsing it or any of its base member has vtable. +/// * If T is an instantiation of an abstract template definition, T has +/// vtable if template definition has vtable +#[derive(Debug, Clone)] +pub struct HasVtableAnalysis<'ctx, 'gen> + where 'gen: 'ctx +{ + ctx: &'ctx BindgenContext<'gen>, + + // The incremental result of this analysis's computation. Everything in this + // set definitely has a vtable. + have_vtable: HashSet<ItemId>, + + // Dependencies saying that if a key ItemId has been inserted into the + // `have_vtable` set, then each of the ids in Vec<ItemId> need to be + // considered again. + // + // This is a subset of the natural IR graph with reversed edges, where we + // only include the edges from the IR graph that can affect whether a type + // has a vtable or not. + dependencies: HashMap<ItemId, Vec<ItemId>>, +} + +impl<'ctx, 'gen> HasVtableAnalysis<'ctx, 'gen> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + // These are the only edges that can affect whether a type has a + // vtable or not. + EdgeKind::TypeReference | + EdgeKind::BaseMember | + EdgeKind::TemplateDeclaration => true, + _ => false, + } + } + + fn insert(&mut self, id: ItemId) -> ConstrainResult { + let was_not_already_in_set = self.have_vtable.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 HasVtableAnalysis<'ctx, 'gen> { + type Node = ItemId; + type Extra = &'ctx BindgenContext<'gen>; + type Output = HashSet<ItemId>; + + fn new(ctx: &'ctx BindgenContext<'gen>) -> HasVtableAnalysis<'ctx, 'gen> { + let have_vtable = HashSet::new(); + let dependencies = generate_dependencies(ctx, Self::consider_edge); + + HasVtableAnalysis { + ctx, + have_vtable, + dependencies, + } + } + + fn initial_worklist(&self) -> Vec<ItemId> { + self.ctx.whitelisted_items().iter().cloned().collect() + } + + fn constrain(&mut self, id: ItemId) -> ConstrainResult { + if self.have_vtable.contains(&id) { + // We've already computed that this type has a vtable and that can't + // change. + return ConstrainResult::Same; + } + + let item = self.ctx.resolve_item(id); + let ty = match item.as_type() { + None => return ConstrainResult::Same, + Some(ty) => ty + }; + + // TODO #851: figure out a way to handle deriving from template type parameters. + match *ty.kind() { + TypeKind::TemplateAlias(t, _) | + TypeKind::Alias(t) | + TypeKind::ResolvedTypeRef(t) | + TypeKind::Reference(t) => { + if self.have_vtable.contains(&t) { + self.insert(id) + } else { + ConstrainResult::Same + } + }, + + TypeKind::Comp(ref info) => { + if info.has_own_virtual_method() { + return self.insert(id); + } + let bases_has_vtable = info.base_members().iter().any(|base| { + self.have_vtable.contains(&base.ty) + }); + if bases_has_vtable { + self.insert(id) + } else { + ConstrainResult::Same + } + }, + + TypeKind::TemplateInstantiation(ref inst) => { + if self.have_vtable.contains(&inst.template_definition()) { + self.insert(id) + } else { + ConstrainResult::Same + } + }, + + _ => ConstrainResult::Same, + } + } + + fn each_depending_on<F>(&self, id: ItemId, mut f: F) + where F: FnMut(ItemId), + { + if let Some(edges) = self.dependencies.get(&id) { + for item in edges { + trace!("enqueue {:?} into worklist", item); + f(*item); + } + } + } +} + +impl<'ctx, 'gen> From<HasVtableAnalysis<'ctx, 'gen>> for HashSet<ItemId> { + fn from(analysis: HasVtableAnalysis<'ctx, 'gen>) -> Self { + analysis.have_vtable + } +} + +/// A convenience trait for the things for which we might wonder if they have a +/// vtable during codegen. +/// +/// This is not for _computing_ whether the thing has a vtable, it is for +/// looking up the results of the HasVtableAnalysis's computations for a +/// specific thing. +pub trait HasVtable { + + /// Implementations can define this type to get access to any extra + /// information required to determine whether they have vtable. If + /// extra information is unneeded, then this should simply be the unit type. + type Extra; + + /// Return `true` if this thing has vtable, `false` otherwise. + fn has_vtable(&self, ctx: &BindgenContext, extra: &Self::Extra) -> bool; +} diff --git a/src/ir/analysis/mod.rs b/src/ir/analysis/mod.rs index 5fe6785b..8a00ecce 100644 --- a/src/ir/analysis/mod.rs +++ b/src/ir/analysis/mod.rs @@ -42,7 +42,13 @@ mod template_params; pub use self::template_params::UsedTemplateParameters; mod derive_debug; pub use self::derive_debug::CannotDeriveDebug; +mod has_vtable; +pub use self::has_vtable::HasVtableAnalysis; +pub use self::has_vtable::HasVtable; +use ir::context::{BindgenContext, ItemId}; +use ir::traversal::{EdgeKind, Trace}; +use std::collections::HashMap; use std::fmt; /// An analysis in the monotone framework. @@ -131,6 +137,30 @@ pub fn analyze<Analysis>(extra: Analysis::Extra) -> Analysis::Output analysis.into() } +/// Generate the dependency map for analysis +pub fn generate_dependencies<F>(ctx: &BindgenContext, consider_edge: F) -> HashMap<ItemId, Vec<ItemId>> + where F: Fn(EdgeKind) -> bool { + 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) && + consider_edge(edge_kind) { + dependencies.entry(sub_item) + .or_insert(vec![]) + .push(item); + } + }, &()); + } + } + dependencies +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/ir/comp.rs b/src/ir/comp.rs index 0960ee11..d6644466 100644 --- a/src/ir/comp.rs +++ b/src/ir/comp.rs @@ -701,7 +701,7 @@ impl FieldMethods for FieldData { } } -impl CanDeriveDefault for Field { +impl<'a> CanDeriveDefault<'a> for Field { type Extra = (); fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { @@ -819,7 +819,7 @@ pub struct CompInfo { /// Whether this type should generate an vtable (TODO: Should be able to /// look at the virtual methods and ditch this field). - has_vtable: bool, + has_own_virtual_method: bool, /// Whether this type has destructor. has_destructor: bool, @@ -870,7 +870,7 @@ impl CompInfo { base_members: vec![], inner_types: vec![], inner_vars: vec![], - has_vtable: false, + has_own_virtual_method: false, has_destructor: false, has_nonempty_base: false, has_non_type_template_params: false, @@ -883,10 +883,10 @@ impl CompInfo { } /// Is this compound type unsized? - pub fn is_unsized(&self, ctx: &BindgenContext) -> bool { - !self.has_vtable(ctx) && self.fields().is_empty() && + pub fn is_unsized(&self, ctx: &BindgenContext, itemid: &ItemId) -> bool { + !ctx.lookup_item_id_has_vtable(itemid) && self.fields().is_empty() && self.base_members.iter().all(|base| { - ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx) + ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx, &base.ty) }) } @@ -964,13 +964,10 @@ impl CompInfo { self.has_non_type_template_params } - /// Does this type have a virtual table? - pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { - self.has_vtable || - self.base_members().iter().any(|base| { - ctx.resolve_type(base.ty) - .has_vtable(ctx) - }) + /// Do we see a virtual function during parsing? + /// Get the has_own_virtual_method boolean. + pub fn has_own_virtual_method(&self) -> bool { + return self.has_own_virtual_method; } /// Get this type's set of methods. @@ -1136,7 +1133,7 @@ impl CompInfo { // Let's just assume that if the cursor we've found is a // definition, it's a valid inner type. // - // [1]: https://github.com/servo/rust-bindgen/issues/482 + // [1]: https://github.com/rust-lang-nursery/rust-bindgen/issues/482 let is_inner_struct = cur.semantic_parent() == cursor || cur.is_definition(); if !is_inner_struct { @@ -1169,7 +1166,7 @@ impl CompInfo { } CXCursor_CXXBaseSpecifier => { let is_virtual_base = cur.is_virtual_base(); - ci.has_vtable |= is_virtual_base; + ci.has_own_virtual_method |= is_virtual_base; let kind = if is_virtual_base { BaseKind::Virtual @@ -1192,7 +1189,7 @@ impl CompInfo { debug_assert!(!(is_static && is_virtual), "How?"); ci.has_destructor |= cur.kind() == CXCursor_Destructor; - ci.has_vtable |= is_virtual; + ci.has_own_virtual_method |= is_virtual; // This used to not be here, but then I tried generating // stylo bindings with this (without path filters), and @@ -1335,8 +1332,8 @@ impl CompInfo { /// Returns whether this type needs an explicit vtable because it has /// virtual methods and none of its base classes has already a vtable. - pub fn needs_explicit_vtable(&self, ctx: &BindgenContext) -> bool { - self.has_vtable(ctx) && + pub fn needs_explicit_vtable(&self, ctx: &BindgenContext, item: &Item) -> bool { + ctx.lookup_item_id_has_vtable(&item.id()) && !self.base_members.iter().any(|base| { // NB: Ideally, we could rely in all these types being `comp`, and // life would be beautiful. @@ -1347,7 +1344,7 @@ impl CompInfo { ctx.resolve_type(base.ty) .canonical_type(ctx) .as_comp() - .map_or(false, |ci| ci.has_vtable(ctx)) + .map_or(false, |_| ctx.lookup_item_id_has_vtable(&base.ty)) }) } @@ -1368,7 +1365,7 @@ impl DotAttributes for CompInfo { { writeln!(out, "<tr><td>CompKind</td><td>{:?}</td></tr>", self.kind)?; - if self.has_vtable { + if self.has_own_virtual_method { writeln!(out, "<tr><td>has_vtable</td><td>true</td></tr>")?; } @@ -1424,12 +1421,12 @@ impl TemplateParameters for CompInfo { } } -impl CanDeriveDefault for CompInfo { - type Extra = Option<Layout>; +impl<'a> CanDeriveDefault<'a> for CompInfo { + type Extra = (&'a Item, Option<Layout>); fn can_derive_default(&self, ctx: &BindgenContext, - layout: Option<Layout>) + (item, layout): (&Item, Option<Layout>)) -> bool { // We can reach here recursively via template parameters of a member, // for example. @@ -1452,8 +1449,8 @@ impl CanDeriveDefault for CompInfo { self.detect_derive_default_cycle.set(true); - let can_derive_default = !self.has_vtable(ctx) && - !self.needs_explicit_vtable(ctx) && + let can_derive_default = !ctx.lookup_item_id_has_vtable(&item.id()) && + !self.needs_explicit_vtable(ctx, item) && self.base_members .iter() .all(|base| base.ty.can_derive_default(ctx, ())) && diff --git a/src/ir/context.rs b/src/ir/context.rs index 6a5fdb83..3232e8e6 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -5,7 +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::analysis::{analyze, UsedTemplateParameters, CannotDeriveDebug}; +use super::analysis::{analyze, UsedTemplateParameters, CannotDeriveDebug, HasVtableAnalysis}; use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; @@ -47,7 +47,7 @@ impl CanDeriveDebug for ItemId { } } -impl CanDeriveDefault for ItemId { +impl<'a> CanDeriveDefault<'a> for ItemId { type Extra = (); fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { @@ -184,6 +184,12 @@ pub struct BindgenContext<'ctx> { /// 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>>, + + /// The set of (`ItemId's of`) types that has vtable. + /// + /// Populated when we enter codegen by `compute_has_vtable`; always `None` + /// before that and `Some` after. + have_vtable: Option<HashSet<ItemId>>, } /// A traversal of whitelisted items. @@ -310,6 +316,7 @@ impl<'ctx> BindgenContext<'ctx> { need_bitfield_allocation: Default::default(), needs_mangling_hack: needs_mangling_hack, cant_derive_debug: None, + have_vtable: None, }; me.add_item(root_module, None, None); @@ -780,6 +787,7 @@ impl<'ctx> BindgenContext<'ctx> { // messes with them correctly. self.assert_every_item_in_a_module(); + self.compute_has_vtable(); self.find_used_template_parameters(); self.compute_cant_derive_debug(); @@ -847,6 +855,22 @@ impl<'ctx> BindgenContext<'ctx> { } } + /// Compute whether the type has vtable. + fn compute_has_vtable(&mut self) { + assert!(self.have_vtable.is_none()); + self.have_vtable = Some(analyze::<HasVtableAnalysis>(self)); + } + + /// Look up whether the item with `id` has vtable or not. + pub fn lookup_item_id_has_vtable(&self, id: &ItemId) -> bool { + assert!(self.in_codegen_phase(), + "We only compute vtables when we enter codegen"); + + // Look up the computed value for whether the item with `id` has a + // vtable or not. + self.have_vtable.as_ref().unwrap().contains(id) + } + fn find_used_template_parameters(&mut self) { if self.options.whitelist_recursively { let used_params = analyze::<UsedTemplateParameters>(self); @@ -1391,7 +1415,13 @@ impl<'ctx> BindgenContext<'ctx> { _ => return None, }; - let spelling = ty.spelling(); + let mut spelling = ty.spelling(); + // avoid the allocation if possible + if spelling.contains(' ') { + // These names are used in generated test names, + // they should be valid identifiers + spelling = spelling.replace(' ', "_"); + } let is_const = ty.is_const(); let layout = ty.fallible_layout().ok(); let ty = Type::new(Some(spelling), layout, type_kind, is_const); diff --git a/src/ir/derive.rs b/src/ir/derive.rs index 59548163..446d2435 100644 --- a/src/ir/derive.rs +++ b/src/ir/derive.rs @@ -90,7 +90,7 @@ pub trait CanDeriveCopy<'a> { /// to be a recursive method that checks whether all the proper members can /// derive default or not, because of the limit rust has on 32 items as max in the /// array. -pub trait CanDeriveDefault { +pub trait CanDeriveDefault<'a> { /// Implementations can define this type to get access to any extra /// information required to determine whether they can derive `Default`. If /// extra information is unneeded, then this should simply be the unit type. diff --git a/src/ir/function.rs b/src/ir/function.rs index 663bb8e3..fbb6121e 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -193,7 +193,7 @@ pub fn cursor_mangling(ctx: &BindgenContext, // We early return here because libclang may crash in some case // if we pass in a variable inside a partial specialized template. - // See servo/rust-bindgen#67, and servo/rust-bindgen#462. + // See rust-lang-nursery/rust-bindgen#67, and rust-lang-nursery/rust-bindgen#462. if cursor.is_in_non_fully_specialized_template() { return None; } @@ -474,7 +474,7 @@ impl Trace for FunctionSig { // Function pointers follow special rules, see: // -// https://github.com/servo/rust-bindgen/issues/547, +// https://github.com/rust-lang-nursery/rust-bindgen/issues/547, // https://github.com/rust-lang/rust/issues/38848, // and https://github.com/rust-lang/rust/issues/40158 // diff --git a/src/ir/item.rs b/src/ir/item.rs index 7f3afefb..b3a26bb7 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -14,6 +14,7 @@ use super::module::Module; use super::template::{AsTemplateParam, TemplateParameters}; use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::{Type, TypeKind}; +use super::analysis::HasVtable; use clang; use clang_sys; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; @@ -277,7 +278,7 @@ impl CanDeriveDebug for Item { } } -impl CanDeriveDefault for Item { +impl<'a> CanDeriveDefault<'a> for Item { type Extra = (); fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { @@ -289,7 +290,7 @@ impl CanDeriveDefault for Item { .map_or(false, |l| l.opaque().can_derive_default(ctx, ())) } else { - ty.can_derive_default(ctx, ()) + ty.can_derive_default(ctx, self) } } _ => false, @@ -947,6 +948,22 @@ impl IsOpaque for Item { } } +impl HasVtable for ItemId { + type Extra = (); + + fn has_vtable(&self, ctx: &BindgenContext, _: &()) -> bool { + ctx.lookup_item_id_has_vtable(self) + } +} + +impl HasVtable for Item { + type Extra = (); + + fn has_vtable(&self, ctx: &BindgenContext, _: &()) -> bool { + ctx.lookup_item_id_has_vtable(&self.id()) + } +} + /// A set of items. pub type ItemSet = BTreeSet<ItemId>; diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 91dd9d6c..03496ad0 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -111,7 +111,7 @@ impl CanTriviallyDeriveDebug for Opaque { } } -impl CanDeriveDefault for Opaque { +impl<'a> CanDeriveDefault<'a> for Opaque { type Extra = (); fn can_derive_default(&self, _: &BindgenContext, _: ()) -> bool { diff --git a/src/ir/template.rs b/src/ir/template.rs index 25d1d438..6147365e 100644 --- a/src/ir/template.rs +++ b/src/ir/template.rs @@ -292,11 +292,6 @@ impl TemplateInstantiation { Some(TemplateInstantiation::new(template_definition, template_args)) } - /// Does this instantiation have a vtable? - pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { - ctx.resolve_type(self.definition).has_vtable(ctx) - } - /// Does this instantiation have a destructor? pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { ctx.resolve_type(self.definition).has_destructor(ctx) || diff --git a/src/ir/ty.rs b/src/ir/ty.rs index 78274d94..b189ceac 100644 --- a/src/ir/ty.rs +++ b/src/ir/ty.rs @@ -237,19 +237,6 @@ impl Type { }) } - /// Whether this type has a vtable. - pub fn has_vtable(&self, ctx: &BindgenContext) -> bool { - // FIXME: Can we do something about template parameters? Huh... - match self.kind { - TypeKind::TemplateAlias(t, _) | - TypeKind::Alias(t) | - TypeKind::ResolvedTypeRef(t) => ctx.resolve_type(t).has_vtable(ctx), - TypeKind::Comp(ref info) => info.has_vtable(ctx), - TypeKind::TemplateInstantiation(ref inst) => inst.has_vtable(ctx), - _ => false, - } - } - /// Returns whether this type has a destructor. pub fn has_destructor(&self, ctx: &BindgenContext) -> bool { match self.kind { @@ -359,6 +346,7 @@ impl IsOpaque for Type { TypeKind::Opaque => true, TypeKind::TemplateInstantiation(ref inst) => inst.is_opaque(ctx, item), TypeKind::Comp(ref comp) => comp.is_opaque(ctx, &()), + TypeKind::ResolvedTypeRef(to) => to.is_opaque(ctx, &()), _ => false, } } @@ -546,10 +534,10 @@ impl CanDeriveDebug for Type { } } -impl CanDeriveDefault for Type { - type Extra = (); +impl<'a> CanDeriveDefault<'a> for Type { + type Extra = &'a Item; - fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool { + fn can_derive_default(&self, ctx: &BindgenContext, item: &Item) -> bool { match self.kind { TypeKind::Array(t, len) => { len <= RUST_DERIVE_IN_ARRAY_LIMIT && @@ -559,7 +547,7 @@ impl CanDeriveDefault for Type { TypeKind::TemplateAlias(t, _) | TypeKind::Alias(t) => t.can_derive_default(ctx, ()), TypeKind::Comp(ref info) => { - info.can_derive_default(ctx, self.layout(ctx)) + info.can_derive_default(ctx, (&item, self.layout(ctx))) } TypeKind::Opaque => { self.layout @@ -744,23 +732,24 @@ impl Type { /// derive whether we should generate a dummy `_address` field for structs, /// to comply to the C and C++ layouts, that specify that every type needs /// to be addressable. - pub fn is_unsized(&self, ctx: &BindgenContext) -> bool { + pub fn is_unsized(&self, ctx: &BindgenContext, itemid: &ItemId) -> bool { debug_assert!(ctx.in_codegen_phase(), "Not yet"); match self.kind { TypeKind::Void => true, - TypeKind::Comp(ref ci) => ci.is_unsized(ctx), + TypeKind::Comp(ref ci) => ci.is_unsized(ctx, itemid), TypeKind::Opaque => self.layout.map_or(true, |l| l.size == 0), TypeKind::Array(inner, size) => { - size == 0 || ctx.resolve_type(inner).is_unsized(ctx) + size == 0 || ctx.resolve_type(inner).is_unsized(ctx, &inner) } TypeKind::ResolvedTypeRef(inner) | TypeKind::Alias(inner) | TypeKind::TemplateAlias(inner, _) => { - ctx.resolve_type(inner).is_unsized(ctx) + ctx.resolve_type(inner).is_unsized(ctx, &inner) } TypeKind::TemplateInstantiation(ref inst) => { - ctx.resolve_type(inst.template_definition()).is_unsized(ctx) + let definition = inst.template_definition(); + ctx.resolve_type(definition).is_unsized(ctx, &definition) } TypeKind::Named | TypeKind::Int(..) | @@ -484,7 +484,7 @@ impl Builder { /// implement some processing on comments to work around issues as described /// in: /// - /// https://github.com/servo/rust-bindgen/issues/426 + /// https://github.com/rust-lang-nursery/rust-bindgen/issues/426 pub fn generate_comments(mut self, doit: bool) -> Self { self.options.generate_comments = doit; self @@ -513,7 +513,7 @@ impl Builder { /// However, some old libclang versions seem to return incorrect results in /// some cases for non-mangled functions, see [1], so we allow disabling it. /// - /// [1]: https://github.com/servo/rust-bindgen/issues/528 + /// [1]: https://github.com/rust-lang-nursery/rust-bindgen/issues/528 pub fn trust_clang_mangling(mut self, doit: bool) -> Self { self.options.enable_mangling = doit; self @@ -1038,7 +1038,7 @@ pub struct BindgenOptions { /// However, some old libclang versions seem to return incorrect results in /// some cases for non-mangled functions, see [1], so we allow disabling it. /// - /// [1]: https://github.com/servo/rust-bindgen/issues/528 + /// [1]: https://github.com/rust-lang-nursery/rust-bindgen/issues/528 pub enable_mangling: bool, /// Whether to prepend the enum name to bitfield or constant variants. diff --git a/src/main.rs b/src/main.rs index f202cc18..9cd4f806 100644 --- a/src/main.rs +++ b/src/main.rs @@ -80,9 +80,9 @@ pub fn main() { fn print_verbose_err() { println!("Bindgen unexpectedly panicked"); println!("This may be caused by one of the known-unsupported \ - things (https://github.com/servo/rust-bindgen#c), \ + things (https://github.com/rust-lang-nursery/rust-bindgen#c), \ please modify the bindgen flags to work around it as \ - described in https://github.com/servo/rust-bindgen#c"); + described in https://github.com/rust-lang-nursery/rust-bindgen#c"); println!("Otherwise, please file an issue at \ - https://github.com/servo/rust-bindgen/issues/new"); + https://github.com/rust-lang-nursery/rust-bindgen/issues/new"); } diff --git a/src/options.rs b/src/options.rs index bf479ca5..23df6370 100644 --- a/src/options.rs +++ b/src/options.rs @@ -65,7 +65,7 @@ pub fn builder_from_flags<I> Arg::with_name("no-doc-comments") .long("no-doc-comments") .help("Avoid including doc comments in the output, see: \ - https://github.com/servo/rust-bindgen/issues/426"), + https://github.com/rust-lang-nursery/rust-bindgen/issues/426"), Arg::with_name("no-recursive-whitelist") .long("no-recursive-whitelist") .help("Avoid whitelisting types recursively."), |