2011-07-18 21:14:01 -05:00
|
|
|
// 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::option;
|
|
|
|
import std::int;
|
|
|
|
import std::option::*;
|
2011-07-18 19:26:37 -05:00
|
|
|
import syntax::ast;
|
2011-07-26 09:47:13 -05:00
|
|
|
import syntax::visit;
|
2011-07-18 19:26:37 -05:00
|
|
|
import driver::session;
|
2011-07-18 21:14:01 -05:00
|
|
|
import middle::resolve;
|
2011-07-18 19:26:37 -05:00
|
|
|
import syntax::codemap::span;
|
|
|
|
|
2011-07-18 21:14:01 -05:00
|
|
|
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;
|
2011-08-04 21:31:47 -05:00
|
|
|
export get_freevar_defs;
|
2011-07-21 16:49:58 -05:00
|
|
|
export has_freevars;
|
2011-07-22 14:34:47 -05:00
|
|
|
export is_freevar_of;
|
|
|
|
export def_lookup;
|
2011-07-18 19:26:37 -05:00
|
|
|
|
2011-07-28 20:38:27 -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
|
2011-08-04 17:44:53 -05:00
|
|
|
// "canonical" referencing node_id per free variable. The set is useful for
|
|
|
|
// testing membership, the list of referencing sites is what you want for most
|
2011-07-28 20:38:27 -05:00
|
|
|
// other things.
|
2011-08-12 09:15:18 -05:00
|
|
|
type freevar_set = hashset<ast::node_id>;
|
2011-08-04 18:20:09 -05:00
|
|
|
type freevar_info = {defs: freevar_set, refs: @[ast::node_id]};
|
2011-08-12 09:15:18 -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,
|
2011-08-12 09:15:18 -05:00
|
|
|
walker: &fn(&visit::vt<()>) ,
|
2011-08-04 18:20:09 -05:00
|
|
|
initial_decls: [ast::node_id]) -> freevar_info {
|
2011-08-04 17:44:53 -05:00
|
|
|
let decls = new_int_hash();
|
2011-08-15 23:54:52 -05:00
|
|
|
for decl: ast::node_id in initial_decls { set_add(decls, decl); }
|
2011-08-04 17:44:53 -05:00
|
|
|
let refs = @mutable ~[];
|
2011-07-18 19:26:37 -05:00
|
|
|
|
2011-08-18 03:01:39 -05:00
|
|
|
let walk_fn = lambda(f: &ast::_fn, _tps: &[ast::ty_param], _sp: &span,
|
|
|
|
_i: &ast::fn_ident, _nid: ast::node_id) {
|
2011-08-15 23:54:52 -05:00
|
|
|
for a: ast::arg in f.decl.inputs { set_add(decls, a.id); }
|
2011-08-04 17:44:53 -05:00
|
|
|
};
|
|
|
|
let walk_expr = lambda(expr: &@ast::expr) {
|
2011-07-27 07:19:39 -05:00
|
|
|
alt expr.node {
|
|
|
|
ast::expr_path(path) {
|
2011-08-04 17:44:53 -05:00
|
|
|
if !def_map.contains_key(expr.id) {
|
|
|
|
sess.span_fatal(expr.span,
|
|
|
|
"internal error in collect_freevars");
|
2011-07-18 19:26:37 -05:00
|
|
|
}
|
2011-08-04 17:44:53 -05:00
|
|
|
alt def_map.get(expr.id) {
|
|
|
|
ast::def_arg(did) { *refs += ~[expr.id]; }
|
|
|
|
ast::def_local(did) { *refs += ~[expr.id]; }
|
|
|
|
ast::def_binding(did) { *refs += ~[expr.id]; }
|
2011-07-27 07:19:39 -05:00
|
|
|
_ {/* no-op */ }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { }
|
2011-07-18 19:26:37 -05:00
|
|
|
}
|
2011-08-04 17:44:53 -05:00
|
|
|
};
|
|
|
|
let walk_local = lambda(local: &@ast::local) {
|
2011-08-03 03:19:36 -05:00
|
|
|
for each b: @ast::pat in ast::pat_bindings(local.node.pat) {
|
2011-08-04 17:44:53 -05:00
|
|
|
set_add(decls, b.id);
|
2011-07-28 05:01:45 -05:00
|
|
|
}
|
2011-08-04 17:44:53 -05:00
|
|
|
};
|
|
|
|
let walk_pat = lambda(p: &@ast::pat) {
|
|
|
|
alt p.node { ast::pat_bind(_) { set_add(decls, p.id); } _ { } }
|
|
|
|
};
|
2011-07-18 19:26:37 -05:00
|
|
|
|
2011-07-27 07:48:34 -05:00
|
|
|
walker(visit::mk_simple_visitor
|
2011-08-04 17:44:53 -05:00
|
|
|
(@{visit_local: walk_local,
|
|
|
|
visit_pat: walk_pat,
|
|
|
|
visit_expr: walk_expr,
|
|
|
|
visit_fn: walk_fn
|
2011-07-27 07:48:34 -05:00
|
|
|
with *visit::default_simple_visitor()}));
|
2011-07-18 19:26:37 -05:00
|
|
|
|
2011-07-18 21:14:01 -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.
|
2011-08-04 17:44:53 -05:00
|
|
|
let canonical_refs = ~[];
|
2011-07-27 07:19:39 -05:00
|
|
|
let defs = new_int_hash();
|
2011-08-15 23:54:52 -05:00
|
|
|
for ref_id_: ast::node_id in *refs {
|
2011-07-27 07:19:39 -05:00
|
|
|
let ref_id = ref_id_;
|
|
|
|
let def_id = ast::def_id_of_def(def_map.get(ref_id)).node;
|
2011-07-28 20:38:27 -05:00
|
|
|
if !decls.contains_key(def_id) && !defs.contains_key(def_id) {
|
2011-08-04 17:44:53 -05:00
|
|
|
canonical_refs += ~[ref_id];
|
2011-07-22 19:46:41 -05:00
|
|
|
set_add(defs, def_id);
|
|
|
|
}
|
2011-07-18 19:26:37 -05:00
|
|
|
}
|
2011-08-04 17:44:53 -05:00
|
|
|
ret {defs: defs, refs: @canonical_refs};
|
2011-07-18 19:26:37 -05:00
|
|
|
}
|
|
|
|
|
2011-07-18 21:14:01 -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 {
|
2011-08-04 17:44:53 -05:00
|
|
|
let freevars = new_int_hash();
|
2011-07-18 21:14:01 -05:00
|
|
|
|
2011-08-04 18:20:09 -05:00
|
|
|
let walk_fn = lambda(f: &ast::_fn, tps: &[ast::ty_param], sp: &span,
|
2011-08-05 13:38:06 -05:00
|
|
|
i: &ast::fn_ident, nid: ast::node_id) {
|
2011-08-12 09:15:18 -05:00
|
|
|
let start_walk = lambda(v: &visit::vt<()>) {
|
2011-08-05 13:38:06 -05:00
|
|
|
v.visit_fn(f, tps, sp, i, nid, (), v);
|
2011-08-04 17:44:53 -05:00
|
|
|
};
|
|
|
|
let vars = collect_freevars(def_map, sess, start_walk, ~[]);
|
|
|
|
freevars.insert(nid, vars);
|
|
|
|
};
|
|
|
|
let walk_expr = lambda(expr: &@ast::expr) {
|
2011-07-27 07:19:39 -05:00
|
|
|
alt expr.node {
|
|
|
|
ast::expr_for_each(local, _, body) {
|
2011-08-12 09:15:18 -05:00
|
|
|
let start_walk = lambda(v: &visit::vt<()>) {
|
2011-08-04 17:44:53 -05:00
|
|
|
v.visit_block(body, (), v);
|
|
|
|
};
|
2011-08-03 03:19:36 -05:00
|
|
|
let bound = ast::pat_binding_ids(local.node.pat);
|
2011-07-27 07:19:39 -05:00
|
|
|
let vars =
|
2011-08-04 17:44:53 -05:00
|
|
|
collect_freevars(def_map, sess, start_walk, bound);
|
|
|
|
freevars.insert(body.node.id, vars);
|
2011-07-27 07:19:39 -05:00
|
|
|
}
|
|
|
|
_ { }
|
2011-07-18 21:14:01 -05:00
|
|
|
}
|
2011-08-04 17:44:53 -05:00
|
|
|
};
|
2011-07-18 21:14:01 -05:00
|
|
|
|
2011-07-27 07:19:39 -05:00
|
|
|
let visitor =
|
2011-08-04 17:44:53 -05:00
|
|
|
visit::mk_simple_visitor(@{visit_fn: walk_fn,
|
|
|
|
visit_expr: walk_expr
|
2011-07-27 07:19:39 -05:00
|
|
|
with *visit::default_simple_visitor()});
|
2011-07-26 09:47:13 -05:00
|
|
|
visit::visit_crate(*crate, (), visitor);
|
2011-07-18 21:14:01 -05:00
|
|
|
|
2011-08-04 17:44:53 -05:00
|
|
|
ret freevars;
|
2011-07-18 21:14:01 -05:00
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2011-08-04 21:31:47 -05:00
|
|
|
fn get_freevar_defs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
|
2011-07-22 19:46:41 -05:00
|
|
|
ret get_freevar_info(tcx, fid).defs;
|
|
|
|
}
|
2011-08-04 18:20:09 -05:00
|
|
|
fn get_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> @[ast::node_id] {
|
2011-07-28 20:38:27 -05:00
|
|
|
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 {
|
2011-08-04 21:31:47 -05:00
|
|
|
ret get_freevar_defs(tcx, fid).size() != 0u;
|
2011-07-21 16:49:58 -05:00
|
|
|
}
|
2011-07-28 20:38:27 -05:00
|
|
|
fn is_freevar_of(tcx: &ty::ctxt, def: ast::node_id, f: ast::node_id) -> bool {
|
2011-08-04 21:31:47 -05:00
|
|
|
ret get_freevar_defs(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) ->
|
2011-08-12 09:15:18 -05:00
|
|
|
option::t<ast::def> {
|
2011-07-27 07:19:39 -05:00
|
|
|
alt tcx.def_map.find(id) {
|
|
|
|
none. { ret none; }
|
|
|
|
some(d) {
|
|
|
|
let did = ast::def_id_of_def(d);
|
2011-08-02 17:13:08 -05:00
|
|
|
if f != -1 && is_freevar_of(tcx, did.node, f) {
|
2011-07-22 14:34:47 -05:00
|
|
|
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:
|