]> git.lizzy.rs Git - rust.git/commitdiff
Refactor and fix FIXME's in mtwt hygiene code
authorEdward Wang <edward.yu.wang@gmail.com>
Mon, 24 Feb 2014 20:47:19 +0000 (04:47 +0800)
committerEdward Wang <edward.yu.wang@gmail.com>
Wed, 5 Mar 2014 14:45:51 +0000 (22:45 +0800)
- Moves mtwt hygiene code into its own file
- Fixes FIXME's which leads to ~2x speed gain in expansion pass
- It is now @-free

src/librustc/middle/resolve.rs
src/libsyntax/ast.rs
src/libsyntax/ast_util.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/mtwt.rs [new file with mode: 0644]
src/libsyntax/lib.rs
src/libsyntax/parse/token.rs

index 07d0758c555c574c62df247472c7929f71e00bd0..0d3586266e869520326fc5199f25a851e14cc140 100644 (file)
@@ -19,8 +19,9 @@
 
 use syntax::ast::*;
 use syntax::ast;
-use syntax::ast_util::{def_id_of_def, local_def, mtwt_resolve};
+use syntax::ast_util::{def_id_of_def, local_def};
 use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method};
+use syntax::ext::mtwt;
 use syntax::parse::token::special_idents;
 use syntax::parse::token;
 use syntax::print::pprust::path_to_str;
