]> git.lizzy.rs Git - rust.git/commitdiff
Extend --pretty flowgraph=ID to include dataflow results in output.
authorFelix S. Klock II <pnkfelix@pnkfx.org>
Wed, 2 Jul 2014 15:50:18 +0000 (17:50 +0200)
committerFelix S. Klock II <pnkfelix@pnkfx.org>
Tue, 15 Jul 2014 10:26:56 +0000 (12:26 +0200)
Use one or more of the following `-Z` flag options to tell the
graphviz renderer to include the corresponding dataflow sets (after
the iterative constraint propagation reaches a fixed-point solution):

  * `-Z flowgraph-print-loans` : loans computed via middle::borrowck
  * `-Z flowgraph-print-moves` : moves computed via middle::borrowck::move_data
  * `-Z flowgraph-print-assigns` : assignments, via middle::borrowck::move_data
  * `-Z flowgraph-print-all` : all of the available sets are included.

Fix #15016.

----

This also adds a module, `syntax::ast_map::blocks`, that captures a
common abstraction shared amongst code blocks and procedure-like
things.  As part of this, moved `ast_map.rs` to subdir
`ast_map/mod.rs`, to follow our directory layout conventions.

(incorporated review feedback from huon, acrichto.)

src/libgraphviz/lib.rs
src/librustc/driver/config.rs
src/librustc/driver/driver.rs
src/librustc/middle/borrowck/graphviz.rs [new file with mode: 0644]
src/librustc/middle/borrowck/mod.rs
src/librustc/middle/borrowck/move_data.rs
src/librustc/middle/cfg/graphviz.rs
src/librustc/middle/dataflow.rs
src/libsyntax/ast_map.rs [deleted file]
src/libsyntax/ast_map/blocks.rs [new file with mode: 0644]
src/libsyntax/ast_map/mod.rs [new file with mode: 0644]

index 9d2f43b9513e6b3ea1f656a467bf55ca156f8388..a3653fa9735901353a3cf331b895fc614e296e0f 100644 (file)
@@ -434,10 +434,37 @@ fn escape_str(s: &str) -> String {
     /// Renders text as string suitable for a label in a .dot file.
     pub fn escape(&self) -> String {
         match self {
-            &LabelStr(ref s) => s.as_slice().escape_default().to_string(),
-            &EscStr(ref s) => LabelText::escape_str(s.as_slice()).to_string(),
+            &LabelStr(ref s) => s.as_slice().escape_default(),
+            &EscStr(ref s) => LabelText::escape_str(s.as_slice()),
         }
     }
+
+    /// Decomposes content into string suitable for making EscStr that
+    /// yields same content as self.  The result obeys the law
+    /// render(`lt`) == render(`EscStr(lt.pre_escaped_content())`) for
+    /// all `lt: LabelText`.
+    fn pre_escaped_content(self) -> str::MaybeOwned<'a> {
+        match self {
+            EscStr(s) => s,
+            LabelStr(s) => if s.as_slice().contains_char('\\') {
+                str::Owned(s.as_slice().escape_default())
+            } else {
+                s
+            },
+        }
+    }
+
+    /// Puts `prefix` on a line above this label, with a blank line separator.
+    pub fn prefix_line(self, prefix: LabelText) -> LabelText {
+        prefix.suffix_line(self)
+    }
+
+    /// Puts `suffix` on a line below this label, with a blank line separator.
+    pub fn suffix_line(self, suffix: LabelText) -> LabelText {
+        let prefix = self.pre_escaped_content().into_string();
+        let suffix = suffix.pre_escaped_content();
+        EscStr(str::Owned(prefix.append(r"\n\n").append(suffix.as_slice())))
+    }
 }
 
 pub type Nodes<'a,N> = MaybeOwnedVector<'a,N>;
@@ -664,10 +691,7 @@ fn test_input(g: LabelledGraph) -> IoResult<String> {
         let mut writer = MemWriter::new();
         render(&g, &mut writer).unwrap();
         let mut r = BufReader::new(writer.get_ref());
-        match r.read_to_string() {
-            Ok(string) => Ok(string.to_string()),
-            Err(err) => Err(err),
-        }
+        r.read_to_string()
     }
 
     // All of the tests use raw-strings as the format for the expected outputs,
index b726c50afe9d1202850cbfdc5b7b73fb20a2d0a6..9fe32c6fbcb789cbd9d6d711652ba598e78c379c 100644 (file)
@@ -179,7 +179,11 @@ macro_rules! debugging_opts(
         AST_JSON,
         AST_JSON_NOEXPAND,
         LS,
-        SAVE_ANALYSIS
+        SAVE_ANALYSIS,
+        FLOWGRAPH_PRINT_LOANS,
+        FLOWGRAPH_PRINT_MOVES,
+        FLOWGRAPH_PRINT_ASSIGNS,
+        FLOWGRAPH_PRINT_ALL
     ]
     0
 )
@@ -215,7 +219,15 @@ pub fn debugging_opts_map() -> Vec<(&'static str, &'static str, u64)> {
      ("ast-json-noexpand", "Print the pre-expansion AST as JSON and halt", AST_JSON_NOEXPAND),
      ("ls", "List the symbols defined by a library crate", LS),
      ("save-analysis", "Write syntax and type analysis information \
-                        in addition to normal output", SAVE_ANALYSIS))
+                        in addition to normal output", SAVE_ANALYSIS),
+     ("flowgraph-print-loans", "Include loan analysis data in \
+                       --pretty flowgraph output", FLOWGRAPH_PRINT_LOANS),
+     ("flowgraph-print-moves", "Include move analysis data in \
+                       --pretty flowgraph output", FLOWGRAPH_PRINT_MOVES),
+     ("flowgraph-print-assigns", "Include assignment analysis data in \
+                       --pretty flowgraph output", FLOWGRAPH_PRINT_ASSIGNS),
+     ("flowgraph-print-all", "Include all dataflow analysis data in \
+                       --pretty flowgraph output", FLOWGRAPH_PRINT_ALL))
 }
 
 /// Declare a macro that will define all CodegenOptions fields and parsers all
index d03bf3593fa41bc19aa4ed3ef767b7de0e31e07c..ca9cbe1306e6e5b6b9d14547a817f3f4813817ea 100644 (file)
@@ -19,6 +19,9 @@
 use lint;
 use metadata::common::LinkMeta;
 use metadata::creader;
+use middle::borrowck::{FnPartsWithCFG};
+use middle::borrowck;
+use borrowck_dot = middle::borrowck::graphviz;
 use middle::cfg;
 use middle::cfg::graphviz::LabelledCFG;
 use middle::{trans, freevars, stability, kind, ty, typeck, reachable};
@@ -40,6 +43,7 @@
 use std::io::fs;
 use std::io::MemReader;
 use syntax::ast;
+use syntax::ast_map::blocks;
 use syntax::attr;
 use syntax::attr::{AttrMetaMethods};
 use syntax::diagnostics;
@@ -662,6 +666,25 @@ fn post(&self,
     }
 }
 
+fn gather_flowgraph_variants(sess: &Session) -> Vec<borrowck_dot::Variant> {
+    let print_loans   = config::FLOWGRAPH_PRINT_LOANS;
+    let print_moves   = config::FLOWGRAPH_PRINT_MOVES;
+    let print_assigns = config::FLOWGRAPH_PRINT_ASSIGNS;
+    let print_all     = config::FLOWGRAPH_PRINT_ALL;
+    let opt = |print_which| sess.debugging_opt(print_which);
+    let mut variants = Vec::new();
+    if opt(print_all) || opt(print_loans) {
+        variants.push(borrowck_dot::Loans);
+    }
+    if opt(print_all) || opt(print_moves) {
+        variants.push(borrowck_dot::Moves);
+    }
+    if opt(print_all) || opt(print_assigns) {
+        variants.push(borrowck_dot::Assigns);
+    }
+    variants
+}
+
 pub fn pretty_print_input(sess: Session,
                           cfg: ast::CrateConfig,
                           input: &Input,
@@ -733,10 +756,17 @@ pub fn pretty_print_input(sess: Session,
                 sess.fatal(format!("--pretty flowgraph couldn't find id: {}",
                                    nodeid).as_slice())
             });
