Skip to content

Commit 7e84c43

Browse files
author
bors-servo
authored
Auto merge of #907 - photoszzt:derive_partial_eq, r=fitzgen
Derive PartialEq Analysis Fix: #878 r? @fitzgen
2 parents 5ca0569 + dc2baed commit 7e84c43

File tree

193 files changed

+946
-414
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

193 files changed

+946
-414
lines changed

src/codegen/mod.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use ir::comment;
1414
use ir::comp::{Base, Bitfield, BitfieldUnit, CompInfo, CompKind, Field,
1515
FieldData, FieldMethods, Method, MethodKind};
1616
use ir::context::{BindgenContext, ItemId};
17-
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash};
17+
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash, CanDerivePartialEq};
1818
use ir::dot;
1919
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
2020
use ir::function::{Abi, Function, FunctionSig};
@@ -1495,6 +1495,10 @@ impl CodeGenerator for CompInfo {
14951495
derives.push("Hash");
14961496
}
14971497

1498+
if item.can_derive_partialeq(ctx) {
1499+
derives.push("PartialEq");
1500+
}
1501+
14981502
if !derives.is_empty() {
14991503
attributes.push(attributes::derives(&derives))
15001504
}
@@ -3569,13 +3573,23 @@ mod utils {
35693573
)
35703574
.unwrap();
35713575

3576+
let union_field_partialeq_impl = quote_item!(&ctx.ext_cx(),
3577+
impl<T> ::$prefix::cmp::PartialEq for __BindgenUnionField<T> {
3578+
fn eq(&self, _other: &__BindgenUnionField<T>) -> bool {
3579+
true
3580+
}
3581+
}
3582+
)
3583+
.unwrap();
3584+
35723585
let items = vec![union_field_decl,
35733586
union_field_impl,
35743587
union_field_default_impl,
35753588
union_field_clone_impl,
35763589
union_field_copy_impl,
35773590
union_field_debug_impl,
3578-
union_field_hash_impl];
3591+
union_field_hash_impl,
3592+
union_field_partialeq_impl];
35793593

35803594
let old_items = mem::replace(result, items);
35813595
result.extend(old_items.into_iter());

src/ir/analysis/derive_partial_eq.rs