@@ -4176,7 +4177,7 @@ fn resolve_local(&mut self, local: &Local) {
     fn binding_mode_map(&mut self, pat: @Pat) -> BindingMap {
         let mut result = HashMap::new();
         pat_bindings(self.def_map, pat, |binding_mode, _id, sp, path| {
-            let name = mtwt_resolve(path_to_ident(path));
+            let name = mtwt::resolve(path_to_ident(path));
             result.insert(name,
                           binding_info {span: sp,
                                         binding_mode: binding_mode});
@@ -4411,7 +4412,7 @@ fn resolve_pattern(&mut self,
                     // what you want).
 
                     let ident = path.segments.get(0).identifier;
-                    let renamed = mtwt_resolve(ident);
+                    let renamed = mtwt::resolve(ident);
 
                     match self.resolve_bare_identifier_pattern(ident) {
                         FoundStructOrEnumVariant(def, lp)
@@ -4965,7 +4966,7 @@ fn resolve_identifier_in_local_ribs(&mut self,
         let search_result;
         match namespace {
             ValueNS => {
-                let renamed = mtwt_resolve(ident);
+                let renamed = mtwt::resolve(ident);
                 let mut value_ribs = self.value_ribs.borrow_mut();
                 search_result = self.search_ribs(value_ribs.get(),
                                                  renamed,
@@ -5213,7 +5214,7 @@ fn resolve_expr(&mut self, expr: &Expr) {
                         let rib = label_ribs.get()[label_ribs.get().len() -
                                                    1];
                         let mut bindings = rib.bindings.borrow_mut();
-                        let renamed = mtwt_resolve(label);
+                        let renamed = mtwt::resolve(label);
                         bindings.get().insert(renamed, def_like);
                     }
 
@@ -5225,7 +5226,7 @@ fn resolve_expr(&mut self, expr: &Expr) {
 
             ExprBreak(Some(label)) | ExprAgain(Some(label)) => {
                 let mut label_ribs = self.label_ribs.borrow_mut();
-                let renamed = mtwt_resolve(label);
+                let renamed = mtwt::resolve(label);
                 match self.search_ribs(label_ribs.get(), renamed, expr.span) {
                     None =>
                         self.resolve_error(expr.span,
index 7efae9305e408e20df0bd6cc7c9ba8475500c011..544d9f2d66935224c2be1c24720bbc6686f84399 100644 (file)
@@ -19,8 +19,6 @@
 
 use std::fmt;
 use std::fmt::Show;
-use std::cell::RefCell;
-use collections::HashMap;
 use std::option::Option;
 use std::rc::Rc;
 use std::vec_ng::Vec;
@@ -42,7 +40,10 @@ pub fn P<T: 'static>(value: T) -> P<T> {
 // macro expansion per Flatt et al., "Macros
 // That Work Together"
 #[deriving(Clone, Hash, TotalEq, TotalOrd, Show)]
-pub struct Ident { name: Name, ctxt: SyntaxContext }
+pub struct Ident {
+    name: Name,
+    ctxt: SyntaxContext
+}
 
 impl Ident {
     /// Construct an identifier with the given name and an empty context:
@@ -88,43 +89,9 @@ fn ne(&self, other: &Ident) -> bool {
 // this uint is a reference to a table stored in thread-local
 // storage.
 pub type SyntaxContext = u32;
-
-// the SCTable contains a table of SyntaxContext_'s. It
-// represents a flattened tree structure, to avoid having
-// managed pointers everywhere (that caused an ICE).
-// the mark_memo and rename_memo fields are side-tables
-// that ensure that adding the same mark to the same context
-// gives you back the same context as before. This shouldn't
-// change the semantics--everything here is immutable--but
-// it should cut down on memory use *a lot*; applying a mark
-// to a tree containing 50 identifiers would otherwise generate
-pub struct SCTable {
-    table: RefCell<Vec<SyntaxContext_> >,
-    mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
-    rename_memo: RefCell<HashMap<(SyntaxContext,Ident,Name),SyntaxContext>>,
-}
-
-// NB: these must be placed in any SCTable...
 pub static EMPTY_CTXT : SyntaxContext = 0;
 pub static ILLEGAL_CTXT : SyntaxContext = 1;
 
-#[deriving(Eq, Encodable, Decodable, Hash)]
-pub enum SyntaxContext_ {
-    EmptyCtxt,
-    Mark (Mrk,SyntaxContext),
-    // flattening the name and syntaxcontext into the rename...
-    // HIDDEN INVARIANTS:
-    // 1) the first name in a Rename node
-    // can only be a programmer-supplied name.
-    // 2) Every Rename node with a given Name in the
-    // "to" slot must have the same name and context
-    // in the "from" slot. In essence, they're all
-    // pointers to a single "rename" event node.
-    Rename (Ident,Name,SyntaxContext),
-    // actually, IllegalCtxt may not be necessary.
-    IllegalCtxt
-}
-
 /// A name is a part of an identifier, representing a string or gensym. It's
 /// the result of interning.
 pub type Name = u32;
index db9ea480e96206fb877393f2566bacab40a3d1b5..3899731b250089634fbf6e8573d9be21923352d4 100644 (file)
 use visit::Visitor;
 use visit;
 
-use std::cell::{Cell, RefCell};
+use std::cell::Cell;
 use std::cmp;
-use collections::HashMap;
 use std::u32;
-use std::local_data;
 use std::vec_ng::Vec;
 
 pub fn path_name_i(idents: &[Ident]) -> ~str {
@@ -651,251 +649,6 @@ pub fn pat_is_ident(pat: @ast::Pat) -> bool {
     }
 }
 
-// HYGIENE FUNCTIONS
-
-/// Extend a syntax context with a given mark
-pub fn new_mark(m:Mrk, tail:SyntaxContext) -> SyntaxContext {
-    new_mark_internal(m,tail,get_sctable())
-}
-
-// Extend a syntax context with a given mark and table
-// FIXME #8215 : currently pub to allow testing
-pub fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable)
-                         -> SyntaxContext {
-    let key = (tail,m);
-    // FIXME #5074 : can't use more natural style because we're missing
-    // flow-sensitivity. Results in two lookups on a hash table hit.
-    // also applies to new_rename_internal, below.
-    // let try_lookup = table.mark_memo.find(&key);
-    let mut mark_memo = table.mark_memo.borrow_mut();
-    match mark_memo.get().contains_key(&key) {
-        false => {
-            let new_idx = {
-                let mut table = table.table.borrow_mut();
-                idx_push(table.get(), Mark(m,tail))
-            };
-            mark_memo.get().insert(key,new_idx);
-            new_idx
-        }
-        true => {
-            match mark_memo.get().find(&key) {
-                None => fail!("internal error: key disappeared 2013042901"),
-                Some(idxptr) => {*idxptr}
-            }
-        }
-    }
-}
-
-/// Extend a syntax context with a given rename
-pub fn new_rename(id:Ident, to:Name, tail:SyntaxContext) -> SyntaxContext {
-    new_rename_internal(id, to, tail, get_sctable())
-}
-
-// Extend a syntax context with a given rename and sctable
-// FIXME #8215 : currently pub to allow testing
-pub fn new_rename_internal(id: Ident,
-                           to: Name,
-                           tail: SyntaxContext,
-                           table: &SCTable)
-                           -> SyntaxContext {
-    let key = (tail,id,to);
-    // FIXME #5074
-    //let try_lookup = table.rename_memo.find(&key);
-    let mut rename_memo = table.rename_memo.borrow_mut();
-    match rename_memo.get().contains_key(&key) {
-        false => {
-            let new_idx = {
-                let mut table = table.table.borrow_mut();
-                idx_push(table.get(), Rename(id,to,tail))
-            };
-            rename_memo.get().insert(key,new_idx);
-            new_idx
-        }
-        true => {
-            match rename_memo.get().find(&key) {
-                None => fail!("internal error: key disappeared 2013042902"),
-                Some(idxptr) => {*idxptr}
-            }
-        }
-    }
-}
-
-/// Make a fresh syntax context table with EmptyCtxt in slot zero
-/// and IllegalCtxt in slot one.
-// FIXME #8215 : currently pub to allow testing
-pub fn new_sctable_internal() -> SCTable {
-    SCTable {
-        table: RefCell::new(vec!(EmptyCtxt,IllegalCtxt)),
-        mark_memo: RefCell::new(HashMap::new()),
-        rename_memo: RefCell::new(HashMap::new()),
-    }
-}
-
-// fetch the SCTable from TLS, create one if it doesn't yet exist.
-pub fn get_sctable() -> @SCTable {
-    local_data_key!(sctable_key: @@SCTable)
-    match local_data::get(sctable_key, |k| k.map(|k| *k)) {
-        None => {
-            let new_table = @@new_sctable_internal();
-            local_data::set(sctable_key,new_table);
-            *new_table
-        },
-        Some(intr) => *intr
-    }
-}
-
-/// print out an SCTable for debugging
-pub fn display_sctable(table : &SCTable) {
-    error!("SC table:");
-    let table = table.table.borrow();
-    for (idx,val) in table.get().iter().enumerate() {
-        error!("{:4u} : {:?}",idx,val);
-    }
-}
-
-
-/// Add a value to the end of a vec, return its index
-fn idx_push<T>(vec: &mut Vec<T> , val: T) -> u32 {
-    vec.push(val);
-    (vec.len() - 1) as u32
-}
-
-/// Resolve a syntax object to a name, per MTWT.
-pub fn mtwt_resolve(id : Ident) -> Name {
-    let resolve_table = get_resolve_table();
-    let mut resolve_table = resolve_table.borrow_mut();
-    resolve_internal(id, get_sctable(), resolve_table.get())
-}
-
-// FIXME #8215: must be pub for testing
-pub type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
-
-// okay, I admit, putting this in TLS is not so nice:
-// fetch the SCTable from TLS, create one if it doesn't yet exist.
-pub fn get_resolve_table() -> @RefCell<ResolveTable> {
-    local_data_key!(resolve_table_key: @@RefCell<ResolveTable>)
-    match local_data::get(resolve_table_key, |k| k.map(|k| *k)) {
-        None => {
-            let new_table = @@RefCell::new(HashMap::new());
-            local_data::set(resolve_table_key, new_table);
-            *new_table
-        },
-        Some(intr) => *intr
-    }
-}
-
-// Resolve a syntax object to a name, per MTWT.
-// adding memoization to possibly resolve 500+ seconds in resolve for librustc (!)
-// FIXME #8215 : currently pub to allow testing
-pub fn resolve_internal(id : Ident,
-                        table : &SCTable,
-                        resolve_table : &mut ResolveTable) -> Name {
-    let key = (id.name,id.ctxt);
-    match resolve_table.contains_key(&key) {
-        false => {
-            let resolved = {
-                let result = {
-                    let table = table.table.borrow();
-                    *table.get().get(id.ctxt as uint)
-                };
-                match result {
-                    EmptyCtxt => id.name,
-                    // ignore marks here:
-                    Mark(_,subctxt) =>
-                        resolve_internal(Ident{name:id.name, ctxt: subctxt},table,resolve_table),
-                    // do the rename if necessary:
-                    Rename(Ident{name,ctxt},toname,subctxt) => {
-                        let resolvedfrom =
-                            resolve_internal(Ident{name:name,ctxt:ctxt},table,resolve_table);
-                        let resolvedthis =
-                            resolve_internal(Ident{name:id.name,ctxt:subctxt},table,resolve_table);
-                        if (resolvedthis == resolvedfrom)
-                            && (marksof(ctxt,resolvedthis,table)
-                                == marksof(subctxt,resolvedthis,table)) {
-                            toname
-                        } else {
-                            resolvedthis
-                        }
-                    }
-                    IllegalCtxt() => fail!("expected resolvable context, got IllegalCtxt")
-                }
-            };
-            resolve_table.insert(key,resolved);
-            resolved
-        }
-        true => {
-            // it's guaranteed to be there, because we just checked that it was
-            // there and we never remove anything from the table:
-            *(resolve_table.find(&key).unwrap())
-        }
-    }
-}
-
-/// Compute the marks associated with a syntax context.
-pub fn mtwt_marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
-    marksof(ctxt, stopname, get_sctable())
-}
-
-// the internal function for computing marks
-// it's not clear to me whether it's better to use a [] mutable
-// vector or a cons-list for this.
-pub fn marksof(ctxt: SyntaxContext, stopname: Name, table: &SCTable) -> Vec<Mrk> {
-    let mut result = Vec::new();
-    let mut loopvar = ctxt;
-    loop {
-        let table_entry = {
-            let table = table.table.borrow();
-            *table.get().get(loopvar as uint)
-        };
-        match table_entry {
-            EmptyCtxt => {
-                return result;
-            },
-            Mark(mark, tl) => {
-                xorPush(&mut result, mark);
-                loopvar = tl;
-            },
-            Rename(_,name,tl) => {
-                // see MTWT for details on the purpose of the stopname.
-                // short version: it prevents duplication of effort.
-                if name == stopname {
-                    return result;
-                } else {
-                    loopvar = tl;
-                }
-            }
-            IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
-        }
-    }
-}
-
-/// Return the outer mark for a context with a mark at the outside.
-/// FAILS when outside is not a mark.
-pub fn mtwt_outer_mark(ctxt: SyntaxContext) -> Mrk {
-    let sctable = get_sctable();
-    let table = sctable.table.borrow();
-    match *table.get().get(ctxt as uint) {
-        ast::Mark(mrk,_) => mrk,
-        _ => fail!("can't retrieve outer mark when outside is not a mark")
-    }
-}
-
-/// Push a name... unless it matches the one on top, in which
-/// case pop and discard (so two of the same marks cancel)
-pub fn xorPush(marks: &mut Vec<Mrk> , mark: Mrk) {
-    if (marks.len() > 0) && (getLast(marks) == mark) {
-        marks.pop().unwrap();
-    } else {
-        marks.push(mark);
-    }
-}
-
-// get the last element of a mutable array.
-// FIXME #4903: , must be a separate procedure for now.
-pub fn getLast(arr: &Vec<Mrk> ) -> Mrk {
-    *arr.last().unwrap()
-}
-
 // are two paths equal when compared unhygienically?
 // since I'm using this to replace ==, it seems appropriate
 // to compare the span, global, etc. fields as well.
@@ -937,9 +690,6 @@ mod test {
     use ast::*;
     use super::*;
     use opt_vec;
-    use collections::HashMap;
-
-    use std::vec_ng::Vec;
 
     fn ident_to_segment(id : &Ident) -> PathSegment {
         PathSegment {identifier:id.clone(),
@@ -957,210 +707,4 @@ fn ident_to_segment(id : &Ident) -> PathSegment {
                                   [Ident{name:3,ctxt:104},
                                     Ident{name:77,ctxt:182}].map(ident_to_segment)));
     }
-
-    #[test] fn xorpush_test () {
-        let mut s = Vec::new();
-        xorPush(&mut s, 14);
-        assert_eq!(s.clone(), vec!(14));
-        xorPush(&mut s, 14);
-        assert_eq!(s.clone(), Vec::new());
-        xorPush(&mut s, 14);
-        assert_eq!(s.clone(), vec!(14));
-        xorPush(&mut s, 15);
-        assert_eq!(s.clone(), vec!(14, 15));
-        xorPush(&mut s, 16);
-        assert_eq!(s.clone(), vec!(14, 15, 16));
-        xorPush(&mut s, 16);
-        assert_eq!(s.clone(), vec!(14, 15));
-        xorPush(&mut s, 15);
-        assert_eq!(s.clone(), vec!(14));
-    }
-
-    fn id(n: Name, s: SyntaxContext) -> Ident {
-        Ident {name: n, ctxt: s}
-    }
-
-    // because of the SCTable, I now need a tidy way of
-    // creating syntax objects. Sigh.
-    #[deriving(Clone, Eq, Show)]
-    enum TestSC {
-        M(Mrk),
-        R(Ident,Name)
-    }
-
-    // unfold a vector of TestSC values into a SCTable,
-    // returning the resulting index
-    fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
-        -> SyntaxContext {
-        tscs.rev_iter().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
-                  {match *tsc {
-                      M(mrk) => new_mark_internal(mrk,tail,table),
-                      R(ident,name) => new_rename_internal(ident,name,tail,table)}})
-    }
-
-    // gather a SyntaxContext back into a vector of TestSCs
-    fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
-        let mut result = Vec::new();
-        loop {
-            let table = table.table.borrow();
-            match *table.get().get(sc as uint) {
-                EmptyCtxt => {return result;},
-                Mark(mrk,tail) => {
-                    result.push(M(mrk));
-                    sc = tail;
-                    continue;
-                },
-                Rename(id,name,tail) => {
-                    result.push(R(id,name));
-                    sc = tail;
-                    continue;
-                }
-                IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
-            }
-        }
-    }
-
-    #[test] fn test_unfold_refold(){
-        let mut t = new_sctable_internal();
-
-        let test_sc = vec!(M(3),R(id(101,0),14),M(9));
-        assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),4);
-        {
-            let table = t.table.borrow();
-            assert!(*table.get().get(2) == Mark(9,0));
-            assert!(*table.get().get(3) == Rename(id(101,0),14,2));
-            assert!(*table.get().get(4) == Mark(3,3));
-        }
-        assert_eq!(refold_test_sc(4,&t),test_sc);
-    }
-
-    // extend a syntax context with a sequence of marks given
-    // in a vector. v[0] will be the outermost mark.
-    fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
-                    -> SyntaxContext {
-        mrks.rev_iter().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
-                   {new_mark_internal(*mrk,tail,table)})
-    }
-
-    #[test] fn unfold_marks_test() {
-        let mut t = new_sctable_internal();
-
-        assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),3);
-        {
-            let table = t.table.borrow();
-            assert!(*table.get().get(2) == Mark(7,0));
-            assert!(*table.get().get(3) == Mark(3,2));
-        }
-    }
-
-    #[test] fn test_marksof () {
-        let stopname = 242;
-        let name1 = 243;
-        let mut t = new_sctable_internal();
-        assert_eq!(marksof (EMPTY_CTXT,stopname,&t),Vec::new());
-        // FIXME #5074: ANF'd to dodge nested calls
-        { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
-         assert_eq! (marksof (ans,stopname,&t),vec!(4,98));}
-        // does xoring work?
-        { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
-         assert_eq! (marksof (ans,stopname,&t), vec!(16));}
-        // does nested xoring work?
-        { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
-         assert_eq! (marksof (ans, stopname,&t), vec!(16));}
-        // rename where stop doesn't match:
-        { let chain = vec!(M(9),
-                        R(id(name1,
-                             new_mark_internal (4, EMPTY_CTXT,&mut t)),
-                          100101102),
-                        M(14));
-         let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
-         assert_eq! (marksof (ans, stopname, &t), vec!(9,14));}
-        // rename where stop does match
-        { let name1sc = new_mark_internal(4, EMPTY_CTXT, &mut t);
-         let chain = vec!(M(9),
-                       R(id(name1, name1sc),
-                         stopname),
-                       M(14));
-         let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
-         assert_eq! (marksof (ans, stopname, &t), vec!(9)); }
-    }
-
-
-    #[test] fn resolve_tests () {
-        let a = 40;
-        let mut t = new_sctable_internal();
-        let mut rt = HashMap::new();
-        // - ctxt is MT
-        assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a);
-        // - simple ignored marks
-        { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
-        // - orthogonal rename where names don't match
-        { let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),51),M(12)),EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
-        // - rename where names do match, but marks don't
-        { let sc1 = new_mark_internal(1,EMPTY_CTXT,&mut t);
-         let sc = unfold_test_sc(vec!(R(id(a,sc1),50),
-                                   M(1),
-                                   M(2)),
-                                 EMPTY_CTXT,&mut t);
-        assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), a);}
-        // - rename where names and marks match
-        { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
-         let sc = unfold_test_sc(vec!(R(id(a,sc1),50),M(1),M(2)),EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
-        // - rename where names and marks match by literal sharing
-        { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
-         let sc = unfold_test_sc(vec!(R(id(a,sc1),50)),sc1,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
-        // - two renames of the same var.. can only happen if you use
-        // local-expand to prevent the inner binding from being renamed
-        // during the rename-pass caused by the first:
-        println!("about to run bad test");
-        { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),50),
-                                    R(id(a,EMPTY_CTXT),51)),
-                                  EMPTY_CTXT,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 51); }
-        // the simplest double-rename:
-        { let a_to_a50 = new_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
-         let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
-         assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),51);
-         // mark on the outside doesn't stop rename:
-         let sc = new_mark_internal(9,a50_to_a51,&mut t);
-         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),51);
-         // but mark on the inside does:
-         let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),51),
-                                              M(9)),
-                                           a_to_a50,
-                                           &mut t);
-         assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),50);}
-    }
-
-    #[test] fn mtwt_resolve_test(){
-        let a = 40;
-        assert_eq!(mtwt_resolve(id(a,EMPTY_CTXT)),a);
-    }
-
-
-    #[test] fn hashing_tests () {
-        let mut t = new_sctable_internal();
-        assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
-        assert_eq!(new_mark_internal(13,EMPTY_CTXT,&mut t),3);
-        // using the same one again should result in the same index:
-        assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
-        // I'm assuming that the rename table will behave the same....
-    }
-
-    #[test] fn resolve_table_hashing_tests() {
-        let mut t = new_sctable_internal();
-        let mut rt = HashMap::new();
-        assert_eq!(rt.len(),0);
-        resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
-        assert_eq!(rt.len(),1);
-        resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
-        assert_eq!(rt.len(),2);
-        resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
-        assert_eq!(rt.len(),2);
-    }
-
 }
