Skip to content

Commit 69075f4

Browse files
authored
Merge pull request #850 from photoszzt/has_vtable
has vtable analysis
2 parents 23dbe48 + 0969a8d commit 69075f4

File tree

10 files changed

+272
-62
lines changed

10 files changed

+272
-62
lines changed

src/codegen/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,7 @@ impl CodeGenerator for CompInfo {
14731473
// the parent too.
14741474
let mut fields = vec![];
14751475
let mut struct_layout = StructLayoutTracker::new(ctx, self, &canonical_name);
1476-
if self.needs_explicit_vtable(ctx) {
1476+
if self.needs_explicit_vtable(ctx, item) {
14771477
let vtable =
14781478
Vtable::new(item.id(), self.methods(), self.base_members());
14791479
vtable.codegen(ctx, result, item);
@@ -1504,7 +1504,7 @@ impl CodeGenerator for CompInfo {
15041504
// NB: We won't include unsized types in our base chain because they
15051505
// would contribute to our size given the dummy field we insert for
15061506
// unsized types.
1507-
if base_ty.is_unsized(ctx) {
1507+
if base_ty.is_unsized(ctx, &base.ty) {
15081508
continue;
15091509
}
15101510

@@ -1583,7 +1583,7 @@ impl CodeGenerator for CompInfo {
15831583
warn!("Opaque type without layout! Expect dragons!");
15841584
}
15851585
}
1586-
} else if !is_union && !self.is_unsized(ctx) {
1586+
} else if !is_union && !self.is_unsized(ctx, &item.id()) {
15871587
if let Some(padding_field) =
15881588
layout.and_then(|layout| {
15891589
struct_layout.pad_struct(layout)
@@ -1607,7 +1607,7 @@ impl CodeGenerator for CompInfo {
16071607
//
16081608
// NOTE: This check is conveniently here to avoid the dummy fields we
16091609
// may add for unused template parameters.
1610-
if self.is_unsized(ctx) {
1610+
if self.is_unsized(ctx, &item.id()) {
16111611
let has_address = if is_opaque {
16121612
// Generate the address field if it's an opaque type and
16131613
// couldn't determine the layout of the blob.
@@ -1707,7 +1707,7 @@ impl CodeGenerator for CompInfo {
17071707
let too_many_base_vtables = self.base_members()
17081708
.iter()
17091709
.filter(|base| {
1710-
ctx.resolve_type(base.ty).has_vtable(ctx)
1710+
ctx.lookup_item_id_has_vtable(&base.ty)
17111711
})
17121712
.count() > 1;
17131713

src/ir/analysis/has_vtable.rs

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//! Determining which types has vtable
2+
use super::{ConstrainResult, MonotoneFramework};
3+
use std::collections::HashSet;
4+
use std::collections::HashMap;
5+
use ir::context::{BindgenContext, ItemId};
6+
use ir::traversal::EdgeKind;
7+
use ir::ty::TypeKind;
8+
use ir::traversal::Trace;
9+
10+
/// An analysis that finds for each IR item whether it has vtable or not
11+
///
12+
/// We use the monotone function `has vtable`, defined as follows:
13+
///
14+
/// * If T is a type alias, a templated alias, an indirection to another type,
15+
/// or a reference of a type, T has vtable if the type T refers to has vtable.
16+
/// * If T is a compound type, T has vtable if we saw a virtual function when
17+
/// parsing it or any of its base member has vtable.
18+
/// * If T is an instantiation of an abstract template definition, T has
19+
/// vtable if template definition has vtable
20+
#[derive(Debug, Clone)]
21+
pub struct HasVtableAnalysis<'ctx, 'gen>
22+
where 'gen: 'ctx
23+
{
24+
ctx: &'ctx BindgenContext<'gen>,
25+
26+
// The incremental result of this analysis's computation. Everything in this
27+
// set definitely has a vtable.
28+
have_vtable: HashSet<ItemId>,
29+
30+
// Dependencies saying that if a key ItemId has been inserted into the
31+
// `have_vtable` set, then each of the ids in Vec<ItemId> need to be
32+
// considered again.
33+
//
34+
// This is a subset of the natural IR graph with reversed edges, where we
35+
// only include the edges from the IR graph that can affect whether a type
36+
// has a vtable or not.
37+
dependencies: HashMap<ItemId, Vec<ItemId>>,
38+
}
39+
40+
impl<'ctx, 'gen> HasVtableAnalysis<'ctx, 'gen> {
41+
fn consider_edge(kind: EdgeKind) -> bool {
42+
match kind {
43+
// These are the only edges that can affect whether a type has a
44+
// vtable or not.
45+
EdgeKind::TypeReference |
46+
EdgeKind::BaseMember |
47+
EdgeKind::TemplateDeclaration => true,
48+
_ => false,
49+
}
50+
}
51+
52+
fn insert(&mut self, id: ItemId) -> ConstrainResult {
53+
let was_not_already_in_set = self.have_vtable.insert(id);
54+
assert!(
55+
was_not_already_in_set,
56+
"We shouldn't try and insert {:?} twice because if it was \
57+
already in the set, `constrain` should have exited early.",
58+
id
59+
);
60+
ConstrainResult::Changed
61+
}
62+
}
63+
64+
impl<'ctx, 'gen> MonotoneFramework for HasVtableAnalysis<'ctx, 'gen> {
65+
type Node = ItemId;
66+
type Extra = &'ctx BindgenContext<'gen>;
67+
type Output = HashSet<ItemId>;
68+
69+
fn new(ctx: &'ctx BindgenContext<'gen>) -> HasVtableAnalysis<'ctx, 'gen> {
70+
let have_vtable = HashSet::new();
71+
let mut dependencies = HashMap::new();
72+
73+
for &item in ctx.whitelisted_items() {
74+
dependencies.entry(item).or_insert(vec![]);
75+
76+
{
77+
// We reverse our natural IR graph edges to find dependencies
78+
// between nodes.
79+
item.trace(ctx, &mut |sub_item: ItemId, edge_kind| {
80+
if ctx.whitelisted_items().contains(&sub_item) &&
81+
Self::consider_edge(edge_kind) {
82+
dependencies.entry(sub_item)
83+
.or_insert(vec![])
84+
.push(item);
85+
}
86+
}, &());
87+
}
88+
}
89+
90+
HasVtableAnalysis {
91+
ctx,
92+
have_vtable,
93+
dependencies,
94+
}
95+
}
96+
97+
fn initial_worklist(&self) -> Vec<ItemId> {
98+
self.ctx.whitelisted_items().iter().cloned().collect()
99+
}
100+
101+
fn constrain(&mut self, id: ItemId) -> ConstrainResult {
102+
if self.have_vtable.contains(&id) {
103+
// We've already computed that this type has a vtable and that can't
104+
// change.
105+
return ConstrainResult::Same;
106+
}
107+
108+
let item = self.ctx.resolve_item(id);
109+
let ty = match item.as_type() {
110+
None => return ConstrainResult::Same,
111+
Some(ty) => ty
112+
};
113+
114+
// TODO #851: figure out a way to handle deriving from template type parameters.
115+
match *ty.kind() {
116+
TypeKind::TemplateAlias(t, _) |
117+
TypeKind::Alias(t) |
118+
TypeKind::ResolvedTypeRef(t) |
119+
TypeKind::Reference(t) => {
120+
if self.have_vtable.contains(&t) {
121+
self.insert(id)
122+
} else {
123+
ConstrainResult::Same
124+
}
125+
},
126+
127+
TypeKind::Comp(ref info) => {
128+
if info.has_own_virtual_method() {
129+
return self.insert(id);
130+
}
131+
let bases_has_vtable = info.base_members().iter().any(|base| {
132+
self.have_vtable.contains(&base.ty)
133+
});
134+
if bases_has_vtable {
135+
self.insert(id)
136+
} else {
137+
ConstrainResult::Same
138+
}
139+
},
140+
141+
TypeKind::TemplateInstantiation(ref inst) => {
142+
if self.have_vtable.contains(&inst.template_definition()) {
143+
self.insert(id)
144+
} else {
145+
ConstrainResult::Same
146+
}
147+
},
148+
149+
_ => ConstrainResult::Same,
150+
}
151+
}
152+
153+
fn each_depending_on<F>(&self, id: ItemId, mut f: F)
154+
where F: FnMut(ItemId),
155+
{
156+
if let Some(edges) = self.dependencies.get(&id) {
157+
for item in edges {
158+
trace!("enqueue {:?} into worklist", item);
159+
f(*item);
160+
}
161+
}
162+
}
163+
}
164+
165+
impl<'ctx, 'gen> From<HasVtableAnalysis<'ctx, 'gen>> for HashSet<ItemId> {
166+
fn from(analysis: HasVtableAnalysis<'ctx, 'gen>) -> Self {
167+
analysis.have_vtable
168+
}
169+
}
170+
171+
/// A convenience trait for the things for which we might wonder if they have a
172+
/// vtable during codegen.
173+
///
174+
/// This is not for _computing_ whether the thing has a vtable, it is for
175+
/// looking up the results of the HasVtableAnalysis's computations for a
176+
/// specific thing.
177+
pub trait HasVtable {
178+
179+
/// Implementations can define this type to get access to any extra
180+
/// information required to determine whether they have vtable. If
181+
/// extra information is unneeded, then this should simply be the unit type.
182+
type Extra;
183+
184+
/// Return `true` if this thing has vtable, `false` otherwise.
185+
fn has_vtable(&self, ctx: &BindgenContext, extra: &Self::Extra) -> bool;
186+
}

src/ir/analysis/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ mod template_params;
4242
pub use self::template_params::UsedTemplateParameters;
4343
mod derive_debug;
4444
pub use self::derive_debug::CannotDeriveDebug;
45+
mod has_vtable;
46+
pub use self::has_vtable::HasVtableAnalysis;
47+
pub use self::has_vtable::HasVtable;
4548

4649
use std::fmt;
4750

src/ir/comp.rs

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ impl FieldMethods for FieldData {
701701
}
702702
}
703703

704-
impl CanDeriveDefault for Field {
704+
impl<'a> CanDeriveDefault<'a> for Field {
705705
type Extra = ();
706706

707707
fn can_derive_default(&self, ctx: &BindgenContext, _: ()) -> bool {
@@ -819,7 +819,7 @@ pub struct CompInfo {
819819

820820
/// Whether this type should generate an vtable (TODO: Should be able to
821821
/// look at the virtual methods and ditch this field).
822-
has_vtable: bool,
822+
has_own_virtual_method: bool,
823823

824824
/// Whether this type has destructor.
825825
has_destructor: bool,
@@ -870,7 +870,7 @@ impl CompInfo {
870870
base_members: vec![],
871871
inner_types: vec![],
872872
inner_vars: vec![],
873-
has_vtable: false,
873+
has_own_virtual_method: false,
874874
has_destructor: false,
875875
has_nonempty_base: false,
876876
has_non_type_template_params: false,
@@ -883,10 +883,10 @@ impl CompInfo {
883883
}
884884

885885
/// Is this compound type unsized?
886-
pub fn is_unsized(&self, ctx: &BindgenContext) -> bool {
887-
!self.has_vtable(ctx) && self.fields().is_empty() &&
886+
pub fn is_unsized(&self, ctx: &BindgenContext, itemid: &ItemId) -> bool {
887+
!ctx.lookup_item_id_has_vtable(itemid) && self.fields().is_empty() &&
888888
self.base_members.iter().all(|base| {
889-
ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx)
889+
ctx.resolve_type(base.ty).canonical_type(ctx).is_unsized(ctx, &base.ty)
890890
})
891891
}
892892

@@ -964,13 +964,10 @@ impl CompInfo {
964964
self.has_non_type_template_params
965965
}
966966

967-
/// Does this type have a virtual table?
968-
pub fn has_vtable(&self, ctx: &BindgenContext) -> bool {
969-
self.has_vtable ||
970-
self.base_members().iter().any(|base| {
971-
ctx.resolve_type(base.ty)
972-
.has_vtable(ctx)
973-
})
967+
/// Do we see a virtual function during parsing?
968+
/// Get the has_own_virtual_method boolean.
969+
pub fn has_own_virtual_method(&self) -> bool {
970+
return self.has_own_virtual_method;
974971
}
975972

976973
/// Get this type's set of methods.
@@ -1169,7 +1166,7 @@ impl CompInfo {
11691166
}
11701167
CXCursor_CXXBaseSpecifier => {
11711168
let is_virtual_base = cur.is_virtual_base();
1172-
ci.has_vtable |= is_virtual_base;
1169+
ci.has_own_virtual_method |= is_virtual_base;
11731170

11741171
let kind = if is_virtual_base {
11751172
BaseKind::Virtual
@@ -1192,7 +1189,7 @@ impl CompInfo {
11921189
debug_assert!(!(is_static && is_virtual), "How?");
11931190

11941191
ci.has_destructor |= cur.kind() == CXCursor_Destructor;
1195-
ci.has_vtable |= is_virtual;
1192+
ci.has_own_virtual_method |= is_virtual;
11961193

11971194
// This used to not be here, but then I tried generating
11981195
// stylo bindings with this (without path filters), and
@@ -1335,8 +1332,8 @@ impl CompInfo {
13351332

13361333
/// Returns whether this type needs an explicit vtable because it has
13371334
/// virtual methods and none of its base classes has already a vtable.
1338-
pub fn needs_explicit_vtable(&self, ctx: &BindgenContext) -> bool {
1339-
self.has_vtable(ctx) &&
1335+
pub fn needs_explicit_vtable(&self, ctx: &BindgenContext, item: &Item) -> bool {
1336+
ctx.lookup_item_id_has_vtable(&item.id()) &&
13401337
!self.base_members.iter().any(|base| {
13411338
// NB: Ideally, we could rely in all these types being `comp`, and
13421339
// life would be beautiful.
@@ -1347,7 +1344,7 @@ impl CompInfo {
13471344
ctx.resolve_type(base.ty)
13481345
.canonical_type(ctx)
13491346
.as_comp()
1350-
.map_or(false, |ci| ci.has_vtable(ctx))
1347+
.map_or(false, |_| ctx.lookup_item_id_has_vtable(&base.ty))
13511348
})
13521349
}
13531350

@@ -1368,7 +1365,7 @@ impl DotAttributes for CompInfo {
13681365
{
13691366
writeln!(out, "<tr><td>CompKind</td><td>{:?}</td></tr>", self.kind)?;
13701367

1371-
if self.has_vtable {
1368+
if self.has_own_virtual_method {
13721369
writeln!(out, "<tr><td>has_vtable</td><td>true</td></tr>")?;
13731370
}
13741371

@@ -1424,12 +1421,12 @@ impl TemplateParameters for CompInfo {
14241421
}
14251422
}
14261423

1427-
impl CanDeriveDefault for CompInfo {
1428-
type Extra = Option<Layout>;
1424+
impl<'a> CanDeriveDefault<'a> for CompInfo {
1425+
type Extra = (&'a Item, Option<Layout>);
14291426

14301427
fn can_derive_default(&self,
14311428
ctx: &BindgenContext,
1432-
layout: Option<Layout>)
1429+
(item, layout): (&Item, Option<Layout>))
14331430
-> bool {
14341431
// We can reach here recursively via template parameters of a member,
14351432
// for example.
@@ -1452,8 +1449,8 @@ impl CanDeriveDefault for CompInfo {
14521449

14531450
self.detect_derive_default_cycle.set(true);
14541451

1455-
let can_derive_default = !self.has_vtable(ctx) &&
1456-
!self.needs_explicit_vtable(ctx) &&
1452+
let can_derive_default = !ctx.lookup_item_id_has_vtable(&item.id()) &&
1453+
!self.needs_explicit_vtable(ctx, item) &&
14571454
self.base_members
14581455
.iter()
14591456
.all(|base| base.ty.can_derive_default(ctx, ())) &&

0 commit comments

Comments
 (0)