Skip to content

Commit 8f43c93

Browse files
author
bors-servo
authored
Auto merge of #824 - photoszzt:can_drive_dbg, r=fitzgen
Use fix point analysis to implement can_derive_debug It's failing about 30 tests now and most of them is related to template. I'm also not so sure about the place to call compute_can_derive_debug in gen. Fix: #767 r? @fitzgen
2 parents 781385f + 01c609c commit 8f43c93

11 files changed

+360
-158
lines changed

src/ir/cant_derive_debug.rs

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
//! Determining which types for which we can emit `#[derive(Debug)]`.
2+
use super::analysis::MonotoneFramework;
3+
use ir::context::{BindgenContext, ItemId};
4+
use ir::item::ItemSet;
5+
use std::collections::HashSet;
6+
use std::collections::HashMap;
7+
use ir::traversal::EdgeKind;
8+
use ir::ty::RUST_DERIVE_IN_ARRAY_LIMIT;
9+
use ir::ty::TypeKind;
10+
use ir::comp::Field;
11+
use ir::traversal::Trace;
12+
use ir::comp::FieldMethods;
13+
use ir::layout::Layout;
14+
use ir::derive::CanTriviallyDeriveDebug;
15+
use ir::comp::CompKind;
16+
17+
/// An analysis that finds for each IR item whether debug cannot be derived.
18+
///
19+
/// We use the monotone constraint function `cant_derive_debug`, defined as
20+
/// follows:
21+
///
22+
/// * If T is Opaque and layout of the type is known, get this layout as opaque
23+
/// type and check whether it can be derived using trivial checks.
24+
/// * If T is Array type, debug cannot be derived if the length of the array is
25+
/// larger than the limit or the type of data the array contains cannot derive
26+
/// debug.
27+
/// * If T is a type alias, a templated alias or an indirection to another type,
28+
/// debug cannot be derived if the type T refers to cannot be derived debug.
29+
/// * If T is a compound type, debug cannot be derived if any of its base member
30+
/// or field cannot be derived debug.
31+
/// * If T is a pointer, T cannot be derived debug if T is a function pointer
32+
/// and the function signature cannot be derived debug.
33+
/// * If T is an instantiation of an abstract template definition, T cannot be
34+
/// derived debug if any of the template arguments or template definition
35+
/// cannot derive debug.
36+
#[derive(Debug, Clone)]
37+
pub struct CantDeriveDebugAnalysis<'ctx, 'gen>
38+
where 'gen: 'ctx
39+
{
40+
ctx: &'ctx BindgenContext<'gen>,
41+
42+
// The incremental result of this analysis's computation. Everything in this
43+
// set cannot derive debug.
44+
cant_derive_debug: HashSet<ItemId>,
45+
46+
// Dependencies saying that if a key ItemId has been inserted into the
47+
// `cant_derive_debug` set, then each of the ids in Vec<ItemId> need to be
48+
// considered again.
49+
//
50+
// This is a subset of the natural IR graph with reversed edges, where we
51+
// only include the edges from the IR graph that can affect whether a type
52+
// can derive debug or not.
53+
dependencies: HashMap<ItemId, Vec<ItemId>>,
54+
}
55+
56+
impl<'ctx, 'gen> CantDeriveDebugAnalysis<'ctx, 'gen> {
57+
fn consider_edge(kind: EdgeKind) -> bool {
58+
match kind {
59+
// These are the only edges that can affect whether a type can derive
60+
// debug or not.
61+
EdgeKind::BaseMember |
62+
EdgeKind::Field |
63+
EdgeKind::TypeReference |
64+
EdgeKind::VarType |
65+
EdgeKind::TemplateArgument |
66+
EdgeKind::TemplateDeclaration |
67+
EdgeKind::TemplateParameterDefinition => true,
68+
69+
EdgeKind::Constructor |
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) -> bool {
80+
let was_already_in = self.cant_derive_debug.insert(id);
81+
assert!(
82+
was_already_in,
83+
format!("We shouldn't try and insert twice because if it was already in the set, \
84+
`constrain` would have exited early.: {:?}", id)
85+
);
86+
true
87+
}
88+
}
89+
90+
impl<'ctx, 'gen> MonotoneFramework for CantDeriveDebugAnalysis<'ctx, 'gen> {
91+
type Node = ItemId;
92+
type Extra = &'ctx BindgenContext<'gen>;
93+
type Output = HashSet<ItemId>;
94+
95+
fn new(ctx: &'ctx BindgenContext<'gen>) -> CantDeriveDebugAnalysis<'ctx, 'gen> {
96+
let cant_derive_debug = HashSet::new();
97+
let mut dependencies = HashMap::new();
98+
let whitelisted_items: HashSet<_> = ctx.whitelisted_items().collect();
99+
100+
let whitelisted_and_blacklisted_items: ItemSet = whitelisted_items.iter()
101+
.cloned()
102+
.flat_map(|i| {
103+
let mut reachable = vec![i];
104+
i.trace(ctx, &mut |s, _| {
105+
reachable.push(s);
106+
}, &());
107+
reachable
108+
})
109+
.collect();
110+
111+
for item in whitelisted_and_blacklisted_items {
112+
dependencies.entry(item).or_insert(vec![]);
113+
114+
{
115+
// We reverse our natural IR graph edges to find dependencies
116+
// between nodes.
117+
item.trace(ctx, &mut |sub_item: ItemId, edge_kind| {
118+
if Self::consider_edge(edge_kind) {
119+
dependencies.entry(sub_item)
120+
.or_insert(vec![])
121+
.push(item);
122+
}
123+
}, &());
124+
}
125+
}
126+
127+
CantDeriveDebugAnalysis {
128+
ctx: ctx,
129+
cant_derive_debug: cant_derive_debug,
130+
dependencies: dependencies,
131+
}
132+
}
133+
134+
fn initial_worklist(&self) -> Vec<ItemId> {
135+
self.ctx.whitelisted_items().collect()
136+
}
137+
138+
fn constrain(&mut self, id: ItemId) -> bool {
139+
if self.cant_derive_debug.contains(&id) {
140+
return false;
141+
}
142+
143+
let item = self.ctx.resolve_item(id);
144+
let ty = match item.as_type() {
145+
None => return false,
146+
Some(ty) => ty
147+
};
148+
149+
match *ty.kind() {
150+
// handle the simple case
151+
// These can derive debug without further information
152+
TypeKind::Void |
153+
TypeKind::NullPtr |
154+
TypeKind::Int(..) |
155+
TypeKind::Float(..) |
156+
TypeKind::Complex(..) |
157+
TypeKind::Function(..) |
158+
TypeKind::Enum(..) |
159+
TypeKind::Reference(..) |
160+
TypeKind::BlockPointer |
161+
TypeKind::Named |
162+
TypeKind::UnresolvedTypeRef(..) |
163+
TypeKind::ObjCInterface(..) |
164+
TypeKind::ObjCId |
165+
TypeKind::ObjCSel => {
166+
return false;
167+
},
168+
TypeKind::Opaque => {
169+
if ty.layout(self.ctx)
170+
.map_or(true, |l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
171+
return false;
172+
} else {
173+
return self.insert(id);
174+
}
175+
},
176+
TypeKind::Array(t, len) => {
177+
if len <= RUST_DERIVE_IN_ARRAY_LIMIT {
178+
if self.cant_derive_debug.contains(&t) {
179+
return self.insert(id);
180+
}
181+
return false;
182+
} else {
183+
return self.insert(id);
184+
}
185+
},
186+
TypeKind::ResolvedTypeRef(t) |
187+
TypeKind::TemplateAlias(t, _) |
188+
TypeKind::Alias(t) => {
189+
if self.cant_derive_debug.contains(&t) {
190+
return self.insert(id);
191+
}
192+
return false;
193+
},
194+
TypeKind::Comp(ref info) => {
195+
if info.has_non_type_template_params() {
196+
if ty.layout(self.ctx).map_or(true,
197+
|l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
198+
return false;
199+
} else {
200+
return self.insert(id);
201+
}
202+
}
203+
if info.kind() == CompKind::Union {
204+
if self.ctx.options().unstable_rust {
205+
return self.insert(id);
206+
}
207+
208+
if ty.layout(self.ctx).map_or(true,
209+
|l| l.opaque().can_trivially_derive_debug(self.ctx, ())) {
210+
return false;
211+
} else {
212+
return self.insert(id);
213+
}
214+
}
215+
let bases_cant_derive = info.base_members()
216+
.iter()
217+
.any(|base| self.cant_derive_debug.contains(&base.ty));
218+
if bases_cant_derive {
219+
return self.insert(id);
220+
}
221+
let fields_cant_derive = info.fields()
222+
.iter()
223+
.any(|f| {
224+
match f {
225+
&Field::DataMember(ref data) => self.cant_derive_debug.contains(&data.ty()),
226+
&Field::Bitfields(ref bfu) => bfu.bitfields()
227+
.iter().any(|b| {
228+
self.cant_derive_debug.contains(&b.ty())
229+
})
230+
}
231+
});
232+
if fields_cant_derive {
233+
return self.insert(id);
234+
}
235+
false
236+
},
237+
TypeKind::Pointer(inner) => {
238+
let inner_type = self.ctx.resolve_type(inner);
239+
if let TypeKind::Function(ref sig) =
240+
*inner_type.canonical_type(self.ctx).kind() {
241+
if sig.can_trivially_derive_debug(&self.ctx, ()) {
242+
return false;
243+
} else {
244+
return self.insert(id);
245+
}
246+
}
247+
false
248+
},
249+
TypeKind::TemplateInstantiation(ref template) => {
250+
let args_cant_derive = template.template_arguments()
251+
.iter()
252+
.any(|arg| self.cant_derive_debug.contains(&arg));
253+
if args_cant_derive {
254+
return self.insert(id);
255+
}
256+
let ty_cant_derive = template.template_definition()
257+
.into_resolver()
258+
.through_type_refs()
259+
.through_type_aliases()
260+
.resolve(self.ctx)
261+
.as_type()
262+
.expect("Instantiations of a non-type?")
263+
.as_comp()
264+
.and_then(|c| {
265+
// For non-type template parameters, we generate an opaque
266+
// blob, and in this case the instantiation has a better
267+
// idea of the layout than the definition does.
268+
if c.has_non_type_template_params() {
269+
let opaque = ty.layout(self.ctx)
270+
.or_else(|| self.ctx.resolve_type(template.template_definition()).layout(self.ctx))
271+
.unwrap_or(Layout::zero())
272+
.opaque();
273+
Some(!opaque.can_trivially_derive_debug(&self.ctx, ()))
274+
} else {
275+
None
276+
}
277+
})
278+
.unwrap_or_else(|| self.cant_derive_debug.contains(&template.template_definition()));
279+
if ty_cant_derive {
280+
return self.insert(id);
281+
}
282+
false
283+
},
284+
}
285+
}
286+
287+
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
288+
where F: FnMut(ItemId),
289+
{
290+
if let Some(edges) = self.dependencies.get(&id) {
291+
for item in edges {
292+
trace!("enqueue {:?} into worklist", item);
293+
f(*item);
294+
}
295+
}
296+
}
297+
}
298+
299+
impl<'ctx, 'gen> From<CantDeriveDebugAnalysis<'ctx, 'gen>> for HashSet<ItemId> {
300+
fn from(analysis: CantDeriveDebugAnalysis<'ctx, 'gen>) -> Self {
301+
analysis.cant_derive_debug
302+
}
303+
}

0 commit comments

Comments
 (0)