Skip to content

Commit 828d468

Browse files
author
bors-servo
authored
Auto merge of #887 - photoszzt:derive_hash, r=fitzgen
Can derive hash analysis r? @fitzgen Fix: #876 I haven't enable too much test yet. Enable the option for all tests changes more than 200 of them. I want some input on which test is good to enable or write a new test.
2 parents b640094 + b4895d9 commit 828d468

File tree

165 files changed

+1344
-260
lines changed

Some content is hidden

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

165 files changed

+1344
-260
lines changed

src/codegen/mod.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use ir::comp::{Base, BitfieldUnit, Bitfield, CompInfo, CompKind, Field,
1313
FieldData, FieldMethods, Method, MethodKind};
1414
use ir::comment;
1515
use ir::context::{BindgenContext, ItemId};
16-
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
16+
use ir::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault, CanDeriveHash};
1717
use ir::dot;
1818
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
1919
use ir::function::{Abi, Function, FunctionSig};
@@ -1440,6 +1440,10 @@ impl CodeGenerator for CompInfo {
14401440
}
14411441
}
14421442

1443+
if item.can_derive_hash(ctx) {
1444+
derives.push("Hash");
1445+
}
1446+
14431447
if !derives.is_empty() {
14441448
attributes.push(attributes::derives(&derives))
14451449
}
@@ -3394,12 +3398,23 @@ mod utils {
33943398
)
33953399
.unwrap();
33963400

3401+
// The actual memory of the filed will be hashed, so that's why these
3402+
// field doesn't do anything with the hash.
3403+
let union_field_hash_impl = quote_item!(&ctx.ext_cx(),
3404+
impl<T> ::$prefix::hash::Hash for __BindgenUnionField<T> {
3405+
fn hash<H: ::$prefix::hash::Hasher>(&self, _state: &mut H) {
3406+
}
3407+
}
3408+
)
3409+
.unwrap();
3410+
33973411
let items = vec![union_field_decl,
33983412
union_field_impl,
33993413
union_field_default_impl,
34003414
union_field_clone_impl,
34013415
union_field_copy_impl,
3402-
union_field_debug_impl];
3416+
union_field_debug_impl,
3417+
union_field_hash_impl];
34033418

34043419
let old_items = mem::replace(result, items);
34053420
result.extend(old_items.into_iter());

src/ir/analysis/derive_hash.rs