index 5cb09ec823221ab86026069e65a9897eb585229b..79068d4046965ad519f26332812f1144129b318a 100644 (file)
@@ -146,9 +146,9 @@ pub enum SyntaxExtension {
 
 pub struct BlockInfo {
     // should macros escape from this scope?
-    macros_escape : bool,
+    macros_escape: bool,
     // what are the pending renames?
-    pending_renames : RenameList,
+    pending_renames: RenameList,
 }
 
 impl BlockInfo {
@@ -161,7 +161,7 @@ pub fn new() -> BlockInfo {
 }
 
 // a list of ident->name renamings
-pub type RenameList = Vec<(ast::Ident,Name)> ;
+pub type RenameList = Vec<(ast::Ident, Name)>;
 
 // The base map of methods for expanding syntax extension
 // AST nodes into full ASTs
index 13c340cb7747b1c6885041a778d6d204c58e08a2..8b23de235b80480500fa8ecf201b0831d17c700a 100644 (file)
@@ -11,9 +11,9 @@
 use ast::{P, Block, Crate, DeclLocal, ExprMac};
 use ast::{Local, Ident, MacInvocTT};
 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
-use ast::{TokenTree};
+use ast::TokenTree;
 use ast;
-use ast_util::{new_rename, new_mark};
+use ext::mtwt;
 use ext::build::AstBuilder;
 use attr;
 use attr::AttrMetaMethods;
@@ -140,9 +140,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
             // Expand any interior macros etc.
             // NB: we don't fold pats yet. Curious.
             let src_expr = fld.fold_expr(src_expr).clone();
-            // Rename label before expansion.
-            let (opt_ident, src_loop_block) = rename_loop_label(opt_ident, src_loop_block, fld);
-            let src_loop_block = fld.fold_block(src_loop_block);
+            let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
 
             let span = e.span;
 
@@ -205,9 +203,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
         }
 
         ast::ExprLoop(loop_block, opt_ident) => {
-            let (opt_ident, loop_block) =
-                rename_loop_label(opt_ident, loop_block, fld);
-            let loop_block = fld.fold_block(loop_block);
+            let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
         }
 
@@ -215,22 +211,38 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
     }
 }
 