-            let block = match node {
-                syntax::ast_map::NodeBlock(block) => block,
-                _ => {
-                    let message = format!("--pretty=flowgraph needs block, got {:?}",
+            let code = blocks::Code::from_node(node);
+            match code {
+                Some(code) => {
+                    let variants = gather_flowgraph_variants(&sess);
+                    let analysis = phase_3_run_analysis_passes(sess, &krate,
+                                                               ast_map, id);
+                    print_flowgraph(variants, analysis, code, out)
+                }
+                None => {
+                    let message = format!("--pretty=flowgraph needs \
+                                           block, fn, or method; got {:?}",
                                           node);
 
                     // point to what was found, if there's an
@@ -746,10 +776,7 @@ pub fn pretty_print_input(sess: Session,
                         None => sess.fatal(message.as_slice())
                     }
                 }
-            };
-            let analysis = phase_3_run_analysis_passes(sess, &krate,
-                                                       ast_map, id);
-            print_flowgraph(analysis, block, out)
+            }
         }
         _ => {
             pprust::print_crate(sess.codemap(),
@@ -765,17 +792,52 @@ pub fn pretty_print_input(sess: Session,
 
 }
 
-fn print_flowgraph<W:io::Writer>(analysis: CrateAnalysis,
-                                 block: ast::P<ast::Block>,
+fn print_flowgraph<W:io::Writer>(variants: Vec<borrowck_dot::Variant>,
+                                 analysis: CrateAnalysis,
+                                 code: blocks::Code,
                                  mut out: W) -> io::IoResult<()> {
     let ty_cx = &analysis.ty_cx;
-    let cfg = cfg::CFG::new(ty_cx, &*block);
-    let lcfg = LabelledCFG { ast_map: &ty_cx.map,
-                             cfg: &cfg,
-                             name: format!("block{}", block.id), };
+    let cfg = match code {
+        blocks::BlockCode(block) => cfg::CFG::new(ty_cx, &*block),
+        blocks::FnLikeCode(fn_like) => cfg::CFG::new(ty_cx, fn_like.body()),
+    };
     debug!("cfg: {:?}", cfg);
-    let r = dot::render(&lcfg, &mut out);
-    return expand_err_details(r);
+
+    match code {
+        _ if variants.len() == 0 => {
+            let lcfg = LabelledCFG {
+                ast_map: &ty_cx.map,
+                cfg: &cfg,
+                name: format!("node_{}", code.id()),
+            };
+            let r = dot::render(&lcfg, &mut out);
+            return expand_err_details(r);
+        }
+        blocks::BlockCode(_) => {
+            ty_cx.sess.err("--pretty flowgraph with -Z flowgraph-print \
+                            annotations requires fn-like node id.");
+            return Ok(())
+        }
+        blocks::FnLikeCode(fn_like) => {
+            let fn_parts = FnPartsWithCFG::from_fn_like(&fn_like, &cfg);
+            let (bccx, analysis_data) =
+                borrowck::build_borrowck_dataflow_data_for_fn(ty_cx, fn_parts);
+
+            let lcfg = LabelledCFG {
+                ast_map: &ty_cx.map,
+                cfg: &cfg,
+                name: format!("node_{}", code.id()),
+            };
+            let lcfg = borrowck_dot::DataflowLabeller {
+                inner: lcfg,
+                variants: variants,
+                borrowck_ctxt: &bccx,
+                analysis_data: &analysis_data,
+            };
+            let r = dot::render(&lcfg, &mut out);
+            return expand_err_details(r);
+        }
+    }
 
     fn expand_err_details(r: io::IoResult<()>) -> io::IoResult<()> {
         r.map_err(|ioerr| {
diff --git a/src/librustc/middle/borrowck/graphviz.rs b/src/librustc/middle/borrowck/graphviz.rs
new file mode 100644 (file)
index 0000000..e2ddb4a
--- /dev/null
@@ -0,0 +1,148 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module provides linkage between rustc::middle::graph and
+//! libgraphviz traits, specialized to attaching borrowck analysis
+//! data to rendered labels.
+
+/// For clarity, rename the graphviz crate locally to dot.
+use dot = graphviz;
+pub use middle::cfg::graphviz::{Node, Edge};
+use cfg_dot = middle::cfg::graphviz;
+
+use middle::borrowck;
+use middle::borrowck::{BorrowckCtxt, LoanPath};
+use middle::cfg::{CFGIndex};
+use middle::dataflow::{DataFlowOperator, DataFlowContext, EntryOrExit};
+use middle::dataflow;
+
+use std::rc::Rc;
+use std::str;
+
+#[deriving(Show)]
+pub enum Variant {
+    Loans,
+    Moves,
+    Assigns,
+}
+
+impl Variant {
+    pub fn short_name(&self) -> &'static str {
+        match *self {
+            Loans   => "loans",
+            Moves   => "moves",
+            Assigns => "assigns",
+        }
+    }
+}
+
+pub struct DataflowLabeller<'a> {
+    pub inner: cfg_dot::LabelledCFG<'a>,
+    pub variants: Vec<Variant>,
+    pub borrowck_ctxt: &'a BorrowckCtxt<'a>,
+    pub analysis_data: &'a borrowck::AnalysisData<'a>,
+}
+
+impl<'a> DataflowLabeller<'a> {
+    fn dataflow_for(&self, e: EntryOrExit, n: &Node<'a>) -> String {
+        let id = n.val1().data.id;
+        debug!("dataflow_for({}, id={}) {}", e, id, self.variants);
+        let mut sets = "".to_string();
+        let mut seen_one = false;
+        for &variant in self.variants.iter() {
+            if seen_one { sets.push_str(" "); } else { seen_one = true; }
+            sets.push_str(variant.short_name());
+            sets.push_str(": ");
+            sets.push_str(self.dataflow_for_variant(e, n, variant).as_slice());
+        }
+        sets
+    }
+
+    fn dataflow_for_variant(&self, e: EntryOrExit, n: &Node, v: Variant) -> String {
+        let cfgidx = n.val0();
+        match v {
+            Loans   => self.dataflow_loans_for(e, cfgidx),
+            Moves   => self.dataflow_moves_for(e, cfgidx),
+            Assigns => self.dataflow_assigns_for(e, cfgidx),
+        }
+    }
+
+    fn build_set<O:DataFlowOperator>(&self,
+                                     e: EntryOrExit,
+                                     cfgidx: CFGIndex,
+                                     dfcx: &DataFlowContext<'a, O>,
+                                     to_lp: |uint| -> Rc<LoanPath>) -> String {
+        let mut saw_some = false;
+        let mut set = "{".to_string();
+        dfcx.each_bit_for_node(e, cfgidx, |index| {
+            let lp = to_lp(index);
+            if saw_some {
+                set.push_str(", ");
+            }
+            let loan_str = self.borrowck_ctxt.loan_path_to_string(&*lp);
+            set.push_str(loan_str.as_slice());
+            saw_some = true;
+            true
+        });
+        set.append("}")
+    }
+
+    fn dataflow_loans_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+        let dfcx = &self.analysis_data.loans;
+        let loan_index_to_path = |loan_index| {
+            let all_loans = &self.analysis_data.all_loans;
+            all_loans.get(loan_index).loan_path()
+        };
+        self.build_set(e, cfgidx, dfcx, loan_index_to_path)
+    }
+
+    fn dataflow_moves_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+        let dfcx = &self.analysis_data.move_data.dfcx_moves;
+        let move_index_to_path = |move_index| {
+            let move_data = &self.analysis_data.move_data.move_data;
+            let moves = move_data.moves.borrow();
+            let move = moves.get(move_index);
+            move_data.path_loan_path(move.path)
+        };
+        self.build_set(e, cfgidx, dfcx, move_index_to_path)
+    }
+
+    fn dataflow_assigns_for(&self, e: EntryOrExit, cfgidx: CFGIndex) -> String {
+        let dfcx = &self.analysis_data.move_data.dfcx_assign;
+        let assign_index_to_path = |assign_index| {
+            let move_data = &self.analysis_data.move_data.move_data;
+            let assignments = move_data.var_assignments.borrow();
+            let assignment = assignments.get(assign_index);
+            move_data.path_loan_path(assignment.path)
+        };
+        self.build_set(e, cfgidx, dfcx, assign_index_to_path)
+    }
+}
+
+impl<'a> dot::Labeller<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> {
+    fn graph_id(&'a self) -> dot::Id<'a> { self.inner.graph_id() }
+    fn node_id(&'a self, n: &Node<'a>) -> dot::Id<'a> { self.inner.node_id(n) }
+    fn node_label(&'a self, n: &Node<'a>) -> dot::LabelText<'a> {
+        let prefix = self.dataflow_for(dataflow::Entry, n);
+        let suffix = self.dataflow_for(dataflow::Exit, n);
+        let inner_label = self.inner.node_label(n);
+        inner_label
+            .prefix_line(dot::LabelStr(str::Owned(prefix)))
+            .suffix_line(dot::LabelStr(str::Owned(suffix)))
+    }
+    fn edge_label(&'a self, e: &Edge<'a>) -> dot::LabelText<'a> { self.inner.edge_label(e) }
+}
+
+impl<'a> dot::GraphWalk<'a, Node<'a>, Edge<'a>> for DataflowLabeller<'a> {
+    fn nodes(&self) -> dot::Nodes<'a, Node<'a>> { self.inner.nodes() }
+    fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.inner.edges() }
+    fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.source(edge) }
+    fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.inner.target(edge) }
+}
index 426a1fbede56b7fb9dca84c9f5e033e7fc9b95df..77b3cfafa63afaa26d6f05fabd7052f212259d82 100644 (file)
@@ -28,6 +28,7 @@
 use std::string::String;
 use syntax::ast;
 use syntax::ast_map;
+use syntax::ast_map::blocks::{FnLikeNode, FnParts};
 use syntax::ast_util;
 use syntax::codemap::Span;
 use syntax::parse::token;
@@ -50,6 +51,8 @@ macro_rules! if_ok(
 
 pub mod gather_loans;
 
+pub mod graphviz;
+
 pub mod move_data;
 
 #[deriving(Clone)]
@@ -116,6 +119,13 @@ fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
     }
 }
 
+/// Collection of conclusions determined via borrow checker analyses.
+pub struct AnalysisData<'a> {
+    pub all_loans: Vec<Loan>,
+    pub loans: DataFlowContext<'a, LoanDataFlowOperator>,
+    pub move_data: move_data::FlowedMoveData<'a>,
+}
+
 fn borrowck_fn(this: &mut BorrowckCtxt,
                fk: &FnKind,
                decl: &ast::FnDecl,
@@ -123,18 +133,35 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
                sp: Span,
                id: ast::NodeId) {
     debug!("borrowck_fn(id={})", id);
+    let cfg = cfg::CFG::new(this.tcx, body);
+    let AnalysisData { all_loans,
+                       loans: loan_dfcx,
+                       move_data:flowed_moves } =
+        build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
+
+    check_loans::check_loans(this, &loan_dfcx, flowed_moves,
+                             all_loans.as_slice(), decl, body);
+
+    visit::walk_fn(this, fk, decl, body, sp, ());
+}
 
+fn build_borrowck_dataflow_data<'a>(this: &mut BorrowckCtxt<'a>,
+                                    fk: &FnKind,
+                                    decl: &ast::FnDecl,
+                                    cfg: &cfg::CFG,
+                                    body: &ast::Block,
+                                    sp: Span,
+                                    id: ast::NodeId) -> AnalysisData<'a> {
     // Check the body of fn items.
     let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
     let (all_loans, move_data) =
         gather_loans::gather_loans_in_fn(this, decl, body);
-    let cfg = cfg::CFG::new(this.tcx, body);
 
     let mut loan_dfcx =
         DataFlowContext::new(this.tcx,
                              "borrowck",
                              Some(decl),
-                             &cfg,
+                             cfg,
                              LoanDataFlowOperator,
                              id_range,
                              all_loans.len());
@@ -142,20 +169,57 @@ fn borrowck_fn(this: &mut BorrowckCtxt,
         loan_dfcx.add_gen(loan.gen_scope, loan_idx);
         loan_dfcx.add_kill(loan.kill_scope, loan_idx);
     }
-    loan_dfcx.add_kills_from_flow_exits(&cfg);
-    loan_dfcx.propagate(&cfg, body);
+    loan_dfcx.add_kills_from_flow_exits(cfg);
+    loan_dfcx.propagate(cfg, body);
 
     let flowed_moves = move_data::FlowedMoveData::new(move_data,
                                                       this.tcx,
-                                                      &cfg,
+                                                      cfg,
                                                       id_range,
                                                       decl,
                                                       body);
 
-    check_loans::check_loans(this, &loan_dfcx, flowed_moves,
-                             all_loans.as_slice(), decl, body);
+    AnalysisData { all_loans: all_loans,
+                   loans: loan_dfcx,
+                   move_data:flowed_moves }
+}
 
-    visit::walk_fn(this, fk, decl, body, sp, ());
+/// This and a `ty::ctxt` is all you need to run the dataflow analyses
+/// used in the borrow checker.
+pub struct FnPartsWithCFG<'a> {
+    pub fn_parts: FnParts<'a>,
+    pub cfg:  &'a cfg::CFG,
+}
+
+impl<'a> FnPartsWithCFG<'a> {
+    pub fn from_fn_like(f: &'a FnLikeNode,
+                        g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
+        FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
+    }
+}
+
+/// Accessor for introspective clients inspecting `AnalysisData` and
+/// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
+pub fn build_borrowck_dataflow_data_for_fn<'a>(
+    tcx: &'a ty::ctxt,
+    input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a>, AnalysisData<'a>) {
+
+    let mut bccx = BorrowckCtxt {
+        tcx: tcx,
+        stats: box(GC) BorrowStats {
+            loaned_paths_same: Cell::new(0),
+            loaned_paths_imm: Cell::new(0),
+            stable_paths: Cell::new(0),
+            guaranteed_paths: Cell::new(0),
+        }
+    };
+
+    let p = input.fn_parts;
+
+    let dataflow_data = build_borrowck_dataflow_data(
+        &mut bccx, &p.kind, p.decl, input.cfg, p.body, p.span, p.id);
+
+    (bccx, dataflow_data)
 }
 
 // ----------------------------------------------------------------------
@@ -198,6 +262,12 @@ pub struct Loan {
     cause: euv::LoanCause,
 }
 
+impl Loan {
+    pub fn loan_path(&self) -> Rc<LoanPath> {
+        self.loan_path.clone()
+    }
+}
+
 #[deriving(PartialEq, Eq, Hash)]
 pub enum LoanPath {
     LpVar(ast::NodeId),               // `x` in doc.rs
index b61596908e60a5f93ea3307e463e44bd7fe42e98..a9c312fc0a455462e60dbc67953dc27aba9c6bfb 100644 (file)
@@ -189,7 +189,7 @@ pub fn new() -> MoveData {
         }
     }
 
