summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/codegen/impl_partialeq.rs121
-rw-r--r--src/codegen/mod.rs91
-rw-r--r--src/ir/analysis/derive_partial_eq_or_partial_ord.rs455
-rw-r--r--src/ir/analysis/mod.rs2
-rw-r--r--src/ir/comp.rs21
-rw-r--r--src/ir/context.rs18
-rw-r--r--src/ir/derive.rs24
-rw-r--r--src/ir/function.rs13
-rw-r--r--src/ir/item.rs8
-rw-r--r--src/ir/layout.rs12
-rw-r--r--src/lib.rs45
-rw-r--r--src/options.rs16
12 files changed, 541 insertions, 285 deletions
diff --git a/src/codegen/impl_partialeq.rs b/src/codegen/impl_partialeq.rs
new file mode 100644
index 00000000..15a8953e
--- /dev/null
+++ b/src/codegen/impl_partialeq.rs
@@ -0,0 +1,121 @@
+
+use ir::comp::{CompInfo, CompKind, Field, FieldMethods};
+use ir::context::BindgenContext;
+use ir::item::{IsOpaque, Item};
+use ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
+use quote;
+
+/// Generate a manual implementation of `PartialEq` trait for the
+/// specified compound type.
+pub fn gen_partialeq_impl(
+ ctx: &BindgenContext,
+ comp_info: &CompInfo,
+ item: &Item,
+ ty_for_impl: &quote::Tokens,
+) -> Option<quote::Tokens> {
+ let mut tokens = vec![];
+
+ if item.is_opaque(ctx, &()) {
+ tokens.push(quote! {
+ &self._bindgen_opaque_blob[..] == &other._bindgen_opaque_blob[..]
+ });
+ } else if comp_info.kind() == CompKind::Union {
+ tokens.push(quote! {
+ &self.bindgen_union_field[..] == &other.bindgen_union_field[..]
+ });
+ } else {
+ for base in comp_info.base_members().iter() {
+ if !base.requires_storage(ctx) {
+ continue;
+ }
+
+ let ty_item = ctx.resolve_item(base.ty);
+ let field_name = &base.field_name;
+
+ if ty_item.is_opaque(ctx, &()) {
+ let field_name = ctx.rust_ident(field_name);
+ tokens.push(quote! {
+ &self. #field_name [..] == &other. #field_name [..]
+ });
+ } else {
+ tokens.push(gen_field(ctx, ty_item, field_name));
+ }
+ }
+
+ for field in comp_info.fields() {
+ match *field {
+ Field::DataMember(ref fd) => {
+ let ty_item = ctx.resolve_item(fd.ty());
+ let name = fd.name().unwrap();
+ tokens.push(gen_field(ctx, ty_item, name));
+ }
+ Field::Bitfields(ref bu) => for bitfield in bu.bitfields() {
+ let name_ident = ctx.rust_ident_raw(bitfield.name());
+ tokens.push(quote! {
+ self.#name_ident () == other.#name_ident ()
+ });
+ },
+ }
+ }
+ }
+
+ Some(quote! {
+ fn eq(&self, other: & #ty_for_impl) -> bool {
+ #( #tokens )&&*
+ }
+ })
+}
+
+fn gen_field(ctx: &BindgenContext, ty_item: &Item, name: &str) -> quote::Tokens {
+ fn quote_equals(name_ident: quote::Ident) -> quote::Tokens {
+ quote! { self.#name_ident == other.#name_ident }
+ }
+
+ let name_ident = ctx.rust_ident(name);
+ let ty = ty_item.expect_type();
+
+ match *ty.kind() {
+ TypeKind::Void |
+ TypeKind::NullPtr |
+ TypeKind::Int(..) |
+ TypeKind::Complex(..) |
+ TypeKind::Float(..) |
+ TypeKind::Enum(..) |
+ TypeKind::TypeParam |
+ TypeKind::UnresolvedTypeRef(..) |
+ TypeKind::BlockPointer |
+ TypeKind::Reference(..) |
+ TypeKind::ObjCInterface(..) |
+ TypeKind::ObjCId |
+ TypeKind::ObjCSel |
+ TypeKind::Comp(..) |
+ TypeKind::Pointer(_) |
+ TypeKind::Function(..) |
+ TypeKind::Opaque => quote_equals(name_ident),
+
+ TypeKind::TemplateInstantiation(ref inst) => {
+ if inst.is_opaque(ctx, &ty_item) {
+ quote! {
+ &self. #name_ident [..] == &other. #name_ident [..]
+ }
+ } else {
+ quote_equals(name_ident)
+ }
+ }
+
+ TypeKind::Array(_, len) => if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
+ quote_equals(name_ident)
+ } else {
+ quote! {
+ &self. #name_ident [..] == &other. #name_ident [..]
+ }
+ },
+
+ TypeKind::ResolvedTypeRef(t) |
+ TypeKind::TemplateAlias(t, _) |
+ TypeKind::Alias(t) => {
+ let inner_item = ctx.resolve_item(t);
+ gen_field(ctx, inner_item, name)
+ }
+ }
+}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 264b701b..9ccd79c1 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -1,4 +1,5 @@
mod impl_debug;
+mod impl_partialeq;
mod error;
mod helpers;
pub mod struct_layout;
@@ -14,7 +15,7 @@ use ir::comp::{Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field,
use ir::context::{BindgenContext, ItemId};
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd,
- CanDerivePartialEq, CanDeriveEq};
+ CanDerivePartialEq, CanDeriveEq, CannotDeriveReason};
use ir::dot;
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
use ir::function::{Abi, Function, FunctionSig};
@@ -1420,6 +1421,7 @@ impl CodeGenerator for CompInfo {
let mut needs_clone_impl = false;
let mut needs_default_impl = false;
let mut needs_debug_impl = false;
+ let mut needs_partialeq_impl = false;
if let Some(comment) = item.comment(ctx) {
attributes.push(attributes::doc(comment));
}
@@ -1475,6 +1477,14 @@ impl CodeGenerator for CompInfo {
if item.can_derive_partialeq(ctx) {
derives.push("PartialEq");
+ } else {
+ needs_partialeq_impl =
+ ctx.options().derive_partialeq &&
+ ctx.options().impl_partialeq &&
+ ctx.lookup_can_derive_partialeq_or_partialord(item.id())
+ .map_or(true, |x| {
+ x == CannotDeriveReason::ArrayTooLarge
+ });
}
if item.can_derive_eq(ctx) {
@@ -1535,25 +1545,14 @@ impl CodeGenerator for CompInfo {
}
for base in self.base_members() {
- // Virtual bases are already taken into account by the vtable
- // pointer.
- //
- // FIXME(emilio): Is this always right?
- if base.is_virtual() {
- continue;
- }
-
- let base_ty = ctx.resolve_type(base.ty);
- // 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, base.ty) {
+ if !base.requires_storage(ctx) {
continue;
}
let inner = base.ty.to_rust_ty_or_opaque(ctx, &());
let field_name = ctx.rust_ident(&base.field_name);
+ let base_ty = ctx.resolve_type(base.ty);
struct_layout.saw_base(base_ty);
fields.push(quote! {
@@ -1667,33 +1666,34 @@ impl CodeGenerator for CompInfo {
}
}
- let mut generics = quote! {};
+ let mut generic_param_names = vec![];
if let Some(ref params) = used_template_params {
- if !params.is_empty() {
- let mut param_names = vec![];
+ for (idx, ty) in params.iter().enumerate() {
+ let param = ctx.resolve_type(*ty);
+ let name = param.name().unwrap();
+ let ident = ctx.rust_ident(name);
+ generic_param_names.push(ident.clone());
- for (idx, ty) in params.iter().enumerate() {
- let param = ctx.resolve_type(*ty);
- let name = param.name().unwrap();
- let ident = ctx.rust_ident(name);
- param_names.push(ident.clone());
-
- let prefix = ctx.trait_prefix();
- let field_name = ctx.rust_ident(format!("_phantom_{}", idx));
- fields.push(quote! {
- pub #field_name : ::#prefix::marker::PhantomData<
- ::#prefix::cell::UnsafeCell<#ident>
- > ,
- });
- }
-
- generics = quote! {
- < #( #param_names ),* >
- };
+ let prefix = ctx.trait_prefix();
+ let field_name = ctx.rust_ident(format!("_phantom_{}", idx));
+ fields.push(quote! {
+ pub #field_name : ::#prefix::marker::PhantomData<
+ ::#prefix::cell::UnsafeCell<#ident>
+ > ,
+ });
}
}
+ let generics = if !generic_param_names.is_empty() {
+ let generic_param_names = generic_param_names.clone();
+ quote! {
+ < #( #generic_param_names ),* >
+ }
+ } else {
+ quote! { }
+ };
+
tokens.append(quote! {
#generics {
#( #fields )*
@@ -1896,6 +1896,27 @@ impl CodeGenerator for CompInfo {
});
}
+ if needs_partialeq_impl {
+ if let Some(impl_) = impl_partialeq::gen_partialeq_impl(ctx, self, item, &ty_for_impl) {
+
+ let partialeq_bounds = if !generic_param_names.is_empty() {
+ let bounds = generic_param_names.iter().map(|t| {
+ quote! { #t: PartialEq }
+ });
+ quote! { where #( #bounds ),* }
+ } else {
+ quote! { }
+ };
+
+ let prefix = ctx.trait_prefix();
+ result.push(quote! {
+ impl #generics ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds {
+ #impl_
+ }
+ });
+ }
+ }
+
if !methods.is_empty() {
result.push(quote! {
impl #generics #ty_for_impl {
diff --git a/src/ir/analysis/derive_partial_eq_or_partial_ord.rs b/src/ir/analysis/derive_partial_eq_or_partial_ord.rs
index 7efce6e5..0cca6c6d 100644
--- a/src/ir/analysis/derive_partial_eq_or_partial_ord.rs
+++ b/src/ir/analysis/derive_partial_eq_or_partial_ord.rs
@@ -3,16 +3,13 @@
use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
use ir::comp::CompKind;
-use ir::comp::Field;
-use ir::comp::FieldMethods;
use ir::context::{BindgenContext, ItemId};
-use ir::derive::CanTriviallyDerivePartialEqOrPartialOrd;
-use ir::item::IsOpaque;
-use ir::traversal::EdgeKind;
+use ir::derive::{CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason};
+use ir::item::{Item, IsOpaque};
+use ir::traversal::{EdgeKind, Trace};
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
-use ir::ty::TypeKind;
+use ir::ty::{TypeKind, Type};
use std::collections::HashMap;
-use std::collections::HashSet;
/// An analysis that finds for each IR item whether `PartialEq`/`PartialOrd`
/// cannot be derived.
@@ -23,9 +20,9 @@ use std::collections::HashSet;
/// * 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, `PartialEq` or partialord cannot be derived if the
-/// length of the array is larger than the limit or the type of data the array
-/// contains cannot derive `PartialEq`/`PartialOrd`.
+/// * If T is Array type, `PartialEq` or partialord cannot be derived if the array is incomplete, if the length of
+/// the array is larger than the limit, or the type of data the array contains cannot derive
+/// `PartialEq`/`PartialOrd`.
///
/// * If T is a type alias, a templated alias or an indirection to another type,
/// `PartialEq`/`PartialOrd` cannot be derived if the type T refers to cannot be
@@ -47,7 +44,7 @@ pub struct CannotDerivePartialEqOrPartialOrd<'ctx> {
// The incremental result of this analysis's computation. Everything in this
// set cannot derive `PartialEq`/`PartialOrd`.
- cannot_derive_partialeq_or_partialord: HashSet<ItemId>,
+ cannot_derive_partialeq_or_partialord: HashMap<ItemId, CannotDeriveReason>,
// Dependencies saying that if a key ItemId has been inserted into the
// `cannot_derive_partialeq_or_partialord` set, then each of the ids
@@ -83,91 +80,66 @@ impl<'ctx> CannotDerivePartialEqOrPartialOrd<'ctx> {
}
}
- fn insert<Id: Into<ItemId>>(&mut self, id: Id) -> ConstrainResult {
+ fn insert<Id: Into<ItemId>>(
+ &mut self,
+ id: Id,
+ reason: CannotDeriveReason,
+ ) -> ConstrainResult {
let id = id.into();
- trace!("inserting {:?} into the cannot_derive_partialeq_or_partialord set", id);
-
- let was_not_already_in_set = self.cannot_derive_partialeq_or_partialord.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
+ trace!(
+ "inserting {:?} into the cannot_derive_partialeq_or_partialord because {:?}",
+ id,
+ reason
);
-
+ let existing = self.cannot_derive_partialeq_or_partialord
+ .insert(id, reason);
+ assert!(can_supersede(existing, Some(reason)));
ConstrainResult::Changed
}
-}
-
-impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
- type Node = ItemId;
- type Extra = &'ctx BindgenContext;
- type Output = HashSet<ItemId>;
-
- fn new(
- ctx: &'ctx BindgenContext,
- ) -> CannotDerivePartialEqOrPartialOrd<'ctx> {
- let cannot_derive_partialeq_or_partialord = HashSet::new();
- let dependencies = generate_dependencies(ctx, Self::consider_edge);
- CannotDerivePartialEqOrPartialOrd {
- ctx,
- cannot_derive_partialeq_or_partialord,
- dependencies,
+ fn constrain_type(
+ &mut self,
+ item: &Item,
+ ty: &Type,
+ ) -> Option<CannotDeriveReason> {
+ if !self.ctx.whitelisted_items().contains(&item.id()) {
+ return Some(CannotDeriveReason::Other);
}
- }
-
- 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_partialeq_or_partialord.contains(&id) {
- trace!(" already know it cannot derive `PartialEq`/`PartialOrd`");
- 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 self.ctx.no_partialeq_by_name(&item) {
- return self.insert(id)
+ return Some(CannotDeriveReason::Other);
}
trace!("ty: {:?}", ty);
if item.is_opaque(self.ctx, &()) {
- let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| {
- l.opaque().can_trivially_derive_partialeq_or_partialord()
- });
- return if layout_can_derive &&
- !(ty.is_union() &&
- self.ctx.options().rust_features().untagged_union()) {
- trace!(" we can trivially derive `PartialEq`/`PartialOrd` for the layout");
- ConstrainResult::Same
- } else {
- trace!(" we cannot derive `PartialEq`/`PartialOrd` for the layout");
- self.insert(id)
- };
- }
+ if ty.is_union()
+ && self.ctx.options().rust_features().untagged_union()
+ {
+ trace!(
+ " cannot derive `PartialEq`/`PartialOrd` for Rust unions"
+ );
+ return Some(CannotDeriveReason::Other);
+ }
- 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);
+ let layout_can_derive = ty.layout(self.ctx)
+ .map_or(CanDerive::Yes, |l| {
+ l.opaque().can_trivially_derive_partialeq_or_partialord()
+ });
+
+ return match layout_can_derive {
+ CanDerive::Yes => {
+ trace!(
+ " we can trivially derive `PartialEq`/`PartialOrd` for the layout"
+ );
+ None
+ }
+ CanDerive::No(reason) => {
+ trace!(
+ " we cannot derive `PartialEq`/`PartialOrd` for the layout"
+ );
+ Some(reason)
+ }
+ };
}
match *ty.kind() {
@@ -186,25 +158,36 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
TypeKind::ObjCInterface(..) |
TypeKind::ObjCId |
TypeKind::ObjCSel => {
- trace!(" simple type that can always derive `PartialEq`/`PartialOrd`");
- ConstrainResult::Same
+ trace!(
+ " simple type that can always derive `PartialEq`/`PartialOrd`"
+ );
+ return None;
}
TypeKind::Array(t, len) => {
- if self.cannot_derive_partialeq_or_partialord.contains(&t.into()) {
+ if self.cannot_derive_partialeq_or_partialord.contains_key(&t.into()) {
trace!(
" arrays of T for which we cannot derive `PartialEq`/`PartialOrd` \
- also cannot derive `PartialEq`/`PartialOrd`"
+ also cannot derive `PartialEq`/`PartialOrd`"
);
- return self.insert(id);
+ return Some(CannotDeriveReason::Other);
}
- if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
- trace!(" array is small enough to derive `PartialEq`/`PartialOrd`");
- ConstrainResult::Same
+ if len == 0 {
+ trace!(
+ " cannot derive `PartialEq`/`PartialOrd` for incomplete arrays"
+ );
+ return Some(CannotDeriveReason::Other);
+ } else if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
+ trace!(
+ " array is small enough to derive `PartialEq`/`PartialOrd`"
+ );
+ return None;
} else {
- trace!(" array is too large to derive `PartialEq`/`PartialOrd`");
- self.insert(id)
+ trace!(
+ " array is too large to derive `PartialEq`/`PartialOrd`"
+ );
+ return Some(CannotDeriveReason::ArrayTooLarge);
}
}
@@ -212,44 +195,30 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
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_partialeq_or_partialord() {
+ if let CanDerive::No(_) =
+ sig.can_trivially_derive_partialeq_or_partialord()
+ {
trace!(
" function pointer that can't trivially derive `PartialEq`/`PartialOrd`"
);
- return self.insert(id);
+ return Some(CannotDeriveReason::Other);
}
}
- trace!(" pointers can derive PartialEq");
- ConstrainResult::Same
+ trace!(" pointers can derive `PartialEq`/`PartialOrd`");
+ return None;
}
TypeKind::Function(ref sig) => {
- if !sig.can_trivially_derive_partialeq_or_partialord() {
+ if let CanDerive::No(_) =
+ sig.can_trivially_derive_partialeq_or_partialord()
+ {
trace!(
" function that can't trivially derive `PartialEq`/`PartialOrd`"
);
- return self.insert(id);
+ return Some(CannotDeriveReason::Other);
}
trace!(" function can derive `PartialEq`/`PartialOrd`");
- ConstrainResult::Same
- }
-
- TypeKind::ResolvedTypeRef(t) |
- TypeKind::TemplateAlias(t, _) |
- TypeKind::Alias(t) => {
- if self.cannot_derive_partialeq_or_partialord.contains(&t.into()) {
- trace!(
- " aliases and type refs to T which cannot derive \
- `PartialEq`/`PartialOrd` also cannot derive `PartialEq`/`PartialOrd`"
- );
- self.insert(id)
- } else {
- trace!(
- " aliases and type refs to T which can derive \
- `PartialEq`/`PartialOrd` can also derive `PartialEq`/`PartialOrd`"
- );
- ConstrainResult::Same
- }
+ return None;
}
TypeKind::Comp(ref info) => {
@@ -260,114 +229,180 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
if info.kind() == CompKind::Union {
if self.ctx.options().rust_features().untagged_union() {
- trace!(" cannot derive `PartialEq`/`PartialOrd` for Rust unions");
- return self.insert(id);
- }
-
- if ty.layout(self.ctx).map_or(true, |l| {
- l.opaque().can_trivially_derive_partialeq_or_partialord()
- })
- {
trace!(
- " union layout can trivially derive `PartialEq`/`PartialOrd`"
+ " cannot derive `PartialEq`/`PartialOrd` for Rust unions"
);
- return ConstrainResult::Same;
- } else {
- trace!(" union layout cannot derive `PartialEq`/`PartialOrd`");
- return self.insert(id);
+ return Some(CannotDeriveReason::Other);
}
- }
- let bases_cannot_derive =
- info.base_members().iter().any(|base| {
- !self.ctx.whitelisted_items().contains(&base.ty.into()) ||
- self.cannot_derive_partialeq_or_partialord.contains(&base.ty.into())
- });
- if bases_cannot_derive {
- trace!(
- " base members cannot derive `PartialEq`/`PartialOrd`, so we can't \
- either"
+ let layout_can_derive = ty.layout(self.ctx).map_or(
+ CanDerive::Yes,
+ |l| {
+ l.opaque()
+ .can_trivially_derive_partialeq_or_partialord()
+ },
);
- 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().into(),
- ) ||
- self.cannot_derive_partialeq_or_partialord.contains(
- &data.ty().into(),
- )
+ return match layout_can_derive {
+ CanDerive::Yes => {
+ trace!(
+ " union layout can trivially derive `PartialEq`/`PartialOrd`"
+ );
+ None
}
- Field::Bitfields(ref bfu) => {
- if bfu.layout().align > RUST_DERIVE_IN_ARRAY_LIMIT {
- trace!(
- " we cannot derive PartialEq for a bitfield larger then \
- the limit"
- );
- return true;
- }
-
- bfu.bitfields().iter().any(|b| {
- !self.ctx.whitelisted_items().contains(
- &b.ty().into(),
- ) ||
- self.cannot_derive_partialeq_or_partialord.contains(
- &b.ty().into(),
- )
- })
+ CanDerive::No(reason) => {
+ trace!(
+ " union layout cannot derive `PartialEq`/`PartialOrd`"
+ );
+ Some(reason)
}
- });
- if fields_cannot_derive {
- trace!(
- " fields cannot derive `PartialEq`/`PartialOrd`, so we can't either"
- );
- return self.insert(id);
+ };
}
+ return self.constrain_join(item);
+ }
- trace!(" comp can derive PartialEq");
- ConstrainResult::Same
+ TypeKind::ResolvedTypeRef(..) |
+ TypeKind::TemplateAlias(..) |
+ TypeKind::Alias(..) |
+ TypeKind::TemplateInstantiation(..) => {
+ return self.constrain_join(item);
}
- TypeKind::TemplateInstantiation(ref template) => {
- let args_cannot_derive =
- template.template_arguments().iter().any(|arg| {
- self.cannot_derive_partialeq_or_partialord.contains(&arg.into())
- });
- if args_cannot_derive {
- trace!(
- " template args cannot derive `PartialEq`/`PartialOrd`, so \
- insantiation can't either"
- );
- return self.insert(id);
+ TypeKind::Opaque => unreachable!(
+ "The early ty.is_opaque check should have handled this case"
+ ),
+ }
+ }
+
+ fn constrain_join(&mut self, item: &Item) -> Option<CannotDeriveReason> {
+ let mut candidate = None;
+
+ item.trace(
+ self.ctx,
+ &mut |sub_id, edge_kind| {
+ // Ignore ourselves, since union with ourself is a
+ // no-op. Ignore edges that aren't relevant to the
+ // analysis.
+ if sub_id == item.id() || !Self::consider_edge(edge_kind) {
+ return;
}
- 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_partialeq_or_partialord.contains(
- &template.template_definition().into(),
- );
- if def_cannot_derive {
- trace!(
- " template definition cannot derive `PartialEq`/`PartialOrd`, so \
- insantiation can't either"
- );
- return self.insert(id);
+ let reason = self.cannot_derive_partialeq_or_partialord
+ .get(&sub_id)
+ .cloned();
+
+ if can_supersede(candidate, reason) {
+ candidate = reason;
}
+ },
+ &(),
+ );
- trace!(" template instantiation can derive `PartialEq`/`PartialOrd`");
- ConstrainResult::Same
- }
+ candidate
+ }
+}
- TypeKind::Opaque => {
- unreachable!(
- "The early ty.is_opaque check should have handled this case"
- )
+/// Check if the one reason could supersede another.
+///
+/// To keep this analysis monotone we should go only in one direction.
+/// If the abscence of the reason is at the bottom and `CannotDeriveReason::Other`
+/// is at the top, then we can only go upwards.
+///
+/// Other
+/// ^
+/// |
+/// ArrayTooLarge
+/// ^
+/// |
+/// None
+///
+fn can_supersede(from: Option<CannotDeriveReason>, to: Option<CannotDeriveReason>) -> bool {
+ fn rank(maybe_reason: Option<CannotDeriveReason>) -> usize {
+ match maybe_reason {
+ None => 0,
+ Some(CannotDeriveReason::ArrayTooLarge) => 1,
+ Some(CannotDeriveReason::Other) => 2,
+ }
+ }
+ rank(from) <= rank(to)
+}
+
+impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
+ type Node = ItemId;
+ type Extra = &'ctx BindgenContext;
+ type Output = HashMap<ItemId, CannotDeriveReason>;
+
+ fn new(
+ ctx: &'ctx BindgenContext,
+ ) -> CannotDerivePartialEqOrPartialOrd<'ctx> {
+ let cannot_derive_partialeq_or_partialord = HashMap::new();
+ let dependencies = generate_dependencies(ctx, Self::consider_edge);
+
+ CannotDerivePartialEqOrPartialOrd {
+ ctx,
+ cannot_derive_partialeq_or_partialord,
+ dependencies,
+ }
+ }
+
+ fn initial_worklist(&self) -> Vec<ItemId> {
+ // The transitive closure of all whitelisted items, including explicitly
+ // blacklisted items.
+ self.ctx
+ .whitelisted_items()
+ .iter()
+ .cloned()
+ .flat_map(|i| {
+ let mut reachable = vec![i];
+ i.trace(
+ self.ctx,
+ &mut |s, _| {
+ reachable.push(s);
+ },
+ &(),
+ );
+ reachable
+ })
+ .collect()
+ }
+
+ fn constrain(&mut self, id: ItemId) -> ConstrainResult {
+ trace!("constrain: {:?}", id);
+
+ if Some(CannotDeriveReason::Other)
+ == self.cannot_derive_partialeq_or_partialord.get(&id).cloned()
+ {
+ trace!(
+ " already know it cannot derive `PartialEq`/`PartialOrd`"
+ );
+ return ConstrainResult::Same;
+ }
+
+ let item = self.ctx.resolve_item(id);
+ let maybe_reason = match item.as_type() {
+ Some(ty) => {
+ self.constrain_type(item, ty).or_else(|| {
+ 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.
+ Some(CannotDeriveReason::ArrayTooLarge)
+ } else {
+ None
+ }
+ })
}
+ None => self.constrain_join(item),
+ };
+
+ if let Some(reason) = maybe_reason {
+ self.insert(id, reason)
+ } else {
+ ConstrainResult::Same
}
}
@@ -384,7 +419,7 @@ impl<'ctx> MonotoneFramework for CannotDerivePartialEqOrPartialOrd<'ctx> {
}
}
-impl<'ctx> From<CannotDerivePartialEqOrPartialOrd<'ctx>> for HashSet<ItemId> {
+impl<'ctx> From<CannotDerivePartialEqOrPartialOrd<'ctx>> for HashMap<ItemId, CannotDeriveReason> {
fn from(analysis: CannotDerivePartialEqOrPartialOrd<'ctx>) -> Self {
analysis.cannot_derive_partialeq_or_partialord
}
diff --git a/src/ir/analysis/mod.rs b/src/ir/analysis/mod.rs
index 179fa33d..dfc96f0a 100644
--- a/src/ir/analysis/mod.rs
+++ b/src/ir/analysis/mod.rs
@@ -59,8 +59,8 @@ mod derive_partial_eq_or_partial_ord;
pub use self::derive_partial_eq_or_partial_ord::CannotDerivePartialEqOrPartialOrd;
mod has_float;
pub use self::has_float::HasFloat;
-
use ir::context::{BindgenContext, ItemId};
+
use ir::traversal::{EdgeKind, Trace};
use std::collections::HashMap;
use std::fmt;
diff --git a/src/ir/comp.rs b/src/ir/comp.rs
index c1dd369b..6a90bcbf 100644
--- a/src/ir/comp.rs
+++ b/src/ir/comp.rs
@@ -806,6 +806,27 @@ impl Base {
pub fn is_virtual(&self) -> bool {
self.kind == BaseKind::Virtual
}
+
+ /// Whether this base class should have it's own field for storage.
+ pub fn requires_storage(&self, ctx: &BindgenContext) -> bool {
+ // Virtual bases are already taken into account by the vtable
+ // pointer.
+ //
+ // FIXME(emilio): Is this always right?
+ if self.is_virtual() {
+ return false;
+ }
+
+ let base_ty = ctx.resolve_type(self.ty);
+ // 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, self.ty) {
+ return false;
+ }
+
+ true
+ }
}
/// A compound type.
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 37b71ba1..27a34162 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -7,7 +7,7 @@ use super::analysis::{CannotDeriveCopy, CannotDeriveDebug,
HasFloat, analyze};
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd,
- CanDerivePartialEq, CanDeriveEq};
+ CanDerivePartialEq, CanDeriveEq, CannotDeriveReason};
use super::int::IntKind;
use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalPath, ItemSet};
use super::item_kind::ItemKind;
@@ -249,7 +249,7 @@ where
{
fn can_derive_partialord(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_partialord &&
- ctx.lookup_can_derive_partialeq_or_partialord(*self)
+ ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none()
}
}
@@ -259,7 +259,7 @@ where
{
fn can_derive_partialeq(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_partialeq &&
- ctx.lookup_can_derive_partialeq_or_partialord(*self)
+ ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none()
}
}
@@ -269,7 +269,7 @@ where
{
fn can_derive_eq(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_eq &&
- ctx.lookup_can_derive_partialeq_or_partialord(*self) &&
+ ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none() &&
!ctx.lookup_has_float(*self)
}
}
@@ -280,7 +280,7 @@ where
{
fn can_derive_ord(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_ord &&
- ctx.lookup_can_derive_partialeq_or_partialord(*self) &&
+ ctx.lookup_can_derive_partialeq_or_partialord(*self).is_none() &&
!ctx.lookup_has_float(*self)
}
}
@@ -423,12 +423,12 @@ pub struct BindgenContext {
/// and is always `None` before that and `Some` after.
cannot_derive_hash: Option<HashSet<ItemId>>,
- /// The set of (`ItemId`s of) types that can't derive hash.
+ /// The map why specified `ItemId`s of) types that can't derive hash.
///
/// This is populated when we enter codegen by
/// `compute_cannot_derive_partialord_partialeq_or_eq` and is always `None`
/// before that and `Some` after.
- cannot_derive_partialeq_or_partialord: Option<HashSet<ItemId>>,
+ cannot_derive_partialeq_or_partialord: Option<HashMap<ItemId, CannotDeriveReason>>,
/// The set of (`ItemId's of`) types that has vtable.
///
@@ -2365,7 +2365,7 @@ impl BindgenContext {
}
/// Look up whether the item with `id` can derive `Partial{Eq,Ord}`.
- pub fn lookup_can_derive_partialeq_or_partialord<Id: Into<ItemId>>(&self, id: Id) -> bool {
+ pub fn lookup_can_derive_partialeq_or_partialord<Id: Into<ItemId>>(&self, id: Id) -> Option<CannotDeriveReason> {
let id = id.into();
assert!(
self.in_codegen_phase(),
@@ -2374,7 +2374,7 @@ impl BindgenContext {
// Look up the computed value for whether the item with `id` can
// derive partialeq or not.
- !self.cannot_derive_partialeq_or_partialord.as_ref().unwrap().contains(&id)
+ self.cannot_derive_partialeq_or_partialord.as_ref().unwrap().get(&id).cloned()
}
/// Look up whether the item with `id` can derive `Copy` or not.
diff --git a/src/ir/derive.rs b/src/ir/derive.rs
index cafbd3b1..52e61133 100644
--- a/src/ir/derive.rs
+++ b/src/ir/derive.rs
@@ -117,5 +117,27 @@ pub trait CanTriviallyDeriveHash {
pub trait CanTriviallyDerivePartialEqOrPartialOrd {
/// Return `true` if `PartialEq` or `PartialOrd` can trivially be derived
/// for this thing, `false` otherwise.
- fn can_trivially_derive_partialeq_or_partialord(&self) -> bool;
+ fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive;
+}
+
+/// Reason why exactly we cannot automatically derive a trait.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum CannotDeriveReason {
+ /// The only thing that stops us from automatically deriving is that
+ /// array with more than maximum number of elements is used.
+ ///
+ /// This means we probably can "manually" implement such trait.
+ ArrayTooLarge,
+
+ /// Any other reason.
+ Other,
+}
+
+/// Whether it is possible or not to derive trait automatically.
+pub enum CanDerive {
+ /// Yes, we can!
+ Yes,
+
+ /// No, we cannot. Contains reason why exactly we can't derive.
+ No(CannotDeriveReason)
}
diff --git a/src/ir/function.rs b/src/ir/function.rs
index ccdfc4f3..3a9f337c 100644
--- a/src/ir/function.rs
+++ b/src/ir/function.rs
@@ -9,7 +9,7 @@ use super::ty::TypeKind;
use clang;
use clang_sys::{self, CXCallingConv};
use ir::derive::{CanTriviallyDeriveDebug, CanTriviallyDeriveHash,
- CanTriviallyDerivePartialEqOrPartialOrd};
+ CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason};
use parse::{ClangItemParser, ClangSubItemParser, ParseError, ParseResult};
use quote;
use std::io;
@@ -560,7 +560,14 @@ impl CanTriviallyDeriveHash for FunctionSig {
}
impl CanTriviallyDerivePartialEqOrPartialOrd for FunctionSig {
- fn can_trivially_derive_partialeq_or_partialord(&self) -> bool {
- self.function_pointers_can_derive()
+ fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive {
+ if self.argument_types.len() > RUST_DERIVE_FUNPTR_LIMIT {
+ return CanDerive::No(CannotDeriveReason::Other);
+ }
+
+ match self.abi {
+ Abi::C | Abi::Unknown(..) => CanDerive::Yes,
+ _ => CanDerive::No(CannotDeriveReason::Other),
+ }
}
}
diff --git a/src/ir/item.rs b/src/ir/item.rs
index 010528b6..dbc352eb 100644
--- a/src/ir/item.rs
+++ b/src/ir/item.rs
@@ -347,21 +347,21 @@ impl CanDeriveHash for Item {
impl CanDerivePartialOrd for Item {
fn can_derive_partialord(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_partialord &&
- ctx.lookup_can_derive_partialeq_or_partialord(self.id())
+ ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none()
}
}
impl CanDerivePartialEq for Item {
fn can_derive_partialeq(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_partialeq &&
- ctx.lookup_can_derive_partialeq_or_partialord(self.id())
+ ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none()
}
}
impl CanDeriveEq for Item {
fn can_derive_eq(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_eq &&
- ctx.lookup_can_derive_partialeq_or_partialord(self.id()) &&
+ ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none() &&
!ctx.lookup_has_float(self.id())
}
}
@@ -369,7 +369,7 @@ impl CanDeriveEq for Item {
impl CanDeriveOrd for Item {
fn can_derive_ord(&self, ctx: &BindgenContext) -> bool {
ctx.options().derive_ord &&
- ctx.lookup_can_derive_partialeq_or_partialord(self.id()) &&
+ ctx.lookup_can_derive_partialeq_or_partialord(self.id()).is_none() &&
!ctx.lookup_has_float(self.id())
}
}
diff --git a/src/ir/layout.rs b/src/ir/layout.rs
index 2df15ef2..0d9c123f 100644
--- a/src/ir/layout.rs
+++ b/src/ir/layout.rs
@@ -2,7 +2,7 @@
use super::derive::{CanTriviallyDeriveCopy, CanTriviallyDeriveDebug,
CanTriviallyDeriveDefault, CanTriviallyDeriveHash,
- CanTriviallyDerivePartialEqOrPartialOrd};
+ CanTriviallyDerivePartialEqOrPartialOrd, CanDerive, CannotDeriveReason};
use super::ty::{RUST_DERIVE_IN_ARRAY_LIMIT, Type, TypeKind};
use clang;
use std::{cmp, mem};
@@ -140,7 +140,13 @@ impl CanTriviallyDeriveHash for Opaque {
}
impl CanTriviallyDerivePartialEqOrPartialOrd for Opaque {
- fn can_trivially_derive_partialeq_or_partialord(&self) -> bool {
- self.array_size_within_derive_limit()
+ fn can_trivially_derive_partialeq_or_partialord(&self) -> CanDerive {
+ self.array_size().map_or(CanDerive::No(CannotDeriveReason::Other), |size| {
+ if size <= RUST_DERIVE_IN_ARRAY_LIMIT {
+ CanDerive::Yes
+ } else {
+ CanDerive::No(CannotDeriveReason::ArrayTooLarge)
+ }
+ })
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 9e54ddc9..b540653e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -239,6 +239,14 @@ impl Builder {
output_vector.push("--no-layout-tests".into());
}
+ if self.options.impl_debug {
+ output_vector.push("--impl-debug".into());
+ }
+
+ if self.options.impl_partialeq {
+ output_vector.push("--impl-partialeq".into());
+ }
+
if !self.options.derive_copy {
output_vector.push("--no-derive-copy".into());
}
@@ -247,10 +255,6 @@ impl Builder {
output_vector.push("--no-derive-debug".into());
}
- if self.options.impl_debug {
- output_vector.push("--impl-debug".into());
- }
-
if !self.options.derive_default {
output_vector.push("--no-derive-default".into());
} else {
@@ -806,6 +810,18 @@ impl Builder {
self
}
+ /// Set whether `Debug` should be implemented, if it can not be derived automatically.
+ pub fn impl_debug(mut self, doit: bool) -> Self {
+ self.options.impl_debug = doit;
+ self
+ }
+
+ /// Set whether `PartialEq` should be implemented, if it can not be derived automatically.
+ pub fn impl_partialeq(mut self, doit: bool) -> Self {
+ self.options.impl_partialeq = doit;
+ self
+ }
+
/// Set whether `Copy` should be derived by default.
pub fn derive_copy(mut self, doit: bool) -> Self {
self.options.derive_copy = doit;
@@ -818,12 +834,6 @@ impl Builder {
self
}
- /// Set whether `Debug` should be implemented, if it can not be derived automatically.
- pub fn impl_debug(mut self, doit: bool) -> Self {
- self.options.impl_debug = doit;
- self
- }
-
/// Set whether `Default` should be derived by default.
pub fn derive_default(mut self, doit: bool) -> Self {
self.options.derive_default = doit;
@@ -1209,6 +1219,14 @@ struct BindgenOptions {
/// True if we should generate layout tests for generated structures.
layout_tests: bool,
+ /// True if we should implement the Debug trait for C/C++ structures and types
+ /// that do not support automatically deriving Debug.
+ impl_debug: bool,
+
+ /// True if we should implement the PartialEq trait for C/C++ structures and types
+ /// that do not support autoamically deriving PartialEq.
+ impl_partialeq: bool,
+
/// True if we should derive Copy trait implementations for C/C++ structures
/// and types.
derive_copy: bool,
@@ -1217,10 +1235,6 @@ struct BindgenOptions {
/// and types.
derive_debug: bool,
- /// True if we should implement the Debug trait for C/C++ structures and types
- /// that do not support automatically deriving Debug.
- impl_debug: bool,
-
/// True if we should derive Default trait implementations for C/C++ structures
/// and types.
derive_default: bool,
@@ -1386,9 +1400,10 @@ impl Default for BindgenOptions {
emit_ir: false,
emit_ir_graphviz: None,
layout_tests: true,
+ impl_debug: false,
+ impl_partialeq: false,
derive_copy: true,
derive_debug: true,
- impl_debug: false,
derive_default: false,
derive_hash: false,
derive_partialord: false,
diff --git a/src/options.rs b/src/options.rs
index aec4a2bd..64ee249e 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -66,14 +66,18 @@ where
Arg::with_name("no-derive-debug")
.long("no-derive-debug")
.help("Avoid deriving Debug on any type."),
- Arg::with_name("impl-debug")
- .long("impl-debug")
- .help("Create Debug implementation, if it can not be derived \
- automatically."),
Arg::with_name("no-derive-default")
.long("no-derive-default")
.hidden(true)
.help("Avoid deriving Default on any type."),
+ Arg::with_name("impl-debug")
+ .long("impl-debug")
+ .help("Create Debug implementation, if it can not be derived \
+ automatically."),
+ Arg::with_name("impl-partialeq")
+ .long("impl-partialeq")
+ .help("Create PartialEq implementation, if it can not be derived \
+ automatically."),
Arg::with_name("with-derive-default")
.long("with-derive-default")
.help("Derive Default on any type."),
@@ -347,6 +351,10 @@ where
builder = builder.impl_debug(true);
}
+ if matches.is_present("impl-partialeq") {
+ builder = builder.impl_partialeq(true);
+ }
+
if matches.is_present("with-derive-default") {
builder = builder.derive_default(true);
}