-// Rename loop label and its all occurrences inside the loop body
-fn rename_loop_label(opt_ident: Option<Ident>,
-                     loop_block: P<Block>,
-                     fld: &mut MacroExpander) -> (Option<Ident>, P<Block>) {
+// Rename loop label and expand its loop body
+//
+// The renaming procedure for loop is different in the sense that the loop
+// body is in a block enclosed by loop head so the renaming of loop label
+// must be propagated to the enclosed context.
+fn expand_loop_block(loop_block: P<Block>,
+                     opt_ident: Option<Ident>,
+                     fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
     match opt_ident {
         Some(label) => {
-            // Generate fresh label and add to the existing pending renames
             let new_label = fresh_name(&label);
             let rename = (label, new_label);
+
+            // The rename *must not* be added to the pending list of current
+            // syntax context otherwise an unrelated `break` or `continue` in
+            // the same context will pick that up in the deferred renaming pass
+            // and be renamed incorrectly.
+            let mut rename_list = vec!(rename);
+            let mut rename_fld = renames_to_fold(&mut rename_list);
+            let renamed_ident = rename_fld.fold_ident(label);
+
+            // The rename *must* be added to the enclosed syntax context for
+            // `break` or `continue` to pick up because by definition they are
+            // in a block enclosed by loop head.
+            fld.extsbox.push_frame();
             fld.extsbox.info().pending_renames.push(rename);
-            let mut pending_renames = vec!(rename);
-            let mut rename_fld = renames_to_fold(&mut pending_renames);
-            (Some(rename_fld.fold_ident(label)),
-             rename_fld.fold_block(loop_block))
+            let expanded_block = expand_block_elts(loop_block, fld);
+            fld.extsbox.pop_frame();
+
+            (expanded_block, Some(renamed_ident))
         }
-        None => (None, loop_block)
+        None => (fld.fold_block(loop_block), opt_ident)
     }
 }
 
@@ -628,9 +640,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
                         rename_fld.fold_pat(expanded_pat)
                     };
                     // add them to the existing pending renames:
