rust/src/comp/middle/freevars.rs

196 lines
7.1 KiB
Rust
Raw Normal View History

// A pass that annotates for each loops and functions with the free
// variables that they contain.
2011-07-18 19:26:37 -05:00
import std::map;
import std::map::*;
2011-07-21 16:49:58 -05:00
import std::ivec;
import std::option;
import std::int;
import std::option::*;
2011-07-18 19:26:37 -05:00
import syntax::ast;
import syntax::visit;
2011-07-18 19:26:37 -05:00
import driver::session;
import middle::resolve;
2011-07-18 19:26:37 -05:00
import syntax::codemap::span;
export annotate_freevars;
export freevar_set;
export freevar_map;
2011-07-22 19:46:41 -05:00
export get_freevar_info;
2011-07-21 16:49:58 -05:00
export get_freevars;
export get_freevar_refs;
2011-07-21 16:49:58 -05:00
export has_freevars;
export is_freevar_of;
export def_lookup;
2011-07-18 19:26:37 -05:00
// Throughout the compiler, variables are generally dealt with using the
// node_ids of the reference sites and not the def_id of the definition
// site. Thus we store a set are the definitions along with a vec of one
// referencing node_id per free variable. The set is useful for testing
// membership, the list of referencing sites is what you want for most
// other things.
2011-07-22 19:46:41 -05:00
type freevar_set = hashset[ast::node_id];
type freevar_info = {defs: freevar_set, refs: @ast::node_id[]};
2011-07-22 19:46:41 -05:00
type freevar_map = hashmap[ast::node_id, freevar_info];
2011-07-18 19:26:37 -05:00
// Searches through part of the AST for all references to locals or
// upvars in this frame and returns the list of definition IDs thus found.
// Since we want to be able to collect upvars in some arbitrary piece
// of the AST, we take a walker function that we invoke with a visitor
// in order to start the search.
2011-07-27 07:19:39 -05:00
fn collect_freevars(def_map: &resolve::def_map, sess: &session::session,
walker: &fn(&visit::vt[()]) ,
initial_decls: ast::node_id[]) -> freevar_info {
2011-07-18 19:26:37 -05:00
type env =
2011-07-27 07:19:39 -05:00
@{mutable refs: ast::node_id[],
decls: hashset[ast::node_id],
def_map: resolve::def_map,
sess: session::session};
2011-07-18 19:26:37 -05:00
2011-07-27 07:19:39 -05:00
fn walk_fn(e: env, f: &ast::_fn, tps: &ast::ty_param[], sp: &span,
i: &ast::fn_ident, nid: ast::node_id) {
for a: ast::arg in f.decl.inputs { e.decls.insert(a.id, ()); }
2011-07-18 19:26:37 -05:00
}
2011-07-27 07:19:39 -05:00
fn walk_expr(e: env, expr: &@ast::expr) {
alt expr.node {
ast::expr_path(path) {
if !e.def_map.contains_key(expr.id) {
e.sess.span_fatal(expr.span,
"internal error in collect_freevars");
2011-07-18 19:26:37 -05:00
}
2011-07-27 07:19:39 -05:00
alt e.def_map.get(expr.id) {
ast::def_arg(did) { e.refs += ~[expr.id]; }
ast::def_local(did) { e.refs += ~[expr.id]; }
ast::def_binding(did) { e.refs += ~[expr.id]; }
_ {/* no-op */ }
}
}
_ { }
2011-07-18 19:26:37 -05:00
}
}
2011-07-27 07:19:39 -05:00
fn walk_local(e: env, local: &@ast::local) {
for each b: @ast::pat in ast::pat_bindings(local.node.pat) {
set_add(e.decls, b.id);
}
2011-07-18 19:26:37 -05:00
}
2011-07-27 07:19:39 -05:00
fn walk_pat(e: env, p: &@ast::pat) {
alt p.node { ast::pat_bind(_) { set_add(e.decls, p.id); } _ { } }
2011-07-18 19:26:37 -05:00
}
2011-07-27 07:19:39 -05:00
let decls: hashset[ast::node_id] = new_int_hash();
for decl: ast::node_id in initial_decls { set_add(decls, decl); }
2011-07-18 19:26:37 -05:00
2011-07-27 07:19:39 -05:00
let e: env =
@{mutable refs: ~[], decls: decls, def_map: def_map, sess: sess};
2011-07-27 07:48:34 -05:00
walker(visit::mk_simple_visitor
(@{visit_local: bind walk_local(e, _),
visit_pat: bind walk_pat(e, _),
visit_expr: bind walk_expr(e, _),
visit_fn: bind walk_fn(e, _, _, _, _, _)
with *visit::default_simple_visitor()}));
2011-07-18 19:26:37 -05:00
// Calculate (refs - decls). This is the set of captured upvars.
2011-07-22 19:46:41 -05:00
// We build a vec of the node ids of the uses and a set of the
// node ids of the definitions.
let refs = ~[];
2011-07-27 07:19:39 -05:00
let defs = new_int_hash();
for ref_id_: ast::node_id in e.refs {
let ref_id = ref_id_;
let def_id = ast::def_id_of_def(def_map.get(ref_id)).node;
if !decls.contains_key(def_id) && !defs.contains_key(def_id) {
refs += ~[ref_id];
2011-07-22 19:46:41 -05:00
set_add(defs, def_id);
}
2011-07-18 19:26:37 -05:00
}
ret {defs: defs, refs: @refs};
2011-07-18 19:26:37 -05:00
}
// Build a map from every function and for-each body to a set of the
// freevars contained in it. The implementation is not particularly
// efficient as it fully recomputes the free variables at every
// node of interest rather than building up the free variables in
// one pass. This could be improved upon if it turns out to matter.
2011-07-27 07:19:39 -05:00
fn annotate_freevars(sess: &session::session, def_map: &resolve::def_map,
crate: &@ast::crate) -> freevar_map {
type env =
2011-07-27 07:19:39 -05:00
{freevars: freevar_map,
def_map: resolve::def_map,
sess: session::session};
2011-07-27 07:19:39 -05:00
fn walk_fn(e: env, f: &ast::_fn, tps: &ast::ty_param[], sp: &span,
i: &ast::fn_ident, nid: ast::node_id) {
fn start_walk(f: &ast::_fn, tps: &ast::ty_param[], sp: &span,
i: &ast::fn_ident, nid: ast::node_id,
v: &visit::vt[()]) {
v.visit_fn(f, tps, sp, i, nid, (), v);
}
2011-07-27 07:19:39 -05:00
let walker = bind start_walk(f, tps, sp, i, nid, _);
let vars = collect_freevars(e.def_map, e.sess, walker, ~[]);
e.freevars.insert(nid, vars);
}
2011-07-27 07:19:39 -05:00
fn walk_expr(e: env, expr: &@ast::expr) {
alt expr.node {
ast::expr_for_each(local, _, body) {
fn start_walk(b: &ast::blk, v: &visit::vt[()]) {
v.visit_block(b, (), v);
}
let bound = ast::pat_binding_ids(local.node.pat);
2011-07-27 07:19:39 -05:00
let vars =
collect_freevars(e.def_map, e.sess, bind start_walk(body, _),
bound);
2011-07-27 07:19:39 -05:00
e.freevars.insert(body.node.id, vars);
}
_ { }
}
}
2011-07-27 07:19:39 -05:00
let e: env = {freevars: new_int_hash(), def_map: def_map, sess: sess};
let visitor =
visit::mk_simple_visitor(@{visit_fn: bind walk_fn(e, _, _, _, _, _),
visit_expr: bind walk_expr(e, _)
with *visit::default_simple_visitor()});
visit::visit_crate(*crate, (), visitor);
ret e.freevars;
}
2011-07-27 07:19:39 -05:00
fn get_freevar_info(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_info {
alt tcx.freevars.find(fid) {
none. { fail "get_freevars: " + int::str(fid) + " has no freevars"; }
some(d) { ret d; }
2011-07-21 16:49:58 -05:00
}
}
fn get_freevar_refs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
2011-07-22 19:46:41 -05:00
ret get_freevar_info(tcx, fid).defs;
}
fn get_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> @ast::node_id[] {
ret get_freevar_info(tcx, fid).refs;
2011-07-22 19:46:41 -05:00
}
2011-07-27 07:19:39 -05:00
fn has_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> bool {
ret get_freevar_refs(tcx, fid).size() != 0u;
2011-07-21 16:49:58 -05:00
}
fn is_freevar_of(tcx: &ty::ctxt, def: ast::node_id, f: ast::node_id) -> bool {
ret get_freevar_refs(tcx, f).contains_key(def);
2011-07-21 16:49:58 -05:00
}
2011-07-27 07:19:39 -05:00
fn def_lookup(tcx: &ty::ctxt, f: ast::node_id, id: ast::node_id) ->
option::t[ast::def] {
alt tcx.def_map.find(id) {
none. { ret none; }
some(d) {
let did = ast::def_id_of_def(d);
if f != -1 && is_freevar_of(tcx, did.node, f) {
ret some(ast::def_upvar(did, @d));
} else { ret some(d); }
}
}
}
2011-07-21 16:49:58 -05:00
2011-07-18 19:26:37 -05:00
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End: