Skip to content

Commit 1fb0b32

Browse files
committed
Allow marking specific template instantiations as opaque
If a template has a specialization that bindgen doesn't understand, it can be helpful to mark it as opaque and continue making forward progress in the meantime. This is something we need in the SpiderMonkey bindings.
1 parent 480c2d1 commit 1fb0b32

13 files changed

+311
-72
lines changed

src/codegen/mod.rs

+23-16
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use ir::dot;
1717
use ir::enum_ty::{Enum, EnumVariant, EnumVariantValue};
1818
use ir::function::{Abi, Function, FunctionSig};
1919
use ir::int::IntKind;
20-
use ir::item::{Item, ItemAncestors, ItemCanonicalName, ItemCanonicalPath,
21-
ItemSet};
20+
use ir::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalName,
21+
ItemCanonicalPath, ItemSet};
2222
use ir::item_kind::ItemKind;
2323
use ir::layout::Layout;
2424
use ir::module::Module;
@@ -580,7 +580,7 @@ impl CodeGenerator for Type {
580580
}
581581

582582
let mut used_template_params = item.used_template_params(ctx);
583-
let inner_rust_type = if item.is_opaque(ctx) {
583+
let inner_rust_type = if item.is_opaque(ctx, &()) {
584584
used_template_params = None;
585585
self.to_opaque(ctx, item)
586586
} else {
@@ -759,8 +759,11 @@ impl CodeGenerator for TemplateInstantiation {
759759
item: &Item) {
760760
// Although uses of instantiations don't need code generation, and are
761761
// just converted to rust types in fields, vars, etc, we take this
762-
// opportunity to generate tests for their layout here.
763-
if !ctx.options().layout_tests {
762+
// opportunity to generate tests for their layout here. If the
763+
// instantiation is opaque, then its presumably because we don't
764+
// properly understand it (maybe because of specializations), and so we
765+
// shouldn't emit layout tests either.
766+
if !ctx.options().layout_tests || self.is_opaque(ctx, item) {
764767
return
765768
}
766769

@@ -1568,7 +1571,7 @@ impl CodeGenerator for CompInfo {
15681571
}
15691572

15701573
// Yeah, sorry about that.
1571-
if item.is_opaque(ctx) {
1574+
if item.is_opaque(ctx, &()) {
15721575
fields.clear();
15731576
methods.clear();
15741577

@@ -1704,7 +1707,7 @@ impl CodeGenerator for CompInfo {
17041707
})
17051708
.count() > 1;
17061709

1707-
let should_skip_field_offset_checks = item.is_opaque(ctx) ||
1710+
let should_skip_field_offset_checks = item.is_opaque(ctx, &()) ||
17081711
too_many_base_vtables;
17091712

17101713
let check_field_offset = if should_skip_field_offset_checks {
@@ -2816,7 +2819,7 @@ impl TryToRustTy for Type {
28162819
.build())
28172820
}
28182821
TypeKind::TemplateInstantiation(ref inst) => {
2819-
inst.try_to_rust_ty(ctx, self)
2822+
inst.try_to_rust_ty(ctx, item)
28202823
}
28212824
TypeKind::ResolvedTypeRef(inner) => inner.try_to_rust_ty(ctx, &()),
28222825
TypeKind::TemplateAlias(inner, _) |
@@ -2828,7 +2831,7 @@ impl TryToRustTy for Type {
28282831
.collect::<Vec<_>>();
28292832

28302833
let spelling = self.name().expect("Unnamed alias?");
2831-
if item.is_opaque(ctx) && !template_params.is_empty() {
2834+
if item.is_opaque(ctx, &()) && !template_params.is_empty() {
28322835
self.try_to_opaque(ctx, item)
28332836
} else if let Some(ty) = utils::type_from_named(ctx,
28342837
spelling,
@@ -2841,7 +2844,7 @@ impl TryToRustTy for Type {
28412844
TypeKind::Comp(ref info) => {
28422845
let template_params = item.used_template_params(ctx);
28432846
if info.has_non_type_template_params() ||
2844-
(item.is_opaque(ctx) && template_params.is_some()) {
2847+
(item.is_opaque(ctx, &()) && template_params.is_some()) {
28452848
return self.try_to_opaque(ctx, item);
28462849
}
28472850

@@ -2895,23 +2898,27 @@ impl TryToRustTy for Type {
28952898
}
28962899

28972900
impl TryToOpaque for TemplateInstantiation {
2898-
type Extra = Type;
2901+
type Extra = Item;
28992902

29002903
fn try_get_layout(&self,
29012904
ctx: &BindgenContext,
2902-
self_ty: &Type)
2905+
item: &Item)
29032906
-> error::Result<Layout> {
2904-
self_ty.layout(ctx).ok_or(error::Error::NoLayoutForOpaqueBlob)
2907+
item.expect_type().layout(ctx).ok_or(error::Error::NoLayoutForOpaqueBlob)
29052908
}
29062909
}
29072910

29082911
impl TryToRustTy for TemplateInstantiation {
2909-
type Extra = Type;
2912+
type Extra = Item;
29102913

29112914
fn try_to_rust_ty(&self,
29122915
ctx: &BindgenContext,
2913-
_: &Type)
2916+
item: &Item)
29142917
-> error::Result<P<ast::Ty>> {
2918+
if self.is_opaque(ctx, item) {
2919+
return Err(error::Error::InstantiationOfOpaqueType);
2920+
}
2921+
29152922
let decl = self.template_definition();
29162923
let mut ty = decl.try_to_rust_ty(ctx, &())?.unwrap();
29172924

@@ -2924,7 +2931,7 @@ impl TryToRustTy for TemplateInstantiation {
29242931
extra_assert!(decl.into_resolver()
29252932
.through_type_refs()
29262933
.resolve(ctx)
2927-
.is_opaque(ctx));
2934+
.is_opaque(ctx, &()));
29282935
return Err(error::Error::InstantiationOfOpaqueType);
29292936
}
29302937
};

src/ir/comp.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use super::annotations::Annotations;
44
use super::context::{BindgenContext, ItemId};
55
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
66
use super::dot::DotAttributes;
7-
use super::item::Item;
7+
use super::item::{IsOpaque, Item};
88
use super::layout::Layout;
99
use super::traversal::{EdgeKind, Trace, Tracer};
1010
use super::template::TemplateParameters;
@@ -1585,7 +1585,7 @@ impl Trace for CompInfo {
15851585
// We unconditionally trace `CompInfo`'s template parameters and inner
15861586
// types for the the usage analysis. However, we don't want to continue
15871587
// tracing anything else, if this type is marked opaque.
1588-
if item.is_opaque(context) {
1588+
if item.is_opaque(context, &()) {
15891589
return;
15901590
}
15911591

src/ir/context.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use super::derive::{CanDeriveCopy, CanDeriveDebug, CanDeriveDefault};
44
use super::int::IntKind;
5-
use super::item::{Item, ItemAncestors, ItemCanonicalPath, ItemSet};
5+
use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalPath, ItemSet};
66
use super::item_kind::ItemKind;
77
use super::module::{Module, ModuleKind};
88
use super::named::{UsedTemplateParameters, analyze};
@@ -339,7 +339,7 @@ impl<'ctx> BindgenContext<'ctx> {
339339
location);
340340
debug_assert!(declaration.is_some() || !item.kind().is_type() ||
341341
item.kind().expect_type().is_builtin_or_named() ||
342-
item.kind().expect_type().is_opaque(),
342+
item.kind().expect_type().is_opaque(self, &item),
343343
"Adding a type without declaration?");
344344

345345
let id = item.id();

src/ir/item.rs

+39-15
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ pub trait ItemCanonicalPath {
6161
fn canonical_path(&self, ctx: &BindgenContext) -> Vec<String>;
6262
}
6363

64+
/// A trait for determining if some IR thing is opaque or not.
65+
pub trait IsOpaque {
66+
/// Extra context the IR thing needs to determine if it is opaque or not.
67+
type Extra;
68+
69+
/// Returns `true` if the thing is opaque, and `false` otherwise.
70+
///
71+
/// May only be called when `ctx` is in the codegen phase.
72+
fn is_opaque(&self, ctx: &BindgenContext, extra: &Self::Extra) -> bool;
73+
}
74+
6475
/// A trait for iterating over an item and its parents and up its ancestor chain
6576
/// up to (but not including) the implicit root module.
6677
pub trait ItemAncestors {
@@ -231,7 +242,7 @@ impl Trace for Item {
231242
// don't want to stop collecting types even though they may be
232243
// opaque.
233244
if ty.should_be_traced_unconditionally() ||
234-
!self.is_opaque(ctx) {
245+
!self.is_opaque(ctx, &()) {
235246
ty.trace(ctx, tracer, self);
236247
}
237248
}
@@ -269,7 +280,7 @@ impl CanDeriveDebug for Item {
269280
let result = ctx.options().derive_debug &&
270281
match self.kind {
271282
ItemKind::Type(ref ty) => {
272-
if self.is_opaque(ctx) {
283+
if self.is_opaque(ctx, &()) {
273284
ty.layout(ctx)
274285
.map_or(true, |l| l.opaque().can_derive_debug(ctx, ()))
275286
} else {
@@ -292,7 +303,7 @@ impl CanDeriveDefault for Item {
292303
ctx.options().derive_default &&
293304
match self.kind {
294305
ItemKind::Type(ref ty) => {
295-
if self.is_opaque(ctx) {
306+
if self.is_opaque(ctx, &()) {
296307
ty.layout(ctx)
297308
.map_or(false,
298309
|l| l.opaque().can_derive_default(ctx, ()))
@@ -317,7 +328,7 @@ impl<'a> CanDeriveCopy<'a> for Item {
317328

318329
let result = match self.kind {
319330
ItemKind::Type(ref ty) => {
320-
if self.is_opaque(ctx) {
331+
if self.is_opaque(ctx, &()) {
321332
ty.layout(ctx)
322333
.map_or(true, |l| l.opaque().can_derive_copy(ctx, ()))
323334
} else {
@@ -335,7 +346,7 @@ impl<'a> CanDeriveCopy<'a> for Item {
335346
fn can_derive_copy_in_array(&self, ctx: &BindgenContext, _: ()) -> bool {
336347
match self.kind {
337348
ItemKind::Type(ref ty) => {
338-
if self.is_opaque(ctx) {
349+
if self.is_opaque(ctx, &()) {
339350
ty.layout(ctx)
340351
.map_or(true, |l| {
341352
l.opaque().can_derive_copy_in_array(ctx, ())
@@ -593,15 +604,6 @@ impl Item {
593604
ctx.hidden_by_name(&self.canonical_path(ctx), self.id)
594605
}
595606

596-
/// Is this item opaque?
597-
pub fn is_opaque(&self, ctx: &BindgenContext) -> bool {
598-
debug_assert!(ctx.in_codegen_phase(),
599-
"You're not supposed to call this yet");
600-
self.annotations.opaque() ||
601-
self.as_type().map_or(false, |ty| ty.is_opaque()) ||
602-
ctx.opaque_by_name(&self.canonical_path(ctx))
603-
}
604-
605607
/// Is this a reference to another type?
606608
pub fn is_type_ref(&self) -> bool {
607609
self.as_type().map_or(false, |ty| ty.is_type_ref())
@@ -858,6 +860,28 @@ impl Item {
858860
}
859861
}
860862

863+
impl IsOpaque for ItemId {
864+
type Extra = ();
865+
866+
fn is_opaque(&self, ctx: &BindgenContext, _: &()) -> bool {
867+
debug_assert!(ctx.in_codegen_phase(),
868+
"You're not supposed to call this yet");
869+
ctx.resolve_item(*self).is_opaque(ctx, &())
870+
}
871+
}
872+
873+
impl IsOpaque for Item {
874+
type Extra = ();
875+
876+
fn is_opaque(&self, ctx: &BindgenContext, _: &()) -> bool {
877+
debug_assert!(ctx.in_codegen_phase(),
878+
"You're not supposed to call this yet");
879+
self.annotations.opaque() ||
880+
self.as_type().map_or(false, |ty| ty.is_opaque(ctx, self)) ||
881+
ctx.opaque_by_name(&self.canonical_path(ctx))
882+
}
883+
}
884+
861885
/// A set of items.
862886
pub type ItemSet = BTreeSet<ItemId>;
863887

@@ -874,7 +898,7 @@ impl DotAttributes for Item {
874898
self.id,
875899
self.name(ctx).get()));
876900

877-
if self.is_opaque(ctx) {
901+
if self.is_opaque(ctx, &()) {
878902
writeln!(out, "<tr><td>opaque</td><td>true</td></tr>")?;
879903
}
880904

src/ir/template.rs

+31-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@
2929
3030
use super::context::{BindgenContext, ItemId};
3131
use super::derive::{CanDeriveCopy, CanDeriveDebug};
32-
use super::item::{Item, ItemAncestors};
32+
use super::item::{IsOpaque, Item, ItemAncestors, ItemCanonicalName,
33+
ItemCanonicalPath};
3334
use super::layout::Layout;
3435
use super::traversal::{EdgeKind, Trace, Tracer};
3536
use clang;
@@ -305,6 +306,35 @@ impl TemplateInstantiation {
305306
}
306307
}
307308

309+
impl IsOpaque for TemplateInstantiation {
310+
type Extra = Item;
311+
312+
/// Is this an opaque template instantiation?
313+
fn is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool {
314+
if self.template_definition().is_opaque(ctx, &()) {
315+
return true;
316+
}
317+
318+
// TODO(#<insert issue here>): this doesn't properly handle opaque
319+
// instantiations where an argument is itself an instantiation because
320+
// `canonical_name` does not insert the template arguments into the
321+
// name, ie it for nested template arguments it creates "Foo" instead of
322+
// "Foo<int>". The fully correct fix is to make `canonical_{name,path}`
323+
// include template arguments properly.
324+
325+
let mut path = item.canonical_path(ctx);
326+
let args: Vec<_> = self.args.iter().map(|a| a.canonical_name(ctx)).collect();
327+
{
328+
let mut last = path.last_mut().unwrap();
329+
last.push('<');
330+
last.push_str(&args.join(", "));
331+
last.push('>');
332+
}
333+
334+
ctx.opaque_by_name(&path)
335+
}
336+
}
337+
308338
impl<'a> CanDeriveCopy<'a> for TemplateInstantiation {
309339
type Extra = ();
310340

src/ir/ty.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use super::dot::DotAttributes;
77
use super::enum_ty::Enum;
88
use super::function::FunctionSig;
99
use super::int::IntKind;
10-
use super::item::Item;
10+
use super::item::{IsOpaque, Item};
1111
use super::layout::{Layout, Opaque};
1212
use super::objc::ObjCInterface;
1313
use super::template::{AsTemplateParam, TemplateInstantiation, TemplateParameters};
@@ -102,14 +102,6 @@ impl Type {
102102
}
103103
}
104104

105-
/// Is this type of kind `TypeKind::Opaque`?
106-
pub fn is_opaque(&self) -> bool {
107-
match self.kind {
108-
TypeKind::Opaque => true,
109-
_ => false,
110-
}
111-
}
112-
113105
/// Is this type of kind `TypeKind::Named`?
114106
pub fn is_named(&self) -> bool {
115107
match self.kind {
@@ -374,6 +366,18 @@ impl Type {
374366
}
375367
}
376368

369+
impl IsOpaque for Type {
370+
type Extra = Item;
371+
372+
fn is_opaque(&self, ctx: &BindgenContext, item: &Item) -> bool {
373+
match self.kind {
374+
TypeKind::Opaque => true,
375+
TypeKind::TemplateInstantiation(ref inst) => inst.is_opaque(ctx, item),
376+
_ => false,
377+
}
378+
}
379+
}
380+
377381
impl AsTemplateParam for Type {
378382
type Extra = Item;
379383

tests/expectations/tests/issue-674-1.rs

-9
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,4 @@ pub mod root {
4343
impl Clone for CapturingContentInfo {
4444
fn clone(&self) -> Self { *self }
4545
}
46-
#[test]
47-
fn __bindgen_test_layout_Maybe_instantiation() {
48-
assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! (
49-
"Size of template specialization: " , stringify ! ( u8 )
50-
));
51-
assert_eq!(::std::mem::align_of::<u8>() , 1usize , concat ! (
52-
"Alignment of template specialization: " , stringify ! ( u8
53-
) ));
54-
}
5546
}

tests/expectations/tests/issue-674-2.rs

-9
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,6 @@ pub mod root {
6767
pub _address: u8,
6868
}
6969
#[test]
70-
fn __bindgen_test_layout_Rooted_instantiation() {
71-
assert_eq!(::std::mem::size_of::<u8>() , 1usize , concat ! (
72-
"Size of template specialization: " , stringify ! ( u8 )
73-
));
74-
assert_eq!(::std::mem::align_of::<u8>() , 1usize , concat ! (
75-
"Alignment of template specialization: " , stringify ! ( u8
76-
) ));
77-
}
78-
#[test]
7970
fn __bindgen_test_layout_StaticRefPtr_instantiation() {
8071
assert_eq!(::std::mem::size_of::<root::StaticRefPtr>() , 1usize ,
8172
concat ! (

0 commit comments

Comments
 (0)