-                    for pr in new_pending_renames.iter() {
-                        fld.extsbox.info().pending_renames.push(*pr)
-                    }
+                    fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
                     // also, don't forget to expand the init:
                     let new_init_opt = init.map(|e| fld.fold_expr(e));
                     let rewritten_local =
@@ -754,11 +764,11 @@ pub struct IdentRenamer<'a> {
 }
 
 impl<'a> Folder for IdentRenamer<'a> {
-    fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
+    fn fold_ident(&mut self, id: Ident) -> Ident {
         let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
-            new_rename(from, to, ctxt)
+            mtwt::new_rename(from, to, ctxt)
         });
-        ast::Ident {
+        Ident {
             name: id.name,
             ctxt: new_ctxt,
         }
@@ -839,10 +849,10 @@ pub fn expand_crate(parse_sess: @parse::ParseSess,
 struct Marker { mark: Mrk }
 
 impl Folder for Marker {
-    fn fold_ident(&mut self, id: ast::Ident) -> ast::Ident {
+    fn fold_ident(&mut self, id: Ident) -> Ident {
         ast::Ident {
             name: id.name,
-            ctxt: new_mark(self.mark, id.ctxt)
+            ctxt: mtwt::new_mark(self.mark, id.ctxt)
         }
     }
     fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
@@ -850,7 +860,7 @@ fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
             MacInvocTT(ref path, ref tts, ctxt) => {
                 MacInvocTT(self.fold_path(path),
                            fold_tts(tts.as_slice(), self),
-                           new_mark(self.mark, ctxt))
+                           mtwt::new_mark(self.mark, ctxt))
             }
         };
         Spanned {
@@ -906,11 +916,10 @@ mod test {
     use super::*;
     use ast;
     use ast::{Attribute_, AttrOuter, MetaWord};
-    use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve};
-    use ast_util;
     use codemap;
     use codemap::Spanned;
     use ext::base::{CrateLoader, MacroCrate};
+    use ext::mtwt;
     use parse;
     use parse::token;
     use util::parser_testing::{string_to_crate_and_sess};
@@ -1139,8 +1148,8 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
         // must be one check clause for each binding:
         assert_eq!(bindings.len(),bound_connections.len());
         for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
-            let binding_name = mtwt_resolve(*bindings.get(binding_idx));
-            let binding_marks = mtwt_marksof(bindings.get(binding_idx).ctxt,invalid_name);
+            let binding_name = mtwt::resolve(*bindings.get(binding_idx));
+            let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
             // shouldmatch can't name varrefs that don't exist:
             assert!((shouldmatch.len() == 0) ||
                     (varrefs.len() > *shouldmatch.iter().max().unwrap()));
@@ -1149,19 +1158,19 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
                     // it should be a path of length 1, and it should
                     // be free-identifier=? or bound-identifier=? to the given binding
                     assert_eq!(varref.segments.len(),1);
-                    let varref_name = mtwt_resolve(varref.segments
-                                                         .get(0)
-                                                         .identifier);
-                    let varref_marks = mtwt_marksof(varref.segments
+                    let varref_name = mtwt::resolve(varref.segments
                                                           .get(0)
-                                                          .identifier
-                                                          .ctxt,
-                                                    invalid_name);
+                                                          .identifier);
+                    let varref_marks = mtwt::marksof(varref.segments
+                                                           .get(0)
+                                                           .identifier
+                                                           .ctxt,
+                                                     invalid_name);
                     if !(varref_name==binding_name) {
                         println!("uh oh, should match but doesn't:");
                         println!("varref: {:?}",varref);
                         println!("binding: {:?}", *bindings.get(binding_idx));
-                        ast_util::display_sctable(get_sctable());
+                        mtwt::with_sctable(|x| mtwt::display_sctable(x));
                     }
                     assert_eq!(varref_name,binding_name);
                     if bound_ident_check {
@@ -1171,8 +1180,8 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
                     }
                 } else {
                     let fail = (varref.segments.len() == 1)
-                        && (mtwt_resolve(varref.segments.get(0).identifier) ==
-                                         binding_name);
+                        && (mtwt::resolve(varref.segments.get(0).identifier)
+                            == binding_name);
                     // temp debugging:
                     if fail {
                         println!("failure on test {}",test_idx);
@@ -1188,7 +1197,7 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
                                  varref.segments.get(0).identifier.name,
                                  string.get());
                         println!("binding: {:?}", *bindings.get(binding_idx));
-                        ast_util::display_sctable(get_sctable());
+                        mtwt::with_sctable(|x| mtwt::display_sctable(x));
                     }
                     assert!(!fail);
                 }
@@ -1218,7 +1227,7 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
             [b] => b,
             _ => fail!("expected just one binding for ext_cx")
         };
-        let resolved_binding = mtwt_resolve(*cxbind);
+        let resolved_binding = mtwt::resolve(*cxbind);
         // find all the xx varrefs:
         let mut path_finder = new_path_finder(Vec::new());
         visit::walk_crate(&mut path_finder, &cr, ());
@@ -1229,26 +1238,17 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
             p.segments.len() == 1
             && "xx" == token::get_ident(p.segments.get(0).identifier).get()
         }).enumerate() {
-            if mtwt_resolve(v.segments.get(0).identifier) !=
-                    resolved_binding {
+            if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
                 println!("uh oh, xx binding didn't match xx varref:");
                 println!("this is xx varref \\# {:?}",idx);
                 println!("binding: {:?}",cxbind);
                 println!("resolves to: {:?}",resolved_binding);
                 println!("varref: {:?}",v.segments.get(0).identifier);
                 println!("resolves to: {:?}",
-                         mtwt_resolve(v.segments.get(0).identifier));
-                let table = get_sctable();
-                println!("SC table:");
-
-                {
-                    let table = table.table.borrow();
-                    for (idx,val) in table.get().iter().enumerate() {
-                        println!("{:4u}: {:?}",idx,val);
-                    }
-                }
+                         mtwt::resolve(v.segments.get(0).identifier));
+                mtwt::with_sctable(|x| mtwt::display_sctable(x));
             }
