Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derive partialeq "manually" when possible #1012

Merged
merged 4 commits into from
Oct 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 [..]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, we don't even need a loop ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was inspired by rustc itself (Just looked how it handles cases below 32 😄 )

}
},

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 @@ -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};
Expand Down Expand Up @@ -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));
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -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 )*
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