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
|
|
|
|
2011-12-13 18:25:51 -06:00
|
|
|
import int;
|
2011-07-18 19:26:37 -05:00
|
|
|
import std::map::*;
|
2011-12-13 18:25:51 -06:00
|
|
|
import option::*;
|
2011-09-12 18:13:28 -05:00
|
|
|
import syntax::{ast, ast_util, visit};
|
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_map;
|
2011-12-16 18:07:54 -06:00
|
|
|
export freevar_info;
|
2011-07-21 16:49:58 -05:00
|
|
|
export get_freevars;
|
|
|
|
export has_freevars;
|
2011-07-18 19:26:37 -05:00
|
|
|
|
2011-09-01 07:35:00 -05:00
|
|
|
// A vector of defs representing the free variables referred to in a function.
|
|
|
|
// (The def_upvar will already have been stripped).
|
2011-12-16 18:07:54 -06:00
|
|
|
type freevar_entry = {
|
|
|
|
def: ast::def, //< The variable being accessed free.
|
|
|
|
span: span //< First span where it is accessed (there can be multiple)
|
|
|
|
};
|
|
|
|
type freevar_info = @[@freevar_entry];
|
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-10-18 17:07:40 -05:00
|
|
|
fn collect_freevars(def_map: resolve::def_map, walker: fn@(visit::vt<int>)) ->
|
2011-09-12 04:27:30 -05:00
|
|
|
freevar_info {
|
2011-09-01 07:35:00 -05:00
|
|
|
let seen = new_int_hash();
|
2011-08-19 17:16:48 -05:00
|
|
|
let refs = @mutable [];
|
2011-07-18 19:26:37 -05:00
|
|
|
|
2011-10-06 05:26:12 -05:00
|
|
|
fn ignore_item(_i: @ast::item, &&_depth: int, _v: visit::vt<int>) { }
|
2011-09-01 07:35:00 -05:00
|
|
|
|
2011-09-02 17:34:58 -05:00
|
|
|
let walk_expr =
|
2011-10-06 05:26:12 -05:00
|
|
|
lambda (expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
|
2011-09-02 17:34:58 -05:00
|
|
|
alt expr.node {
|
2011-12-09 10:16:04 -06:00
|
|
|
ast::expr_fn(f, captures) {
|
2011-12-09 18:56:48 -06:00
|
|
|
if f.proto != ast::proto_bare {
|
2011-09-02 17:34:58 -05:00
|
|
|
visit::visit_expr(expr, depth + 1, v);
|
2011-08-19 17:16:48 -05:00
|
|
|
}
|
2011-09-02 17:34:58 -05:00
|
|
|
}
|
|
|
|
ast::expr_path(path) {
|
|
|
|
let def = def_map.get(expr.id), i = 0;
|
|
|
|
while i < depth {
|
2011-09-12 06:13:20 -05:00
|
|
|
alt copy def {
|
2011-09-02 17:34:58 -05:00
|
|
|
ast::def_upvar(_, inner, _) { def = *inner; }
|
|
|
|
_ { break; }
|
|
|
|
}
|
|
|
|
i += 1;
|
2011-09-01 07:35:00 -05:00
|
|
|
}
|
2011-09-02 17:34:58 -05:00
|
|
|
if i == depth { // Made it to end of loop
|
|
|
|
let dnum = ast_util::def_id_of_def(def).node;
|
|
|
|
if !seen.contains_key(dnum) {
|
2011-12-16 18:07:54 -06:00
|
|
|
*refs += [@{def:def, span:expr.span}];
|
2011-09-02 17:34:58 -05:00
|
|
|
seen.insert(dnum, ());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ { visit::visit_expr(expr, depth, v); }
|
2011-07-27 07:19:39 -05:00
|
|
|
}
|
2011-09-02 17:34:58 -05:00
|
|
|
};
|
2011-09-01 07:35:00 -05:00
|
|
|
|
2011-09-02 17:34:58 -05:00
|
|
|
walker(visit::mk_vt(@{visit_item: ignore_item, visit_expr: walk_expr
|
|
|
|
with *visit::default_visitor()}));
|
2011-09-01 07:35:00 -05:00
|
|
|
ret @*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-09-12 04:27:30 -05:00
|
|
|
fn annotate_freevars(def_map: resolve::def_map, crate: @ast::crate) ->
|
2011-09-02 17:34:58 -05:00
|
|
|
freevar_map {
|
2011-08-04 17:44:53 -05:00
|
|
|
let freevars = new_int_hash();
|
2011-07-18 21:14:01 -05:00
|
|
|
|
2011-09-02 17:34:58 -05:00
|
|
|
let walk_fn =
|
2011-09-12 04:27:30 -05:00
|
|
|
lambda (f: ast::_fn, tps: [ast::ty_param], sp: span, i: ast::fn_ident,
|
|
|
|
nid: ast::node_id) {
|
2011-09-02 17:34:58 -05:00
|
|
|
let start_walk =
|
2011-09-12 04:27:30 -05:00
|
|
|
lambda (v: visit::vt<int>) {
|
2011-09-02 17:34:58 -05:00
|
|
|
v.visit_fn(f, tps, sp, i, nid, 1, v);
|
|
|
|
};
|
2011-09-01 07:35:00 -05:00
|
|
|
let vars = collect_freevars(def_map, start_walk);
|
2011-09-02 17:34:58 -05:00
|
|
|
freevars.insert(nid, vars);
|
|
|
|
};
|
2011-07-18 21:14:01 -05:00
|
|
|
|
2011-07-27 07:19:39 -05:00
|
|
|
let visitor =
|
2011-10-21 06:42:26 -05:00
|
|
|
visit::mk_simple_visitor(@{visit_fn: walk_fn
|
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-09-12 04:27:30 -05:00
|
|
|
fn get_freevars(tcx: ty::ctxt, fid: ast::node_id) -> freevar_info {
|
2011-07-27 07:19:39 -05:00
|
|
|
alt tcx.freevars.find(fid) {
|
2011-09-02 17:34:58 -05:00
|
|
|
none. { fail "get_freevars: " + int::str(fid) + " has no freevars"; }
|
2011-07-27 07:19:39 -05:00
|
|
|
some(d) { ret d; }
|
2011-07-21 16:49:58 -05:00
|
|
|
}
|
|
|
|
}
|
2011-09-12 04:27:30 -05:00
|
|
|
fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool {
|
2011-12-13 18:25:51 -06:00
|
|
|
ret vec::len(*get_freevars(tcx, fid)) != 0u;
|
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
|
|
|
|
// End:
|