Skip to content

Commit fb61b8f

Browse files
committed
Add a reachability checker, only export metadata for reachable items
Closes #1934
1 parent 4e4bd58 commit fb61b8f

File tree

3 files changed

+146
-7
lines changed

3 files changed

+146
-7
lines changed

src/rustc/metadata/encoder.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ export encode_def_id;
2727

2828
type abbrev_map = map::hashmap<ty::t, tyencode::ty_abbrev>;
2929

30-
type encode_ctxt = {ccx: crate_ctxt, type_abbrevs: abbrev_map};
30+
type encode_ctxt = {ccx: crate_ctxt,
31+
type_abbrevs: abbrev_map,
32+
reachable: reachable::map};
3133

3234
// Path table encoding
3335
fn encode_name(ebml_w: ebml::writer, name: str) {
@@ -73,11 +75,11 @@ fn encode_native_module_item_paths(ebml_w: ebml::writer, nmod: native_mod,
7375
}
7476
}
7577

76-
fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str],
77-
&index: [entry<str>]) {
78+
fn encode_module_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt,
79+
module: _mod, path: [str], &index: [entry<str>]) {
7880
// FIXME factor out add_to_index/start/encode_name/encode_def_id/end ops
7981
for it: @item in module.items {
80-
if !ast_util::is_exported(it.ident, module) { cont; }
82+
if !ecx.reachable.contains_key(it.id) { cont; }
8183
alt it.node {
8284
item_const(_, _) {
8385
add_to_index(ebml_w, path, index, it.ident);
@@ -92,7 +94,8 @@ fn encode_module_item_paths(ebml_w: ebml::writer, module: _mod, path: [str],
9294
ebml_w.start_tag(tag_paths_data_mod);
9395
encode_name(ebml_w, it.ident);
9496
encode_def_id(ebml_w, local_def(it.id));
95-
encode_module_item_paths(ebml_w, _mod, path + [it.ident], index);
97+
encode_module_item_paths(ebml_w, ecx, _mod, path + [it.ident],
98+
index);
9699
ebml_w.end_tag();
97100
}
98101
item_native_mod(nmod) {
@@ -151,7 +154,7 @@ fn encode_item_paths(ebml_w: ebml::writer, ecx: @encode_ctxt, crate: @crate)
151154
let index: [entry<str>] = [];
152155
let path: [str] = [];
153156
ebml_w.start_tag(tag_paths);
154-
encode_module_item_paths(ebml_w, crate.node.module, path, index);
157+
encode_module_item_paths(ebml_w, ecx, crate.node.module, path, index);
155158
encode_reexport_paths(ebml_w, ecx, index);
156159
ebml_w.end_tag();
157160
ret index;
@@ -334,6 +337,12 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
334337
}
335338

336339
let tcx = ecx.ccx.tcx;
340+
let must_write = alt item.node {
341+
item_enum(_, _) | item_res(_, _, _, _, _) { true }
342+
_ { false }
343+
};
344+
if !must_write && !ecx.reachable.contains_key(item.id) { ret; }
345+
337346
alt item.node {
338347
item_const(_, _) {
339348
ebml_w.start_tag(tag_items_data_item);
@@ -488,6 +497,7 @@ fn encode_info_for_item(ecx: @encode_ctxt, ebml_w: ebml::writer, item: @item,
488497

489498
fn encode_info_for_native_item(ecx: @encode_ctxt, ebml_w: ebml::writer,
490499
nitem: @native_item, path: ast_map::path) {
500+
if !ecx.reachable.contains_key(nitem.id) { ret; }
491501
ebml_w.start_tag(tag_items_data_item);
492502
alt nitem.node {
493503
native_item_fn(fn_decl, tps) {
@@ -723,8 +733,11 @@ fn encode_hash(ebml_w: ebml::writer, hash: str) {
723733

724734
fn encode_metadata(cx: crate_ctxt, crate: @crate) -> [u8] {
725735

736+
let reachable = reachable::find_reachable(cx, crate.node.module);
726737
let abbrevs = ty::new_ty_hash();
727-
let ecx = @{ccx: cx, type_abbrevs: abbrevs};
738+
let ecx = @{ccx: cx,
739+
type_abbrevs: abbrevs,
740+
reachable: reachable};
728741

729742
let buf = io::mk_mem_buffer();
730743
let buf_w = io::mem_buffer_writer(buf);

src/rustc/metadata/reachable.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Finds items that are externally reachable, to determine which items
2+
// need to have their metadata (and possibly their AST) serialized.
3+
// All items that can be referred to through an exported name are
4+
// reachable, and when a reachable thing is inline or generic, it
5+
// makes all other generics or inline functions that it references
6+
// reachable as well.
7+
8+
import middle::{resolve, ast_map, typeck};
9+
import syntax::ast::*;
10+
import syntax::visit;
11+
import syntax::ast_util::def_id_of_def;
12+
import front::attr;
13+
14+
export map, find_reachable;
15+
16+
type map = std::map::map<node_id, ()>;
17+
18+
type ctx = {ccx: middle::trans::common::crate_ctxt,
19+
rmap: map};
20+
21+
fn find_reachable(ccx: middle::trans::common::crate_ctxt, crate_mod: _mod)
22+
-> map {
23+
let rmap = std::map::new_int_hash();
24+
traverse_public_mod({ccx: ccx, rmap: rmap}, crate_mod);
25+
rmap
26+
}
27+
28+
fn traverse_exports(cx: ctx, vis: [@view_item]) -> bool {
29+
let found_export = false;
30+
for vi in vis {
31+
alt vi.node {
32+
view_item_export(vps) {
33+
found_export = true;
34+
for vp in vps {
35+
alt vp.node {
36+
view_path_simple(_, _, id) | view_path_glob(_, id) |
37+
view_path_list(_, _, id) {
38+
traverse_export(cx, id);
39+
}
40+
}
41+
}
42+
}
43+
_ {}
44+
}
45+
}
46+
found_export
47+
}
48+
49+
fn traverse_export(cx: ctx, exp_id: node_id) {
50+
option::may(cx.ccx.exp_map.find(exp_id)) {|defs|
51+
for def in defs { traverse_def_id(cx, def.id); }
52+
}
53+
}
54+
55+
fn traverse_def_id(cx: ctx, did: def_id) {
56+
if did.crate != local_crate { ret; }
57+
alt cx.ccx.tcx.items.get(did.node) {
58+
ast_map::node_item(item, _) { traverse_public_item(cx, item); }
59+
ast_map::node_method(_, impl_id, _) { traverse_def_id(cx, impl_id); }
60+
ast_map::node_native_item(item, _) { cx.rmap.insert(item.id, ()); }
61+
ast_map::node_variant(v, _, _) { cx.rmap.insert(v.node.id, ()); }
62+
_ {}
63+
}
64+
}
65+
66+
fn traverse_public_mod(cx: ctx, m: _mod) {
67+
if !traverse_exports(cx, m.view_items) {
68+
// No exports, so every local item is exported
69+
for item in m.items { traverse_public_item(cx, item); }
70+
}
71+
}
72+
73+
fn traverse_public_item(cx: ctx, item: @item) {
74+
if cx.rmap.contains_key(item.id) { ret; }
75+
cx.rmap.insert(item.id, ());
76+
alt item.node {
77+
item_mod(m) { traverse_public_mod(cx, m); }
78+
item_native_mod(nm) {
79+
if !traverse_exports(cx, nm.view_items) {
80+
for item in nm.items { cx.rmap.insert(item.id, ()); }
81+
}
82+
}
83+
item_res(_, tps, blk, _, _) | item_fn(_, tps, blk) {
84+
if tps.len() > 0u ||
85+
attr::find_inline_attr(item.attrs) != attr::ia_none {
86+
traverse_inline_body(cx, blk);
87+
}
88+
}
89+
item_impl(tps, _, _, ms) {
90+
for m in ms {
91+
if tps.len() > 0u || m.tps.len() > 0u ||
92+
attr::find_inline_attr(m.attrs) != attr::ia_none {
93+
traverse_inline_body(cx, m.body);
94+
}
95+
}
96+
}
97+
item_class(_tps, _items, _) {} // FIXME handle these when stable
98+
item_const(_, _) | item_ty(_, _) | item_enum(_, _) | item_iface(_, _) {}
99+
}
100+
}
101+
102+
fn traverse_inline_body(cx: ctx, body: blk) {
103+
fn traverse_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
104+
alt e.node {
105+
expr_path(_) {
106+
traverse_def_id(cx, def_id_of_def(cx.ccx.tcx.def_map.get(e.id)));
107+
}
108+
expr_field(_, _, _) {
109+
alt cx.ccx.maps.method_map.find(e.id) {
110+
some(typeck::method_static(did)) { traverse_def_id(cx, did); }
111+
_ {}
112+
}
113+
}
114+
_ {}
115+
}
116+
visit::visit_expr(e, cx, v);
117+
}
118+
// Ignore nested items
119+
fn traverse_item(_i: @item, _cx: ctx, _v: visit::vt<ctx>) {}
120+
visit::visit_block(body, cx, visit::mk_vt(@{
121+
visit_expr: traverse_expr,
122+
visit_item: traverse_item
123+
with *visit::default_visitor()
124+
}));
125+
}

src/rustc/rustc.rc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ mod metadata {
128128
mod creader;
129129
mod cstore;
130130
mod csearch;
131+
mod reachable;
131132
}
132133

133134
mod driver {

0 commit comments

Comments
 (0)