Skip to content

Commit 4e74c39

Browse files
author
bors-servo
authored
Auto merge of #1012 - pepyakin:derive-partialeq-manually, r=fitzgen
Derive partialeq "manually" when possible Fixes #879 r? @fitzgen
2 parents c66598d + 617c55b commit 4e74c39

36 files changed

+2023
-690
lines changed

src/codegen/impl_partialeq.rs

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
2+
use ir::comp::{CompInfo, CompKind, Field, FieldMethods};
3+
use ir::context::BindgenContext;
4+
use ir::item::{IsOpaque, Item};
5+
use ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
6+
use quote;
7+
8+
/// Generate a manual implementation of `PartialEq` trait for the
9+
/// specified compound type.
10+
pub fn gen_partialeq_impl(
11+
ctx: &BindgenContext,
12+
comp_info: &CompInfo,
13+
item: &Item,
14+
ty_for_impl: &quote::Tokens,
15+
) -> Option<quote::Tokens> {
16+
let mut tokens = vec![];
17+
18+
if item.is_opaque(ctx, &()) {
19+
tokens.push(quote! {
20+
&self._bindgen_opaque_blob[..] == &other._bindgen_opaque_blob[..]
21+
});
22+
} else if comp_info.kind() == CompKind::Union {
23+
assert!(!ctx.options().rust_features().untagged_union());
24+
tokens.push(quote! {
25+
&self.bindgen_union_field[..] == &other.bindgen_union_field[..]
26+
});
27+
} else {
28+
for base in comp_info.base_members().iter() {
29+
if !base.requires_storage(ctx) {
30+
continue;
31+
}
32+
33+
let ty_item = ctx.resolve_item(base.ty);
34+
let field_name = &base.field_name;
35+
36+
if ty_item.is_opaque(ctx, &()) {
37+
let field_name = ctx.rust_ident(field_name);
38+
tokens.push(quote! {
39+
&self. #field_name [..] == &other. #field_name [..]
40+
});
41+
} else {
42+
tokens.push(gen_field(ctx, ty_item, field_name));
43+
}
44+
}
45+
46+
for field in comp_info.fields() {
47+
match *field {
48+
Field::DataMember(ref fd) => {
49+
let ty_item = ctx.resolve_item(fd.ty());
50+
let name = fd.name().unwrap();
51+
tokens.push(gen_field(ctx, ty_item, name));
52+
}
53+
Field::Bitfields(ref bu) => for bitfield in bu.bitfields() {
54+
let name_ident = ctx.rust_ident_raw(bitfield.name());
55+
tokens.push(quote! {
56+
self.#name_ident () == other.#name_ident ()
57+
});
58+
},
59+
}
60+
}
61+
}
62+
63+
Some(quote! {
64+
fn eq(&self, other: & #ty_for_impl) -> bool {
65+
#( #tokens )&&*
66+
}
67+
})
68+
}
69+
70+
fn gen_field(ctx: &BindgenContext, ty_item: &Item, name: &str) -> quote::Tokens {
71+
fn quote_equals(name_ident: quote::Ident) -> quote::Tokens {
72+
quote! { self.#name_ident == other.#name_ident }
73+
}
74+
75+
let name_ident = ctx.rust_ident(name);
76+
let ty = ty_item.expect_type();
77+
78+
match *ty.kind() {
79+
TypeKind::Void |
80+
TypeKind::NullPtr |
81+
TypeKind::Int(..) |
82+
TypeKind::Complex(..) |
83+
TypeKind::Float(..) |
84+
TypeKind::Enum(..) |
85+
TypeKind::TypeParam |
86+
TypeKind::UnresolvedTypeRef(..) |
87+
TypeKind::BlockPointer |
88+
TypeKind::Reference(..) |
89+
TypeKind::ObjCInterface(..) |
90+
TypeKind::ObjCId |
91+
TypeKind::ObjCSel |
92+
TypeKind::Comp(..) |
93+
TypeKind::Pointer(_) |
94+
TypeKind::Function(..) |
95+
TypeKind::Opaque => quote_equals(name_ident),
96+
97+
TypeKind::TemplateInstantiation(ref inst) => {
98+
if inst.is_opaque(ctx, &ty_item) {
99+
quote! {
100+
&self. #name_ident [..] == &other. #name_ident [..]
101+
}
102+
} else {
103+
quote_equals(name_ident)
104+
}
105+
}
106+
107+
TypeKind::Array(_, len) => if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
108+
quote_equals(name_ident)
109+
} else {
110+
quote! {
111+
&self. #name_ident [..] == &other. #name_ident [..]
112+
}
113+
},
114+
115+
TypeKind::ResolvedTypeRef(t) |
116+
TypeKind::TemplateAlias(t, _) |
117+
TypeKind::Alias(t) => {
118+
let inner_item = ctx.resolve_item(t);
119+
gen_field(ctx, inner_item, name)
120+
}
121+
}
122+
}

src/codegen/mod.rs