-            assert_eq!(mtwt_resolve(v.segments.get(0).identifier),
+            assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
                        resolved_binding);
         };
     }
diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs
new file mode 100644 (file)
index 0000000..b0ed215
--- /dev/null
@@ -0,0 +1,482 @@
+// Copyright 2012-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.
+
+//! Machinery for hygienic macros, as described in the MTWT[1] paper.
+//!
+//! [1] Matthew Flatt, Ryan Culpepper, David Darais, and Robert Bruce Findler.
+//! 2012. *Macros that work together: Compile-time bindings, partial expansion,
+//! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216.
+//! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093
+
+use ast::{Ident, Mrk, Name, SyntaxContext};
+
+use std::cell::RefCell;
+use std::local_data;
+use std::rc::Rc;
+use std::vec_ng::Vec;
+
+use collections::HashMap;
+
+// the SCTable contains a table of SyntaxContext_'s. It
+// represents a flattened tree structure, to avoid having
+// managed pointers everywhere (that caused an ICE).
+// the mark_memo and rename_memo fields are side-tables
+// that ensure that adding the same mark to the same context
+// gives you back the same context as before. This shouldn't
+// change the semantics--everything here is immutable--but
+// it should cut down on memory use *a lot*; applying a mark
+// to a tree containing 50 identifiers would otherwise generate
+pub struct SCTable {
+    table: RefCell<Vec<SyntaxContext_>>,
+    mark_memo: RefCell<HashMap<(SyntaxContext,Mrk),SyntaxContext>>,
+    rename_memo: RefCell<HashMap<(SyntaxContext,Ident,Name),SyntaxContext>>,
+}
+
+#[deriving(Eq, Encodable, Decodable, Hash)]
+pub enum SyntaxContext_ {
+    EmptyCtxt,
+    Mark (Mrk,SyntaxContext),
+    // flattening the name and syntaxcontext into the rename...
+    // HIDDEN INVARIANTS:
+    // 1) the first name in a Rename node
+    // can only be a programmer-supplied name.
+    // 2) Every Rename node with a given Name in the
+    // "to" slot must have the same name and context
+    // in the "from" slot. In essence, they're all
+    // pointers to a single "rename" event node.
+    Rename (Ident,Name,SyntaxContext),
+    // actually, IllegalCtxt may not be necessary.
+    IllegalCtxt
+}
+
+/// Extend a syntax context with a given mark
+pub fn new_mark(m: Mrk, tail: SyntaxContext) -> SyntaxContext {
+    with_sctable(|table| new_mark_internal(m, tail, table))
+}
+
+// Extend a syntax context with a given mark and table
+fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable) -> SyntaxContext {
+    let key = (tail, m);
+    let mut mark_memo = table.mark_memo.borrow_mut();
+    let new_ctxt = |_: &(SyntaxContext, Mrk)|
+                   idx_push(table.table.borrow_mut().get(), Mark(m, tail));
+
+    *mark_memo.get().find_or_insert_with(key, new_ctxt)
+}
+
+/// Extend a syntax context with a given rename
+pub fn new_rename(id: Ident, to:Name,
+                  tail: SyntaxContext) -> SyntaxContext {
+    with_sctable(|table| new_rename_internal(id, to, tail, table))
+}
+
+// Extend a syntax context with a given rename and sctable
+fn new_rename_internal(id: Ident,
+                       to: Name,
+                       tail: SyntaxContext,
+                       table: &SCTable) -> SyntaxContext {
+    let key = (tail,id,to);
+    let mut rename_memo = table.rename_memo.borrow_mut();
+    let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)|
+                   idx_push(table.table.borrow_mut().get(), Rename(id, to, tail));
+
+    *rename_memo.get().find_or_insert_with(key, new_ctxt)
+}
+
+/// Fetch the SCTable from TLS, create one if it doesn't yet exist.
+pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
+    local_data_key!(sctable_key: Rc<SCTable>)
+
+    local_data::get(sctable_key, |opt_ts| {
+        let table = match opt_ts {
+            None => {
+                let ts = Rc::new(new_sctable_internal());
+                local_data::set(sctable_key, ts.clone());
+                ts
+            }
+            Some(ts) => ts.clone()
+        };
+        op(table.borrow())
+    })
+}
+
+// Make a fresh syntax context table with EmptyCtxt in slot zero
+// and IllegalCtxt in slot one.
+fn new_sctable_internal() -> SCTable {
+    SCTable {
+        table: RefCell::new(vec!(EmptyCtxt, IllegalCtxt)),
+        mark_memo: RefCell::new(HashMap::new()),
+        rename_memo: RefCell::new(HashMap::new()),
+    }
+}
+
+/// Print out an SCTable for debugging
+pub fn display_sctable(table: &SCTable) {
+    error!("SC table:");
+    let table = table.table.borrow();
+    for (idx,val) in table.get().iter().enumerate() {
+        error!("{:4u} : {:?}",idx,val);
+    }
+}
+
+
+// Add a value to the end of a vec, return its index
+fn idx_push<T>(vec: &mut Vec<T> , val: T) -> u32 {
+    vec.push(val);
+    (vec.len() - 1) as u32
+}
+
+/// Resolve a syntax object to a name, per MTWT.
+pub fn resolve(id: Ident) -> Name {
+    with_sctable(|sctable| {
+        with_resolve_table_mut(|resolve_table| {
+            resolve_internal(id, sctable, resolve_table)
+        })
+    })
+}
+
+type ResolveTable = HashMap<(Name,SyntaxContext),Name>;
+
+// okay, I admit, putting this in TLS is not so nice:
+// fetch the SCTable from TLS, create one if it doesn't yet exist.
+fn with_resolve_table_mut<T>(op: |&mut ResolveTable| -> T) -> T {
+    local_data_key!(resolve_table_key: Rc<RefCell<ResolveTable>>)
+
+    local_data::get(resolve_table_key, |opt_ts| {
+        let table = match opt_ts {
+            None => {
+                let ts = Rc::new(RefCell::new(HashMap::new()));
+                local_data::set(resolve_table_key, ts.clone());
+                ts
+            }
+            Some(ts) => ts.clone()
+        };
+        op(table.borrow().borrow_mut().get())
+    })
+}
+
+// Resolve a syntax object to a name, per MTWT.
+// adding memorization to possibly resolve 500+ seconds in resolve for librustc (!)
+fn resolve_internal(id: Ident,
+                    table: &SCTable,
+                    resolve_table: &mut ResolveTable) -> Name {
+    let key = (id.name, id.ctxt);
+
+    match resolve_table.find(&key) {
+        Some(&name) => return name,
+        None => {}
+    }
+
+    let resolved = {
+        let result = *table.table.borrow().get().get(id.ctxt as uint);
+        match result {
+            EmptyCtxt => id.name,
+            // ignore marks here:
+            Mark(_,subctxt) =>
+                resolve_internal(Ident{name:id.name, ctxt: subctxt},
+                                 table, resolve_table),
+            // do the rename if necessary:
+            Rename(Ident{name, ctxt}, toname, subctxt) => {
+                let resolvedfrom =
+                    resolve_internal(Ident{name:name, ctxt:ctxt},
+                                     table, resolve_table);
+                let resolvedthis =
+                    resolve_internal(Ident{name:id.name, ctxt:subctxt},
+                                     table, resolve_table);
+                if (resolvedthis == resolvedfrom)
+                    && (marksof_internal(ctxt, resolvedthis, table)
+                        == marksof_internal(subctxt, resolvedthis, table)) {
+                    toname
+                } else {
+                    resolvedthis
+                }
+            }
+            IllegalCtxt() => fail!("expected resolvable context, got IllegalCtxt")
+        }
+    };
+    resolve_table.insert(key, resolved);
+    resolved
+}
+
+/// Compute the marks associated with a syntax context.
+pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec<Mrk> {
+    with_sctable(|table| marksof_internal(ctxt, stopname, table))
+}
+
+// the internal function for computing marks
+// it's not clear to me whether it's better to use a [] mutable
+// vector or a cons-list for this.
+fn marksof_internal(ctxt: SyntaxContext,
+                    stopname: Name,
+                    table: &SCTable) -> Vec<Mrk> {
+    let mut result = Vec::new();
+    let mut loopvar = ctxt;
+    loop {
+        let table_entry = {
+            let table = table.table.borrow();
+            *table.get().get(loopvar as uint)
+        };
+        match table_entry {
+            EmptyCtxt => {
+                return result;
+            },
+            Mark(mark, tl) => {
+                xorPush(&mut result, mark);
+                loopvar = tl;
+            },
+            Rename(_,name,tl) => {
+                // see MTWT for details on the purpose of the stopname.
+                // short version: it prevents duplication of effort.
+                if name == stopname {
+                    return result;
+                } else {
+                    loopvar = tl;
+                }
+            }
+            IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
+        }
+    }
+}
+
+/// Return the outer mark for a context with a mark at the outside.
+/// FAILS when outside is not a mark.
+pub fn outer_mark(ctxt: SyntaxContext) -> Mrk {
+    with_sctable(|sctable| {
+        match *sctable.table.borrow().get().get(ctxt as uint) {
+            Mark(mrk, _) => mrk,
+            _ => fail!("can't retrieve outer mark when outside is not a mark")
+        }
+    })
+}
+
+// Push a name... unless it matches the one on top, in which
+// case pop and discard (so two of the same marks cancel)
+fn xorPush(marks: &mut Vec<Mrk>, mark: Mrk) {
+    if (marks.len() > 0) && (*marks.last().unwrap() == mark) {
+        marks.pop().unwrap();
+    } else {
+        marks.push(mark);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use ast::*;
+    use super::{resolve, xorPush, new_mark_internal, new_sctable_internal};
+    use super::{new_rename_internal, marksof_internal, resolve_internal};
+    use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
+    use std::vec_ng::Vec;
+    use collections::HashMap;
+
+    #[test] fn xorpush_test () {
+        let mut s = Vec::new();
+        xorPush(&mut s, 14);
+        assert_eq!(s.clone(), vec!(14));
+        xorPush(&mut s, 14);
+        assert_eq!(s.clone(), Vec::new());
+        xorPush(&mut s, 14);
+        assert_eq!(s.clone(), vec!(14));
+        xorPush(&mut s, 15);
+        assert_eq!(s.clone(), vec!(14, 15));
+        xorPush(&mut s, 16);
+        assert_eq!(s.clone(), vec!(14, 15, 16));
+        xorPush(&mut s, 16);
+        assert_eq!(s.clone(), vec!(14, 15));
+        xorPush(&mut s, 15);
+        assert_eq!(s.clone(), vec!(14));
+    }
+
+    fn id(n: Name, s: SyntaxContext) -> Ident {
+        Ident {name: n, ctxt: s}
+    }
+
+    // because of the SCTable, I now need a tidy way of
+    // creating syntax objects. Sigh.
+    #[deriving(Clone, Eq, Show)]
+    enum TestSC {
+        M(Mrk),
+        R(Ident,Name)
+    }
+
+    // unfold a vector of TestSC values into a SCTable,
+    // returning the resulting index
+    fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
+        -> SyntaxContext {
+        tscs.rev_iter().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
+                  {match *tsc {
+                      M(mrk) => new_mark_internal(mrk,tail,table),
+                      R(ident,name) => new_rename_internal(ident,name,tail,table)}})
+    }
+
+    // gather a SyntaxContext back into a vector of TestSCs
+    fn refold_test_sc(mut sc: SyntaxContext, table : &SCTable) -> Vec<TestSC> {
+        let mut result = Vec::new();
+        loop {
+            let table = table.table.borrow();
+            match *table.get().get(sc as uint) {
+                EmptyCtxt => {return result;},
+                Mark(mrk,tail) => {
+                    result.push(M(mrk));
+                    sc = tail;
+                    continue;
+                },
+                Rename(id,name,tail) => {
+                    result.push(R(id,name));
+                    sc = tail;
+                    continue;
+                }
+                IllegalCtxt => fail!("expected resolvable context, got IllegalCtxt")
+            }
+        }
+    }
+
+    #[test] fn test_unfold_refold(){
+        let mut t = new_sctable_internal();
+
+        let test_sc = vec!(M(3),R(id(101,0),14),M(9));
+        assert_eq!(unfold_test_sc(test_sc.clone(),EMPTY_CTXT,&mut t),4);
+        {
+            let table = t.table.borrow();
+            assert!(*table.get().get(2) == Mark(9,0));
+            assert!(*table.get().get(3) == Rename(id(101,0),14,2));
+            assert!(*table.get().get(4) == Mark(3,3));
+        }
+        assert_eq!(refold_test_sc(4,&t),test_sc);
+    }
+
+    // extend a syntax context with a sequence of marks given
+    // in a vector. v[0] will be the outermost mark.
+    fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
+                    -> SyntaxContext {
+        mrks.rev_iter().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
+                   {new_mark_internal(*mrk,tail,table)})
+    }
+
+    #[test] fn unfold_marks_test() {
+        let mut t = new_sctable_internal();
+
+        assert_eq!(unfold_marks(vec!(3,7),EMPTY_CTXT,&mut t),3);
+        {
+            let table = t.table.borrow();
+            assert!(*table.get().get(2) == Mark(7,0));
+            assert!(*table.get().get(3) == Mark(3,2));
+        }
+    }
+
+    #[test] fn test_marksof () {
+        let stopname = 242;
+        let name1 = 243;
+        let mut t = new_sctable_internal();
+        assert_eq!(marksof_internal (EMPTY_CTXT,stopname,&t),Vec::new());
+        // FIXME #5074: ANF'd to dodge nested calls
+        { let ans = unfold_marks(vec!(4,98),EMPTY_CTXT,&mut t);
+         assert_eq! (marksof_internal (ans,stopname,&t),vec!(4,98));}
+        // does xoring work?
+        { let ans = unfold_marks(vec!(5,5,16),EMPTY_CTXT,&mut t);
+         assert_eq! (marksof_internal (ans,stopname,&t), vec!(16));}
+        // does nested xoring work?
+        { let ans = unfold_marks(vec!(5,10,10,5,16),EMPTY_CTXT,&mut t);
+         assert_eq! (marksof_internal (ans, stopname,&t), vec!(16));}
+        // rename where stop doesn't match:
+        { let chain = vec!(M(9),
+                        R(id(name1,
+                             new_mark_internal (4, EMPTY_CTXT,&mut t)),
+                          100101102),
+                        M(14));
+         let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
+         assert_eq! (marksof_internal (ans, stopname, &t), vec!(9,14));}
+        // rename where stop does match
+        { let name1sc = new_mark_internal(4, EMPTY_CTXT, &mut t);
+         let chain = vec!(M(9),
+                       R(id(name1, name1sc),
+                         stopname),
+                       M(14));
+         let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
+         assert_eq! (marksof_internal (ans, stopname, &t), vec!(9)); }
+    }
+
+
+    #[test] fn resolve_tests () {
+        let a = 40;
+        let mut t = new_sctable_internal();
+        let mut rt = HashMap::new();
+        // - ctxt is MT
+        assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a);
+        // - simple ignored marks
+        { let sc = unfold_marks(vec!(1,2,3),EMPTY_CTXT,&mut t);
+         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
+        // - orthogonal rename where names don't match
+        { let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),51),M(12)),EMPTY_CTXT,&mut t);
+         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
+        // - rename where names do match, but marks don't
+        { let sc1 = new_mark_internal(1,EMPTY_CTXT,&mut t);
+         let sc = unfold_test_sc(vec!(R(id(a,sc1),50),
+                                   M(1),
+                                   M(2)),
+                                 EMPTY_CTXT,&mut t);
+        assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), a);}
+        // - rename where names and marks match
+        { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
+         let sc = unfold_test_sc(vec!(R(id(a,sc1),50),M(1),M(2)),EMPTY_CTXT,&mut t);
+         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
+        // - rename where names and marks match by literal sharing
+        { let sc1 = unfold_test_sc(vec!(M(1),M(2)),EMPTY_CTXT,&mut t);
+         let sc = unfold_test_sc(vec!(R(id(a,sc1),50)),sc1,&mut t);
+         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 50); }
+        // - two renames of the same var.. can only happen if you use
+        // local-expand to prevent the inner binding from being renamed
+        // during the rename-pass caused by the first:
+        println!("about to run bad test");
+        { let sc = unfold_test_sc(vec!(R(id(a,EMPTY_CTXT),50),
+                                    R(id(a,EMPTY_CTXT),51)),
+                                  EMPTY_CTXT,&mut t);
+         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 51); }
+        // the simplest double-rename:
+        { let a_to_a50 = new_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
+         let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
+         assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),51);
+         // mark on the outside doesn't stop rename:
+         let sc = new_mark_internal(9,a50_to_a51,&mut t);
+         assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),51);
+         // but mark on the inside does:
+         let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),51),
+                                              M(9)),
+                                           a_to_a50,
+                                           &mut t);
+         assert_eq!(resolve_internal(id(a,a50_to_a51_b),&mut t, &mut rt),50);}
+    }
+
+    #[test] fn mtwt_resolve_test(){
+        let a = 40;
+        assert_eq!(resolve(id(a,EMPTY_CTXT)),a);
+    }
+
+
+    #[test] fn hashing_tests () {
+        let mut t = new_sctable_internal();
+        assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
+        assert_eq!(new_mark_internal(13,EMPTY_CTXT,&mut t),3);
+        // using the same one again should result in the same index:
+        assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
+        // I'm assuming that the rename table will behave the same....
+    }
+
+    #[test] fn resolve_table_hashing_tests() {
+        let mut t = new_sctable_internal();
+        let mut rt = HashMap::new();
+        assert_eq!(rt.len(),0);
+        resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
+        assert_eq!(rt.len(),1);
+        resolve_internal(id(39,EMPTY_CTXT),&mut t, &mut rt);
+        assert_eq!(rt.len(),2);
+        resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
+        assert_eq!(rt.len(),2);
+    }
+}
index 9d2810d8344ef27d7210d6314ce92aefde9a249a..0d7e54bb69d36226f1fe2c9e4b539ae5ae22fd74 100644 (file)
@@ -87,6 +87,7 @@ pub mod tt {
         pub mod macro_rules;
     }
 
