Skip to content

Commit

Permalink
Auto merge of #1012 - pepyakin:derive-partialeq-manually, r=fitzgen
Browse files Browse the repository at this point in the history
Derive partialeq "manually" when possible

Fixes #879

r? @fitzgen
  • Loading branch information
bors-servo authored Oct 2, 2017
2 parents 0c5f4e1 + b7bd43a commit 48ca92b
Show file tree
Hide file tree
Showing 36 changed files with 2,023 additions and 690 deletions.
122 changes: 122 additions & 0 deletions src/codegen/impl_partialeq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

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 {
assert!(!ctx.options().rust_features().untagged_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)
}
}
}
91 changes: 56 additions & 35 deletions src/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod impl_debug;
mod impl_partialeq;
mod error;
mod helpers;
pub mod struct_layout;
Expand All @@ -13,7 +14,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};
Expand Down Expand Up @@ -1419,6 +1420,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));
}
Expand Down Expand Up @@ -1474,6 +1476,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_item_id_can_derive_partialeq_or_partialord(item.id())
.map_or(true, |x| {
x == CannotDeriveReason::ArrayTooLarge
});
}

if item.can_derive_eq(ctx) {
Expand Down Expand Up @@ -1534,25 +1544,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! {
Expand Down Expand Up @@ -1666,33 +1665,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 )*
Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit 48ca92b

Please sign in to comment.