rust/src/rustc/middle/freevars.rs

123 lines
4.3 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 syntax::print::pprust::path_to_str;
2011-07-18 19:26:37 -05:00
import std::map::*;
import option::*;
2011-09-12 18:13:28 -05:00
import syntax::{ast, ast_util, visit};
import syntax::ast::{serialize_span, deserialize_span};
2011-07-18 19:26:37 -05:00
import syntax::codemap::span;
export annotate_freevars;
export freevar_map;
export freevar_info;
export freevar_entry, serialize_freevar_entry, deserialize_freevar_entry;
2011-07-21 16:49:58 -05:00
export get_freevars;
export has_freevars;
2011-07-18 19:26:37 -05:00
// A vector of defs representing the free variables referred to in a function.
// (The def_upvar will already have been stripped).
#[auto_serialize]
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];
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.
2012-07-17 17:23:59 -05:00
fn collect_freevars(def_map: resolve3::DefMap, blk: ast::blk)
-> freevar_info {
let seen = int_hash();
let refs = @mut ~[];
2011-07-18 19:26:37 -05:00
fn ignore_item(_i: @ast::item, &&_depth: int, _v: visit::vt<int>) { }
2012-01-10 11:51:15 -06:00
let walk_expr = fn@(expr: @ast::expr, &&depth: int, v: visit::vt<int>) {
2012-08-06 14:34:08 -05:00
match expr.node {
2012-08-03 21:59:04 -05:00
ast::expr_fn(proto, decl, _, _) => {
if proto != ast::proto_bare {
2011-09-02 17:34:58 -05:00
visit::visit_expr(expr, depth + 1, v);
}
2011-09-02 17:34:58 -05:00
}
2012-08-03 21:59:04 -05:00
ast::expr_fn_block(_, _, _) => {
visit::visit_expr(expr, depth + 1, v);
}
2012-08-03 21:59:04 -05:00
ast::expr_path(path) => {
let mut i = 0;
2012-08-06 14:34:08 -05:00
match def_map.find(expr.id) {
2012-08-03 21:59:04 -05:00
none => fail (~"Not found: " + path_to_str(path)),
some(df) => {
let mut def = df;
while i < depth {
2012-08-06 14:34:08 -05:00
match copy def {
2012-08-03 21:59:04 -05:00
ast::def_upvar(_, inner, _) => { def = *inner; }
_ => break
}
i += 1;
}
if i == depth { // Made it to end of loop
let dnum = ast_util::def_id_of_def(def).node;
if !seen.contains_key(dnum) {
vec::push(*refs, @{def:def, span:expr.span});
seen.insert(dnum, ());
}
}
2011-09-02 17:34:58 -05:00
}
}
2011-09-02 17:34:58 -05:00
}
2012-08-03 21:59:04 -05:00
_ => visit::visit_expr(expr, depth, v)
2011-07-27 07:19:39 -05:00
}
2011-09-02 17:34:58 -05:00
};
let v = visit::mk_vt(@{visit_item: ignore_item, visit_expr: walk_expr
with *visit::default_visitor()});
v.visit_block(blk, 1, v);
2012-08-01 19:30:05 -05:00
return @*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.
2012-07-17 17:23:59 -05:00
fn annotate_freevars(def_map: resolve3::DefMap, crate: @ast::crate) ->
2011-09-02 17:34:58 -05:00
freevar_map {
let freevars = int_hash();
2012-01-10 11:51:15 -06:00
let walk_fn = fn@(_fk: visit::fn_kind, _decl: ast::fn_decl,
blk: ast::blk, _sp: span, nid: ast::node_id) {
let vars = collect_freevars(def_map, blk);
freevars.insert(nid, vars);
};
2011-07-27 07:19:39 -05:00
let visitor =
visit::mk_simple_visitor(@{visit_fn: walk_fn
with *visit::default_simple_visitor()});
visit::visit_crate(*crate, (), visitor);
2012-08-01 19:30:05 -05:00
return freevars;
}
fn get_freevars(tcx: ty::ctxt, fid: ast::node_id) -> freevar_info {
2012-08-06 14:34:08 -05:00
match tcx.freevars.find(fid) {
2012-08-03 21:59:04 -05:00
none => fail ~"get_freevars: " + int::str(fid) + ~" has no freevars",
some(d) => return d
2011-07-21 16:49:58 -05:00
}
}
fn has_freevars(tcx: ty::ctxt, fid: ast::node_id) -> bool {
2012-08-01 19:30:05 -05:00
return 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: