Skip to content

Commit 1e0d808

Browse files
fitzgenKowasaki
authored andcommitted
Correctly handle blacklisted items in the template analysis
The template analysis operates on whitelisted items, and uses our tracing infrastructure to move between them. Usually, that means we can only reach other whitelisted items by tracing, because the set of whitelisted items is the transitive closure of all the items explicitly whitelisted. The exception is when some type is explicitly blacklisted. It could still be reachable via tracing from a whitelisted item, but is not considered whitelisted due to the blacklisting. The easy fix is to run the template analysis on the whole IR graph rather than just the whitelisted set. This is an approximately one line change in the analysis, however is not desirable due to performance concerns. The whole point of whitelisting is that there may be *many* types in a header, but only a *few* the user cares about, or there might be types that aren't explicitly needed and that are too complicated for bindgen to handle generally (often in `<type_traits>`). In these situations, we don't want to waste cycles or even confuse ourselves by considering such types! Instead, we keep the whitelisted item set around and check by hand whether any given item is in it during the template type parameter analysis. Additionally, we make the decision that blacklisted template definitions use all of their type parameters. This seems like a reasonable choice because the type will likely be ported to Rust manually by the bindgen user, and they will be looking at the C++ definition with all of its type parameters. They can always insert `PhantomData`s manually, so it also gives the most flexibility. Fixes rust-lang#584
1 parent 5b2f125 commit 1e0d808

File tree

4 files changed

+145
-14
lines changed

4 files changed

+145
-14
lines changed

src/ir/context.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,14 @@ impl<'ctx> BindgenContext<'ctx> {
631631
/// This method may only be called during the codegen phase, because the
632632
/// template usage information is only computed as we enter the codegen
633633
/// phase.
634+
///
635+
/// If the item is blacklisted, then we say that it always uses the template
636+
/// parameter. This is a little subtle. The template parameter usage
637+
/// analysis only considers whitelisted items, and if any blacklisted item
638+
/// shows up in the generated bindings, it is the user's responsibility to
639+
/// manually provide a definition for them. To give them the most
640+
/// flexibility when doing that, we assume that they use every template
641+
/// parameter and always pass template arguments through in instantiations.
634642
pub fn uses_template_parameter(&self,
635643
item: ItemId,
636644
template_param: ItemId)
@@ -643,7 +651,7 @@ impl<'ctx> BindgenContext<'ctx> {
643651
.expect("should have found template parameter usage if we're in codegen")
644652
.get(&item)
645653
.map(|items_used_params| items_used_params.contains(&template_param))
646-
.unwrap_or(false)
654+
.unwrap_or_else(|| self.resolve_item(item).is_hidden(self))
647655
}
648656

649657
// This deserves a comment. Builtin types don't get a valid declaration, so

src/ir/named.rs

+43-13
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ use super::item::ItemSet;
131131
use super::template::AsNamed;
132132
use super::traversal::{EdgeKind, Trace};
133133
use super::ty::{TemplateDeclaration, TypeKind};
134-
use std::collections::HashMap;
134+
use std::collections::{HashMap, HashSet};
135135
use std::fmt;
136136

137137
/// An analysis in the monotone framework.
@@ -263,6 +263,8 @@ pub struct UsedTemplateParameters<'ctx, 'gen>
263263
used: HashMap<ItemId, Option<ItemSet>>,
264264

265265
dependencies: HashMap<ItemId, Vec<ItemId>>,
266+
267+
whitelisted_items: HashSet<ItemId>,
266268
}
267269