+337
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
//! Determining which types for which we can emit `#[derive(Hash)]`.
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::CanTriviallyDeriveHash;
14+
use ir::comp::CompKind;
15+
16+
/// An analysis that finds for each IR item whether hash cannot be derived.
17+
///
18+
/// We use the monotone constraint function `cannot_derive_hash`, 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, hash 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+
/// hash.
26+
/// * If T is a type alias, a templated alias or an indirection to another type,
27+
/// hash cannot be derived if the type T refers to cannot be derived hash.
28+
/// * If T is a compound type, hash cannot be derived if any of its base member
29+
/// or field cannot be derived hash.
30+
/// * If T is a pointer, T cannot be derived hash if T is a function pointer
31+
/// and the function signature cannot be derived hash.
32+
/// * If T is an instantiation of an abstract template definition, T cannot be
33+
/// derived hash if any of the template arguments or template definition
34+
/// cannot derive hash.
35+
#[derive(Debug, Clone)]
36+
pub struct CannotDeriveHash<'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 hash.
43+
cannot_derive_hash: HashSet<ItemId>,
44+
45+
// Dependencies saying that if a key ItemId has been inserted into the
46+
// `cannot_derive_hash` 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 hash or not.
52+
dependencies: HashMap<ItemId, Vec<ItemId>>,
53+
}
54+
55+
impl<'ctx, 'gen> CannotDeriveHash<'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+
// hash 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_hash set", id);
81+
82+
let was_not_already_in_set = self.cannot_derive_hash.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 CannotDeriveHash<'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>) -> CannotDeriveHash<'ctx, 'gen> {
100+
let cannot_derive_hash = HashSet::new();
101+
let dependencies = generate_dependencies(ctx, Self::consider_edge);
102+
103+
CannotDeriveHash {
104+
ctx,
105+
cannot_derive_hash,
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_hash.contains(&id) {
118+
trace!(" already know it cannot derive Hash");
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+
if item.is_opaque(self.ctx, &()) {
132+
let layout_can_derive = ty.layout(self.ctx).map_or(true, |l| {
133+
l.opaque().can_trivially_derive_hash()
134+
});
135+
return if layout_can_derive {
136+
trace!(" we can trivially derive Hash for the layout");
137+
ConstrainResult::Same
138+
} else {
139+
trace!(" we cannot derive Hash for the layout");
140+
self.insert(id)
141+
};
142+
}
143+
144+
if ty.layout(self.ctx).map_or(false, |l| l.align > RUST_DERIVE_IN_ARRAY_LIMIT) {
145+
// We have to be conservative: the struct *could* have enough
146+
// padding that we emit an array that is longer than
147+
// `RUST_DERIVE_IN_ARRAY_LIMIT`. If we moved padding calculations
148+
// into the IR and computed them before this analysis, then we could
149+
// be precise rather than conservative here.
150+
return self.insert(id);
151+
}
152+
153+
match *ty.kind() {
154+
// Handle the simple cases. These can derive hash without further
155+
// information.
156+
TypeKind::Void |
157+
TypeKind::NullPtr |
158+
TypeKind::Int(..) |
159+
TypeKind::Enum(..) |
160+
TypeKind::Named |
161+
TypeKind::UnresolvedTypeRef(..) |
162+
TypeKind::BlockPointer |
163+
TypeKind::Reference(..) |
164+
TypeKind::ObjCInterface(..) |
165+
TypeKind::ObjCId |
166+
TypeKind::ObjCSel => {
167+
trace!(" simple type that can always derive Hash");
168+
ConstrainResult::Same
169+
}
170+
171+
TypeKind::Complex(..) |
172+
TypeKind::Float(..) => {
173+
trace!(" float cannot derive Hash");
174+
self.insert(id)
175+
}
176+
177+
TypeKind::Array(t, len) => {
178+
if self.cannot_derive_hash.contains(&t) {
179+
trace!(" arrays of T for which we cannot derive Hash \
180+
also cannot derive Hash");
181+
return self.insert(id);
182+
}
183+
184+
if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
185+
trace!(" array is small enough to derive Hash");
186+
ConstrainResult::Same
187+
} else {
188+
trace!(" array is too large to derive Hash");
189+
self.insert(id)
190+
}
191+
}
192+
193+
TypeKind::Pointer(inner) => {
194+
let inner_type = self.ctx.resolve_type(inner).canonical_type(self.ctx);
195+
if let TypeKind::Function(ref sig) = *inner_type.kind() {
196+
if !sig.can_trivially_derive_hash() {
197+
trace!(" function pointer that can't trivially derive Hash");
198+
return self.insert(id);
199+
}
200+
}
201+
trace!(" pointers can derive Hash");
202+
ConstrainResult::Same
203+
}
204+
205+
TypeKind::Function(ref sig) => {
206+
if !sig.can_trivially_derive_hash() {
207+
trace!(" function that can't trivially derive Hash");
208+
return self.insert(id);
209+
}
210+
trace!(" function can derive Hash");
211+
ConstrainResult::Same
212+
}
213+
214+
TypeKind::ResolvedTypeRef(t) |
215+
TypeKind::TemplateAlias(t, _) |
216+
TypeKind::Alias(t) => {
217+
if self.cannot_derive_hash.contains(&t) {
218+
trace!(" aliases and type refs to T which cannot derive \
219+
Hash also cannot derive Hash");
220+
self.insert(id)
221+
} else {
222+
trace!(" aliases and type refs to T which can derive \
223+
Hash can also derive Hash");
224+
ConstrainResult::Same
225+
}
226+
}
227+
228+
TypeKind::Comp(ref info) => {
229+
assert!(
230+
!info.has_non_type_template_params(),
231+
"The early ty.is_opaque check should have handled this case"
232+
);
233+
234+
if info.kind() == CompKind::Union {
235+
if self.ctx.options().unstable_rust {
236+
trace!(" cannot derive Hash for Rust unions");
237+
return self.insert(id);
238+
}
239+
240+
if ty.layout(self.ctx)
241+
.map_or(true,
242+
|l| l.opaque().can_trivially_derive_hash()) {
243+
trace!(" union layout can trivially derive Hash");
244+
return ConstrainResult::Same;
245+
} else {
246+
trace!(" union layout cannot derive Hash");
247+
return self.insert(id);
248+
}
249+
}
250+
251+
let bases_cannot_derive = info.base_members()
252+
.iter()
253+
.any(|base| !self.ctx.whitelisted_items().contains(&base.ty) ||
254+
self.cannot_derive_hash.contains(&base.ty));
255+
if bases_cannot_derive {
256+
trace!(" base members cannot derive Hash, so we can't \
257+
either");
258+
return self.insert(id);
259+
}
260+
261+
let fields_cannot_derive = info.fields()
262+
.iter()
263+
.any(|f| {
264+
match *f {
265+
Field::DataMember(ref data) => {
266+
!self.ctx.whitelisted_items().contains(&data.ty()) ||
267+
self.cannot_derive_hash.contains(&data.ty())
268+
}
269+
Field::Bitfields(ref bfu) => {
270+
bfu.bitfields()
271+
.iter().any(|b| {
272+
!self.ctx.whitelisted_items().contains(&b.ty()) ||
273+
self.cannot_derive_hash.contains(&b.ty())
274+
})
275+
}
276+
}
277+
});
278+
if fields_cannot_derive {
279+
trace!(" fields cannot derive Hash, so we can't either");
280+
return self.insert(id);
281+
}
282+
283+
trace!(" comp can derive Hash");
284+
ConstrainResult::Same
285+
}
286+
287+
TypeKind::TemplateInstantiation(ref template) => {
288+
let args_cannot_derive = template.template_arguments()
289+
.iter()
290+
.any(|arg| self.cannot_derive_hash.contains(&arg));
291+
if args_cannot_derive {
292+
trace!(" template args cannot derive Hash, so \
293+
insantiation can't either");
294+
return self.insert(id);
295+
}
296+
297+
assert!(
298+
!template.template_definition().is_opaque(self.ctx, &()),
299+
"The early ty.is_opaque check should have handled this case"
300+
);
301+
let def_cannot_derive = self.cannot_derive_hash
302+
.contains(&template.template_definition());
303+
if def_cannot_derive {
304+
trace!(" template definition cannot derive Hash, so \
305+
insantiation can't either");
306+
return self.insert(id);
307+
}
308+
309+
trace!(" template instantiation can derive Hash");
310+
ConstrainResult::Same
311+
}
312+
313+
TypeKind::Opaque => {
314+
unreachable!(
315+
"The early ty.is_opaque check should have handled this case"
316+
)
317+
}
318+
}
319+
}
320+
321+
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
322+
where F: FnMut(ItemId),
323+
{
324+
if let Some(edges) = self.dependencies.get(&id) {
325+
for item in edges {
326+
trace!("enqueue {:?} into worklist", item);
327+
f(*item);
328+
}
329+
}
330+
}
331+
}
332+
333+
impl<'ctx, 'gen> From<CannotDeriveHash<'ctx, 'gen>> for HashSet<ItemId> {
334+
fn from(analysis: CannotDeriveHash<'ctx, 'gen>) -> Self {
335+
analysis.cannot_derive_hash
336+
}
337+
}

src/ir/analysis/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ mod derive_copy;
5151
pub use self::derive_copy::CannotDeriveCopy;
5252
mod has_type_param_in_array;
5353
pub use self::has_type_param_in_array::HasTypeParameterInArray;
54+
mod derive_hash;
55+
pub use self::derive_hash::CannotDeriveHash;
5456

5557
use ir::context::{BindgenContext, ItemId};
5658
use ir::traversal::{EdgeKind, Trace};

0 commit comments

Comments
 (0)