248 lines
7.9 KiB
Rust
248 lines
7.9 KiB
Rust
// 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 core::prelude::*;
|
|
use core::iterator::IteratorUtil;
|
|
|
|
use middle::resolve;
|
|
use middle::ty;
|
|
use middle::typeck;
|
|
|
|
use core::hashmap::HashSet;
|
|
use syntax::ast;
|
|
use syntax::ast::*;
|
|
use syntax::ast_util::def_id_of_def;
|
|
use syntax::attr;
|
|
use syntax::codemap;
|
|
use syntax::print::pprust::expr_to_str;
|
|
use syntax::{visit, ast_map};
|
|
|
|
pub type map = @HashSet<node_id>;
|
|
|
|
struct ctx<'self> {
|
|
exp_map2: resolve::ExportMap2,
|
|
tcx: ty::ctxt,
|
|
method_map: typeck::method_map,
|
|
rmap: &'self mut HashSet<node_id>,
|
|
}
|
|
|
|
pub fn find_reachable(crate_mod: &_mod, exp_map2: resolve::ExportMap2,
|
|
tcx: ty::ctxt, method_map: typeck::method_map) -> map {
|
|
let mut rmap = HashSet::new();
|
|
{
|
|
let cx = @mut 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: @mut 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: @mut ctx, did: def_id) {
|
|
if did.crate != local_crate { return; }
|
|
match cx.tcx.items.find(&did.node) {
|
|
None => (), // This can happen for self, for example
|
|
Some(&ast_map::node_item(item, _)) => traverse_public_item(cx, item),
|
|
Some(&ast_map::node_method(_, impl_id, _)) => traverse_def_id(cx, impl_id),
|
|
Some(&ast_map::node_foreign_item(item, _, _, _)) => {
|
|
let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
|
|
cx.rmap.insert(item.id);
|
|
}
|
|
Some(&ast_map::node_variant(ref v, _, _)) => {
|
|
let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
|
|
cx.rmap.insert(v.node.id);
|
|
}
|
|
_ => ()
|
|
}
|
|
}
|
|
|
|
fn traverse_public_mod(cx: @mut 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: @mut ctx, item: @item) {
|
|
{
|
|
// FIXME #6021: naming rmap shouldn't be necessary
|
|
let cx = &mut *cx;
|
|
let rmap: &mut HashSet<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| {
|
|
let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
|
|
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
|
|
{
|
|
{
|
|
let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
|
|
cx.rmap.insert(m.id);
|
|
}
|
|
traverse_inline_body(cx, &m.body);
|
|
}
|
|
}
|
|
}
|
|
item_struct(ref struct_def, _) => {
|
|
for struct_def.ctor_id.iter().advance |&ctor_id| {
|
|
let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
|
|
cx.rmap.insert(ctor_id);
|
|
}
|
|
}
|
|
item_ty(t, _) => {
|
|
traverse_ty(t, cx,
|
|
visit::mk_vt(@visit::Visitor {visit_ty: traverse_ty,
|
|
..*visit::default_visitor()}))
|
|
}
|
|
item_const(*) |
|
|
item_enum(*) | item_trait(*) => (),
|
|
item_mac(*) => fail!("item macros unimplemented")
|
|
}
|
|
}
|
|
|
|
fn traverse_ty<'a>(ty: @Ty, cx: @mut ctx<'a>, v: visit::vt<@mut ctx<'a>>) {
|
|
{
|
|
let cx = &mut *cx; // FIXME(#6269) reborrow @mut to &mut
|
|
if cx.rmap.contains(&ty.id) { return; }
|
|
cx.rmap.insert(ty.id);
|
|
}
|
|
|
|
match ty.node {
|
|
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
|
|
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? */ }
|
|
}
|
|
for p.types.each |t| {
|
|
(v.visit_ty)(*t, cx, v);
|
|
}
|
|
}
|
|
_ => visit::visit_ty(ty, cx, v)
|
|
}
|
|
}
|
|
|
|
fn traverse_inline_body(cx: @mut ctx, body: &blk) {
|
|
fn traverse_expr<'a>(e: @expr, cx: @mut ctx<'a>,
|
|
v: visit::vt<@mut ctx<'a>>) {
|
|
match e.node {
|
|
expr_path(_) => {
|
|
match cx.tcx.def_map.find(&e.id) {
|
|
Some(&d) => {
|
|
traverse_def_id(cx, def_id_of_def(d));
|
|
}
|
|
None => cx.tcx.sess.span_bug(
|
|
e.span,
|
|
fmt!("Unbound node id %? while traversing %s",
|
|
e.id,
|
|
expr_to_str(e, cx.tcx.sess.intr())))
|
|
}
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
_ => ()
|
|
}
|
|
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: @mut ctx, _v: visit::vt<@mut ctx>) {
|
|
traverse_public_item(cx, i);
|
|
}
|
|
visit::visit_block(body, cx, visit::mk_vt(@visit::Visitor {
|
|
visit_expr: traverse_expr,
|
|
visit_item: traverse_item,
|
|
..*visit::default_visitor()
|
|
}));
|
|
}
|
|
|
|
fn traverse_all_resources_and_impls(cx: @mut 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_impl(*) => {
|
|
traverse_public_item(cx, i);
|
|
}
|
|
_ => ()
|
|
}
|
|
},
|
|
..*visit::default_visitor()
|
|
}));
|
|
}
|