+334
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
//! Determining which types for which we can emit `#[derive(PartialEq)]`.
2+
3+
use super::{ConstrainResult, MonotoneFramework, generate_dependencies};
4+
use std::collections::HashSet;
5+
use std::collections::HashMap;
6+
use ir::context::{BindgenContext, ItemId};
7+
use ir::item::IsOpaque;
8+
use ir::traversal::EdgeKind;
9+
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
10+
use ir::ty::TypeKind;
11+
use ir::comp::Field;
12+
use ir::comp::FieldMethods;
13+
use ir::derive::CanTriviallyDerivePartialEq;
14+
use ir::comp::CompKind;
15+
16+
/// An analysis that finds for each IR item whether partialeq cannot be derived.
17+
///
18+
/// We use the monotone constraint function `cannot_derive_partial_eq`, defined as
19+
/// follows:
20+
///
21+
/// * If T is Opaque and layout of the type is known, get this layout as opaque
22+
/// type and check whether it can be derived using trivial checks.
23+
/// * If T is Array type, partialeq cannot be derived if the length of the array is
24+
/// larger than the limit or the type of data the array contains cannot derive
25+
/// partialeq.
26+
/// * If T is a type alias, a templated alias or an indirection to another type,
27+
/// partialeq cannot be derived if the type T refers to cannot be derived partialeq.
28+
/// * If T is a compound type, partialeq cannot be derived if any of its base member
29+
/// or field cannot be derived partialeq.
30+
/// * If T is a pointer, T cannot be derived partialeq if T is a function pointer
31+
/// and the function signature cannot be derived partialeq.
32+
/// * If T is an instantiation of an abstract template definition, T cannot be
33+
/// derived partialeq if any of the template arguments or template definition
34+
/// cannot derive partialeq.
35+
#[derive(Debug, Clone)]
36+
pub struct CannotDerivePartialEq<'ctx, 'gen>
37+
where 'gen: 'ctx
38+
{
39+
ctx: &'ctx BindgenContext<'gen>,
40+
41+
// The incremental result of this analysis's computation. Everything in this
42+
// set cannot derive partialeq.
43+
cannot_derive_partialeq: HashSet<ItemId>,
44+
45+
// Dependencies saying that if a key ItemId has been inserted into the
46+
// `cannot_derive_partialeq` set, then each of the ids in Vec<ItemId> need to be
47+
// considered again.
48+
//
49+
// This is a subset of the natural IR graph with reversed edges, where we
50+
// only include the edges from the IR graph that can affect whether a type
51+
// can derive partialeq or not.
52+
dependencies: HashMap<ItemId, Vec<ItemId>>,
53+
}
54+
55+
impl<'ctx, 'gen> CannotDerivePartialEq<'ctx, 'gen> {
56+
fn consider_edge(kind: EdgeKind) -> bool {
57+
match kind {
58+
// These are the only edges that can affect whether a type can derive
59+
// partialeq or not.
60+
EdgeKind::BaseMember |
61+
EdgeKind::Field |
62+
EdgeKind::TypeReference |
63+
EdgeKind::VarType |
64+
EdgeKind::TemplateArgument |
65+
EdgeKind::TemplateDeclaration |
66+
EdgeKind::TemplateParameterDefinition => true,
67+
68+
EdgeKind::Constructor |
69+
EdgeKind::Destructor |
70+
EdgeKind::FunctionReturn |
71+
EdgeKind::FunctionParameter |
72+
EdgeKind::InnerType |
73+
EdgeKind::InnerVar |
74+
EdgeKind::Method => false,
75+
EdgeKind::Generic => false,
76+
}
77+
}
78+
79+
fn insert(&mut self, id: ItemId) -> ConstrainResult {
80+
trace!("inserting {:?} into the cannot_derive_partialeq set", id);
81+
82+
let was_not_already_in_set = self.cannot_derive_partialeq.insert(id);
83+
assert!(
84+
was_not_already_in_set,
85+
"We shouldn't try and insert {:?} twice because if it was \
86+
already in the set, `constrain` should have exited early.",
87+
id
88+
);
89+
90+
ConstrainResult::Changed
91+
}
92+
}
93+
94+
impl<'ctx, 'gen> MonotoneFramework for CannotDerivePartialEq<'ctx, 'gen> {
95+
type Node = ItemId;
96+
type Extra = &'ctx BindgenContext<'gen>;
97+
type Output = HashSet<ItemId>;
98+
99+
fn new(ctx: &'ctx BindgenContext<'gen>) -> CannotDerivePartialEq<'ctx, 'gen> {
100+
let cannot_derive_partialeq = HashSet::new();
101+
let dependencies = generate_dependencies(ctx, Self::consider_edge);
102+
103+
CannotDerivePartialEq {
104+
ctx,
105+
cannot_derive_partialeq,
106+
dependencies,
107+
}
108+
}
109+
110+
fn initial_worklist(&self) -> Vec<ItemId> {
111+
self.ctx.whitelisted_items().iter().cloned().collect()
112+
}
113+
114+
fn constrain(&mut self, id: ItemId) -> ConstrainResult {
115+
trace!("constrain: {:?}", id);
116+
117+
if self.cannot_derive_partialeq.contains(&id) {
118+
trace!(" already know it cannot derive PartialEq");
119+
return ConstrainResult::Same;
120+
}
121+
122+
let item = self.ctx.resolve_item(id);
123+
let ty = match item.as_type() {
124+
Some(ty) => ty,
125+
None => {
126+
trace!(" not a type; ignoring");
127+
return ConstrainResult::Same;
128+
}
129+
};
130+
131+
trace!("ty: {:?}", ty);
132+
if item.is_opaque(self.ctx, &()) {
133+
let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| {
134+
l.opaque().can_trivially_derive_partialeq()
135+
});
136+
return if layout_can_derive {
137+
trace!(" we can trivially derive PartialEq for the layout");
138+
ConstrainResult::Same
139+
} else {
140+
trace!(" we cannot derive PartialEq for the layout");
141+
self.insert(id)
142+
};
143+
}
144+
145+
if ty.layout(self.ctx).map_or(false, |l| l.align > RUST_DERIVE_IN_ARRAY_LIMIT) {
146+
// We have to be conservative: the struct *could* have enough
147+
// padding that we emit an array that is longer than
148+
// `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
149+
// into the IR and computed them before this analysis, then we could
150+
// be precise rather than conservative here.
151+
return self.insert(id);
152+
}
153+
154+
match *ty.kind() {
155+
// Handle the simple cases. These can derive partialeq without further
156+
// information.
157+
TypeKind::Void |
158+
TypeKind::NullPtr |
159+
TypeKind::Int(..) |
160+
TypeKind::Complex(..) |
161+
TypeKind::Float(..) |
162+
TypeKind::Enum(..) |
163+
TypeKind::Named |
164+
TypeKind::UnresolvedTypeRef(..) |
165+
TypeKind::BlockPointer |
166+
TypeKind::Reference(..) |
167+
TypeKind::ObjCInterface(..) |
168+
TypeKind::ObjCId |
169+
TypeKind::ObjCSel => {
170+
trace!(" simple type that can always derive PartialEq");
171+
ConstrainResult::Same
172+
}
173+
174+
TypeKind::Array(t, len) => {
175+
if self.cannot_derive_partialeq.contains(&t) {
176+
trace!(" arrays of T for which we cannot derive PartialEq \
177+
also cannot derive PartialEq");
178+
return self.insert(id);
179+
}
180+
181+
if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
182+
trace!(" array is small enough to derive PartialEq");
183+
ConstrainResult::Same
184+
} else {
185+
trace!(" array is too large to derive PartialEq");
186+
self.insert(id)
187+
}
188+
}
189+
190+
TypeKind::Pointer(inner) => {
191+
let inner_type = self.ctx.resolve_type(inner).canonical_type(self.ctx);
192+
if let TypeKind::Function(ref sig) = *inner_type.kind() {
193+
if !sig.can_trivially_derive_partialeq() {
194+
trace!(" function pointer that can't trivially derive PartialEq");
195+
return self.insert(id);
196+
}
197+
}
198+
trace!(" pointers can derive PartialEq");
199+
ConstrainResult::Same
200+
}
201+
202+
TypeKind::Function(ref sig) => {
203+
if !sig.can_trivially_derive_partialeq() {
204+
trace!(" function that can't trivially derive PartialEq");
205+
return self.insert(id);
206+
}
207+
trace!(" function can derive PartialEq");
208+
ConstrainResult::Same
209+
}
210+
211+
TypeKind::ResolvedTypeRef(t) |
212+
TypeKind::TemplateAlias(t, _) |
213+
TypeKind::Alias(t) => {
214+
if self.cannot_derive_partialeq.contains(&t) {
215+
trace!(" aliases and type refs to T which cannot derive \
216+
PartialEq also cannot derive PartialEq");
217+
self.insert(id)
218+
} else {
219+
trace!(" aliases and type refs to T which can derive \
220+
PartialEq can also derive PartialEq");
221+
ConstrainResult::Same
222+
}
223+
}
224+
225+
TypeKind::Comp(ref info) => {
226+
assert!(
227+
!info.has_non_type_template_params(),
228+
"The early ty.is_opaque check should have handled this case"
229+
);
230+
231+
if info.kind() == CompKind::Union {
232+
if self.ctx.options().rust_features().untagged_union() {
233+
trace!(" cannot derive PartialEq for Rust unions");
234+
return self.insert(id);
235+
}
236+
237+
if ty.layout(self.ctx)
238+
.map_or(true,
239+
|l| l.opaque().can_trivially_derive_partialeq()) {
240+
trace!(" union layout can trivially derive PartialEq");
241+
return ConstrainResult::Same;
242+
} else {
243+
trace!(" union layout cannot derive PartialEq");
244+
return self.insert(id);
245+
}
246+
}
247+
248+
let bases_cannot_derive = info.base_members()
249+
.iter()
250+
.any(|base| !self.ctx.whitelisted_items().contains(&base.ty) ||
251+
self.cannot_derive_partialeq.contains(&base.ty));
252+
if bases_cannot_derive {
253+
trace!(" base members cannot derive PartialEq, so we can't \
254+
either");
255+
return self.insert(id);
256+
}
257+
258+
let fields_cannot_derive = info.fields()
259+
.iter()
260+
.any(|f| {
261+
match *f {
262+
Field::DataMember(ref data) => {
263+
!self.ctx.whitelisted_items().contains(&data.ty()) ||
264+
self.cannot_derive_partialeq.contains(&data.ty())
265+
}
266+
Field::Bitfields(ref bfu) => {
267+
bfu.bitfields()
268+
.iter().any(|b| {
269+
!self.ctx.whitelisted_items().contains(&b.ty()) ||
270+
self.cannot_derive_partialeq.contains(&b.ty())
271+
})
272+
}
273+
}
274+
});
275+
if fields_cannot_derive {
276+
trace!(" fields cannot derive PartialEq, so we can't either");
277+
return self.insert(id);
278+
}
279+
280+
trace!(" comp can derive PartialEq");
281+
ConstrainResult::Same
282+
}
283+
284+
TypeKind::TemplateInstantiation(ref template) => {
285+
let args_cannot_derive = template.template_arguments()
286+
.iter()
287+
.any(|arg| self.cannot_derive_partialeq.contains(&arg));
288+
if args_cannot_derive {
289+
trace!(" template args cannot derive PartialEq, so \
290+
insantiation can't either");
291+
return self.insert(id);
292+
}
293+
294+
assert!(
295+
!template.template_definition().is_opaque(self.ctx, &()),
296+
"The early ty.is_opaque check should have handled this case"
297+
);
298+
let def_cannot_derive = self.cannot_derive_partialeq
299+
.contains(&template.template_definition());
300+
if def_cannot_derive {
301+
trace!(" template definition cannot derive PartialEq, so \
302+
insantiation can't either");
303+
return self.insert(id);
304+
}
305+
306+
trace!(" template instantiation can derive PartialEq");
307+
ConstrainResult::Same
308+
}
309+
310+
TypeKind::Opaque => {
311+
unreachable!(
312+
"The early ty.is_opaque check should have handled this case"
313+
)
314+
}
315+
}
316+
}
317+
318+
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
319+
where F: FnMut(ItemId),
320+
{
321+
if let Some(edges) = self.dependencies.get(&id) {
322+
for item in edges {
323+
trace!("enqueue {:?} into worklist", item);
324+
f(*item);
325+
}
326+
}
327+
}
328+
}
329+
330+
impl<'ctx, 'gen> From<CannotDerivePartialEq<'ctx, 'gen>> for HashSet<ItemId> {
331+
fn from(analysis: CannotDerivePartialEq<'ctx, 'gen>) -> Self {
332+
analysis.cannot_derive_partialeq
333+
}
334+
}

src/ir/analysis/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ mod has_type_param_in_array;
5353
pub use self::has_type_param_in_array::HasTypeParameterInArray;
5454
mod derive_hash;
5555
pub use self::derive_hash::CannotDeriveHash;
56+
mod derive_partial_eq;
57+
pub use self::derive_partial_eq::CannotDerivePartialEq;
5658

5759
use ir::context::{BindgenContext, ItemId};
5860
use ir::traversal::{EdgeKind, Trace};

0 commit comments

Comments
 (0)