diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/codegen/mod.rs | 19 | ||||
-rw-r--r-- | src/ir/analysis/derive_hash.rs | 337 | ||||
-rw-r--r-- | src/ir/analysis/mod.rs | 2 | ||||
-rw-r--r-- | src/ir/context.rs | 38 | ||||
-rw-r--r-- | src/ir/derive.rs | 27 | ||||
-rw-r--r-- | src/ir/function.rs | 19 | ||||
-rw-r--r-- | src/ir/item.rs | 8 | ||||
-rw-r--r-- | src/ir/layout.rs | 11 | ||||
-rw-r--r-- | src/lib.rs | 15 | ||||
-rw-r--r-- | src/options.rs | 7 |
10 files changed, 474 insertions, 9 deletions
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs index 5c00a53d..9521fb1e 100644 --- a/src/codegen/mod.rs +++ b/src/codegen/mod.rs @@ -13,7 +13,7 @@ use ir::comp::{Base, BitfieldUnit, Bitfield, CompInfo, CompKind, Field, FieldData, FieldMethods, Method, MethodKind}; use ir::comment; use ir::context::{BindgenContext, ItemId}; -use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; +use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash}; use ir::dot; use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue}; use ir::function::{Abi, Function, FunctionSig}; @@ -1440,6 +1440,10 @@ impl CodeGenerator for CompInfo { } } + if item.can_derive_hash(ctx) { + derives.push("Hash"); + } + if !derives.is_empty() { attributes.push(attributes::derives(&derives)) } @@ -3394,12 +3398,23 @@ mod utils { ) .unwrap(); + // The actual memory of the filed will be hashed, so that's why these + // field doesn't do anything with the hash. + let union_field_hash_impl = quote_item!(&ctx.ext_cx(), + impl<T> ::$prefix::hash::Hash for __BindgenUnionField<T> { + fn hash<H: ::$prefix::hash::Hasher>(&self, _state: &mut H) { + } + } + ) + .unwrap(); + let items = vec![union_field_decl, union_field_impl, union_field_default_impl, union_field_clone_impl, union_field_copy_impl, - union_field_debug_impl]; + union_field_debug_impl, + union_field_hash_impl]; let old_items = mem::replace(result, items); result.extend(old_items.into_iter()); diff --git a/src/ir/analysis/derive_hash.rs b/src/ir/analysis/derive_hash.rs new file mode 100644 index 00000000..2456143c --- /dev/null +++ b/src/ir/analysis/derive_hash.rs @@ -0,0 +1,337 @@ +//! Determining which types for which we can emit `#[derive(Hash)]`. + +use super::{ConstrainResult, MonotoneFramework, generate_dependencies}; +use std::collections::HashSet; +use std::collections::HashMap; +use ir::context::{BindgenContext, ItemId}; +use ir::item::IsOpaque; +use ir::traversal::EdgeKind; +use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT; +use ir::ty::TypeKind; +use ir::comp::Field; +use ir::comp::FieldMethods; +use ir::derive::CanTriviallyDeriveHash; +use ir::comp::CompKind; + +/// An analysis that finds for each IR item whether hash cannot be derived. +/// +/// We use the monotone constraint function `cannot_derive_hash`, 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, hash cannot be derived if the length of the array is +/// larger than the limit or the type of data the array contains cannot derive +/// hash. +/// * If T is a type alias, a templated alias or an indirection to another type, +/// hash cannot be derived if the type T refers to cannot be derived hash. +/// * If T is a compound type, hash cannot be derived if any of its base member +/// or field cannot be derived hash. +/// * If T is a pointer, T cannot be derived hash if T is a function pointer +/// and the function signature cannot be derived hash. +/// * If T is an instantiation of an abstract template definition, T cannot be +/// derived hash if any of the template arguments or template definition +/// cannot derive hash. +#[derive(Debug, Clone)] +pub struct CannotDeriveHash<'ctx, 'gen> + where 'gen: 'ctx +{ + ctx: &'ctx BindgenContext<'gen>, + + // The incremental result of this analysis's computation. Everything in this + // set cannot derive hash. + cannot_derive_hash: HashSet<ItemId>, + + // Dependencies saying that if a key ItemId has been inserted into the + // `cannot_derive_hash` 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 hash or not. + dependencies: HashMap<ItemId, Vec<ItemId>>, +} + +impl<'ctx, 'gen> CannotDeriveHash<'ctx, 'gen> { + fn consider_edge(kind: EdgeKind) -> bool { + match kind { + // These are the only edges that can affect whether a type can derive + // hash 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 { + trace!("inserting {:?} into the cannot_derive_hash set", id); + + let was_not_already_in_set = self.cannot_derive_hash.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 CannotDeriveHash<'ctx, 'gen> { + type Node = ItemId; + type Extra = &'ctx BindgenContext<'gen>; + type Output = HashSet<ItemId>; + + fn new(ctx: &'ctx BindgenContext<'gen>) -> CannotDeriveHash<'ctx, 'gen> { + let cannot_derive_hash = HashSet::new(); + let dependencies = generate_dependencies(ctx, Self::consider_edge); + + CannotDeriveHash { + ctx, + cannot_derive_hash, + dependencies, + } + } + + fn initial_worklist(&self) -> Vec<ItemId> { + self.ctx.whitelisted_items().iter().cloned().collect() + } + + fn constrain(&mut self, id: ItemId) -> ConstrainResult { + trace!("constrain: {:?}", id); + + if self.cannot_derive_hash.contains(&id) { + trace!(" already know it cannot derive Hash"); + return ConstrainResult::Same; + } + + let item = self.ctx.resolve_item(id); + let ty = match item.as_type() { + Some(ty) => ty, + None => { + trace!(" not a type; ignoring"); + return ConstrainResult::Same; + } + }; + + if item.is_opaque(self.ctx, &()) { + let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| { + l.opaque().can_trivially_derive_hash() + }); + return if layout_can_derive { + trace!(" we can trivially derive Hash for the layout"); + ConstrainResult::Same + } else { + trace!(" we cannot derive Hash for the layout"); + self.insert(id) + }; + } + + if ty.layout(self.ctx).map_or(false, |l| l.align > RUST_DERIVE_IN_ARRAY_LIMIT) { + // We have to be conservative: the struct *could* have enough + // padding that we emit an array that is longer than + // `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations + // into the IR and computed them before this analysis, then we could + // be precise rather than conservative here. + return self.insert(id); + } + + match *ty.kind() { + // Handle the simple cases. These can derive hash without further + // information. + TypeKind::Void | + TypeKind::NullPtr | + TypeKind::Int(..) | + TypeKind::Enum(..) | + TypeKind::Named | + TypeKind::UnresolvedTypeRef(..) | + TypeKind::BlockPointer | + TypeKind::Reference(..) | + TypeKind::ObjCInterface(..) | + TypeKind::ObjCId | + TypeKind::ObjCSel => { + trace!(" simple type that can always derive Hash"); + ConstrainResult::Same + } + + TypeKind::Complex(..) | + TypeKind::Float(..) => { + trace!(" float cannot derive Hash"); + self.insert(id) + } + + TypeKind::Array(t, len) => { + if self.cannot_derive_hash.contains(&t) { + trace!(" arrays of T for which we cannot derive Hash \ + also cannot derive Hash"); + return self.insert(id); + } + + if len <= RUST_DERIVE_IN_ARRAY_LIMIT { + trace!(" array is small enough to derive Hash"); + ConstrainResult::Same + } else { + trace!(" array is too large to derive Hash"); + self.insert(id) + } + } + + 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_hash() { + trace!(" function pointer that can't trivially derive Hash"); + return self.insert(id); + } + } + trace!(" pointers can derive Hash"); + ConstrainResult::Same + } + + TypeKind::Function(ref sig) => { + if !sig.can_trivially_derive_hash() { + trace!(" function that can't trivially derive Hash"); + return self.insert(id); + } + trace!(" function can derive Hash"); + ConstrainResult::Same + } + + TypeKind::ResolvedTypeRef(t) | + TypeKind::TemplateAlias(t, _) | + TypeKind::Alias(t) => { + if self.cannot_derive_hash.contains(&t) { + trace!(" aliases and type refs to T which cannot derive \ + Hash also cannot derive Hash"); + self.insert(id) + } else { + trace!(" aliases and type refs to T which can derive \ + Hash can also derive Hash"); + ConstrainResult::Same + } + } + + TypeKind::Comp(ref info) => { + 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 Hash for Rust unions"); + return self.insert(id); + } + + if ty.layout(self.ctx) + .map_or(true, + |l| l.opaque().can_trivially_derive_hash()) { + trace!(" union layout can trivially derive Hash"); + return ConstrainResult::Same; + } else { + trace!(" union layout cannot derive Hash"); + return self.insert(id); + } + } + + let bases_cannot_derive = info.base_members() + .iter() + .any(|base| !self.ctx.whitelisted_items().contains(&base.ty) || + self.cannot_derive_hash.contains(&base.ty)); + if bases_cannot_derive { + trace!(" base members cannot derive Hash, so we can't \ + either"); + return self.insert(id); + } + + let fields_cannot_derive = info.fields() + .iter() + .any(|f| { + match *f { + Field::DataMember(ref data) => { + !self.ctx.whitelisted_items().contains(&data.ty()) || + self.cannot_derive_hash.contains(&data.ty()) + } + Field::Bitfields(ref bfu) => { + bfu.bitfields() + .iter().any(|b| { + !self.ctx.whitelisted_items().contains(&b.ty()) || + self.cannot_derive_hash.contains(&b.ty()) + }) + } + } + }); + if fields_cannot_derive { + trace!(" fields cannot derive Hash, so we can't either"); + return self.insert(id); + } + + trace!(" comp can derive Hash"); + ConstrainResult::Same + } + + TypeKind::TemplateInstantiation(ref template) => { + let args_cannot_derive = template.template_arguments() + .iter() + .any(|arg| self.cannot_derive_hash.contains(&arg)); + if args_cannot_derive { + trace!(" template args cannot derive Hash, so \ + insantiation can't either"); + return self.insert(id); + } + + 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_hash + .contains(&template.template_definition()); + if def_cannot_derive { + trace!(" template definition cannot derive Hash, so \ + insantiation can't either"); + return self.insert(id); + } + + trace!(" template instantiation can derive Hash"); + ConstrainResult::Same + } + + TypeKind::Opaque => { + unreachable!( + "The early ty.is_opaque check should have handled this case" + ) + } + } + } + + 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<CannotDeriveHash<'ctx, 'gen>> for HashSet<ItemId> { + fn from(analysis: CannotDeriveHash<'ctx, 'gen>) -> Self { + analysis.cannot_derive_hash + } +} diff --git a/src/ir/analysis/mod.rs b/src/ir/analysis/mod.rs index f77c0886..28ca09aa 100644 --- a/src/ir/analysis/mod.rs +++ b/src/ir/analysis/mod.rs @@ -51,6 +51,8 @@ mod derive_copy; pub use self::derive_copy::CannotDeriveCopy; mod has_type_param_in_array; pub use self::has_type_param_in_array::HasTypeParameterInArray; +mod derive_hash; +pub use self::derive_hash::CannotDeriveHash; use ir::context::{BindgenContext, ItemId}; use ir::traversal::{EdgeKind, Trace}; diff --git a/src/ir/context.rs b/src/ir/context.rs index 2e0899ed..5caebeda 100644 --- a/src/ir/context.rs +++ b/src/ir/context.rs @@ -1,12 +1,13 @@ //! Common context that is passed around during parsing and codegen. -use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault}; +use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash}; use super::int::IntKind; use super::item::{IsOpaque, HasTypeParamInArray, Item, ItemAncestors, ItemCanonicalPath, ItemSet}; use super::item_kind::ItemKind; use super::module::{Module, ModuleKind}; use super::analysis::{analyze, UsedTemplateParameters, CannotDeriveDebug, HasVtableAnalysis, - CannotDeriveDefault, CannotDeriveCopy, HasTypeParameterInArray}; + CannotDeriveDefault, CannotDeriveCopy, HasTypeParameterInArray, + CannotDeriveHash}; use super::template::{TemplateInstantiation, TemplateParameters}; use super::traversal::{self, Edge, ItemTraversal}; use super::ty::{FloatKind, Type, TypeKind}; @@ -58,6 +59,12 @@ impl<'a> CanDeriveCopy<'a> for ItemId { } } +impl CanDeriveHash for ItemId { + fn can_derive_hash(&self, ctx: &BindgenContext) -> bool { + ctx.options().derive_hash && ctx.lookup_item_id_can_derive_hash(*self) + } +} + /// A key used to index a resolved type, so we only process it once. /// /// This is almost always a USR string (an unique identifier generated by @@ -194,6 +201,12 @@ pub struct BindgenContext<'ctx> { /// and is always `None` before that and `Some` after. cannot_derive_copy_in_array: Option<HashSet<ItemId>>, + /// The set of (`ItemId`s of) types that can't derive hash. + /// + /// This is populated when we enter codegen by `compute_can_derive_hash` + /// and is always `None` before that and `Some` after. + cannot_derive_hash: Option<HashSet<ItemId>>, + /// The set of (`ItemId's of`) types that has vtable. /// /// Populated when we enter codegen by `compute_has_vtable`; always `None` @@ -334,6 +347,7 @@ impl<'ctx> BindgenContext<'ctx> { cannot_derive_default: None, cannot_derive_copy: None, cannot_derive_copy_in_array: None, + cannot_derive_hash: None, have_vtable: None, has_type_param_in_array: None, }; @@ -812,6 +826,7 @@ impl<'ctx> BindgenContext<'ctx> { self.compute_cannot_derive_default(); self.compute_cannot_derive_copy(); self.compute_has_type_param_in_array(); + self.compute_cannot_derive_hash(); let ret = cb(self); self.gen_ctx = None; @@ -1818,8 +1833,25 @@ impl<'ctx> BindgenContext<'ctx> { self.cannot_derive_copy = Some(analyze::<CannotDeriveCopy>(self)); } + /// Compute whether we can derive hash. + fn compute_cannot_derive_hash(&mut self) { + assert!(self.cannot_derive_hash.is_none()); + self.cannot_derive_hash = Some(analyze::<CannotDeriveHash>(self)); + } + /// Look up whether the item with `id` can - /// derive debug or not. + /// derive hash or not. + pub fn lookup_item_id_can_derive_hash(&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 hash or not. + !self.cannot_derive_hash.as_ref().unwrap().contains(&id) + } + + /// Look up whether the item with `id` can + /// derive copy or not. pub fn lookup_item_id_can_derive_copy(&self, id: ItemId) -> bool { assert!(self.in_codegen_phase(), "We only compute can_derive_debug when we enter codegen"); diff --git a/src/ir/derive.rs b/src/ir/derive.rs index 6d8c2c87..128ef9f2 100644 --- a/src/ir/derive.rs +++ b/src/ir/derive.rs @@ -71,3 +71,30 @@ pub trait CanTriviallyDeriveDefault { /// otherwise. fn can_trivially_derive_default(&self) -> bool; } + +/// A trait that encapsulates the logic for whether or not we can derive `Hash` +/// for a given thing. +/// +/// This should ideally be a no-op that just returns `true`, but instead needs +/// 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 CanDeriveHash { + + /// Return `true` if `Default` can be derived for this thing, `false` + /// otherwise. + fn can_derive_hash(&self, + ctx: &BindgenContext) + -> bool; +} + +/// A trait that encapsulates the logic for whether or not we can derive `Hash`. +/// The difference between this trait and the CanDeriveHash 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 CanTriviallyDeriveHash { + + /// Return `true` if `Hash` can be derived for this thing, `false` + /// otherwise. + fn can_trivially_derive_hash(&self) -> bool; +} diff --git a/src/ir/function.rs b/src/ir/function.rs index 99ab8772..20c026a4 100644 --- a/src/ir/function.rs +++ b/src/ir/function.rs @@ -8,11 +8,13 @@ use super::traversal::{EdgeKind, Trace, Tracer}; use super::ty::TypeKind; use clang; use clang_sys::{self, CXCallingConv}; -use ir::derive::CanTriviallyDeriveDebug; +use ir::derive::{CanTriviallyDeriveDebug, CanTriviallyDeriveHash}; use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult}; use std::io; use syntax::abi; +const RUST_DERIVE_FUNPTR_LIMIT: usize = 12; + /// What kind of a function are we looking at? #[derive(Debug, Copy, Clone, PartialEq)] pub enum FunctionKind { @@ -481,7 +483,20 @@ impl Trace for FunctionSig { // Note that copy is always derived, so we don't need to implement it. impl CanTriviallyDeriveDebug for FunctionSig { fn can_trivially_derive_debug(&self) -> bool { - const RUST_DERIVE_FUNPTR_LIMIT: usize = 12; + if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT { + return false; + } + + match self.abi { + Abi::Known(abi::Abi::C) | + Abi::Unknown(..) => true, + _ => false, + } + } +} + +impl CanTriviallyDeriveHash for FunctionSig { + fn can_trivially_derive_hash(&self) -> bool { if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT { return false; } diff --git a/src/ir/item.rs b/src/ir/item.rs index a17f26fb..237618c8 100644 --- a/src/ir/item.rs +++ b/src/ir/item.rs @@ -5,7 +5,7 @@ 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::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash}; use super::dot::DotAttributes; use super::function::{Function, FunctionKind}; use super::item_kind::ItemKind; @@ -294,6 +294,12 @@ impl<'a> CanDeriveCopy<'a> for Item { } } +impl CanDeriveHash for Item { + fn can_derive_hash(&self, ctx: &BindgenContext) -> bool { + ctx.options().derive_hash && ctx.lookup_item_id_can_derive_hash(self.id()) + } +} + /// An item is the base of the bindgen representation, it can be either a /// module, a type, a function, or a variable (see `ItemKind` for more /// information). diff --git a/src/ir/layout.rs b/src/ir/layout.rs index 9ec04d20..bac664f1 100644 --- a/src/ir/layout.rs +++ b/src/ir/layout.rs @@ -1,7 +1,8 @@ //! Intermediate representation for the physical layout of some type. use super::derive::{CanTriviallyDeriveDebug, - CanTriviallyDeriveDefault, CanTriviallyDeriveCopy}; + CanTriviallyDeriveDefault, CanTriviallyDeriveCopy, + CanTriviallyDeriveHash}; use super::ty::{RUST_DERIVE_IN_ARRAY_LIMIT, Type, TypeKind}; use clang; use std::{cmp, mem}; @@ -123,3 +124,11 @@ impl CanTriviallyDeriveCopy for Opaque { .map_or(false, |size| size <= RUST_DERIVE_IN_ARRAY_LIMIT) } } + +impl CanTriviallyDeriveHash for Opaque { + + fn can_trivially_derive_hash(&self) -> bool { + self.array_size() + .map_or(false, |size| size <= RUST_DERIVE_IN_ARRAY_LIMIT) + } +} @@ -242,6 +242,10 @@ impl Builder { output_vector.push("--with-derive-default".into()); } + if self.options.derive_hash { + output_vector.push("--with-derive-hash".into()); + } + if !self.options.generate_comments { output_vector.push("--no-doc-comments".into()); } @@ -652,6 +656,12 @@ impl Builder { self } + /// Set whether `Hash` should be derived by default. + pub fn derive_hash(mut self, doit: bool) -> Self { + self.options.derive_hash = doit; + self + } + /// Emit Clang AST. pub fn emit_clang_ast(mut self) -> Builder { self.options.emit_ast = true; @@ -955,6 +965,10 @@ pub struct BindgenOptions { /// and types. pub derive_default: bool, + /// True if we should derive Hash trait implementations for C/C++ structures + /// and types. + pub derive_hash: bool, + /// True if we can use unstable Rust code in the bindings, false if we /// cannot. pub unstable_rust: bool, @@ -1064,6 +1078,7 @@ impl Default for BindgenOptions { layout_tests: true, derive_debug: true, derive_default: false, + derive_hash: false, enable_cxx_namespaces: false, disable_name_namespacing: false, unstable_rust: false, diff --git a/src/options.rs b/src/options.rs index f2ed5494..706936af 100644 --- a/src/options.rs +++ b/src/options.rs @@ -62,6 +62,9 @@ pub fn builder_from_flags<I> Arg::with_name("with-derive-default") .long("with-derive-default") .help("Derive Default on any type."), + Arg::with_name("with-derive-hash") + .long("with-derive-hash") + .help("Derive hash on any type."), Arg::with_name("no-doc-comments") .long("no-doc-comments") .help("Avoid including doc comments in the output, see: \ @@ -265,6 +268,10 @@ pub fn builder_from_flags<I> builder = builder.derive_default(true); } + if matches.is_present("with-derive-hash") { + builder = builder.derive_hash(true); + } + if matches.is_present("no-derive-default") { builder = builder.derive_default(false); } |