+56-35
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod impl_debug;
2+
mod impl_partialeq;
23
mod error;
34
mod helpers;
45
pub mod struct_layout;
@@ -14,7 +15,7 @@ use ir::comp::{Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field,
1415
use ir::context::{BindgenContext, ItemId};
1516
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault,
1617
CanDeriveHash, CanDerivePartialOrd, CanDeriveOrd,
17-
CanDerivePartialEq, CanDeriveEq};
18+
CanDerivePartialEq, CanDeriveEq, CannotDeriveReason};
1819
use ir::dot;
1920
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
2021
use ir::function::{Abi, Function, FunctionSig};
@@ -1420,6 +1421,7 @@ impl CodeGenerator for CompInfo {
14201421
let mut needs_clone_impl = false;
14211422
let mut needs_default_impl = false;
14221423
let mut needs_debug_impl = false;
1424+
let mut needs_partialeq_impl = false;
14231425
if let Some(comment) = item.comment(ctx) {
14241426
attributes.push(attributes::doc(comment));
14251427
}
@@ -1475,6 +1477,14 @@ impl CodeGenerator for CompInfo {
14751477

14761478
if item.can_derive_partialeq(ctx) {
14771479
derives.push("PartialEq");
1480+
} else {
1481+
needs_partialeq_impl =
1482+
ctx.options().derive_partialeq &&
1483+
ctx.options().impl_partialeq &&
1484+
ctx.lookup_can_derive_partialeq_or_partialord(item.id())
1485+
.map_or(true, |x| {
1486+
x == CannotDeriveReason::ArrayTooLarge
1487+
});
14781488
}
14791489

14801490
if item.can_derive_eq(ctx) {
@@ -1535,25 +1545,14 @@ impl CodeGenerator for CompInfo {
15351545
}
15361546

15371547
for base in self.base_members() {
1538-
// Virtual bases are already taken into account by the vtable
1539-
// pointer.
1540-
//
1541-
// FIXME(emilio): Is this always right?
1542-
if base.is_virtual() {
1543-
continue;
1544-
}
1545-
1546-
let base_ty = ctx.resolve_type(base.ty);
1547-
// NB: We won't include unsized types in our base chain because they
1548-
// would contribute to our size given the dummy field we insert for
1549-
// unsized types.
1550-
if base_ty.is_unsized(ctx, base.ty) {
1548+
if !base.requires_storage(ctx) {
15511549
continue;
15521550
}
15531551

15541552
let inner = base.ty.to_rust_ty_or_opaque(ctx, &());
15551553
let field_name = ctx.rust_ident(&base.field_name);
15561554

1555+
let base_ty = ctx.resolve_type(base.ty);
15571556
struct_layout.saw_base(base_ty);
15581557

15591558
fields.push(quote! {
@@ -1667,33 +1666,34 @@ impl CodeGenerator for CompInfo {
16671666
}
16681667
}
16691668

1670-
let mut generics = quote! {};
1669+
let mut generic_param_names = vec![];
16711670

16721671
if let Some(ref params) = used_template_params {
1673-
if !params.is_empty() {
1674-
let mut param_names = vec![];
1672+
for (idx, ty) in params.iter().enumerate() {
1673+
let param = ctx.resolve_type(*ty);
1674+
let name = param.name().unwrap();
1675+
let ident = ctx.rust_ident(name);
1676+
generic_param_names.push(ident.clone());
16751677

1676-
for (idx, ty) in params.iter().enumerate() {
1677-
let param = ctx.resolve_type(*ty);
1678-
let name = param.name().unwrap();
1679-
let ident = ctx.rust_ident(name);
1680-
param_names.push(ident.clone());
1681-
1682-
let prefix = ctx.trait_prefix();
1683-
let field_name = ctx.rust_ident(format!("_phantom_{}", idx));
1684-
fields.push(quote! {
1685-
pub #field_name : ::#prefix::marker::PhantomData<
1686-
::#prefix::cell::UnsafeCell<#ident>
1687-
> ,
1688-
});
1689-
}
1690-
1691-
generics = quote! {
1692-
< #( #param_names ),* >
1693-
};
1678+
let prefix = ctx.trait_prefix();
1679+
let field_name = ctx.rust_ident(format!("_phantom_{}", idx));
1680+
fields.push(quote! {
1681+
pub #field_name : ::#prefix::marker::PhantomData<
1682+
::#prefix::cell::UnsafeCell<#ident>
1683+
> ,
1684+
});
16941685
}
16951686
}
16961687

1688+
let generics = if !generic_param_names.is_empty() {
1689+
let generic_param_names = generic_param_names.clone();
1690+
quote! {
1691+
< #( #generic_param_names ),* >
1692+
}
1693+
} else {
1694+
quote! { }
1695+
};
1696+
16971697
tokens.append(quote! {
16981698
#generics {
16991699
#( #fields )*
@@ -1896,6 +1896,27 @@ impl CodeGenerator for CompInfo {
18961896
});
18971897
}
18981898

1899+
if needs_partialeq_impl {
1900+
if let Some(impl_) = impl_partialeq::gen_partialeq_impl(ctx, self, item, &ty_for_impl) {
1901+
1902+
let partialeq_bounds = if !generic_param_names.is_empty() {
1903+
let bounds = generic_param_names.iter().map(|t| {
1904+
quote! { #t: PartialEq }
1905+
});
1906+
quote! { where #( #bounds ),* }
1907+
} else {
1908+
quote! { }
1909+
};
1910+
1911+
let prefix = ctx.trait_prefix();
1912+
result.push(quote! {
1913+
impl #generics ::#prefix::cmp::PartialEq for #ty_for_impl #partialeq_bounds {
1914+
#impl_
1915+
}
1916+
});
1917+
}
1918+
}
1919+
18991920
if !methods.is_empty() {
19001921
result.push(quote! {
19011922
impl #generics #ty_for_impl {

0 commit comments

Comments
 (0)