rust/src/rustc/middle/trans/reachable.rs

230 lines
7.3 KiB
Rust
Raw Normal View History

// Finds items that are externally reachable, to determine which items
// need to have their metadata (and possibly their AST) serialized.
// All items that can be referred to through an exported name are
// reachable, and when a reachable thing is inline or generic, it
// makes all other generics or inline functions that it references
// reachable as well.
import syntax::ast::*;
import syntax::{visit, ast_util, ast_map};
import syntax::ast_util::def_id_of_def;
import syntax::attr;
import syntax::print::pprust::expr_to_str;
import std::map::hashmap;
import driver::session::*;
export map, find_reachable;
type map = std::map::hashmap<node_id, ()>;
2012-07-17 17:23:59 -05:00
type ctx = {exp_map: resolve3::ExportMap,
tcx: ty::ctxt,
method_map: typeck::method_map,
rmap: map};
2012-07-17 17:23:59 -05:00
fn find_reachable(crate_mod: _mod, exp_map: resolve3::ExportMap,
tcx: ty::ctxt, method_map: typeck::method_map) -> map {
let rmap = std::map::int_hash();
let cx = {exp_map: exp_map, tcx: tcx, method_map: method_map, rmap: rmap};
traverse_public_mod(cx, crate_mod);
traverse_all_resources_and_impls(cx, crate_mod);
rmap
}
fn traverse_exports(cx: ctx, vis: ~[@view_item]) -> bool {
let mut found_export = false;
2012-06-30 18:19:07 -05:00
for vec::each(vis) |vi| {
alt vi.node {
2012-08-03 21:59:04 -05:00
view_item_export(vps) => {
found_export = true;
2012-06-30 18:19:07 -05:00
for vec::each(vps) |vp| {
alt vp.node {
view_path_simple(_, _, id) | view_path_glob(_, id) |
2012-08-03 21:59:04 -05:00
view_path_list(_, _, id) => {
traverse_export(cx, id);
}
}
}
}
2012-08-03 21:59:04 -05:00
_ => ()
}
}
found_export
}
fn traverse_export(cx: ctx, exp_id: node_id) {
2012-06-30 18:19:07 -05:00
do option::iter(cx.exp_map.find(exp_id)) |defs| {
for vec::each(defs) |def| { traverse_def_id(cx, def.id); }
}
}
fn traverse_def_id(cx: ctx, did: def_id) {
2012-08-01 19:30:05 -05:00
if did.crate != local_crate { return; }
let n = alt cx.tcx.items.find(did.node) {
2012-08-03 21:59:04 -05:00
none => return, // This can happen for self, for example
some(n) => n
};
alt n {
2012-08-03 21:59:04 -05:00
ast_map::node_item(item, _) => traverse_public_item(cx, item),
ast_map::node_method(_, impl_id, _) => traverse_def_id(cx, impl_id),
ast_map::node_foreign_item(item, _, _) => {
cx.rmap.insert(item.id, ());
}
ast_map::node_variant(v, _, _) => { cx.rmap.insert(v.node.id, ()); }
// If it's a ctor, consider the parent reachable
2012-08-03 21:59:04 -05:00
ast_map::node_ctor(_, _, _, parent_id, _) => {
2012-06-05 23:17:16 -05:00
traverse_def_id(cx, parent_id);
}
2012-08-03 21:59:04 -05:00
_ => ()
}
}
fn traverse_public_mod(cx: ctx, m: _mod) {
if !traverse_exports(cx, m.view_items) {
// No exports, so every local item is exported
2012-06-30 18:19:07 -05:00
for vec::each(m.items) |item| { traverse_public_item(cx, item); }
}
}
fn traverse_public_item(cx: ctx, item: @item) {
2012-08-01 19:30:05 -05:00
if cx.rmap.contains_key(item.id) { return; }
cx.rmap.insert(item.id, ());
alt item.node {
2012-08-03 21:59:04 -05:00
item_mod(m) => traverse_public_mod(cx, m),
item_foreign_mod(nm) => {
if !traverse_exports(cx, nm.view_items) {
2012-06-30 18:19:07 -05:00
for vec::each(nm.items) |item| { cx.rmap.insert(item.id, ()); }
}
}
2012-08-03 21:59:04 -05:00
item_fn(_, tps, blk) => {
if tps.len() > 0u ||
attr::find_inline_attr(item.attrs) != attr::ia_none {
traverse_inline_body(cx, blk);
}
}
2012-08-03 21:59:04 -05:00
item_impl(tps, _, _, ms) => {
2012-06-30 18:19:07 -05:00
for vec::each(ms) |m| {
if tps.len() > 0u || m.tps.len() > 0u ||
attr::find_inline_attr(m.attrs) != attr::ia_none {
cx.rmap.insert(m.id, ());
traverse_inline_body(cx, m.body);
}
}
}
2012-08-03 21:59:04 -05:00
item_class(tps, _traits, items, m_ctor, m_dtor) => {
do option::iter(m_ctor) |ctor| {
cx.rmap.insert(ctor.node.id, ());
if tps.len() > 0u || attr::find_inline_attr(ctor.node.attrs)
!= attr::ia_none {
traverse_inline_body(cx, ctor.node.body);
}
}
2012-06-30 18:19:07 -05:00
do option::iter(m_dtor) |dtor| {
cx.rmap.insert(dtor.node.id, ());
if tps.len() > 0u || attr::find_inline_attr(dtor.node.attrs)
!= attr::ia_none {
traverse_inline_body(cx, dtor.node.body);
}
}
2012-06-30 18:19:07 -05:00
for vec::each(items) |item| {
alt item.node {
2012-08-03 21:59:04 -05:00
class_method(m) => {
cx.rmap.insert(m.id, ());
if tps.len() > 0u ||
attr::find_inline_attr(m.attrs) != attr::ia_none {
traverse_inline_body(cx, m.body);
}
}
2012-08-03 21:59:04 -05:00
_ => ()
}
}
}
2012-08-03 21:59:04 -05:00
item_ty(t, _) => {
traverse_ty(t, cx, mk_ty_visitor());
}
item_const(*) |
2012-08-03 21:59:04 -05:00
item_enum(*) | item_trait(*) => (),
item_mac(*) => fail ~"item macros unimplemented"
}
}
fn mk_ty_visitor() -> visit::vt<ctx> {
visit::mk_vt(@{visit_ty: traverse_ty with *visit::default_visitor()})
}
fn traverse_ty(ty: @ty, cx: ctx, v: visit::vt<ctx>) {
2012-08-01 19:30:05 -05:00
if cx.rmap.contains_key(ty.id) { return; }
cx.rmap.insert(ty.id, ());
alt ty.node {
2012-08-03 21:59:04 -05:00
ty_path(p, p_id) => {
alt cx.tcx.def_map.find(p_id) {
// Kind of a hack to check this here, but I'm not sure what else
// to do
2012-08-03 21:59:04 -05:00
some(def_prim_ty(_)) => { /* do nothing */ }
some(d) => traverse_def_id(cx, def_id_of_def(d)),
none => { /* do nothing -- but should we fail here? */ }
}
2012-06-30 18:19:07 -05:00
for p.types.each |t| { v.visit_ty(t, cx, v); };
}
2012-08-03 21:59:04 -05:00
_ => visit::visit_ty(ty, cx, v)
}
}
fn traverse_inline_body(cx: ctx, body: blk) {
fn traverse_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
alt e.node {
2012-08-03 21:59:04 -05:00
expr_path(_) => {
alt cx.tcx.def_map.find(e.id) {
2012-08-03 21:59:04 -05:00
some(d) => {
traverse_def_id(cx, def_id_of_def(d));
}
2012-08-03 21:59:04 -05:00
none => cx.tcx.sess.span_bug(e.span, fmt!{"Unbound node \
id %? while traversing %s", e.id, expr_to_str(e)})
}
}
2012-08-03 21:59:04 -05:00
expr_field(_, _, _) => {
alt cx.method_map.find(e.id) {
2012-08-03 21:59:04 -05:00
some({origin: typeck::method_static(did), _}) => {
traverse_def_id(cx, did);
}
2012-08-03 21:59:04 -05:00
_ => ()
}
}
2012-08-03 21:59:04 -05:00
_ => ()
}
visit::visit_expr(e, cx, v);
}
// Don't ignore nested items: for example if a generic fn contains a
// generic impl (as in deque::create), we need to monomorphize the
// impl as well
fn traverse_item(i: @item, cx: ctx, _v: visit::vt<ctx>) {
traverse_public_item(cx, i);
}
visit::visit_block(body, cx, visit::mk_vt(@{
visit_expr: traverse_expr,
visit_item: traverse_item
with *visit::default_visitor()
}));
}
fn traverse_all_resources_and_impls(cx: ctx, crate_mod: _mod) {
visit::visit_mod(crate_mod, ast_util::dummy_sp(), 0, cx, visit::mk_vt(@{
2012-06-30 18:19:07 -05:00
visit_expr: |_e, _cx, _v| { },
visit_item: |i, cx, v| {
visit::visit_item(i, cx, v);
alt i.node {
2012-08-03 21:59:04 -05:00
item_class(_, _, _, _, some(_)) => {
traverse_public_item(cx, i);
}
2012-08-03 21:59:04 -05:00
item_impl(*) => {
traverse_public_item(cx, i);
}
2012-08-03 21:59:04 -05:00
_ => ()
}
}
with *visit::default_visitor()
}));
}