1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 // A pass that annotates for each loops and functions with the free
12 // variables that they contain.
14 #[allow(non_camel_case_types)];
18 use util::nodemap::{NodeMap, NodeSet};
20 use syntax::codemap::Span;
21 use syntax::{ast, ast_util};
23 use syntax::visit::Visitor;
25 // A vector of defs representing the free variables referred to in a function.
26 // (The def_upvar will already have been stripped).
27 #[deriving(Encodable, Decodable)]
28 pub struct freevar_entry {
29 def: ast::Def, //< The variable being accessed free.
30 span: Span //< First span where it is accessed (there can be multiple)
32 pub type freevar_info = @~[@freevar_entry];
33 pub type freevar_map = NodeMap<freevar_info>;
35 struct CollectFreevarsVisitor {
37 refs: ~[@freevar_entry],
38 def_map: resolve::DefMap,
41 impl Visitor<int> for CollectFreevarsVisitor {
43 fn visit_item(&mut self, _: &ast::Item, _: int) {
47 fn visit_expr(&mut self, expr: &ast::Expr, depth: int) {
49 ast::ExprFnBlock(..) | ast::ExprProc(..) => {
50 visit::walk_expr(self, expr, depth + 1)
52 ast::ExprPath(..) => {
54 let def_map = self.def_map.borrow();
55 match def_map.get().find(&expr.id) {
56 None => fail!("path not found"),
61 ast::DefUpvar(_, inner, _, _) => { def = *inner; }
66 if i == depth { // Made it to end of loop
67 let dnum = ast_util::def_id_of_def(def).node;
68 if !self.seen.contains(&dnum) {
69 self.refs.push(@freevar_entry {
73 self.seen.insert(dnum);
79 _ => visit::walk_expr(self, expr, depth)
86 // Searches through part of the AST for all references to locals or
87 // upvars in this frame and returns the list of definition IDs thus found.
88 // Since we want to be able to collect upvars in some arbitrary piece
89 // of the AST, we take a walker function that we invoke with a visitor
90 // in order to start the search.
91 fn collect_freevars(def_map: resolve::DefMap, blk: &ast::Block) -> freevar_info {
92 let seen = NodeSet::new();
95 let mut v = CollectFreevarsVisitor {
101 v.visit_block(blk, 1);
102 let CollectFreevarsVisitor {
109 struct AnnotateFreevarsVisitor {
110 def_map: resolve::DefMap,
111 freevars: freevar_map,
114 impl Visitor<()> for AnnotateFreevarsVisitor {
115 fn visit_fn(&mut self, fk: &visit::FnKind, fd: &ast::FnDecl,
116 blk: &ast::Block, s: Span, nid: ast::NodeId, _: ()) {
117 let vars = collect_freevars(self.def_map, blk);
118 self.freevars.insert(nid, vars);
119 visit::walk_fn(self, fk, fd, blk, s, nid, ());
123 // Build a map from every function and for-each body to a set of the
124 // freevars contained in it. The implementation is not particularly
125 // efficient as it fully recomputes the free variables at every
126 // node of interest rather than building up the free variables in
127 // one pass. This could be improved upon if it turns out to matter.
128 pub fn annotate_freevars(def_map: resolve::DefMap, krate: &ast::Crate) ->
130 let mut visitor = AnnotateFreevarsVisitor {
132 freevars: NodeMap::new(),
134 visit::walk_crate(&mut visitor, krate, ());
136 let AnnotateFreevarsVisitor {
143 pub fn get_freevars(tcx: ty::ctxt, fid: ast::NodeId) -> freevar_info {
144 let freevars = tcx.freevars.borrow();
145 match freevars.get().find(&fid) {
146 None => fail!("get_freevars: {} has no freevars", fid),
151 pub fn has_freevars(tcx: ty::ctxt, fid: ast::NodeId) -> bool {
152 !get_freevars(tcx, fid).is_empty()