-    fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath> {
+    pub fn path_loan_path(&self, index: MovePathIndex) -> Rc<LoanPath> {
         self.paths.borrow().get(index.get()).loan_path.clone()
     }
 
@@ -534,7 +534,7 @@ fn kill_moves(&self,
 impl<'a> FlowedMoveData<'a> {
     pub fn new(move_data: MoveData,
                tcx: &'a ty::ctxt,
-               cfg: &'a cfg::CFG,
+               cfg: &cfg::CFG,
                id_range: ast_util::IdRange,
                decl: &ast::FnDecl,
                body: &ast::Block)
index 9f44f0babc72abfaae37bf70e1133627ee020df8..e9bcdff070de1764d9e8d0c47ce0c03b913364b9 100644 (file)
@@ -117,3 +117,4 @@ fn edges(&self) -> dot::Edges<'a, Edge<'a>> { self.cfg.edges() }
     fn source(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.source(edge) }
     fn target(&self, edge: &Edge<'a>) -> Node<'a> { self.cfg.target(edge) }
 }
+
index 7d9178162a65adb11de93f371623b028d93cda68..b28c0158584e6c96a06c054a5115ce14fa429d19 100644 (file)
@@ -28,6 +28,9 @@
 use syntax::print::{pp, pprust};
 use util::nodemap::NodeMap;
 
+#[deriving(Show)]
+pub enum EntryOrExit { Entry, Exit }
+
 #[deriving(Clone)]
 pub struct DataFlowContext<'a, O> {
     tcx: &'a ty::ctxt,
@@ -93,17 +96,18 @@ fn to_cfgidx_or_die(id: ast::NodeId, index: &NodeMap<CFGIndex>) -> CFGIndex {
 }
 
 impl<'a, O:DataFlowOperator> DataFlowContext<'a, O> {
-    fn has_bitset(&self, n: ast::NodeId) -> bool {
+    fn has_bitset_for_nodeid(&self, n: ast::NodeId) -> bool {
         assert!(n != ast::DUMMY_NODE_ID);
         match self.nodeid_to_index.find(&n) {
             None => false,
-            Some(&cfgidx) => {
-                let node_id = cfgidx.node_id();
-                node_id < self.index_to_bitset.len() &&
-                    self.index_to_bitset.get(node_id).is_some()
-            }
+            Some(&cfgidx) => self.has_bitset_for_cfgidx(cfgidx),
         }
     }
+    fn has_bitset_for_cfgidx(&self, cfgidx: CFGIndex) -> bool {
+        let node_id = cfgidx.node_id();
+        node_id < self.index_to_bitset.len() &&
+            self.index_to_bitset.get(node_id).is_some()
+    }
     fn get_bitset_index(&self, cfgidx: CFGIndex) -> uint {
         let node_id = cfgidx.node_id();
         self.index_to_bitset.get(node_id).unwrap()
@@ -160,7 +164,7 @@ fn pre(&self,
             pprust::NodePat(pat) => pat.id
         };
 
-        if self.has_bitset(id) {
+        if self.has_bitset_for_nodeid(id) {
             let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
             let (start, end) = self.compute_id_range_frozen(cfgidx);
             let on_entry = self.on_entry.slice(start, end);
@@ -287,7 +291,7 @@ pub fn add_kill(&mut self, id: ast::NodeId, bit: uint) {
     }
 
     fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) {
-        //! Applies the gen and kill sets for `id` to `bits`
+        //! Applies the gen and kill sets for `cfgidx` to `bits`
         debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]",
                self.analysis_name, cfgidx, mut_bits_to_string(bits));
         let (start, end) = self.compute_id_range(cfgidx);
@@ -300,6 +304,21 @@ fn apply_gen_kill(&mut self, cfgidx: CFGIndex, bits: &mut [uint]) {
                self.analysis_name, cfgidx, mut_bits_to_string(bits));
     }
 
+    fn apply_gen_kill_frozen(&self, cfgidx: CFGIndex, bits: &mut [uint]) {
+        //! Applies the gen and kill sets for `cfgidx` to `bits`
+        //! Only useful after `propagate()` has been called.
+        debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [before]",
+               self.analysis_name, cfgidx, mut_bits_to_string(bits));
+        let (start, end) = self.compute_id_range_frozen(cfgidx);
+        let gens = self.gens.slice(start, end);
+        bitwise(bits, gens, &Union);
+        let kills = self.kills.slice(start, end);
+        bitwise(bits, kills, &Subtract);
+
+        debug!("{:s} apply_gen_kill(cfgidx={}, bits={}) [after]",
+               self.analysis_name, cfgidx, mut_bits_to_string(bits));
+    }
+
     fn compute_id_range_frozen(&self, cfgidx: CFGIndex) -> (uint, uint) {
         let n = self.get_bitset_index(cfgidx);
         let start = n * self.words_per_id;
@@ -327,21 +346,45 @@ pub fn each_bit_on_entry_frozen(&self,
                                     -> bool {
         //! Iterates through each bit that is set on entry to `id`.
         //! Only useful after `propagate()` has been called.
-        if !self.has_bitset(id) {
+        if !self.has_bitset_for_nodeid(id) {
             return true;
         }
         let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
+        self.each_bit_for_node(Entry, cfgidx, f)
+    }
+
+    pub fn each_bit_for_node(&self,
+                             e: EntryOrExit,
+                             cfgidx: CFGIndex,
+                             f: |uint| -> bool)
+                             -> bool {
+        //! Iterates through each bit that is set on entry/exit to `cfgidx`.
+        //! Only useful after `propagate()` has been called.
+        if !self.has_bitset_for_cfgidx(cfgidx) {
+            return true;
+        }
         let (start, end) = self.compute_id_range_frozen(cfgidx);
         let on_entry = self.on_entry.slice(start, end);
-        debug!("{:s} each_bit_on_entry_frozen(id={:?}, on_entry={})",
-               self.analysis_name, id, bits_to_string(on_entry));
-        self.each_bit(on_entry, f)
+        let temp_bits;
+        let slice = match e {
+            Entry => on_entry,
+            Exit => {
+                let mut t = on_entry.to_owned();
+                self.apply_gen_kill_frozen(cfgidx, t.as_mut_slice());
+                temp_bits = t;
+                temp_bits.as_slice()
+            }
+        };
+        debug!("{:s} each_bit_for_node({}, cfgidx={}) bits={}",
+               self.analysis_name, e, cfgidx, bits_to_string(slice));
+        self.each_bit(slice, f)
     }
 
     pub fn each_gen_bit_frozen(&self, id: ast::NodeId, f: |uint| -> bool)
                                -> bool {
         //! Iterates through each bit in the gen set for `id`.
-        if !self.has_bitset(id) {
+        //! Only useful after `propagate()` has been called.
+        if !self.has_bitset_for_nodeid(id) {
             return true;
         }
         let cfgidx = to_cfgidx_or_die(id, &self.nodeid_to_index);
diff --git a/src/libsyntax/ast_map.rs b/src/libsyntax/ast_map.rs
deleted file mode 100644 (file)
index b8a0a31..0000000
+++ /dev/null
@@ -1,758 +0,0 @@
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use abi;
-use ast::*;
-use ast_util;
-use codemap::Span;
-use fold::Folder;
-use fold;
-use parse::token;
-use print::pprust;
-use util::small_vector::SmallVector;
-
-use std::cell::RefCell;
-use std::fmt;
-use std::gc::{Gc, GC};
-use std::iter;
-use std::slice;
-
-#[deriving(Clone, PartialEq)]
-pub enum PathElem {
-    PathMod(Name),
-    PathName(Name)
-}
-
-impl PathElem {
-    pub fn name(&self) -> Name {
-        match *self {
-            PathMod(name) | PathName(name) => name
-        }
-    }
-}
-
-impl fmt::Show for PathElem {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let slot = token::get_name(self.name());
-        write!(f, "{}", slot)
-    }
-}
-
-#[deriving(Clone)]
-struct LinkedPathNode<'a> {
-    node: PathElem,
-    next: LinkedPath<'a>,
-}
-
-type LinkedPath<'a> = Option<&'a LinkedPathNode<'a>>;
-
-impl<'a> Iterator<PathElem> for LinkedPath<'a> {
-    fn next(&mut self) -> Option<PathElem> {
-        match *self {
-            Some(node) => {
-                *self = node.next;
-                Some(node.node)
-            }
-            None => None
-        }
-    }
-}
-
-// HACK(eddyb) move this into libstd (value wrapper for slice::Items).
-#[deriving(Clone)]
-pub struct Values<'a, T>(pub slice::Items<'a, T>);
-
-impl<'a, T: Copy> Iterator<T> for Values<'a, T> {
-    fn next(&mut self) -> Option<T> {
-        let &Values(ref mut items) = self;
-        items.next().map(|&x| x)
-    }
-}
-
-/// The type of the iterator used by with_path.
-pub type PathElems<'a, 'b> = iter::Chain<Values<'a, PathElem>, LinkedPath<'b>>;
-
-pub fn path_to_string<PI: Iterator<PathElem>>(mut path: PI) -> String {
-    let itr = token::get_ident_interner();
-
-    path.fold(String::new(), |mut s, e| {
-        let e = itr.get(e.name());
-        if !s.is_empty() {
-            s.push_str("::");
-        }
-        s.push_str(e.as_slice());
-        s
-    }).to_string()
-}
-
-#[deriving(Clone)]
-pub enum Node {
-    NodeItem(Gc<Item>),
-    NodeForeignItem(Gc<ForeignItem>),
-    NodeTraitMethod(Gc<TraitMethod>),
-    NodeMethod(Gc<Method>),
-    NodeVariant(P<Variant>),
-    NodeExpr(Gc<Expr>),
-    NodeStmt(Gc<Stmt>),
-    NodeArg(Gc<Pat>),
-    NodeLocal(Gc<Pat>),
-    NodePat(Gc<Pat>),
-    NodeBlock(P<Block>),
-
-    /// NodeStructCtor represents a tuple struct.
-    NodeStructCtor(Gc<StructDef>),
-
-    NodeLifetime(Gc<Lifetime>),
-}
-
-/// Represents an entry and its parent Node ID
-/// The odd layout is to bring down the total size.
-#[deriving(Clone)]
-enum MapEntry {
-    /// Placeholder for holes in the map.
-    NotPresent,
-
-    /// All the node types, with a parent ID.
-    EntryItem(NodeId, Gc<Item>),
-    EntryForeignItem(NodeId, Gc<ForeignItem>),
-    EntryTraitMethod(NodeId, Gc<TraitMethod>),
-    EntryMethod(NodeId, Gc<Method>),
-    EntryVariant(NodeId, P<Variant>),
-    EntryExpr(NodeId, Gc<Expr>),
-    EntryStmt(NodeId, Gc<Stmt>),
-    EntryArg(NodeId, Gc<Pat>),
-    EntryLocal(NodeId, Gc<Pat>),
-    EntryPat(NodeId, Gc<Pat>),
-    EntryBlock(NodeId, P<Block>),
-    EntryStructCtor(NodeId, Gc<StructDef>),
-    EntryLifetime(NodeId, Gc<Lifetime>),
-
-    /// Roots for node trees.
-    RootCrate,
-    RootInlinedParent(P<InlinedParent>)
-}
-
-struct InlinedParent {
-    path: Vec<PathElem> ,
-    /// Required by NodeTraitMethod and NodeMethod.
-    def_id: DefId
-}
-
-impl MapEntry {
-    fn parent(&self) -> Option<NodeId> {
-        Some(match *self {
-            EntryItem(id, _) => id,
-            EntryForeignItem(id, _) => id,
-            EntryTraitMethod(id, _) => id,
-            EntryMethod(id, _) => id,
-            EntryVariant(id, _) => id,
-            EntryExpr(id, _) => id,
-            EntryStmt(id, _) => id,
-            EntryArg(id, _) => id,
-            EntryLocal(id, _) => id,
-            EntryPat(id, _) => id,
-            EntryBlock(id, _) => id,
-            EntryStructCtor(id, _) => id,
-            EntryLifetime(id, _) => id,
-            _ => return None
-        })
-    }
-
-    fn to_node(&self) -> Option<Node> {
-        Some(match *self {
-            EntryItem(_, p) => NodeItem(p),
-            EntryForeignItem(_, p) => NodeForeignItem(p),
-            EntryTraitMethod(_, p) => NodeTraitMethod(p),
-            EntryMethod(_, p) => NodeMethod(p),
-            EntryVariant(_, p) => NodeVariant(p),
-            EntryExpr(_, p) => NodeExpr(p),
-            EntryStmt(_, p) => NodeStmt(p),
-            EntryArg(_, p) => NodeArg(p),
-            EntryLocal(_, p) => NodeLocal(p),
-            EntryPat(_, p) => NodePat(p),
-            EntryBlock(_, p) => NodeBlock(p),
-            EntryStructCtor(_, p) => NodeStructCtor(p),
-            EntryLifetime(_, p) => NodeLifetime(p),
-            _ => return None
-        })
-    }
-}
-
-/// Represents a mapping from Node IDs to AST elements and their parent
-/// Node IDs
-pub struct Map {
-    /// NodeIds are sequential integers from 0, so we can be
-    /// super-compact by storing them in a vector. Not everything with
-    /// a NodeId is in the map, but empirically the occupancy is about
-    /// 75-80%, so there's not too much overhead (certainly less than
-    /// a hashmap, since they (at the time of writing) have a maximum
-    /// of 75% occupancy).
-    ///
-    /// Also, indexing is pretty quick when you've got a vector and
-    /// plain old integers.
-    map: RefCell<Vec<MapEntry> >
-}
-
-impl Map {
-    fn find_entry(&self, id: NodeId) -> Option<MapEntry> {
-        let map = self.map.borrow();
-        if map.len() > id as uint {
-            Some(*map.get(id as uint))
-        } else {
-            None
-        }
-    }
-
-    /// Retrieve the Node corresponding to `id`, failing if it cannot
-    /// be found.
-    pub fn get(&self, id: NodeId) -> Node {
-        match self.find(id) {
-            Some(node) => node,
-            None => fail!("couldn't find node id {} in the AST map", id)
-        }
-    }
-
-    /// Retrieve the Node corresponding to `id`, returning None if
-    /// cannot be found.
-    pub fn find(&self, id: NodeId) -> Option<Node> {
-        self.find_entry(id).and_then(|x| x.to_node())
-    }
-
-    /// Retrieve the parent NodeId for `id`, or `id` itself if no
-    /// parent is registered in this map.
-    pub fn get_parent(&self, id: NodeId) -> NodeId {
-        self.find_entry(id).and_then(|x| x.parent()).unwrap_or(id)
-    }
-
-    pub fn get_parent_did(&self, id: NodeId) -> DefId {
-        let parent = self.get_parent(id);
-        match self.find_entry(parent) {
-            Some(RootInlinedParent(data)) => data.def_id,
-            _ => ast_util::local_def(parent)
-        }
-    }
-
-    pub fn get_foreign_abi(&self, id: NodeId) -> abi::Abi {
-        let parent = self.get_parent(id);
-        let abi = match self.find_entry(parent) {
-            Some(EntryItem(_, i)) => match i.node {
-                ItemForeignMod(ref nm) => Some(nm.abi),
-                _ => None
-            },
-            /// Wrong but OK, because the only inlined foreign items are intrinsics.
-            Some(RootInlinedParent(_)) => Some(abi::RustIntrinsic),
-            _ => None
-        };
-        match abi {
-            Some(abi) => abi,
-            None => fail!("expected foreign mod or inlined parent, found {}",
-                          self.node_to_string(parent))
-        }
-    }
-
-    pub fn get_foreign_vis(&self, id: NodeId) -> Visibility {
-        let vis = self.expect_foreign_item(id).vis;
-        match self.find(self.get_parent(id)) {
-            Some(NodeItem(i)) => vis.inherit_from(i.vis),
-            _ => vis
-        }
-    }
-
-    pub fn expect_item(&self, id: NodeId) -> Gc<Item> {
-        match self.find(id) {
-            Some(NodeItem(item)) => item,
-            _ => fail!("expected item, found {}", self.node_to_string(id))
-        }
-    }
-
-    pub fn expect_struct(&self, id: NodeId) -> Gc<StructDef> {
-        match self.find(id) {
-            Some(NodeItem(i)) => {
-                match i.node {
-                    ItemStruct(struct_def, _) => struct_def,
-                    _ => fail!("struct ID bound to non-struct")
-                }
-            }
-            Some(NodeVariant(ref variant)) => {
-                match (*variant).node.kind {
-                    StructVariantKind(struct_def) => struct_def,
-                    _ => fail!("struct ID bound to enum variant that isn't struct-like"),
-                }
-            }
-            _ => fail!(format!("expected struct, found {}", self.node_to_string(id))),
-        }
-    }
-
-    pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
-        match self.find(id) {
-            Some(NodeVariant(variant)) => variant,
-            _ => fail!(format!("expected variant, found {}", self.node_to_string(id))),
-        }
-    }
-
-    pub fn expect_foreign_item(&self, id: NodeId) -> Gc<ForeignItem> {
-        match self.find(id) {
-            Some(NodeForeignItem(item)) => item,
-            _ => fail!("expected foreign item, found {}", self.node_to_string(id))
-        }
-    }
-
-    /// returns the name associated with the given NodeId's AST
-    pub fn get_path_elem(&self, id: NodeId) -> PathElem {
-        let node = self.get(id);
-        match node {
-            NodeItem(item) => {
-                match item.node {
-                    ItemMod(_) | ItemForeignMod(_) => {
-                        PathMod(item.ident.name)
-                    }
-                    _ => PathName(item.ident.name)
-                }
-            }
-            NodeForeignItem(i) => PathName(i.ident.name),
-            NodeMethod(m) => match m.node {
-                MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
-                MethMac(_) => fail!("no path elem for {:?}", node)
-            },
-            NodeTraitMethod(tm) => match *tm {
-                Required(ref m) => PathName(m.ident.name),
-                Provided(m) => match m.node {
-                    MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
-                    MethMac(_) => fail!("no path elem for {:?}", node),
-                }
-            },
-            NodeVariant(v) => PathName(v.node.name.name),
-            _ => fail!("no path elem for {:?}", node)
-        }
-    }
-
-    pub fn with_path<T>(&self, id: NodeId, f: |PathElems| -> T) -> T {
-        self.with_path_next(id, None, f)
-    }
-
-    pub fn path_to_string(&self, id: NodeId) -> String {
-        self.with_path(id, |path| path_to_string(path))
-    }
-
-    fn path_to_str_with_ident(&self, id: NodeId, i: Ident) -> String {
-        self.with_path(id, |path| {
-            path_to_string(path.chain(Some(PathName(i.name)).move_iter()))
-        })
-    }
-
-    fn with_path_next<T>(&self, id: NodeId, next: LinkedPath, f: |PathElems| -> T) -> T {
-        let parent = self.get_parent(id);
-        let parent = match self.find_entry(id) {
-            Some(EntryForeignItem(..)) | Some(EntryVariant(..)) => {
-                // Anonymous extern items, enum variants and struct ctors
-                // go in the parent scope.
-                self.get_parent(parent)
-            }
-            // But tuple struct ctors don't have names, so use the path of its
-            // parent, the struct item. Similarly with closure expressions.
-            Some(EntryStructCtor(..)) | Some(EntryExpr(..)) => {
-                return self.with_path_next(parent, next, f);
-            }
-            _ => parent
-        };
-        if parent == id {
-            match self.find_entry(id) {
-                Some(RootInlinedParent(data)) => {
-                    f(Values(data.path.iter()).chain(next))
-                }
-                _ => f(Values([].iter()).chain(next))
-            }
-        } else {
-            self.with_path_next(parent, Some(&LinkedPathNode {
-                node: self.get_path_elem(id),
-                next: next
-            }), f)
-        }
-    }
-
-    /// Given a node ID and a closure, apply the closure to the array
-    /// of attributes associated with the AST corresponding to the Node ID
-    pub fn with_attrs<T>(&self, id: NodeId, f: |Option<&[Attribute]>| -> T) -> T {
-        let node = self.get(id);
-        let attrs = match node {
-            NodeItem(ref i) => Some(i.attrs.as_slice()),
-            NodeForeignItem(ref fi) => Some(fi.attrs.as_slice()),
-            NodeTraitMethod(ref tm) => match **tm {
-                Required(ref type_m) => Some(type_m.attrs.as_slice()),
-                Provided(ref m) => Some(m.attrs.as_slice())
-            },
-            NodeMethod(ref m) => Some(m.attrs.as_slice()),
-            NodeVariant(ref v) => Some(v.node.attrs.as_slice()),
-            // unit/tuple structs take the attributes straight from
-            // the struct definition.
-            // FIXME(eddyb) make this work again (requires access to the map).
-            NodeStructCtor(_) => {
-                return self.with_attrs(self.get_parent(id), f);
-            }
-            _ => None
-        };
-        f(attrs)
-    }
-
-    pub fn opt_span(&self, id: NodeId) -> Option<Span> {
-        let sp = match self.find(id) {
-            Some(NodeItem(item)) => item.span,
-            Some(NodeForeignItem(foreign_item)) => foreign_item.span,
-            Some(NodeTraitMethod(trait_method)) => {
-                match *trait_method {
-                    Required(ref type_method) => type_method.span,
-                    Provided(ref method) => method.span,
-                }
-            }
-            Some(NodeMethod(method)) => method.span,
-            Some(NodeVariant(variant)) => variant.span,
-            Some(NodeExpr(expr)) => expr.span,
-            Some(NodeStmt(stmt)) => stmt.span,
-            Some(NodeArg(pat)) | Some(NodeLocal(pat)) => pat.span,
-            Some(NodePat(pat)) => pat.span,
-            Some(NodeBlock(block)) => block.span,
-            Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
-            _ => return None,
-        };
-        Some(sp)
-    }
-
-    pub fn span(&self, id: NodeId) -> Span {
-        self.opt_span(id)
-            .unwrap_or_else(|| fail!("AstMap.span: could not find span for id {}", id))
-    }
-
-    pub fn node_to_string(&self, id: NodeId) -> String {
-        node_id_to_string(self, id)
-    }
-}
-
-pub trait FoldOps {
-    fn new_id(&self, id: NodeId) -> NodeId {
-        id
-    }
-    fn new_span(&self, span: Span) -> Span {
-        span
-    }
-}
-
-/// A Folder that walks over an AST and constructs a Node ID Map. Its
-/// fold_ops argument has the opportunity to replace Node IDs and spans.
-pub struct Ctx<'a, F> {
-    map: &'a Map,
-    /// The node in which we are currently mapping (an item or a method).
-    /// When equal to DUMMY_NODE_ID, the next mapped node becomes the parent.
-    parent: NodeId,
-    fold_ops: F
-}
-
-impl<'a, F> Ctx<'a, F> {
-    fn insert(&self, id: NodeId, entry: MapEntry) {
-        (*self.map.map.borrow_mut()).grow_set(id as uint, &NotPresent, entry);
-    }
-}
-
-impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
-    fn new_id(&mut self, id: NodeId) -> NodeId {
-        let id = self.fold_ops.new_id(id);
-        if self.parent == DUMMY_NODE_ID {
-            self.parent = id;
-        }
-        id
-    }
-
-    fn new_span(&mut self, span: Span) -> Span {
-        self.fold_ops.new_span(span)
-    }
-
-    fn fold_item(&mut self, i: Gc<Item>) -> SmallVector<Gc<Item>> {
-        let parent = self.parent;
-        self.parent = DUMMY_NODE_ID;
-
-        let i = fold::noop_fold_item(&*i, self).expect_one("expected one item");
-        assert_eq!(self.parent, i.id);
-
-        match i.node {
-            ItemImpl(_, _, _, ref ms) => {
-                for &m in ms.iter() {
-                    self.insert(m.id, EntryMethod(self.parent, m));
-                }
-            }
-            ItemEnum(ref enum_definition, _) => {
-                for &v in enum_definition.variants.iter() {
-                    self.insert(v.node.id, EntryVariant(self.parent, v));
-                }
-            }
-            ItemForeignMod(ref nm) => {
-                for nitem in nm.items.iter() {
-                    self.insert(nitem.id, EntryForeignItem(self.parent,
-                                                           nitem.clone()));
-                }
-            }
-            ItemStruct(ref struct_def, _) => {
-                // If this is a tuple-like struct, register the constructor.
-                match struct_def.ctor_id {
-                    Some(ctor_id) => {
-                        self.insert(ctor_id, EntryStructCtor(self.parent,
-                                                             struct_def.clone()));
-                    }
-                    None => {}
-                }
-            }
-            ItemTrait(_, _, ref traits, ref methods) => {
-                for t in traits.iter() {
-                    self.insert(t.ref_id, EntryItem(self.parent, i));
-                }
-
-                for tm in methods.iter() {
-                    match *tm {
-                        Required(ref m) => {
-                            self.insert(m.id, EntryTraitMethod(self.parent,
-                                                               box(GC) (*tm).clone()));
-                        }
-                        Provided(m) => {
-                            self.insert(m.id, EntryTraitMethod(self.parent,
-                                                               box(GC) Provided(m)));
-                        }
-                    }
-                }
-            }
-            _ => {}
-        }
-
-        self.parent = parent;
-        self.insert(i.id, EntryItem(self.parent, i));
-
-        SmallVector::one(i)
-    }
-
-    fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
-        let pat = fold::noop_fold_pat(pat, self);
-        match pat.node {
-            PatIdent(..) => {
-                // Note: this is at least *potentially* a pattern...
-                self.insert(pat.id, EntryLocal(self.parent, pat));
-            }
-            _ => {
-                self.insert(pat.id, EntryPat(self.parent, pat));
-            }
-        }
-
-        pat
-    }
-
-    fn fold_expr(&mut self, expr: Gc<Expr>) -> Gc<Expr> {
-        let expr = fold::noop_fold_expr(expr, self);
-
-        self.insert(expr.id, EntryExpr(self.parent, expr));
-
-        expr
-    }
-
-    fn fold_stmt(&mut self, stmt: &Stmt) -> SmallVector<Gc<Stmt>> {
-        let stmt = fold::noop_fold_stmt(stmt, self).expect_one("expected one statement");
-        self.insert(ast_util::stmt_id(&*stmt), EntryStmt(self.parent, stmt));
-        SmallVector::one(stmt)
-    }
-
-    fn fold_type_method(&mut self, m: &TypeMethod) -> TypeMethod {
-        let parent = self.parent;
-        self.parent = DUMMY_NODE_ID;
-        let m = fold::noop_fold_type_method(m, self);
-        assert_eq!(self.parent, m.id);
-        self.parent = parent;
-        m
-    }
-
-    fn fold_method(&mut self, m: Gc<Method>) -> SmallVector<Gc<Method>> {
-        let parent = self.parent;
-        self.parent = DUMMY_NODE_ID;
-        let m = fold::noop_fold_method(&*m, self).expect_one(
-            "noop_fold_method must produce exactly one method");
-        assert_eq!(self.parent, m.id);
-        self.parent = parent;
-        SmallVector::one(m)
-    }
-
-    fn fold_fn_decl(&mut self, decl: &FnDecl) -> P<FnDecl> {
-        let decl = fold::noop_fold_fn_decl(decl, self);
-        for a in decl.inputs.iter() {
-            self.insert(a.id, EntryArg(self.parent, a.pat));
-        }
-        decl
-    }
-
-    fn fold_block(&mut self, block: P<Block>) -> P<Block> {
-        let block = fold::noop_fold_block(block, self);
-        self.insert(block.id, EntryBlock(self.parent, block));
-        block
-    }
-
-    fn fold_lifetime(&mut self, lifetime: &Lifetime) -> Lifetime {
-        let lifetime = fold::noop_fold_lifetime(lifetime, self);
-        self.insert(lifetime.id, EntryLifetime(self.parent, box(GC) lifetime));
-        lifetime
-    }
-
-    fn fold_mac(&mut self, mac: &Mac) -> Mac {
-        fold::fold_mac(mac, self)
-    }
-}
-
-pub fn map_crate<F: FoldOps>(krate: Crate, fold_ops: F) -> (Crate, Map) {
-    let map = Map { map: RefCell::new(Vec::new()) };
-    let krate = {
-        let mut cx = Ctx {
-            map: &map,
-            parent: CRATE_NODE_ID,
-            fold_ops: fold_ops
-        };
-        cx.insert(CRATE_NODE_ID, RootCrate);
-        cx.fold_crate(krate)
-    };
-
-    if log_enabled!(::log::DEBUG) {
-        let map = map.map.borrow();
-        // This only makes sense for ordered stores; note the
-        // enumerate to count the number of entries.
-        let (entries_less_1, _) = (*map).iter().filter(|&x| {
-            match *x {
-                NotPresent => false,
-                _ => true
-            }
-        }).enumerate().last().expect("AST map was empty after folding?");
-
-        let entries = entries_less_1 + 1;
-        let vector_length = (*map).len();
-        debug!("The AST map has {} entries with a maximum of {}: occupancy {:.1}%",
-              entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
-    }
-
-    (krate, map)
-}
-
-/// Used for items loaded from external crate that are being inlined into this
-/// crate.  The `path` should be the path to the item but should not include
-/// the item itself.
-pub fn map_decoded_item<F: FoldOps>(map: &Map,
-                                    path: Vec<PathElem> ,
-                                    fold_ops: F,
-                                    fold: |&mut Ctx<F>| -> InlinedItem)
-                                    -> InlinedItem {
-    let mut cx = Ctx {
-        map: map,
-        parent: DUMMY_NODE_ID,
-        fold_ops: fold_ops
-    };
-
-    // Generate a NodeId for the RootInlinedParent inserted below.
-    cx.new_id(DUMMY_NODE_ID);
-
-    // Methods get added to the AST map when their impl is visited.  Since we
-    // don't decode and instantiate the impl, but just the method, we have to
-    // add it to the table now. Likewise with foreign items.
-    let mut def_id = DefId { krate: LOCAL_CRATE, node: DUMMY_NODE_ID };
-    let ii = fold(&mut cx);
-    match ii {
-        IIItem(_) => {}
-        IIMethod(impl_did, is_provided, m) => {
-            let entry = if is_provided {
-                EntryTraitMethod(cx.parent, box(GC) Provided(m))
-            } else {
-                EntryMethod(cx.parent, m)
-            };
-            cx.insert(m.id, entry);
-            def_id = impl_did;
-        }
-        IIForeign(i) => {
-            cx.insert(i.id, EntryForeignItem(cx.parent, i));
-        }
-    }
-
-    cx.insert(cx.parent, RootInlinedParent(P(InlinedParent {
-        path: path,
-        def_id: def_id
-    })));
-
-    ii
-}
-
-fn node_id_to_string(map: &Map, id: NodeId) -> String {
-    match map.find(id) {
-        Some(NodeItem(item)) => {
-            let path_str = map.path_to_str_with_ident(id, item.ident);
-            let item_str = match item.node {
-                ItemStatic(..) => "static",
-                ItemFn(..) => "fn",
-                ItemMod(..) => "mod",
-                ItemForeignMod(..) => "foreign mod",
-                ItemTy(..) => "ty",
-                ItemEnum(..) => "enum",
-                ItemStruct(..) => "struct",
-                ItemTrait(..) => "trait",
-                ItemImpl(..) => "impl",
-                ItemMac(..) => "macro"
-            };
-            format!("{} {} (id={})", item_str, path_str, id)
-        }
-        Some(NodeForeignItem(item)) => {
-            let path_str = map.path_to_str_with_ident(id, item.ident);
-            format!("foreign item {} (id={})", path_str, id)
-        }
-        Some(NodeMethod(m)) => match m.node {
-            MethDecl(ident, _, _, _, _, _, _) =>
-                format!("method {} in {} (id={})",
-                        token::get_ident(ident),
-                        map.path_to_string(id), id),
-            MethMac(ref mac) =>
-                format!("method macro {} (id={})",
-                        pprust::mac_to_string(mac), id)
-        },
-        Some(NodeTraitMethod(ref tm)) => {
-            let m = ast_util::trait_method_to_ty_method(&**tm);
-            format!("method {} in {} (id={})",
-                    token::get_ident(m.ident),
-                    map.path_to_string(id), id)
-        }
-        Some(NodeVariant(ref variant)) => {
-            format!("variant {} in {} (id={})",
-                    token::get_ident(variant.node.name),
-                    map.path_to_string(id), id)
-        }
-        Some(NodeExpr(ref expr)) => {
-            format!("expr {} (id={})", pprust::expr_to_string(&**expr), id)
-        }
-        Some(NodeStmt(ref stmt)) => {
-            format!("stmt {} (id={})", pprust::stmt_to_string(&**stmt), id)
-        }
-        Some(NodeArg(ref pat)) => {
-            format!("arg {} (id={})", pprust::pat_to_string(&**pat), id)
-        }
-        Some(NodeLocal(ref pat)) => {
-            format!("local {} (id={})", pprust::pat_to_string(&**pat), id)
-        }
-        Some(NodePat(ref pat)) => {
-            format!("pat {} (id={})", pprust::pat_to_string(&**pat), id)
-        }
-        Some(NodeBlock(ref block)) => {
-            format!("block {} (id={})", pprust::block_to_string(&**block), id)
-        }
-        Some(NodeStructCtor(_)) => {
-            format!("struct_ctor {} (id={})", map.path_to_string(id), id)
-        }
-        Some(NodeLifetime(ref l)) => {
-            format!("lifetime {} (id={})",
-                    pprust::lifetime_to_string(&**l), id)
-        }
-        None => {
-            format!("unknown node (id={})", id)
-        }
-    }
-}
diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs
new file mode 100644 (file)
index 0000000..1280b88
--- /dev/null
@@ -0,0 +1,218 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This module provides a simplified abstraction for working with
+//! code blocks identified by their integer node-id.  In particular,
+//! it captures a common set of attributes that all "function-like
+//! things" (represented by `FnLike` instances) share.  For example,
+//! all `FnLike` instances have a type signature (be it explicit or
+//! inferred).  And all `FnLike` instances have a body, i.e. the code
+//! that is run when the function-like thing it represents is invoked.
+//!
+//! With the above abstraction in place, one can treat the program
+//! text as a collection of blocks of code (and most such blocks are
+//! nested within a uniquely determined `FnLike`), and users can ask
+//! for the `Code` associated with a particular NodeId.
+
+use abi;
+use ast::{P, Block, FnDecl, NodeId};
+use ast;
+use ast_map::{Node};
+use ast_map;
+use ast_util;
+use codemap::Span;
+use visit;
+
+/// An FnLikeNode is a Node that is like a fn, in that it has a decl
+/// and a body (as well as a NodeId, a span, etc).
+///
+/// More specifically, it is one of either:
+///   - A function item,
+///   - A closure expr (i.e. an ExprFnBlock or ExprProc), or
+///   - The default implementation for a trait method.
+///
+/// To construct one, use the `Code::from_node` function.
+pub struct FnLikeNode { node: ast_map::Node }
+
+/// MaybeFnLike wraps a method that indicates if an object
+/// corresponds to some FnLikeNode.
+pub trait MaybeFnLike { fn is_fn_like(&self) -> bool; }
+
+/// Components shared by fn-like things (fn items, methods, closures).
+pub struct FnParts<'a> {
+    pub decl: P<FnDecl>,
+    pub body: P<Block>,
+    pub kind: visit::FnKind<'a>,
+    pub span: Span,
+    pub id:   NodeId,
+}
+
+impl MaybeFnLike for ast::Item {
+    fn is_fn_like(&self) -> bool {
+        match self.node { ast::ItemFn(..) => true, _ => false, }
+    }
+}
+
+impl MaybeFnLike for ast::TraitMethod {
+    fn is_fn_like(&self) -> bool {
+        match *self { ast::Provided(_) => true, _ => false, }
+    }
+}
+
+impl MaybeFnLike for ast::Expr {
+    fn is_fn_like(&self) -> bool {
+        match self.node {
+            ast::ExprFnBlock(..) | ast::ExprProc(..) => true,
+            _ => false,
+        }
+    }
+}
+
+/// Carries either an FnLikeNode or a Block, as these are the two
+/// constructs that correspond to "code" (as in, something from which
+/// we can construct a control-flow graph).
+pub enum Code {
+    FnLikeCode(FnLikeNode),
+    BlockCode(P<Block>),
+}
+
+impl Code {
+    pub fn id(&self) -> ast::NodeId {
+        match *self {
+            FnLikeCode(node) => node.id(),
+            BlockCode(block) => block.id,
+        }
+    }
+
+    /// Attempts to construct a Code from presumed FnLike or Block node input.
+    pub fn from_node(node: Node) -> Option<Code> {
+        fn new(node: Node) -> FnLikeNode { FnLikeNode { node: node } }
+        match node {
+            ast_map::NodeItem(item) if item.is_fn_like() =>
+                Some(FnLikeCode(new(node))),
+            ast_map::NodeTraitMethod(tm) if tm.is_fn_like() =>
+                Some(FnLikeCode(new(node))),
+            ast_map::NodeMethod(_) =>
+                Some(FnLikeCode(new(node))),
+            ast_map::NodeExpr(e) if e.is_fn_like() =>
+                Some(FnLikeCode(new(node))),
+            ast_map::NodeBlock(block) =>
+                Some(BlockCode(block)),
+            _ =>
+                None,
+        }
+    }
+}
+
+/// These are all the components one can extract from a fn item for
+/// use when implementing FnLikeNode operations.
+struct ItemFnParts<'a> {
+    ident:    ast::Ident,
+    decl:     P<ast::FnDecl>,
+    style:    ast::FnStyle,
+    abi:      abi::Abi,
+    generics: &'a ast::Generics,
+    body:     P<Block>,
+    id:       ast::NodeId,
+    span:     Span
+}
+
+/// These are all the components one can extract from a closure expr
+/// for use when implementing FnLikeNode operations.
+struct ClosureParts {
+    decl: P<FnDecl>,
+    body: P<Block>,
+    id: NodeId,
+    span: Span
+}
+
+impl ClosureParts {
+    fn new(d: P<FnDecl>, b: P<Block>, id: NodeId, s: Span) -> ClosureParts {
+        ClosureParts { decl: d, body: b, id: id, span: s }
+    }
+}
+
+impl FnLikeNode {
+    pub fn to_fn_parts<'a>(&'a self) -> FnParts<'a> {
+        FnParts {
+            decl: self.decl(),
+            body: self.body(),
+            kind: self.kind(),
+            span: self.span(),
+            id:   self.id(),
+        }
+    }
+
+    pub fn body<'a>(&'a self) -> P<Block> {
+        self.handle(|i: ItemFnParts|     i.body,
+                    |m: &'a ast::Method| ast_util::method_body(m),
+                    |c: ClosureParts|    c.body)
+    }
+
+    pub fn decl<'a>(&'a self) -> P<FnDecl> {
+        self.handle(|i: ItemFnParts|     i.decl,
+                    |m: &'a ast::Method| ast_util::method_fn_decl(m),
+                    |c: ClosureParts|    c.decl)
+    }
+
+    pub fn span<'a>(&'a self) -> Span {
+        self.handle(|i: ItemFnParts|     i.span,
+                    |m: &'a ast::Method| m.span,
+                    |c: ClosureParts|    c.span)
+    }
+
+    pub fn id<'a>(&'a self) -> NodeId {
+        self.handle(|i: ItemFnParts|     i.id,
+                    |m: &'a ast::Method| m.id,
+                    |c: ClosureParts|    c.id)
+    }
+
+    pub fn kind<'a>(&'a self) -> visit::FnKind<'a> {
+        let item = |p: ItemFnParts<'a>| -> visit::FnKind<'a> {
+            visit::FkItemFn(p.ident, p.generics, p.style, p.abi)
+        };
+        let closure = |_: ClosureParts| {
+            visit::FkFnBlock
+        };
+        let method = |m: &'a ast::Method| {
+            visit::FkMethod(ast_util::method_ident(m), ast_util::method_generics(m), m)
+        };
+        self.handle(item, method, closure)
+    }
+
+    fn handle<'a, A>(&'a self,
+                     item_fn: |ItemFnParts<'a>| -> A,
+                     method: |&'a ast::Method| -> A,
+                     closure: |ClosureParts| -> A) -> A {
+        match self.node {
+            ast_map::NodeItem(ref i) => match i.node {
+                ast::ItemFn(decl, style, abi, ref generics, block) =>
+                    item_fn(ItemFnParts{
+                        ident: i.ident, decl: decl, style: style, body: block,
+                        generics: generics, abi: abi, id: i.id, span: i.span
+                    }),
+                _ => fail!("item FnLikeNode that is not fn-like"),
+            },
+            ast_map::NodeTraitMethod(ref t) => match **t {
+                ast::Provided(ref m) => method(&**m),
+                _ => fail!("trait method FnLikeNode that is not fn-like"),
+            },
+            ast_map::NodeMethod(ref m) => method(&**m),
+            ast_map::NodeExpr(ref e) => match e.node {
+                ast::ExprFnBlock(ref decl, ref block) =>
+                    closure(ClosureParts::new(*decl, *block, e.id, e.span)),
+                ast::ExprProc(ref decl, ref block) =>
+                    closure(ClosureParts::new(*decl, *block, e.id, e.span)),
+                _ => fail!("expr FnLikeNode that is not fn-like"),
+            },
+            _ => fail!("other FnLikeNode that is not fn-like"),
+        }
+    }
+}
diff --git a/src/libsyntax/ast_map/mod.rs b/src/libsyntax/ast_map/mod.rs
new file mode 100644 (file)
index 0000000..50e487b
--- /dev/null
@@ -0,0 +1,760 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use abi;
+use ast::*;
+use ast_util;
+use codemap::Span;
+use fold::Folder;
+use fold;
+use parse::token;
+use print::pprust;
+use util::small_vector::SmallVector;
+
+use std::cell::RefCell;
+use std::fmt;
+use std::gc::{Gc, GC};
+use std::iter;
+use std::slice;
+
+pub mod blocks;
+
+#[deriving(Clone, PartialEq)]
+pub enum PathElem {
+    PathMod(Name),
+    PathName(Name)
+}
+
+impl PathElem {
+    pub fn name(&self) -> Name {
+        match *self {
+            PathMod(name) | PathName(name) => name
+        }
+    }
+}
+
+impl fmt::Show for PathElem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let slot = token::get_name(self.name());
+        write!(f, "{}", slot)
+    }
+}
+
+#[deriving(Clone)]
+struct LinkedPathNode<'a> {
+    node: PathElem,
+    next: LinkedPath<'a>,
+}
+
+type LinkedPath<'a> = Option<&'a LinkedPathNode<'a>>;
+
+impl<'a> Iterator<PathElem> for LinkedPath<'a> {
+    fn next(&mut self) -> Option<PathElem> {
+        match *self {
+            Some(node) => {
+                *self = node.next;
+                Some(node.node)
+            }
+            None => None
+        }
+    }
+}
+
+// HACK(eddyb) move this into libstd (value wrapper for slice::Items).
+#[deriving(Clone)]
+pub struct Values<'a, T>(pub slice::Items<'a, T>);
+
+impl<'a, T: Copy> Iterator<T> for Values<'a, T> {
+    fn next(&mut self) -> Option<T> {
+        let &Values(ref mut items) = self;
+        items.next().map(|&x| x)
+    }
+}
+
+/// The type of the iterator used by with_path.
+pub type PathElems<'a, 'b> = iter::Chain<Values<'a, PathElem>, LinkedPath<'b>>;
+
+pub fn path_to_string<PI: Iterator<PathElem>>(mut path: PI) -> String {
+    let itr = token::get_ident_interner();
+
+    path.fold(String::new(), |mut s, e| {
+        let e = itr.get(e.name());
+        if !s.is_empty() {
+            s.push_str("::");
+        }
+        s.push_str(e.as_slice());
+        s
+    }).to_string()
+}
+
+#[deriving(Clone)]
+pub enum Node {
+    NodeItem(Gc<Item>),
+    NodeForeignItem(Gc<ForeignItem>),
+    NodeTraitMethod(Gc<TraitMethod>),
+    NodeMethod(Gc<Method>),
+    NodeVariant(P<Variant>),
+    NodeExpr(Gc<Expr>),
+    NodeStmt(Gc<Stmt>),
+    NodeArg(Gc<Pat>),
+    NodeLocal(Gc<Pat>),
+    NodePat(Gc<Pat>),
+    NodeBlock(P<Block>),
+
+    /// NodeStructCtor represents a tuple struct.
+    NodeStructCtor(Gc<StructDef>),
+
+    NodeLifetime(Gc<Lifetime>),
+}
+
+/// Represents an entry and its parent Node ID
+/// The odd layout is to bring down the total size.
+#[deriving(Clone)]
+enum MapEntry {
+    /// Placeholder for holes in the map.
+    NotPresent,
+
+    /// All the node types, with a parent ID.
+    EntryItem(NodeId, Gc<Item>),
+    EntryForeignItem(NodeId, Gc<ForeignItem>),
+    EntryTraitMethod(NodeId, Gc<TraitMethod>),
+    EntryMethod(NodeId, Gc<Method>),
+    EntryVariant(NodeId, P<Variant>),
+    EntryExpr(NodeId, Gc<Expr>),
+    EntryStmt(NodeId, Gc<Stmt>),
+    EntryArg(NodeId, Gc<Pat>),
+    EntryLocal(NodeId, Gc<Pat>),
+    EntryPat(NodeId, Gc<Pat>),
+    EntryBlock(NodeId, P<Block>),
+    EntryStructCtor(NodeId, Gc<StructDef>),
+    EntryLifetime(NodeId, Gc<Lifetime>),
+
+    /// Roots for node trees.
+    RootCrate,
+    RootInlinedParent(P<InlinedParent>)
+}
+
+struct InlinedParent {
+    path: Vec<PathElem> ,
+    /// Required by NodeTraitMethod and NodeMethod.
+    def_id: DefId
+}
+
+impl MapEntry {
+    fn parent(&self) -> Option<NodeId> {
+        Some(match *self {
+            EntryItem(id, _) => id,
+            EntryForeignItem(id, _) => id,
+            EntryTraitMethod(id, _) => id,
+            EntryMethod(id, _) => id,
+            EntryVariant(id, _) => id,
+            EntryExpr(id, _) => id,
+            EntryStmt(id, _) => id,
+            EntryArg(id, _) => id,
+            EntryLocal(id, _) => id,
+            EntryPat(id, _) => id,
+            EntryBlock(id, _) => id,
+            EntryStructCtor(id, _) => id,
+            EntryLifetime(id, _) => id,
+            _ => return None
+        })
+    }
+
+    fn to_node(&self) -> Option<Node> {
+        Some(match *self {
+            EntryItem(_, p) => NodeItem(p),
+            EntryForeignItem(_, p) => NodeForeignItem(p),
+            EntryTraitMethod(_, p) => NodeTraitMethod(p),
+            EntryMethod(_, p) => NodeMethod(p),
+            EntryVariant(_, p) => NodeVariant(p),
+            EntryExpr(_, p) => NodeExpr(p),
+            EntryStmt(_, p) => NodeStmt(p),
+            EntryArg(_, p) => NodeArg(p),
+            EntryLocal(_, p) => NodeLocal(p),
+            EntryPat(_, p) => NodePat(p),
+            EntryBlock(_, p) => NodeBlock(p),
+            EntryStructCtor(_, p) => NodeStructCtor(p),
+            EntryLifetime(_, p) => NodeLifetime(p),
+            _ => return None
+        })
+    }
+}
+
+/// Represents a mapping from Node IDs to AST elements and their parent
+/// Node IDs
+pub struct Map {
+    /// NodeIds are sequential integers from 0, so we can be
+    /// super-compact by storing them in a vector. Not everything with
+    /// a NodeId is in the map, but empirically the occupancy is about
+    /// 75-80%, so there's not too much overhead (certainly less than
+    /// a hashmap, since they (at the time of writing) have a maximum
+    /// of 75% occupancy).
+    ///
+    /// Also, indexing is pretty quick when you've got a vector and
+    /// plain old integers.
+    map: RefCell<Vec<MapEntry> >
+}
+
+impl Map {
+    fn find_entry(&self, id: NodeId) -> Option<MapEntry> {
+        let map = self.map.borrow();
+        if map.len() > id as uint {
+            Some(*map.get(id as uint))
+        } else {
+            None
+        }
+    }
+
+    /// Retrieve the Node corresponding to `id`, failing if it cannot
+    /// be found.
+    pub fn get(&self, id: NodeId) -> Node {
+        match self.find(id) {
+            Some(node) => node,
+            None => fail!("couldn't find node id {} in the AST map", id)
+        }
+    }
+
+    /// Retrieve the Node corresponding to `id`, returning None if
+    /// cannot be found.
+    pub fn find(&self, id: NodeId) -> Option<Node> {
+        self.find_entry(id).and_then(|x| x.to_node())
+    }
+
+    /// Retrieve the parent NodeId for `id`, or `id` itself if no
+    /// parent is registered in this map.
+    pub fn get_parent(&self, id: NodeId) -> NodeId {
+        self.find_entry(id).and_then(|x| x.parent()).unwrap_or(id)
+    }
+
+    pub fn get_parent_did(&self, id: NodeId) -> DefId {
+        let parent = self.get_parent(id);
+        match self.find_entry(parent) {
+            Some(RootInlinedParent(data)) => data.def_id,
+            _ => ast_util::local_def(parent)
+        }
+    }
+
+    pub fn get_foreign_abi(&self, id: NodeId) -> abi::Abi {
+        let parent = self.get_parent(id);
+        let abi = match self.find_entry(parent) {
+            Some(EntryItem(_, i)) => match i.node {
+                ItemForeignMod(ref nm) => Some(nm.abi),
+                _ => None
+            },
+            /// Wrong but OK, because the only inlined foreign items are intrinsics.
+            Some(RootInlinedParent(_)) => Some(abi::RustIntrinsic),
+            _ => None
+        };
+        match abi {
+            Some(abi) => abi,
+            None => fail!("expected foreign mod or inlined parent, found {}",
+                          self.node_to_string(parent))
+        }
+    }
+
+    pub fn get_foreign_vis(&self, id: NodeId) -> Visibility {
+        let vis = self.expect_foreign_item(id).vis;
+        match self.find(self.get_parent(id)) {
+            Some(NodeItem(i)) => vis.inherit_from(i.vis),
+            _ => vis
+        }
+    }
+
+    pub fn expect_item(&self, id: NodeId) -> Gc<Item> {
+        match self.find(id) {
+            Some(NodeItem(item)) => item,
+            _ => fail!("expected item, found {}", self.node_to_string(id))
+        }
+    }
+
+    pub fn expect_struct(&self, id: NodeId) -> Gc<StructDef> {
+        match self.find(id) {
+            Some(NodeItem(i)) => {
+                match i.node {
+                    ItemStruct(struct_def, _) => struct_def,
+                    _ => fail!("struct ID bound to non-struct")
+                }
+            }
+            Some(NodeVariant(ref variant)) => {
+                match (*variant).node.kind {
+                    StructVariantKind(struct_def) => struct_def,
+                    _ => fail!("struct ID bound to enum variant that isn't struct-like"),
+                }
+            }
+            _ => fail!(format!("expected struct, found {}", self.node_to_string(id))),
+        }
+    }
+
+    pub fn expect_variant(&self, id: NodeId) -> P<Variant> {
+        match self.find(id) {
+            Some(NodeVariant(variant)) => variant,
+            _ => fail!(format!("expected variant, found {}", self.node_to_string(id))),
+        }
+    }
+
+    pub fn expect_foreign_item(&self, id: NodeId) -> Gc<ForeignItem> {
+        match self.find(id) {
+            Some(NodeForeignItem(item)) => item,
+            _ => fail!("expected foreign item, found {}", self.node_to_string(id))
+        }
+    }
+
+    /// returns the name associated with the given NodeId's AST
+    pub fn get_path_elem(&self, id: NodeId) -> PathElem {
+        let node = self.get(id);
+        match node {
+            NodeItem(item) => {
+                match item.node {
+                    ItemMod(_) | ItemForeignMod(_) => {
+                        PathMod(item.ident.name)
+                    }
+                    _ => PathName(item.ident.name)
+                }
+            }
+            NodeForeignItem(i) => PathName(i.ident.name),
+            NodeMethod(m) => match m.node {
+                MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
+                MethMac(_) => fail!("no path elem for {:?}", node)
+            },
+            NodeTraitMethod(tm) => match *tm {
+                Required(ref m) => PathName(m.ident.name),
+                Provided(m) => match m.node {
+                    MethDecl(ident, _, _, _, _, _, _) => PathName(ident.name),
+                    MethMac(_) => fail!("no path elem for {:?}", node),
+                }
+            },
+            NodeVariant(v) => PathName(v.node.name.name),
+            _ => fail!("no path elem for {:?}", node)
+        }
+    }
+
+    pub fn with_path<T>(&self, id: NodeId, f: |PathElems| -> T) -> T {
+        self.with_path_next(id, None, f)
+    }
+
+    pub fn path_to_string(&self, id: NodeId) -> String {
+        self.with_path(id, |path| path_to_string(path))
+    }
+
+    fn path_to_str_with_ident(&self, id: NodeId, i: Ident) -> String {
+        self.with_path(id, |path| {
+            path_to_string(path.chain(Some(PathName(i.name)).move_iter()))
+        })
+    }
+
+    fn with_path_next<T>(&self, id: NodeId, next: LinkedPath, f: |PathElems| -> T) -> T {
+        let parent = self.get_parent(id);
+        let parent = match self.find_entry(id) {
+            Some(EntryForeignItem(..)) | Some(EntryVariant(..)) => {
+                // Anonymous extern items, enum variants and struct ctors
+                // go in the parent scope.
+                self.get_parent(parent)
+            }
+            // But tuple struct ctors don't have names, so use the path of its
+            // parent, the struct item. Similarly with closure expressions.
+            Some(EntryStructCtor(..)) | Some(EntryExpr(..)) => {
+                return self.with_path_next(parent, next, f);
+            }
+            _ => parent
+        };
+        if parent == id {
+            match self.find_entry(id) {
+                Some(RootInlinedParent(data)) => {
+                    f(Values(data.path.iter()).chain(next))
+                }
+                _ => f(Values([].iter()).chain(next))
+            }
+        } else {
+            self.with_path_next(parent, Some(&LinkedPathNode {
+                node: self.get_path_elem(id),
+                next: next
+            }), f)
+        }
+    }
+
+    /// Given a node ID and a closure, apply the closure to the array
+    /// of attributes associated with the AST corresponding to the Node ID
+    pub fn with_attrs<T>(&self, id: NodeId, f: |Option<&[Attribute]>| -> T) -> T {
+        let node = self.get(id);
+        let attrs = match node {
+            NodeItem(ref i) => Some(i.attrs.as_slice()),
+            NodeForeignItem(ref fi) => Some(fi.attrs.as_slice()),
+            NodeTraitMethod(ref tm) => match **tm {
+                Required(ref type_m) => Some(type_m.attrs.as_slice()),
+                Provided(ref m) => Some(m.attrs.as_slice())
+            },
+            NodeMethod(ref m) => Some(m.attrs.as_slice()),
+            NodeVariant(ref v) => Some(v.node.attrs.as_slice()),
+            // unit/tuple structs take the attributes straight from
+            // the struct definition.
+            // FIXME(eddyb) make this work again (requires access to the map).
+            NodeStructCtor(_) => {
+                return self.with_attrs(self.get_parent(id), f);
+            }
+            _ => None
+        };
+        f(attrs)
+    }
+
+    pub fn opt_span(&self, id: NodeId) -> Option<Span> {
+        let sp = match self.find(id) {
+            Some(NodeItem(item)) => item.span,
+            Some(NodeForeignItem(foreign_item)) => foreign_item.span,
+            Some(NodeTraitMethod(trait_method)) => {
+                match *trait_method {
+                    Required(ref type_method) => type_method.span,
+                    Provided(ref method) => method.span,
+                }
+            }
+            Some(NodeMethod(method)) => method.span,
+            Some(NodeVariant(variant)) => variant.span,
+            Some(NodeExpr(expr)) => expr.span,
+            Some(NodeStmt(stmt)) => stmt.span,
+            Some(NodeArg(pat)) | Some(NodeLocal(pat)) => pat.span,
+            Some(NodePat(pat)) => pat.span,
+            Some(NodeBlock(block)) => block.span,
+            Some(NodeStructCtor(_)) => self.expect_item(self.get_parent(id)).span,
+            _ => return None,
+        };
+        Some(sp)
+    }
+
+    pub fn span(&self, id: NodeId) -> Span {
+        self.opt_span(id)
+            .unwrap_or_else(|| fail!("AstMap.span: could not find span for id {}", id))
+    }
+
+    pub fn node_to_string(&self, id: NodeId) -> String {
+        node_id_to_string(self, id)
+    }
+}
+
+pub trait FoldOps {
+    fn new_id(&self, id: NodeId) -> NodeId {
+        id
+    }
+    fn new_span(&self, span: Span) -> Span {
+        span
+    }
+}
+
+/// A Folder that walks over an AST and constructs a Node ID Map. Its
+/// fold_ops argument has the opportunity to replace Node IDs and spans.
+pub struct Ctx<'a, F> {
+    map: &'a Map,
+    /// The node in which we are currently mapping (an item or a method).
+    /// When equal to DUMMY_NODE_ID, the next mapped node becomes the parent.
+    parent: NodeId,
+    fold_ops: F
+}
+
+impl<'a, F> Ctx<'a, F> {
+    fn insert(&self, id: NodeId, entry: MapEntry) {
+        (*self.map.map.borrow_mut()).grow_set(id as uint, &NotPresent, entry);
+    }
+}
+
+impl<'a, F: FoldOps> Folder for Ctx<'a, F> {
+    fn new_id(&mut self, id: NodeId) -> NodeId {
+        let id = self.fold_ops.new_id(id);
+        if self.parent == DUMMY_NODE_ID {
+            self.parent = id;
+        }
+        id
+    }
+
+    fn new_span(&mut self, span: Span) -> Span {
+        self.fold_ops.new_span(span)
+    }
+
+    fn fold_item(&mut self, i: Gc<Item>) -> SmallVector<Gc<Item>> {
+        let parent = self.parent;
+        self.parent = DUMMY_NODE_ID;
+
+        let i = fold::noop_fold_item(&*i, self).expect_one("expected one item");
+        assert_eq!(self.parent, i.id);
+
+        match i.node {
+            ItemImpl(_, _, _, ref ms) => {
+                for &m in ms.iter() {
+                    self.insert(m.id, EntryMethod(self.parent, m));
+                }
+            }
+            ItemEnum(ref enum_definition, _) => {
+                for &v in enum_definition.variants.iter() {
+                    self.insert(v.node.id, EntryVariant(self.parent, v));
+                }
+            }
+            ItemForeignMod(ref nm) => {
+                for nitem in nm.items.iter() {
+                    self.insert(nitem.id, EntryForeignItem(self.parent,
+                                                           nitem.clone()));
+                }
+            }
+            ItemStruct(ref struct_def, _) => {
+                // If this is a tuple-like struct, register the constructor.
+                match struct_def.ctor_id {
+                    Some(ctor_id) => {
+                        self.insert(ctor_id, EntryStructCtor(self.parent,
+                                                             struct_def.clone()));
+                    }
+                    None => {}
+                }
+            }
+            ItemTrait(_, _, ref traits, ref methods) => {
+                for t in traits.iter() {
+                    self.insert(t.ref_id, EntryItem(self.parent, i));
+                }
+
+                for tm in methods.iter() {
+                    match *tm {
+                        Required(ref m) => {
+                            self.insert(m.id, EntryTraitMethod(self.parent,
+                                                               box(GC) (*tm).clone()));
+                        }
+                        Provided(m) => {
+                            self.insert(m.id, EntryTraitMethod(self.parent,
+                                                               box(GC) Provided(m)));
+                        }
+                    }
+                }
+            }
+            _ => {}
+        }
+
+        self.parent = parent;
+        self.insert(i.id, EntryItem(self.parent, i));
+
+        SmallVector::one(i)
+    }
+
+    fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
+        let pat = fold::noop_fold_pat(pat, self);
+        match pat.node {
+            PatIdent(..) => {
+                // Note: this is at least *potentially* a pattern...
+                self.insert(pat.id, EntryLocal(self.parent, pat));
+            }
+            _ => {
+                self.insert(pat.id, EntryPat(self.parent, pat));
+            }
+        }
+
+        pat
+    }
+
+    fn fold_expr(&mut self, expr: Gc<Expr>) -> Gc<Expr> {
+        let expr = fold::noop_fold_expr(expr, self);
+
+        self.insert(expr.id, EntryExpr(self.parent, expr));
+
+        expr
+    }
+
+    fn fold_stmt(&mut self, stmt: &Stmt) -> SmallVector<Gc<Stmt>> {
+        let stmt = fold::noop_fold_stmt(stmt, self).expect_one("expected one statement");
+        self.insert(ast_util::stmt_id(&*stmt), EntryStmt(self.parent, stmt));
+        SmallVector::one(stmt)
+    }
+
+    fn fold_type_method(&mut self, m: &TypeMethod) -> TypeMethod {
+        let parent = self.parent;
+        self.parent = DUMMY_NODE_ID;
+        let m = fold::noop_fold_type_method(m, self);
+        assert_eq!(self.parent, m.id);
+        self.parent = parent;
+        m
+    }
+
+    fn fold_method(&mut self, m: Gc<Method>) -> SmallVector<Gc<Method>> {
+        let parent = self.parent;
+        self.parent = DUMMY_NODE_ID;
+        let m = fold::noop_fold_method(&*m, self).expect_one(
+            "noop_fold_method must produce exactly one method");
+        assert_eq!(self.parent, m.id);
+        self.parent = parent;
+        SmallVector::one(m)
+    }
+
+    fn fold_fn_decl(&mut self, decl: &FnDecl) -> P<FnDecl> {
+        let decl = fold::noop_fold_fn_decl(decl, self);
+        for a in decl.inputs.iter() {
+            self.insert(a.id, EntryArg(self.parent, a.pat));
+        }
+        decl
+    }
+
+    fn fold_block(&mut self, block: P<Block>) -> P<Block> {
+        let block = fold::noop_fold_block(block, self);
+        self.insert(block.id, EntryBlock(self.parent, block));
+        block
+    }
+
+    fn fold_lifetime(&mut self, lifetime: &Lifetime) -> Lifetime {
+        let lifetime = fold::noop_fold_lifetime(lifetime, self);
+        self.insert(lifetime.id, EntryLifetime(self.parent, box(GC) lifetime));
+        lifetime
+    }
+
+    fn fold_mac(&mut self, mac: &Mac) -> Mac {
+        fold::fold_mac(mac, self)
+    }
+}
+
+pub fn map_crate<F: FoldOps>(krate: Crate, fold_ops: F) -> (Crate, Map) {
+    let map = Map { map: RefCell::new(Vec::new()) };
+    let krate = {
+        let mut cx = Ctx {
+            map: &map,
+            parent: CRATE_NODE_ID,
+            fold_ops: fold_ops
+        };
+        cx.insert(CRATE_NODE_ID, RootCrate);
+        cx.fold_crate(krate)
+    };
+
+    if log_enabled!(::log::DEBUG) {
+        let map = map.map.borrow();
+        // This only makes sense for ordered stores; note the
+        // enumerate to count the number of entries.
+        let (entries_less_1, _) = (*map).iter().filter(|&x| {
+            match *x {
+                NotPresent => false,
+                _ => true
+            }
+        }).enumerate().last().expect("AST map was empty after folding?");
+
+        let entries = entries_less_1 + 1;
+        let vector_length = (*map).len();
+        debug!("The AST map has {} entries with a maximum of {}: occupancy {:.1}%",
+              entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
+    }
+
+    (krate, map)
+}
+
+/// Used for items loaded from external crate that are being inlined into this
+/// crate.  The `path` should be the path to the item but should not include
+/// the item itself.
+pub fn map_decoded_item<F: FoldOps>(map: &Map,
+                                    path: Vec<PathElem> ,
+                                    fold_ops: F,
+                                    fold: |&mut Ctx<F>| -> InlinedItem)
+                                    -> InlinedItem {
+    let mut cx = Ctx {
+        map: map,
+        parent: DUMMY_NODE_ID,
+        fold_ops: fold_ops
+    };
+
+    // Generate a NodeId for the RootInlinedParent inserted below.
+    cx.new_id(DUMMY_NODE_ID);
+
+    // Methods get added to the AST map when their impl is visited.  Since we
+    // don't decode and instantiate the impl, but just the method, we have to
+    // add it to the table now. Likewise with foreign items.
+    let mut def_id = DefId { krate: LOCAL_CRATE, node: DUMMY_NODE_ID };
+    let ii = fold(&mut cx);
+    match ii {
+        IIItem(_) => {}
+        IIMethod(impl_did, is_provided, m) => {
+            let entry = if is_provided {
+                EntryTraitMethod(cx.parent, box(GC) Provided(m))
+            } else {
+                EntryMethod(cx.parent, m)
+            };
+            cx.insert(m.id, entry);
+            def_id = impl_did;
+        }
+        IIForeign(i) => {
+            cx.insert(i.id, EntryForeignItem(cx.parent, i));
+        }
+    }
+
+    cx.insert(cx.parent, RootInlinedParent(P(InlinedParent {
+        path: path,
+        def_id: def_id
+    })));
+
+    ii
+}
+
+fn node_id_to_string(map: &Map, id: NodeId) -> String {
+    match map.find(id) {
+        Some(NodeItem(item)) => {
+            let path_str = map.path_to_str_with_ident(id, item.ident);
+            let item_str = match item.node {
+                ItemStatic(..) => "static",
+                ItemFn(..) => "fn",
+                ItemMod(..) => "mod",
+                ItemForeignMod(..) => "foreign mod",
+                ItemTy(..) => "ty",
+                ItemEnum(..) => "enum",
+                ItemStruct(..) => "struct",
+                ItemTrait(..) => "trait",
+                ItemImpl(..) => "impl",
+                ItemMac(..) => "macro"
+            };
+            format!("{} {} (id={})", item_str, path_str, id)
+        }
+        Some(NodeForeignItem(item)) => {
+            let path_str = map.path_to_str_with_ident(id, item.ident);
+            format!("foreign item {} (id={})", path_str, id)
+        }
+        Some(NodeMethod(m)) => match m.node {
+            MethDecl(ident, _, _, _, _, _, _) =>
+                format!("method {} in {} (id={})",
+                        token::get_ident(ident),
+                        map.path_to_string(id), id),
+            MethMac(ref mac) =>
+                format!("method macro {} (id={})",
+                        pprust::mac_to_string(mac), id)
+        },
+        Some(NodeTraitMethod(ref tm)) => {
+            let m = ast_util::trait_method_to_ty_method(&**tm);
+            format!("method {} in {} (id={})",
+                    token::get_ident(m.ident),
+                    map.path_to_string(id), id)
+        }
+        Some(NodeVariant(ref variant)) => {
+            format!("variant {} in {} (id={})",
+                    token::get_ident(variant.node.name),
+                    map.path_to_string(id), id)
+        }
+        Some(NodeExpr(ref expr)) => {
+            format!("expr {} (id={})", pprust::expr_to_string(&**expr), id)
+        }
+        Some(NodeStmt(ref stmt)) => {
+            format!("stmt {} (id={})", pprust::stmt_to_string(&**stmt), id)
+        }
+        Some(NodeArg(ref pat)) => {
+            format!("arg {} (id={})", pprust::pat_to_string(&**pat), id)
+        }
+        Some(NodeLocal(ref pat)) => {
+            format!("local {} (id={})", pprust::pat_to_string(&**pat), id)
+        }
+        Some(NodePat(ref pat)) => {
+            format!("pat {} (id={})", pprust::pat_to_string(&**pat), id)
+        }
+        Some(NodeBlock(ref block)) => {
+            format!("block {} (id={})", pprust::block_to_string(&**block), id)
+        }
+        Some(NodeStructCtor(_)) => {
+            format!("struct_ctor {} (id={})", map.path_to_string(id), id)
+        }
+        Some(NodeLifetime(ref l)) => {
+            format!("lifetime {} (id={})",
+                    pprust::lifetime_to_string(&**l), id)
+        }
+        None => {
+            format!("unknown node (id={})", id)
+        }
+    }
+}