268270
impl<'ctx, 'gen> UsedTemplateParameters<'ctx, 'gen> {
@@ -316,21 +318,30 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
316318
-> UsedTemplateParameters<'ctx, 'gen> {
317319
let mut used = HashMap::new();
318320
let mut dependencies = HashMap::new();
321+
let whitelisted_items: HashSet<_> = ctx.whitelisted_items().collect();
319322

320-
for item in ctx.whitelisted_items() {
323+
for item in whitelisted_items.iter().cloned() {
321324
dependencies.entry(item).or_insert(vec![]);
322325
used.insert(item, Some(ItemSet::new()));
323326

324327
{
325328
// We reverse our natural IR graph edges to find dependencies
326329
// between nodes.
327-
item.trace(ctx,
328-
&mut |sub_item, _| {
329-
dependencies.entry(sub_item)
330-
.or_insert(vec![])
331-
.push(item);
332-
},
333-
&());
330+
item.trace(ctx, &mut |sub_item, _| {
331+
// We won't be generating code for items that aren't
332+
// whitelisted, so don't bother keeping track of their
333+
// template parameters. But isn't whitelisting the
334+
// transitive closure of reachable items from the explicitly
335+
// whitelisted items? Usually! The exception is explicitly
336+
// blacklisted items.
337+
if !whitelisted_items.contains(&sub_item) {
338+
return;
339+
}
340+
341+
dependencies.entry(sub_item)
342+
.or_insert(vec![])
343+
.push(item);
344+
}, &());
334345
}
335346

336347
// Additionally, whether a template instantiation's template
@@ -361,6 +372,7 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
361372
ctx: ctx,
362373
used: used,
363374
dependencies: dependencies,
375+
whitelisted_items: whitelisted_items,
364376
}
365377
}
366378

@@ -395,6 +407,19 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
395407
used_by_this_id.insert(id);
396408
}
397409

410+
// We say that blacklisted items use all of their template
411+
// parameters. The blacklisted type is most likely implemented
412+
// explicitly by the user, since it won't be in the generated
413+
// bindings, and we don't know exactly what they'll to with template
414+
// parameters, but we can push the issue down the line to them.
415+
Some(&TypeKind::TemplateInstantiation(ref inst))
416+
if !self.whitelisted_items.contains(&inst.template_definition()) => {
417+
let args = inst.template_arguments()
418+
.iter()
419+
.filter_map(|a| a.as_named(self.ctx, &()));
420+
used_by_this_id.extend(args);
421+
}
422+
398423
// A template instantiation's concrete template argument is
399424
// only used if the template declaration uses the
400425
// corresponding template parameter.
@@ -404,12 +429,12 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
404429

405430
let params = decl.self_template_params(self.ctx)
406431
.unwrap_or(vec![]);
432+
407433
for (arg, param) in args.iter().zip(params.iter()) {
408-
let used_by_definition = self.used
409-
[&inst.template_definition()]
434+
let used_by_def = self.used[&inst.template_definition()]
410435
.as_ref()
411436
.unwrap();
412-
if used_by_definition.contains(param) {
437+
if used_by_def.contains(param) {
413438
if let Some(named) = arg.as_named(self.ctx, &()) {
414439
used_by_this_id.insert(named);
415440
}
@@ -421,7 +446,12 @@ impl<'ctx, 'gen> MonotoneFramework for UsedTemplateParameters<'ctx, 'gen> {
421446
// parameter usage.
422447
_ => {
423448
item.trace(self.ctx, &mut |sub_id, edge_kind| {
424-
if sub_id == id || !Self::consider_edge(edge_kind) {
449+
// Ignore ourselves, since union with ourself is a
450+
// no-op. Ignore edges that aren't relevant to the
451+
// analysis. Ignore edges to blacklisted items.
452+
if sub_id == id ||
453+
!Self::consider_edge(edge_kind) ||
454+
!self.whitelisted_items.contains(&sub_id) {
425455
return;
426456
}
427457

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/* automatically generated by rust-bindgen */
2+
3+
4+
#![allow(non_snake_case)]
5+
6+
pub type RefPtr<T> = T;
7+
8+
#[repr(C)]
9+
#[derive(Debug, Copy)]
10+
pub struct b {
11+
pub _base: g,
12+
}
13+
#[test]
14+
fn bindgen_test_layout_b() {
15+
assert_eq!(::std::mem::size_of::<b>() , 1usize , concat ! (
16+
"Size of: " , stringify ! ( b ) ));
17+
assert_eq! (::std::mem::align_of::<b>() , 1usize , concat ! (
18+
"Alignment of " , stringify ! ( b ) ));
19+
}
20+
impl Clone for b {
21+
fn clone(&self) -> Self { *self }
22+
}
23+
impl Default for b {
24+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
25+
}
26+
#[repr(C)]
27+
#[derive(Debug, Default, Copy)]
28+
pub struct A {
29+
pub _address: u8,
30+
}
31+
pub type A_a = b;
32+
#[test]
33+
fn bindgen_test_layout_A() {
34+
assert_eq!(::std::mem::size_of::<A>() , 1usize , concat ! (
35+
"Size of: " , stringify ! ( A ) ));
36+
assert_eq! (::std::mem::align_of::<A>() , 1usize , concat ! (
37+
"Alignment of " , stringify ! ( A ) ));
38+
}
39+
impl Clone for A {
40+
fn clone(&self) -> Self { *self }
41+
}
42+
#[repr(C)]
43+
#[derive(Debug, Copy, Clone)]
44+
pub struct e<c> {
45+
pub d: RefPtr<c>,
46+
}
47+
impl <c> Default for e<c> {
48+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
49+
}
50+
#[repr(C)]
51+
#[derive(Debug, Default, Copy, Clone)]
52+
pub struct f {
53+
pub _address: u8,
54+
}
55+
#[repr(C)]
56+
#[derive(Debug, Copy)]
57+
pub struct g {
58+
pub h: f,
59+
}
60+
#[test]
61+
fn bindgen_test_layout_g() {
62+
assert_eq!(::std::mem::size_of::<g>() , 1usize , concat ! (
63+
"Size of: " , stringify ! ( g ) ));
64+
assert_eq! (::std::mem::align_of::<g>() , 1usize , concat ! (
65+
"Alignment of " , stringify ! ( g ) ));
66+
assert_eq! (unsafe { & ( * ( 0 as * const g ) ) . h as * const _ as usize
67+
} , 0usize , concat ! (
68+
"Alignment of field: " , stringify ! ( g ) , "::" , stringify
69+
! ( h ) ));
70+
}
71+
impl Clone for g {
72+
fn clone(&self) -> Self { *self }
73+
}
74+
impl Default for g {
75+
fn default() -> Self { unsafe { ::std::mem::zeroed() } }
76+
}
77+
extern "C" {
78+
#[link_name = "_Z25Servo_Element_GetSnapshotv"]
79+
pub fn Servo_Element_GetSnapshot() -> A;
80+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// bindgen-flags: --blacklist-type RefPtr --whitelist-function 'Servo_.*' --raw-line 'pub type RefPtr<T> = T;' -- -std=c++14
2+
template <class> class RefPtr;
3+
class b;
4+
class A {
5+
typedef b a;
6+
};
7+
template <class c> class e { RefPtr<c> d; };
8+
template <class> class f {};
9+
class g {
10+
f<e<int>> h;
11+
};
12+
class b : g {};
13+
A Servo_Element_GetSnapshot();

0 commit comments

Comments
 (0)