diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index 655973c3d33d4..5b599a06925f6 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -796,9 +796,9 @@ fn extract_vec_elems<'a>( // matches should fit that sort of pattern or NONE (however, some of the // matches may be wildcards like _ or identifiers). macro_rules! any_pat ( - ($m:expr, $pattern:pat) => ( + ($m:expr, $col:expr, $pattern:pat) => ( ($m).iter().any(|br| { - match br.pats.get(col).node { + match br.pats.get($col).node { $pattern => true, _ => false } @@ -807,11 +807,11 @@ macro_rules! any_pat ( ) fn any_uniq_pat(m: &[Match], col: uint) -> bool { - any_pat!(m, ast::PatBox(_)) + any_pat!(m, col, ast::PatBox(_)) } fn any_region_pat(m: &[Match], col: uint) -> bool { - any_pat!(m, ast::PatRegion(_)) + any_pat!(m, col, ast::PatRegion(_)) } fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool { diff --git a/src/libstd/io/extensions.rs b/src/libstd/io/extensions.rs index 277aca2332d47..ca3eee01575fd 100644 --- a/src/libstd/io/extensions.rs +++ b/src/libstd/io/extensions.rs @@ -508,14 +508,15 @@ mod bench { use prelude::*; use self::test::Bencher; + // why is this a macro? wouldn't an inlined function work just as well? macro_rules! u64_from_be_bytes_bench_impl( - ($size:expr, $stride:expr, $start_index:expr) => + ($b:expr, $size:expr, $stride:expr, $start_index:expr) => ({ use super::u64_from_be_bytes; let data = Vec::from_fn($stride*100+$start_index, |i| i as u8); let mut sum = 0u64; - b.iter(|| { + $b.iter(|| { let mut i = $start_index; while i < data.len() { sum += u64_from_be_bytes(data.as_slice(), i, $size); @@ -527,31 +528,31 @@ mod bench { #[bench] fn u64_from_be_bytes_4_aligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(4, 4, 0); + u64_from_be_bytes_bench_impl!(b, 4, 4, 0); } #[bench] fn u64_from_be_bytes_4_unaligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(4, 4, 1); + u64_from_be_bytes_bench_impl!(b, 4, 4, 1); } #[bench] fn u64_from_be_bytes_7_aligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(7, 8, 0); + u64_from_be_bytes_bench_impl!(b, 7, 8, 0); } #[bench] fn u64_from_be_bytes_7_unaligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(7, 8, 1); + u64_from_be_bytes_bench_impl!(b, 7, 8, 1); } #[bench] fn u64_from_be_bytes_8_aligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(8, 8, 0); + u64_from_be_bytes_bench_impl!(b, 8, 8, 0); } #[bench] fn u64_from_be_bytes_8_unaligned(b: &mut Bencher) { - u64_from_be_bytes_bench_impl!(8, 8, 1); + u64_from_be_bytes_bench_impl!(b, 8, 8, 1); } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 237d0660a41dc..ce1302c8db874 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -190,6 +190,8 @@ pub struct TyParam { pub span: Span } +/// Represents lifetimes and type parameters attached to a declaration +/// of a function, enum, trait, etc. #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub struct Generics { pub lifetimes: Vec, @@ -288,7 +290,7 @@ pub enum Pat_ { PatWild, PatWildMulti, // A PatIdent may either be a new bound variable, - // or a nullary enum (in which case the second field + // or a nullary enum (in which case the third field // is None). // In the nullary enum case, the parser can't determine // which it is. The resolver determines this, and @@ -453,10 +455,10 @@ pub enum Expr_ { ExprCast(Gc, P), ExprIf(Gc, P, Option>), ExprWhile(Gc, P), - // FIXME #6993: change to Option + // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(Gc, Gc, P, Option), // Conditionless loop (can be exited with break, cont, or ret) - // FIXME #6993: change to Option + // FIXME #6993: change to Option ... or not, if these are hygienic. ExprLoop(P, Option), ExprMatch(Gc, Vec), ExprFnBlock(P, P), @@ -468,9 +470,8 @@ pub enum Expr_ { ExprField(Gc, SpannedIdent, Vec>), ExprIndex(Gc, Gc), - /// Expression that looks like a "name". For example, - /// `std::slice::from_elem::` is an ExprPath that's the "name" part - /// of a function call. + /// Variable reference, possibly containing `::` and/or + /// type parameters, e.g. foo::bar:: ExprPath(Path), ExprAddrOf(Mutability, Gc), @@ -643,6 +644,8 @@ pub struct TypeField { pub span: Span, } +/// Represents a required method in a trait declaration, +/// one without a default implementation #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub struct TypeMethod { pub ident: Ident, @@ -656,6 +659,8 @@ pub struct TypeMethod { pub vis: Visibility, } +/// Represents a method declaration in a trait declaration, possibly +/// including a default implementation // A trait method is either required (meaning it doesn't have an // implementation, just a signature) or provided (meaning it has a default // implementation). @@ -741,6 +746,7 @@ impl fmt::Show for Onceness { } } +/// Represents the type of a closure #[deriving(PartialEq, Eq, Encodable, Decodable, Hash)] pub struct ClosureTy { pub lifetimes: Vec, @@ -809,6 +815,7 @@ pub struct InlineAsm { pub dialect: AsmDialect } +/// represents an argument in a function header #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub struct Arg { pub ty: P, @@ -836,7 +843,7 @@ impl Arg { } } -// represents the header (not the body) of a function declaration +/// represents the header (not the body) of a function declaration #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub struct FnDecl { pub inputs: Vec, @@ -1107,6 +1114,7 @@ pub enum Item_ { ItemTy(P, Generics), ItemEnum(EnumDef, Generics), ItemStruct(Gc, Generics), + /// Represents a Trait Declaration ItemTrait(Generics, Sized, Vec , Vec ), ItemImpl(Generics, Option, // (optional) trait this impl implements diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index d2e69204d333c..cf69277594fd5 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -19,6 +19,7 @@ use parse::parser; use parse::token; use parse::token::{InternedString, intern, str_to_ident}; use util::small_vector::SmallVector; +use ext::mtwt; use std::collections::HashMap; use std::gc::{Gc, GC}; @@ -273,7 +274,7 @@ pub struct BlockInfo { // should macros escape from this scope? pub macros_escape: bool, // what are the pending renames? - pub pending_renames: RenameList, + pub pending_renames: mtwt::RenameList, } impl BlockInfo { @@ -285,9 +286,6 @@ impl BlockInfo { } } -// a list of ident->name renamings -pub type RenameList = Vec<(ast::Ident, Name)>; - // The base map of methods for expanding syntax extension // AST nodes into full ASTs pub fn syntax_expander_table() -> SyntaxEnv { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index c3413293e52ae..b30b62c8901d4 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -21,6 +21,7 @@ use codemap; use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use crateid::CrateId; use ext::base::*; +use fold; use fold::*; use parse; use parse::token::{fresh_mark, fresh_name, intern}; @@ -228,6 +229,20 @@ pub fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident)) } + ast::ExprFnBlock(fn_decl, block) => { + let (rewritten_fn_decl, rewritten_block) + = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); + let new_node = ast::ExprFnBlock(rewritten_fn_decl, rewritten_block); + box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)} + } + + ast::ExprProc(fn_decl, block) => { + let (rewritten_fn_decl, rewritten_block) + = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); + let new_node = ast::ExprProc(rewritten_fn_decl, rewritten_block); + box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)} + } + _ => noop_fold_expr(e, fld) } } @@ -267,7 +282,8 @@ fn expand_loop_block(loop_block: P, } } -// eval $e with a new exts frame: +// eval $e with a new exts frame. +// must be a macro so that $e isn't evaluated too early. macro_rules! with_exts_frame ( ($extsboxexpr:expr,$macros_escape:expr,$e:expr) => ({$extsboxexpr.push_frame(); @@ -342,15 +358,16 @@ fn expand_item(it: Gc, fld: &mut MacroExpander) fn expand_item_modifiers(mut it: Gc, fld: &mut MacroExpander) -> Gc { - let (modifiers, attrs) = it.attrs.partitioned(|attr| { + // partition the attributes into ItemModifiers and others + let (modifiers, other_attrs) = it.attrs.partitioned(|attr| { match fld.extsbox.find(&intern(attr.name().get())) { Some(&ItemModifier(_)) => true, _ => false } }); - + // update the attrs, leave everything else alone. Is this mutation really a good idea? it = box(GC) ast::Item { - attrs: attrs, + attrs: other_attrs, ..(*it).clone() }; @@ -383,6 +400,19 @@ fn expand_item_modifiers(mut it: Gc, fld: &mut MacroExpander) expand_item_modifiers(it, fld) } +/// Expand item_underscore +fn expand_item_underscore(item: &ast::Item_, fld: &mut MacroExpander) -> ast::Item_ { + match *item { + ast::ItemFn(decl, fn_style, abi, ref generics, body) => { + let (rewritten_fn_decl, rewritten_body) + = expand_and_rename_fn_decl_and_block(decl,body,fld); + let expanded_generics = fold::fold_generics(generics,fld); + ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body) + } + _ => noop_fold_item_underscore(&*item, fld) + } +} + // does this attribute list contain "macro_escape" ? fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool { attr::contains_name(attrs, "macro_escape") @@ -609,7 +639,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander) } = **local; // expand the pat (it might contain macro uses): let expanded_pat = fld.fold_pat(pat); - // find the pat_idents in the pattern: + // find the PatIdents in the pattern: // oh dear heaven... this is going to include the enum // names, as well... but that should be okay, as long as // the new names are gensyms for the old ones. @@ -653,6 +683,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander) } } +// expand the arm of a 'match', renaming for macro hygiene fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm { // expand pats... they might contain macro uses: let expanded_pats : Vec> = arm.pats.iter().map(|pat| fld.fold_pat(*pat)).collect(); @@ -662,22 +693,15 @@ fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm { // all of the pats must have the same set of bindings, so use the // first one to extract them and generate new names: let first_pat = expanded_pats.get(0); - // code duplicated from 'let', above. Perhaps this can be lifted - // into a separate function: let idents = pattern_bindings(*first_pat); - let mut new_pending_renames = + let new_renames = idents.iter().map(|id| (*id,fresh_name(id))).collect(); - // rewrite all of the patterns using the new names (the old - // ones have already been applied). Note that we depend here - // on the guarantee that after expansion, there can't be any - // Path expressions (a.k.a. varrefs) left in the pattern. If - // this were false, we'd need to apply this renaming only to - // the bindings, and not to the varrefs, using a more targeted - // fold-er. - let mut rename_fld = IdentRenamer{renames:&mut new_pending_renames}; + // apply the renaming, but only to the PatIdents: + let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames}; let rewritten_pats = - expanded_pats.iter().map(|pat| rename_fld.fold_pat(*pat)).collect(); + expanded_pats.iter().map(|pat| rename_pats_fld.fold_pat(*pat)).collect(); // apply renaming and then expansion to the guard and the body: + let mut rename_fld = IdentRenamer{renames:&new_renames}; let rewritten_guard = arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g))); let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body)); @@ -689,45 +713,47 @@ fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm { } } - - -// a visitor that extracts the pat_ident (binding) paths -// from a given thingy and puts them in a mutable -// array +/// A visitor that extracts the PatIdent (binding) paths +/// from a given thingy and puts them in a mutable +/// array #[deriving(Clone)] -struct NameFinderContext { +struct PatIdentFinder { ident_accumulator: Vec , } -impl Visitor<()> for NameFinderContext { +impl Visitor<()> for PatIdentFinder { fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) { match *pattern { - // we found a pat_ident! - ast::Pat { - id: _, - node: ast::PatIdent(_, ref path1, ref inner), - span: _ - } => { + ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => { self.ident_accumulator.push(path1.node); - // visit optional subpattern of pat_ident: + // visit optional subpattern of PatIdent: for subpat in inner.iter() { self.visit_pat(&**subpat, ()) } } - // use the default traversal for non-pat_idents + // use the default traversal for non-PatIdents _ => visit::walk_pat(self, pattern, ()) } } } -// find the pat_ident paths in a pattern +/// find the PatIdent paths in a pattern fn pattern_bindings(pat : &ast::Pat) -> Vec { - let mut name_finder = NameFinderContext{ident_accumulator:Vec::new()}; + let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()}; name_finder.visit_pat(pat,()); name_finder.ident_accumulator } +/// find the PatIdent paths in a +fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec { + let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()}; + for arg in fn_decl.inputs.iter() { + pat_idents.visit_pat(arg.pat,()); + } + pat_idents.ident_accumulator +} + // expand a block. pushes a new exts_frame, then calls expand_block_elts fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P { // see note below about treatment of exts table @@ -844,34 +870,71 @@ fn expand_pat(p: Gc, fld: &mut MacroExpander) -> Gc { } } -// a tree-folder that applies every rename in its (mutable) list -// to every identifier, including both bindings and varrefs -// (and lots of things that will turn out to be neither) +/// A tree-folder that applies every rename in its (mutable) list +/// to every identifier, including both bindings and varrefs +/// (and lots of things that will turn out to be neither) pub struct IdentRenamer<'a> { - renames: &'a mut RenameList, + renames: &'a mtwt::RenameList, } impl<'a> Folder for IdentRenamer<'a> { fn fold_ident(&mut self, id: Ident) -> Ident { - let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| { - mtwt::new_rename(from, to, ctxt) - }); Ident { name: id.name, - ctxt: new_ctxt, + ctxt: mtwt::apply_renames(self.renames, id.ctxt), } } } -fn new_span(cx: &ExtCtxt, sp: Span) -> Span { - /* this discards information in the case of macro-defining macros */ - Span { - lo: sp.lo, - hi: sp.hi, - expn_info: cx.backtrace(), +/// A tree-folder that applies every rename in its list to +/// the idents that are in PatIdent patterns. This is more narrowly +/// focused than IdentRenamer, and is needed for FnDecl, +/// where we want to rename the args but not the fn name or the generics etc. +pub struct PatIdentRenamer<'a> { + renames: &'a mtwt::RenameList, +} + +impl<'a> Folder for PatIdentRenamer<'a> { + fn fold_pat(&mut self, pat: Gc) -> Gc { + match pat.node { + ast::PatIdent(binding_mode, Spanned{span: ref sp, node: id}, ref sub) => { + let new_ident = Ident{name: id.name, + ctxt: mtwt::apply_renames(self.renames, id.ctxt)}; + let new_node = + ast::PatIdent(binding_mode, + Spanned{span: self.new_span(*sp), node: new_ident}, + sub.map(|p| self.fold_pat(p))); + box(GC) ast::Pat { + id: pat.id, + span: self.new_span(pat.span), + node: new_node, + } + }, + _ => noop_fold_pat(pat, self) + } } } +/// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the +/// PatIdents in its arguments to perform renaming in the FnDecl and +/// the block, returning both the new FnDecl and the new Block. +fn expand_and_rename_fn_decl_and_block(fn_decl: &ast::FnDecl, block: Gc, + fld: &mut MacroExpander) + -> (Gc, Gc) { + let expanded_decl = fld.fold_fn_decl(fn_decl); + let idents = fn_decl_arg_bindings(expanded_decl); + let renames = + idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect(); + // first, a renamer for the PatIdents, for the fn_decl: + let mut rename_pat_fld = PatIdentRenamer{renames: &renames}; + let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl); + // now, a renamer for *all* idents, for the body: + let mut rename_fld = IdentRenamer{renames: &renames}; + let rewritten_body = fld.fold_block(rename_fld.fold_block(block)); + (rewritten_fn_decl,rewritten_body) +} + +/// A tree-folder that performs macro expansion pub struct MacroExpander<'a, 'b> { pub extsbox: SyntaxEnv, pub cx: &'a mut ExtCtxt<'b>, @@ -890,6 +953,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_item(item, self) } + fn fold_item_underscore(&mut self, item: &ast::Item_) -> ast::Item_ { + expand_item_underscore(item, self) + } + fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector> { expand_stmt(stmt, self) } @@ -907,6 +974,15 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } } +fn new_span(cx: &ExtCtxt, sp: Span) -> Span { + /* this discards information in the case of macro-defining macros */ + Span { + lo: sp.lo, + hi: sp.hi, + expn_info: cx.backtrace(), + } +} + pub struct ExpansionConfig { pub deriving_hash_type_parameter: bool, pub crate_id: CrateId, @@ -966,7 +1042,7 @@ impl Folder for Marker { fn fold_ident(&mut self, id: Ident) -> Ident { ast::Ident { name: id.name, - ctxt: mtwt::new_mark(self.mark, id.ctxt) + ctxt: mtwt::apply_mark(self.mark, id.ctxt) } } fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac { @@ -974,7 +1050,7 @@ impl Folder for Marker { MacInvocTT(ref path, ref tts, ctxt) => { MacInvocTT(self.fold_path(path), fold_tts(tts.as_slice(), self), - mtwt::new_mark(self.mark, ctxt)) + mtwt::apply_mark(self.mark, ctxt)) } }; Spanned { @@ -1028,13 +1104,14 @@ fn original_span(cx: &ExtCtxt) -> Gc { #[cfg(test)] mod test { use super::{pattern_bindings, expand_crate, contains_macro_escape}; - use super::{NameFinderContext}; + use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer}; use ast; use ast::{Attribute_, AttrOuter, MetaWord}; use attr; use codemap; use codemap::Spanned; use ext::mtwt; + use fold::Folder; use parse; use parse::token; use util::parser_testing::{string_to_parser}; @@ -1072,7 +1149,24 @@ mod test { path_finder.path_accumulator } + /// A Visitor that extracts the identifiers from a thingy. + // as a side note, I'm starting to want to abstract over these.... + struct IdentFinder{ + ident_accumulator: Vec + } + + impl Visitor<()> for IdentFinder { + fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident, _: ()){ + self.ident_accumulator.push(id); + } + } + /// Find the idents in a crate + fn crate_idents(the_crate: &ast::Crate) -> Vec { + let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()}; + visit::walk_crate(&mut ident_finder, the_crate, ()); + ident_finder.ident_accumulator + } // these following tests are quite fragile, in that they don't test what // *kind* of failure occurs. @@ -1167,12 +1261,11 @@ mod test { // find the pat_ident paths in a crate fn crate_bindings(the_crate : &ast::Crate) -> Vec { - let mut name_finder = NameFinderContext{ident_accumulator:Vec::new()}; + let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()}; visit::walk_crate(&mut name_finder, the_crate, ()); name_finder.ident_accumulator } - //fn expand_and_resolve(crate_str: @str) -> ast::crate { //let expanded_ast = expand_crate_str(crate_str); // println!("expanded: {:?}\n",expanded_ast); @@ -1298,18 +1391,37 @@ mod test { // but *shouldn't* bind because it was inserted by a different macro.... // can't write this test case until we have macro-generating macros. - // FIXME #9383 : lambda var hygiene - // interesting... can't even write this test, yet, because the name-finder - // only finds pattern vars. Time to upgrade test framework. - /*#[test] - fn issue_9383(){ + // item fn hygiene + // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};} + #[test] fn issue_9383(){ run_renaming_test( - &("macro_rules! bad_macro (($ex:expr) => ({(|_x| { $ex }) (9) })) - fn takes_x(_x : int) { assert_eq!(bad_macro!(_x),8); } - fn main() { takes_x(8); }", - vec!(vec!()),false), + &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex })) + fn q(x:int) { bad_macro!(x); }", + vec!(vec!(1),vec!(0)),true), 0) - }*/ + } + + // closure arg hygiene (ExprFnBlock) + // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);} + #[test] fn closure_arg_hygiene(){ + run_renaming_test( + &("macro_rules! inject_x (()=>(x)) + fn f(){(|x : int| {(inject_x!() + x)})(3);}", + vec!(vec!(1)), + true), + 0) + } + + // closure arg hygiene (ExprProc) + // expands to fn f(){(proc(x_1 : int) {(x_2 + x_1)})(3);} + #[test] fn closure_arg_hygiene_2(){ + run_renaming_test( + &("macro_rules! inject_x (()=>(x)) + fn f(){ (proc(x : int){(inject_x!() + x)})(3); }", + vec!(vec!(1)), + true), + 0) + } // run one of the renaming tests fn run_renaming_test(t: &RenamingTest, test_idx: uint) { @@ -1359,9 +1471,9 @@ mod test { assert_eq!(varref_marks,binding_marks.clone()); } } else { + let varref_name = mtwt::resolve(varref.segments.get(0).identifier); let fail = (varref.segments.len() == 1) - && (mtwt::resolve(varref.segments.get(0).identifier) - == binding_name); + && (varref_name == binding_name); // temp debugging: if fail { let varref_idents : Vec @@ -1372,7 +1484,8 @@ mod test { println!("text of test case: \"{}\"", teststr); println!(""); println!("uh oh, matches but shouldn't:"); - println!("varref: {}",varref_idents); + println!("varref #{}: {}, resolves to {}",idx, varref_idents, + varref_name); // good lord, you can't make a path with 0 segments, can you? let string = token::get_ident(varref.segments .get(0) @@ -1380,7 +1493,9 @@ mod test { println!("varref's first segment's uint: {}, and string: \"{}\"", varref.segments.get(0).identifier.name, string.get()); - println!("binding: {}", *bindings.get(binding_idx)); + println!("binding #{}: {}, resolves to {}", + binding_idx, *bindings.get(binding_idx), + binding_name); mtwt::with_sctable(|x| mtwt::display_sctable(x)); } assert!(!fail); @@ -1443,13 +1558,43 @@ foo_module!() // 'None' is listed as an identifier pattern because we don't yet know that // it's the name of a 0-ary variant, and that 'i' appears twice in succession. #[test] - fn crate_idents(){ + fn crate_bindings_test(){ let the_crate = string_to_crate("fn main (a : int) -> int {|b| { match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string()); let idents = crate_bindings(&the_crate); assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y"))); } - // + // test the IdentRenamer directly + #[test] + fn ident_renamer_test () { + let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string()); + let f_ident = token::str_to_ident("f"); + let x_ident = token::str_to_ident("x"); + let int_ident = token::str_to_ident("int"); + let renames = vec!((x_ident,16)); + let mut renamer = IdentRenamer{renames: &renames}; + let renamed_crate = renamer.fold_crate(the_crate); + let idents = crate_idents(&renamed_crate); + let resolved : Vec = idents.iter().map(|id| mtwt::resolve(*id)).collect(); + assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,16,16)); + } + + // test the PatIdentRenamer; only PatIdents get renamed + #[test] + fn pat_ident_renamer_test () { + let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string()); + let f_ident = token::str_to_ident("f"); + let x_ident = token::str_to_ident("x"); + let int_ident = token::str_to_ident("int"); + let renames = vec!((x_ident,16)); + let mut renamer = PatIdentRenamer{renames: &renames}; + let renamed_crate = renamer.fold_crate(the_crate); + let idents = crate_idents(&renamed_crate); + let resolved : Vec = idents.iter().map(|id| mtwt::resolve(*id)).collect(); + let x_name = x_ident.name; + assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,x_name,x_name)); + } + } diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs index 48895d34022c4..18466e381a58b 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/mtwt.rs @@ -54,38 +54,51 @@ pub enum SyntaxContext_ { IllegalCtxt } +/// A list of ident->name renamings +pub type RenameList = Vec<(Ident, Name)>; + /// 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)) +pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext { + with_sctable(|table| apply_mark_internal(m, ctxt, 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); +// Extend a syntax context with a given mark and sctable (explicit memoization) +fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext { + let key = (ctxt, m); let new_ctxt = |_: &(SyntaxContext, Mrk)| - idx_push(&mut *table.table.borrow_mut(), Mark(m, tail)); + idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt)); *table.mark_memo.borrow_mut().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)) +pub fn apply_rename(id: Ident, to:Name, + ctxt: SyntaxContext) -> SyntaxContext { + with_sctable(|table| apply_rename_internal(id, to, ctxt, table)) } -// Extend a syntax context with a given rename and sctable -fn new_rename_internal(id: Ident, +// Extend a syntax context with a given rename and sctable (explicit memoization) +fn apply_rename_internal(id: Ident, to: Name, - tail: SyntaxContext, + ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext { - let key = (tail,id,to); + let key = (ctxt,id,to); let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)| - idx_push(&mut *table.table.borrow_mut(), Rename(id, to, tail)); + idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt)); *table.rename_memo.borrow_mut().find_or_insert_with(key, new_ctxt) } +/// Apply a list of renamings to a context +// if these rename lists get long, it would make sense +// to consider memoizing this fold. This may come up +// when we add hygiene to item names. +pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext { + renames.iter().fold(ctxt, |ctxt, &(from, to)| { + apply_rename(from, to, ctxt) + }) +} + /// Fetch the SCTable from TLS, create one if it doesn't yet exist. pub fn with_sctable(op: |&SCTable| -> T) -> T { local_data_key!(sctable_key: Rc) @@ -263,9 +276,9 @@ fn xor_push(marks: &mut Vec, mark: Mrk) { #[cfg(test)] mod tests { - use ast::*; - use super::{resolve, xor_push, new_mark_internal, new_sctable_internal}; - use super::{new_rename_internal, marksof_internal, resolve_internal}; + use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext}; + use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal}; + use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal}; use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt}; use std::collections::HashMap; @@ -306,8 +319,8 @@ mod tests { -> SyntaxContext { tscs.iter().rev().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)}}) + M(mrk) => apply_mark_internal(mrk,tail,table), + R(ident,name) => apply_rename_internal(ident,name,tail,table)}}) } // gather a SyntaxContext back into a vector of TestSCs @@ -352,7 +365,7 @@ mod tests { fn unfold_marks(mrks: Vec , tail: SyntaxContext, table: &SCTable) -> SyntaxContext { mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk| - {new_mark_internal(*mrk,tail,table)}) + {apply_mark_internal(*mrk,tail,table)}) } #[test] fn unfold_marks_test() { @@ -384,13 +397,13 @@ mod tests { // rename where stop doesn't match: { let chain = vec!(M(9), R(id(name1, - new_mark_internal (4, EMPTY_CTXT,&mut t)), + apply_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 name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t); let chain = vec!(M(9), R(id(name1, name1sc), stopname), @@ -414,7 +427,7 @@ mod tests { { 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 sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t); let sc = unfold_test_sc(vec!(R(id(a,sc1),50), M(1), M(2)), @@ -437,11 +450,11 @@ mod tests { 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); + { let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t); + let a50_to_a51 = apply_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); + let sc = apply_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), @@ -461,10 +474,10 @@ mod tests { #[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); + assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2); + assert_eq!(apply_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); + assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2); // I'm assuming that the rename table will behave the same.... } @@ -480,4 +493,13 @@ mod tests { resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt); assert_eq!(rt.len(),2); } + + #[test] + fn new_resolves_test() { + let renames = vec!((Ident{name:23,ctxt:EMPTY_CTXT},24), + (Ident{name:29,ctxt:EMPTY_CTXT},29)); + let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT); + assert_eq!(resolve(Ident{name:23,ctxt:new_ctxt1}),24); + assert_eq!(resolve(Ident{name:29,ctxt:new_ctxt1}),29); + } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index f9d7078da3dcb..04e6612daf1f0 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -794,7 +794,7 @@ pub fn noop_fold_pat(p: Gc, folder: &mut T) -> Gc { PatIdent(binding_mode, ref pth1, ref sub) => { PatIdent(binding_mode, Spanned{span: folder.new_span(pth1.span), - node: folder.fold_ident(pth1.node)}, + node: folder.fold_ident(pth1.node)}, sub.map(|x| folder.fold_pat(x))) } PatLit(e) => PatLit(folder.fold_expr(e)), diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index a93e8270d9866..dcf37e37ff0a7 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -765,7 +765,7 @@ mod test { use ext::mtwt; fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident { - ast::Ident{name:id.name,ctxt:mtwt::new_mark(m,id.ctxt)} + ast::Ident{name:id.name,ctxt:mtwt::apply_mark(m,id.ctxt)} } #[test] fn mtwt_token_eq_test() {