Skip to content

Commit e91d448

Browse files
author
bors-servo
authored
Auto merge of #866 - photoszzt:can_derive_copy_array, r=fitzgen
Can derive copy analysis r? @fitzgen or @emilio. Fix: #766
2 parents 4142a89 + 89dde21 commit e91d448

File tree

11 files changed

+652
-226
lines changed

11 files changed

+652
-226
lines changed

src/codegen/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1424,7 +1424,7 @@ impl CodeGenerator for CompInfo {
14241424
needs_default_impl = ctx.options().derive_default;
14251425
}
14261426

1427-
if item.can_derive_copy(ctx, ()) &&
1427+
if item.can_derive_copy(ctx) &&
14281428
!item.annotations().disallow_copy() {
14291429
derives.push("Copy");
14301430
if used_template_params.is_some() {

src/ir/analysis/derive_copy.rs

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

0 commit comments

Comments
 (0)