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

261 lines
8.1 KiB
Rust
Raw Normal View History

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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.
use middle::resolve;
use middle::ty;
use middle::typeck;
use core::prelude::*;
2013-03-22 22:26:41 -04:00
use core::hashmap::linear::LinearSet;
use syntax::ast;
2012-09-04 11:54:36 -07:00
use syntax::ast::*;
use syntax::ast_util::def_id_of_def;
use syntax::attr;
use syntax::codemap;
2012-09-04 11:54:36 -07:00
use syntax::print::pprust::expr_to_str;
use syntax::{visit, ast_util, ast_map};
2013-03-22 22:26:41 -04:00
pub type map = @LinearSet<node_id>;
struct ctx {
exp_map2: resolve::ExportMap2,
tcx: ty::ctxt,
method_map: typeck::method_map,
2013-03-22 22:26:41 -04:00
rmap: &'self mut LinearSet<node_id>,
}
pub fn find_reachable(crate_mod: &_mod, exp_map2: resolve::ExportMap2,
tcx: ty::ctxt, method_map: typeck::method_map) -> map {
2013-03-22 22:26:41 -04:00
let mut rmap = LinearSet::new();
{
let cx = ctx {
exp_map2: exp_map2,
tcx: tcx,
method_map: method_map,
rmap: &mut rmap
};
traverse_public_mod(cx, ast::crate_node_id, crate_mod);
traverse_all_resources_and_impls(cx, crate_mod);
}
return @rmap;
}
fn traverse_exports(cx: ctx, mod_id: node_id) -> bool {
let mut found_export = false;
match cx.exp_map2.find(&mod_id) {
Some(ref exp2s) => {
for (*exp2s).each |e2| {
found_export = true;
traverse_def_id(cx, e2.def_id)
};
}
None => ()
}
return found_export;
}
fn traverse_def_id(cx: ctx, did: def_id) {
2012-08-01 17:30:05 -07:00
if did.crate != local_crate { return; }
let n = match cx.tcx.items.find(&did.node) {
2012-08-20 12:23:37 -07:00
None => return, // This can happen for self, for example
Some(ref n) => (/*bad*/copy *n)
};
2012-08-06 12:34:08 -07:00
match n {
2012-08-03 19:59:04 -07: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, _, _, _) => {
2013-03-22 22:26:41 -04:00
cx.rmap.insert(item.id);
2012-08-03 19:59:04 -07:00
}
2012-12-04 21:13:02 -08:00
ast_map::node_variant(ref v, _, _) => {
2013-03-22 22:26:41 -04:00
cx.rmap.insert(v.node.id);
2012-12-04 21:13:02 -08:00
}
2012-08-03 19:59:04 -07:00
_ => ()
}
}
fn traverse_public_mod(cx: ctx, mod_id: node_id, m: &_mod) {
if !traverse_exports(cx, mod_id) {
// No exports, so every local item is exported
for m.items.each |item| {
traverse_public_item(cx, *item);
}
}
}
fn traverse_public_item(cx: ctx, item: @item) {
2013-03-22 22:26:41 -04:00
// XXX: it shouldn't be necessary to do this
let rmap: &mut LinearSet<node_id> = cx.rmap;
if rmap.contains(&item.id) { return; }
rmap.insert(item.id);
match item.node {
item_mod(ref m) => traverse_public_mod(cx, item.id, m),
item_foreign_mod(ref nm) => {
if !traverse_exports(cx, item.id) {
for nm.items.each |item| {
2013-03-22 22:26:41 -04:00
cx.rmap.insert(item.id);
}
}
}
item_fn(_, _, ref generics, ref blk) => {
if generics.ty_params.len() > 0u ||
attr::find_inline_attr(item.attrs) != attr::ia_none {
traverse_inline_body(cx, blk);
}
}
item_impl(ref generics, _, _, ref ms) => {
for ms.each |m| {
if generics.ty_params.len() > 0u ||
m.generics.ty_params.len() > 0u ||
attr::find_inline_attr(m.attrs) != attr::ia_none
{
2013-03-22 22:26:41 -04:00
cx.rmap.insert(m.id);
traverse_inline_body(cx, &m.body);
}
}
}
item_struct(ref struct_def, ref generics) => {
for struct_def.ctor_id.each |&ctor_id| {
2013-03-22 22:26:41 -04:00
cx.rmap.insert(ctor_id);
}
for struct_def.dtor.each |dtor| {
2013-03-22 22:26:41 -04:00
cx.rmap.insert(dtor.node.id);
if generics.ty_params.len() > 0u ||
attr::find_inline_attr(dtor.node.attrs) != attr::ia_none
{
traverse_inline_body(cx, &dtor.node.body);
}
}
}
2012-08-03 19:59:04 -07:00
item_ty(t, _) => {
traverse_ty(t, cx, mk_ty_visitor());
}
item_const(*) |
2012-08-03 19:59:04 -07:00
item_enum(*) | item_trait(*) => (),
item_mac(*) => fail!(~"item macros unimplemented")
}
}
fn mk_ty_visitor() -> visit::vt<ctx> {
visit::mk_vt(@visit::Visitor {visit_ty: traverse_ty,
..*visit::default_visitor()})
}
2013-03-22 22:26:41 -04:00
fn traverse_ty(ty: @Ty, cx: ctx<'a>, v: visit::vt<ctx<'a>>) {
// XXX: it shouldn't be necessary to do this
let rmap: &mut LinearSet<node_id> = cx.rmap;
if rmap.contains(&ty.id) { return; }
rmap.insert(ty.id);
2012-08-06 12:34:08 -07:00
match ty.node {
2012-08-03 19:59:04 -07:00
ty_path(p, p_id) => {
match cx.tcx.def_map.find(&p_id) {
// Kind of a hack to check this here, but I'm not sure what else
// to do
2013-03-22 22:26:41 -04:00
Some(&def_prim_ty(_)) => { /* do nothing */ }
Some(&d) => traverse_def_id(cx, def_id_of_def(d)),
2012-08-20 12:23:37 -07:00
None => { /* do nothing -- but should we fail here? */ }
}
for p.types.each |t| {
(v.visit_ty)(*t, cx, v);
}
}
2012-08-03 19:59:04 -07:00
_ => visit::visit_ty(ty, cx, v)
}
}
fn traverse_inline_body(cx: ctx, body: &blk) {
2013-03-22 22:26:41 -04:00
fn traverse_expr(e: @expr, cx: ctx<'a>, v: visit::vt<ctx<'a>>) {
2012-08-06 12:34:08 -07:00
match e.node {
2012-08-03 19:59:04 -07:00
expr_path(_) => {
match cx.tcx.def_map.find(&e.id) {
2013-03-22 22:26:41 -04:00
Some(&d) => {
traverse_def_id(cx, def_id_of_def(d));
}
2012-08-20 12:23:37 -07:00
None => cx.tcx.sess.span_bug(e.span, fmt!("Unbound node \
2012-07-18 16:18:02 -07:00
id %? while traversing %s", e.id,
2012-08-22 17:24:52 -07:00
expr_to_str(e, cx.tcx.sess.intr())))
}
}
2012-08-03 19:59:04 -07:00
expr_field(_, _, _) => {
match cx.method_map.find(&e.id) {
Some(typeck::method_map_entry {
origin: typeck::method_static(did),
_
}) => {
traverse_def_id(cx, did);
}
2012-08-03 19:59:04 -07:00
_ => ()
}
}
expr_method_call(*) => {
match cx.method_map.find(&e.id) {
Some(typeck::method_map_entry {
origin: typeck::method_static(did),
_
}) => {
traverse_def_id(cx, did);
}
Some(_) => {}
None => {
cx.tcx.sess.span_bug(e.span, ~"expr_method_call not in \
method map");
}
}
}
2012-08-03 19:59:04 -07: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::Visitor {
visit_expr: traverse_expr,
2012-09-04 13:29:32 -07:00
visit_item: traverse_item,
..*visit::default_visitor()
}));
}
fn traverse_all_resources_and_impls(cx: ctx, crate_mod: &_mod) {
visit::visit_mod(
crate_mod,
codemap::dummy_sp(),
0,
cx,
visit::mk_vt(@visit::Visitor {
visit_expr: |_e, _cx, _v| { },
visit_item: |i, cx, v| {
visit::visit_item(i, cx, v);
match i.node {
item_struct(sdef, _) if sdef.dtor.is_some() => {
traverse_public_item(cx, i);
}
item_impl(*) => {
traverse_public_item(cx, i);
}
_ => ()
}
},
..*visit::default_visitor()
}));
}