+    pub mod mtwt;
 
     pub mod cfg;
     pub mod fmt;
index 1499a1b4c19be37efb990b1af9b7c0b0d81d36b4..71ee32b4aade7e434f112bf5977a013a66099a07 100644 (file)
@@ -11,6 +11,7 @@
 use ast;
 use ast::{P, Ident, Name, Mrk};
 use ast_util;
+use ext::mtwt;
 use parse::token;
 use util::interner::{RcStr, StrInterner};
 use util::interner;
@@ -722,7 +723,7 @@ pub fn is_reserved_keyword(tok: &Token) -> bool {
 pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
     match (t1,t2) {
         (&IDENT(id1,_),&IDENT(id2,_)) | (&LIFETIME(id1),&LIFETIME(id2)) =>
-            ast_util::mtwt_resolve(id1) == ast_util::mtwt_resolve(id2),
+            mtwt::resolve(id1) == mtwt::resolve(id2),
         _ => *t1 == *t2
     }
 }
@@ -732,10 +733,10 @@ pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool {
 mod test {
     use super::*;
     use ast;
-    use ast_util;
+    use ext::mtwt;
 
     fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident {
-        ast::Ident{name:id.name,ctxt:ast_util::new_mark(m,id.ctxt)}
+        ast::Ident{name:id.name,ctxt:mtwt::new_mark(m,id.ctxt)}
     }
 
     #[test] fn mtwt_token_eq_test() {