Skip to content

Commit bc08b35

Browse files
committed
Do not derive Copy/Debug for some opaque types
When we treat a type as opaque, either because it is explicitly annotated as such or because it is a template that has a non-type parameter, we need to check if the size is larger than RUST_DERIVE_IN_ARRAY_LIMIT. Performing this check requires information that is up the stack, in the `Item` rather than in the `CompInfo` or `Type`. Therefore, I introduced two traits to encapsulate whether a thing can derive `Debug` and `Copy` (the `CanDeriveDebug` and `CanDeriveCopy` traits, respectively) and implemented them for ALL THE THINGS. This allowes us to perform our various checks at the level where we have access to the necessary info, and to let sub-levels do their own checks with their sub-info. Fixes #372.
1 parent d49bae2 commit bc08b35

File tree

10 files changed

+436
-174
lines changed

10 files changed

+436
-174
lines changed

libbindgen/src/codegen/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use aster;
66
use ir::annotations::FieldAccessorKind;
77
use ir::comp::{CompInfo, CompKind, Field, Method, MethodKind};
88
use ir::context::{BindgenContext, ItemId};
9+
use ir::derive::{CanDeriveCopy, CanDeriveDebug};
910
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
1011
use ir::function::{Function, FunctionSig};
1112
use ir::int::IntKind;
@@ -765,12 +766,11 @@ impl CodeGenerator for CompInfo {
765766

766767
let is_union = self.kind() == CompKind::Union;
767768
let mut derives = vec![];
768-
let ty = item.expect_type();
769-
if ty.can_derive_debug(ctx) {
769+
if item.can_derive_debug(ctx, ()) {
770770
derives.push("Debug");
771771
}
772772

773-
if item.can_derive_copy(ctx) && !item.annotations().disallow_copy() {
773+
if item.can_derive_copy(ctx, ()) && !item.annotations().disallow_copy() {
774774
derives.push("Copy");
775775
if !applicable_template_args.is_empty() {
776776
// FIXME: This requires extra logic if you have a big array in a

libbindgen/src/ir/comp.rs

+123-79
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::cell::Cell;
66
use std::cmp;
77
use super::annotations::Annotations;
88
use super::context::{BindgenContext, ItemId};
9+
use super::derive::{CanDeriveCopy, CanDeriveDebug};
910
use super::item::Item;
1011
use super::layout::Layout;
1112
use super::ty::{RUST_DERIVE_IN_ARRAY_LIMIT, Type};
@@ -154,6 +155,26 @@ impl Field {
154155
}
155156
}
156157

158+
impl CanDeriveDebug for Field {
159+
type Extra = ();
160+
161+
fn can_derive_debug(&self, ctx: &BindgenContext, _: ()) -> bool {
162+
self.ty.can_derive_debug(ctx, ())
163+
}
164+
}
165+
166+
impl<'a> CanDeriveCopy<'a> for Field {
167+
type Extra = ();
168+
169+
fn can_derive_copy(&self, ctx: &BindgenContext, _: ()) -> bool {
170+
self.ty.can_derive_copy(ctx, ())
171+
}
172+
173+
fn can_derive_copy_in_array(&self, ctx: &BindgenContext, _: ()) -> bool {
174+
self.ty.can_derive_copy_in_array(ctx, ())
175+
}
176+
}
177+
157178
/// A compound type.
158179
///
159180
/// Either a struct or union, a compound type is built up from the combination
@@ -263,50 +284,6 @@ impl CompInfo {
263284
}
264285
}
265286

266-
/// Can we derive the `Debug` trait for this compound type?
267-
pub fn can_derive_debug(&self,
268-
ctx: &BindgenContext,
269-
layout: Option<Layout>)
270-
-> bool {
271-
// We can reach here recursively via template parameters of a member,
272-
// for example.
273-
if self.detect_derive_debug_cycle.get() {
274-
warn!("Derive debug cycle detected!");
275-
return true;
276-
}
277-
278-
if self.kind == CompKind::Union {
279-
if ctx.options().unstable_rust {
280-
return false;
281-
}
282-
283-
let layout = layout.unwrap_or_else(Layout::zero);
284-
let size_divisor = cmp::max(1, layout.align);
285-
return layout.size / size_divisor <= RUST_DERIVE_IN_ARRAY_LIMIT;
286-
}
287-
288-
self.detect_derive_debug_cycle.set(true);
289-
290-
let can_derive_debug = {
291-
self.base_members
292-
.iter()
293-
.all(|ty| ctx.resolve_type(*ty).can_derive_debug(ctx)) &&
294-
self.template_args
295-
.iter()
296-
.all(|ty| ctx.resolve_type(*ty).can_derive_debug(ctx)) &&
297-
self.fields
298-
.iter()
299-
.all(|f| ctx.resolve_type(f.ty).can_derive_debug(ctx)) &&
300-
self.ref_template.map_or(true, |template| {
301-
ctx.resolve_type(template).can_derive_debug(ctx)
302-
})
303-
};
304-
305-
self.detect_derive_debug_cycle.set(false);
306-
307-
can_derive_debug
308-
}
309-
310287
/// Is this compound type unsized?
311288
pub fn is_unsized(&self, ctx: &BindgenContext) -> bool {
312289
!self.has_vtable(ctx) && self.fields.is_empty() &&
@@ -356,41 +333,6 @@ impl CompInfo {
356333
has_destructor
357334
}
358335

359-
/// Can we derive the `Copy` trait for this type?
360-
pub fn can_derive_copy(&self, ctx: &BindgenContext, item: &Item) -> bool {
361-
// NOTE: Take into account that while unions in C and C++ are copied by
362-
// default, the may have an explicit destructor in C++, so we can't
363-
// defer this check just for the union case.
364-
if self.has_destructor(ctx) {
365-
return false;
366-
}
367-
368-
if self.kind == CompKind::Union {
369-
if !ctx.options().unstable_rust {
370-
return true;
371-
}
372-
373-
// https://github.com/rust-lang/rust/issues/36640
374-
if !self.template_args.is_empty() || self.ref_template.is_some() ||
375-
!item.applicable_template_args(ctx).is_empty() {
376-
return false;
377-
}
378-
}
379-
380-
// With template args, use a safe subset of the types,
381-
// since copyability depends on the types itself.
382-
self.ref_template
383-
.as_ref()
384-
.map_or(true, |t| ctx.resolve_item(*t).can_derive_copy(ctx)) &&
385-
self.base_members
386-
.iter()
387-
.all(|t| ctx.resolve_item(*t).can_derive_copy(ctx)) &&
388-
self.fields.iter().all(|field| {
389-
ctx.resolve_item(field.ty)
390-
.can_derive_copy(ctx)
391-
})
392-
}
393-
394336
/// Is this type a template specialization?
395337
pub fn is_template_specialization(&self) -> bool {
396338
self.ref_template.is_some()
@@ -858,6 +800,108 @@ impl CompInfo {
858800
}
859801
}
860802

803+
impl CanDeriveDebug for CompInfo {
804+
type Extra = Option<Layout>;
805+
806+
fn can_derive_debug(&self,
807+
ctx: &BindgenContext,
808+
layout: Option<Layout>)
809+
-> bool {
810+
if self.has_non_type_template_params() {
811+
return layout.map_or(false, |l| l.opaque().can_derive_debug(ctx, ()));
812+
}
813+
814+
// We can reach here recursively via template parameters of a member,
815+
// for example.
816+
if self.detect_derive_debug_cycle.get() {
817+
warn!("Derive debug cycle detected!");
818+
return true;
819+
}
820+
821+
if self.kind == CompKind::Union {
822+
if ctx.options().unstable_rust {
823+
return false;
824+
}
825+
826+
let layout = layout.unwrap_or_else(Layout::zero);
827+
let size_divisor = cmp::max(1, layout.align);
828+
return layout.size / size_divisor <= RUST_DERIVE_IN_ARRAY_LIMIT;
829+
}
830+
831+
self.detect_derive_debug_cycle.set(true);
832+
833+
let can_derive_debug = {
834+
self.base_members
835+
.iter()
836+
.all(|id| id.can_derive_debug(ctx, ())) &&
837+
self.template_args
838+
.iter()
839+
.all(|id| id.can_derive_debug(ctx, ())) &&
840+
self.fields
841+
.iter()
842+
.all(|f| f.can_derive_debug(ctx, ())) &&
843+
self.ref_template.map_or(true, |id| {
844+
id.can_derive_debug(ctx, ())
845+
})
846+
};
847+
848+
self.detect_derive_debug_cycle.set(false);
849+
850+
can_derive_debug
851+
}
852+
}
853+
854+
impl<'a> CanDeriveCopy<'a> for CompInfo {
855+
type Extra = (&'a Item, Option<Layout>);
856+
857+
fn can_derive_copy(&self,
858+
ctx: &BindgenContext,
859+
(item, layout): (&Item, Option<Layout>))
860+
-> bool {
861+
if self.has_non_type_template_params() {
862+
return layout.map_or(false, |l| l.opaque().can_derive_copy(ctx, ()));
863+
}
864+
865+
// NOTE: Take into account that while unions in C and C++ are copied by
866+
// default, the may have an explicit destructor in C++, so we can't
867+
// defer this check just for the union case.
868+
if self.has_destructor(ctx) {
869+
return false;
870+
}
871+
872+
if self.kind == CompKind::Union {
873+
if !ctx.options().unstable_rust {
874+
return true;
875+
}
876+
877+
// https://github.com/rust-lang/rust/issues/36640
878+
if !self.template_args.is_empty() || self.ref_template.is_some() ||
879+
!item.applicable_template_args(ctx).is_empty() {
880+
return false;
881+
}
882+
}
883+
884+
// With template args, use a safe subset of the types,
885+
// since copyability depends on the types itself.
886+
self.ref_template
887+
.as_ref()
888+
.map_or(true, |t| t.can_derive_copy(ctx, ())) &&
889+
self.base_members
890+
.iter()
891+
.all(|t| t.can_derive_copy(ctx, ())) &&
892+
self.fields.iter().all(|field| {
893+
field.can_derive_copy(ctx, ())
894+
})
895+
}
896+
897+
fn can_derive_copy_in_array(&self,
898+
ctx: &BindgenContext,
899+
extra: (&Item, Option<Layout>))
900+
-> bool {
901+
self.can_derive_copy(ctx, extra)
902+
}
903+
}
904+
861905
impl TypeCollector for CompInfo {
862906
type Extra = Item;
863907

libbindgen/src/ir/context.rs

+21
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use std::cell::Cell;
99
use std::collections::{HashMap, VecDeque, hash_map};
1010
use std::collections::btree_map::{self, BTreeMap};
1111
use std::fmt;
12+
use super::derive::{CanDeriveCopy, CanDeriveDebug};
1213
use super::int::IntKind;
1314
use super::item::{Item, ItemCanonicalPath};
1415
use super::item_kind::ItemKind;
@@ -32,6 +33,26 @@ impl ItemId {
3233
}
3334
}
3435

36+
impl CanDeriveDebug for ItemId {
37+
type Extra = ();
38+
39+
fn can_derive_debug(&self, ctx: &BindgenContext, _: ()) -> bool {
40+
ctx.resolve_item(*self).can_derive_debug(ctx, ())
41+
}
42+
}
43+
44+
impl<'a> CanDeriveCopy<'a> for ItemId {
45+
type Extra = ();
46+
47+
fn can_derive_copy(&self, ctx: &BindgenContext, _: ()) -> bool {
48+
ctx.resolve_item(*self).can_derive_copy(ctx, ())
49+
}
50+
51+
fn can_derive_copy_in_array(&self, ctx: &BindgenContext, _: ()) -> bool {
52+
ctx.resolve_item(*self).can_derive_copy_in_array(ctx, ())
53+
}
54+
}
55+
3556
/// A key used to index a resolved type, so we only process it once.
3657
///
3758
/// This is almost always a USR string (an unique identifier generated by

libbindgen/src/ir/derive.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! Traits for determining whether we can derive traits for a thing or not.
2+
3+
use super::context::BindgenContext;
4+
5+
/// A trait that encapsulates the logic for whether or not we can derive `Debug`
6+
/// for a given thing.
7+
///
8+
/// This should ideally be a no-op that just returns `true`, but instead needs
9+
/// to be a recursive method that checks whether all the proper members can
10+
/// derive debug or not, because of the limit rust has on 32 items as max in the
11+
/// array.
12+
pub trait CanDeriveDebug {
13+
/// Implementations can define this type to get access to any extra
14+
/// information required to determine whether they can derive `Debug`. If
15+
/// extra information is unneeded, then this should simply be the unit type.
16+
type Extra;
17+
18+
/// Return `true` if `Debug` can be derived for this thing, `false`
19+
/// otherwise.
20+
fn can_derive_debug(&self,
21+
ctx: &BindgenContext,
22+
extra: Self::Extra)
23+
-> bool;
24+
}
25+
26+
/// A trait that encapsulates the logic for whether or not we can derive `Copy`
27+
/// for a given thing.
28+
pub trait CanDeriveCopy<'a> {
29+
/// Implementations can define this type to get access to any extra
30+
/// information required to determine whether they can derive `Copy`. If
31+
/// extra information is unneeded, then this should simply be the unit type.
32+
type Extra;
33+
34+
/// Return `true` if `Copy` can be derived for this thing, `false`
35+
/// otherwise.
36+
fn can_derive_copy(&'a self,
37+
ctx: &'a BindgenContext,
38+
extra: Self::Extra)
39+
-> bool;
40+
41+
/// For some reason, deriving copies of an array of a type that is not known
42+
/// to be `Copy` is a compile error. e.g.:
43+
///
44+
/// ```rust
45+
/// #[derive(Copy, Clone)]
46+
/// struct A<T> {
47+
/// member: T,
48+
/// }
49+
/// ```
50+
///
51+
/// is fine, while:
52+
///
53+
/// ```rust,ignore
54+
/// #[derive(Copy, Clone)]
55+
/// struct A<T> {
56+
/// member: [T; 1],
57+
/// }
58+
/// ```
59+
///
60+
/// is an error.
61+
///
62+
/// That's the whole point of the existence of `can_derive_copy_in_array`.
63+
fn can_derive_copy_in_array(&'a self,
64+
ctx: &'a BindgenContext,
65+
extra: Self::Extra)
66+
-> bool;
67+
}

0 commit comments

Comments
 (0)