Skip to content

Commit d1d3500

Browse files
author
bors-servo
authored
Auto merge of #861 - photoszzt:derive_default, r=fitzgen
Can derive default analysis r? @fitzgen
2 parents 4f92dd4 + 0231a21 commit d1d3500

18 files changed

+427
-187
lines changed

src/codegen/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,7 @@ impl CodeGenerator for CompInfo {
14181418
derives.push("Debug");
14191419
}
14201420

1421-
if item.can_derive_default(ctx, ()) {
1421+
if item.can_derive_default(ctx) {
14221422
derives.push("Default");
14231423
} else {
14241424
needs_default_impl = ctx.options().derive_default;

src/ir/analysis/derive_default.rs

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

src/ir/analysis/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub use self::derive_debug::CannotDeriveDebug;
4545
mod has_vtable;
4646
pub use self::has_vtable::HasVtableAnalysis;
4747
pub use self::has_vtable::HasVtable;
48+
mod derive_default;
49+
pub use self::derive_default::CannotDeriveDefault;
4850

4951
use ir::context::{BindgenContext, ItemId};
5052
use ir::traversal::{EdgeKind, Trace};

0 commit comments

Comments
 (0)