From 72ee6af4d4e4d16a81e3f518f9c75f513a9a7dea Mon Sep 17 00:00:00 2001 From: John Clements Date: Thu, 5 Sep 2013 14:14:31 -0700 Subject: compare macro tokens hygienically (commented out) --- src/libsyntax/parse/parser.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 51c5522ae2f..62abe3850c9 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3518,7 +3518,10 @@ impl Parser { } fn is_self_ident(&self) -> bool { - *self.token == token::IDENT(special_idents::self_, false) + match *self.token { + token::IDENT(id, false) => id.name == special_idents::self_.name, + _ => false + } } fn expect_self_ident(&self) { -- cgit 1.4.1-3-g733a5 From fa6c981606b89ebed80b0dd5e829d86cdb3078d8 Mon Sep 17 00:00:00 2001 From: John Clements Date: Tue, 25 Jun 2013 11:40:51 -0700 Subject: add hygiene support fns, move them around. also adds test cases --- src/libsyntax/ast.rs | 2 +- src/libsyntax/ast_util.rs | 3 - src/libsyntax/ext/expand.rs | 205 ++++++++++++++++++++++++++++------- src/libsyntax/parse/token.rs | 10 +- src/libsyntax/util/parser_testing.rs | 15 ++- 5 files changed, 175 insertions(+), 60 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index c2087928e8d..ef5282551a1 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -571,7 +571,7 @@ pub enum token_tree { // These only make sense for right-hand-sides of MBE macros: // a kleene-style repetition sequence with a span, a tt_forest, - // an optional separator (?), and a boolean where true indicates + // an optional separator, and a boolean where true indicates // zero or more (*), and false indicates one or more (+). tt_seq(Span, @mut ~[token_tree], Option<::parse::token::Token>, bool), diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 0e402731c66..aeca145ea18 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -825,9 +825,6 @@ pub fn pat_is_ident(pat: @ast::Pat) -> bool { // HYGIENE FUNCTIONS -/// Construct an identifier with the given name and an empty context: -pub fn new_ident(name: Name) -> Ident { Ident {name: name, ctxt: 0}} - /// Extend a syntax context with a given mark pub fn new_mark(m:Mrk, tail:SyntaxContext) -> SyntaxContext { new_mark_internal(m,tail,get_sctable()) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8781dd1feff..81a47e0e485 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{Block, Crate, NodeId, Expr_, ExprMac, Ident, mac_invoc_tt}; -use ast::{item_mac, Stmt_, StmtMac, StmtExpr, StmtSemi}; -use ast::{ILLEGAL_CTXT}; +use ast::{Block, Crate, NodeId, DeclLocal, Expr_, ExprMac, Local, Ident, mac_invoc_tt}; +use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi}; +use ast::{ILLEGAL_CTXT, SCTable, token_tree}; use ast; use ast_util::{new_rename, new_mark, mtwt_resolve}; use attr; @@ -23,7 +23,7 @@ use opt_vec; use parse; use parse::{parse_item_from_source_str}; use parse::token; -use parse::token::{ident_to_str, intern}; +use parse::token::{fresh_name, ident_to_str, intern}; use visit; use visit::Visitor; @@ -521,6 +521,71 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv, } +// expand a non-macro stmt. this is essentially the fallthrough for +// expand_stmt, above. +fn expand_non_macro_stmt (exts: SyntaxEnv, + s: &Stmt_, + sp: Span, + fld: @ast_fold, + orig: @fn(&Stmt_, Span, @ast_fold) -> (Option, Span)) + -> (Option,Span) { + // is it a let? + match *s { + StmtDecl(@Spanned{node: DeclLocal(ref local), span: stmt_span}, node_id) => { + let block_info = get_block_info(exts); + let pending_renames = block_info.pending_renames; + + // take it apart: + let @Local{is_mutbl:is_mutbl, + ty:_, + pat:pat, + init:init, + id:id, + span:span + } = *local; + // types can't be copied automatically because of the owned ptr in ty_tup... + let ty = local.ty.clone(); + // expand the pat (it might contain exprs... #:(o)> + let expanded_pat = fld.fold_pat(pat); + // find the pat_idents in the pattern: + // oh dear heaven... this is going to include the enum names, as well.... + let idents = @mut ~[]; + let name_finder = new_name_finder(idents); + name_finder.visit_pat(expanded_pat,()); + // generate fresh names, push them to a new pending list + let new_pending_renames = @mut ~[]; + for ident in idents.iter() { + let new_name = fresh_name(ident); + new_pending_renames.push((*ident,new_name)); + } + let mut rename_fld = renames_to_fold(new_pending_renames); + // rewrite the pattern using the new names (the old ones + // have already been applied): + let rewritten_pat = rename_fld.fold_pat(expanded_pat); + // add them to the existing pending renames: + for pr in new_pending_renames.iter() {pending_renames.push(*pr)} + // also, don't forget to expand the init: + let new_init_opt = init.map(|e| fld.fold_expr(*e)); + let rewritten_local = + @Local{is_mutbl:is_mutbl, + ty:ty, + pat:rewritten_pat, + init:new_init_opt, + id:id, + span:span}; + (Some(StmtDecl(@Spanned{node:DeclLocal(rewritten_local), + span: stmt_span},node_id)), + sp) + }, + _ => { + orig(s, sp, fld) + } + } +} + +// a visitor that extracts the pat_ident paths +// from a given pattern and puts them in a mutable +// array (passed in to the traversal) #[deriving(Clone)] struct NewNameFinderContext { ident_accumulator: @mut ~[ast::Ident], @@ -674,30 +739,10 @@ pub fn new_name_finder(idents: @mut ~[ast::Ident]) -> @mut Visitor<()> { context as @mut Visitor<()> } -pub fn expand_block(extsbox: @mut SyntaxEnv, - _cx: @ExtCtxt, - blk: &Block, - fld: @ast_fold, - orig: @fn(&Block, @ast_fold) -> Block) - -> Block { - // see note below about treatment of exts table - with_exts_frame!(extsbox,false,orig(blk,fld)) -} - - -// get the (innermost) BlockInfo from an exts stack -fn get_block_info(exts : SyntaxEnv) -> BlockInfo { - match exts.find_in_topmost_frame(&intern(special_block_name)) { - Some(@BlockInfo(bi)) => bi, - _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo", - @" block")) - } -} - - // given a mutable list of renames, return a tree-folder that applies those // renames. -fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold { +// FIXME #4536: currently pub to allow testing +pub fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold { let afp = default_ast_fold(); let f_pre = @AstFoldFns { fold_ident: |id,_| { @@ -713,15 +758,56 @@ fn renames_to_fold(renames : @mut ~[(ast::Ident,ast::Name)]) -> @ast_fold { make_fold(f_pre) } -// perform a bunch of renames -fn apply_pending_renames(folder : @ast_fold, stmt : ast::Stmt) -> @ast::Stmt { - match folder.fold_stmt(&stmt) { - Some(s) => s, - None => fail!(fmt!("renaming of stmt produced None")) +pub fn expand_block(extsbox: @mut SyntaxEnv, + _cx: @ExtCtxt, + blk: &Block, + fld: @ast_fold, + orig: @fn(&Block, @ast_fold) -> Block) + -> Block { + // see note below about treatment of exts table + with_exts_frame!(extsbox,false,orig(blk,fld)) +} + + +pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: @ast_fold) -> Block { + let block_info = get_block_info(exts); + let pending_renames = block_info.pending_renames; + let mut rename_fld = renames_to_fold(pending_renames); + let new_view_items = b.view_items.map(|x| fld.fold_view_item(x)); + let mut new_stmts = ~[]; + for x in b.stmts.iter() { + match fld.fold_stmt(mustbesome(rename_fld.fold_stmt(*x))) { + Some(s) => new_stmts.push(s), + None => () + } + } + let new_expr = b.expr.map(|x| fld.fold_expr(rename_fld.fold_expr(*x))); + Block{ + view_items: new_view_items, + stmts: new_stmts, + expr: new_expr, + id: fld.new_id(b.id), + rules: b.rules, + span: b.span, } } +// rename_fold should never return "None". +fn mustbesome(val : Option) -> T { + match val { + Some(v) => v, + None => fail!("rename_fold returned None") + } +} +// get the (innermost) BlockInfo from an exts stack +fn get_block_info(exts : SyntaxEnv) -> BlockInfo { + match exts.find_in_topmost_frame(&intern(special_block_name)) { + Some(@BlockInfo(bi)) => bi, + _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo", + @" block")) + } +} pub fn new_span(cx: @ExtCtxt, sp: Span) -> Span { /* this discards information in the case of macro-defining macros */ @@ -1228,12 +1314,15 @@ mod test { use super::*; use ast; use ast::{Attribute_, AttrOuter, MetaWord, EMPTY_CTXT}; + use ast_util::{get_sctable, new_rename}; use codemap; use codemap::Spanned; use parse; - use parse::token::{intern, get_ident_interner}; + use parse::token::{gensym, intern, get_ident_interner}; use print::pprust; - use util::parser_testing::{string_to_item, string_to_pat, strs_to_idents}; + use std; + use util::parser_testing::{string_to_crate_and_sess, string_to_item, string_to_pat}; + use util::parser_testing::{strs_to_idents}; // make sure that fail! is present #[test] fn fail_exists_test () { @@ -1333,26 +1422,60 @@ mod test { #[test] fn renaming () { - let maybe_item_ast = string_to_item(@"fn a() -> int { let b = 13; b }"); - let item_ast = match maybe_item_ast { - Some(x) => x, - None => fail!("test case fail") - }; + let item_ast = string_to_item(@"fn a() -> int { let b = 13; b }").unwrap(); let a_name = intern("a"); - let a2_name = intern("a2"); + let a2_name = gensym("a2"); let renamer = new_ident_renamer(ast::Ident{name:a_name,ctxt:EMPTY_CTXT}, a2_name); let renamed_ast = fun_to_ident_folder(renamer).fold_item(item_ast).unwrap(); let resolver = new_ident_resolver(); - let resolved_ast = fun_to_ident_folder(resolver).fold_item(renamed_ast).unwrap(); + let resolver_fold = fun_to_ident_folder(resolver); + let resolved_ast = resolver_fold.fold_item(renamed_ast).unwrap(); let resolved_as_str = pprust::item_to_str(resolved_ast, get_ident_interner()); assert_eq!(resolved_as_str,~"fn a2() -> int { let b = 13; b }"); + // try a double-rename, with pending_renames. + let a3_name = gensym("a3"); + let ctxt2 = new_rename(ast::Ident::new(a_name),a2_name,EMPTY_CTXT); + let pending_renames = @mut ~[(ast::Ident::new(a_name),a2_name), + (ast::Ident{name:a_name,ctxt:ctxt2},a3_name)]; + let double_renamed = renames_to_fold(pending_renames).fold_item(item_ast).unwrap(); + let resolved_again = resolver_fold.fold_item(double_renamed).unwrap(); + let double_renamed_as_str = pprust::item_to_str(resolved_again, + get_ident_interner()); + assert_eq!(double_renamed_as_str,~"fn a3() -> int { let b = 13; b }"); + + } + fn fake_print_crate(s: @pprust::ps, crate: &ast::Crate) { + pprust::print_mod(s, &crate.module, crate.attrs); } - // sigh... it looks like I have two different renaming mechanisms, now... + // "fn a() -> int { let b = 13; let c = b; b+c }" --> b & c should get new names, in the expr too. + // "macro_rules! f (($x:ident) => ($x + b)) fn a() -> int { let b = 13; f!(b)}" --> one should + // be renamed, one should not. + + fn expand_and_resolve_and_pretty_print (crate_str : @str) -> ~str { + let resolver = new_ident_resolver(); + let resolver_fold = fun_to_ident_folder(resolver); + let (crate_ast,ps) = string_to_crate_and_sess(crate_str); + // the cfg argument actually does matter, here... + let expanded_ast = expand_crate(ps,~[],crate_ast); + // std::io::println(fmt!("expanded: %?\n",expanded_ast)); + let resolved_ast = resolver_fold.fold_crate(expanded_ast); + pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner()) + } + + #[test] + fn automatic_renaming () { + let teststrs = + ~[@"fn a() -> int { let b = 13; let c = b; b+c }", + @"macro_rules! f (($x:ident) => ($x + b)) fn a() -> int { let b = 13; f!(b)}"]; + for s in teststrs.iter() { + std::io::println(expand_and_resolve_and_pretty_print(*s)); + } + } #[test] fn pat_idents(){ diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 591b4b10bd3..8de597733ae 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -552,9 +552,9 @@ pub fn gensym_ident(str : &str) -> ast::Ident { // by using a gensym with a name that has a random number // at the end. So, the gensym guarantees the uniqueness, // and the int helps to avoid confusion. -pub fn fresh_name(src_name : &str) -> Name { +pub fn fresh_name(src_name : &ast::Ident) -> Name { let num = rand::rng().gen_uint_range(0,0xffff); - gensym(fmt!("%s_%u",src_name,num)) + gensym(fmt!("%s_%u",ident_to_str(src_name),num)) } /** @@ -697,9 +697,5 @@ pub fn is_reserved_keyword(tok: &Token) -> bool { #[cfg(test)] mod test { use super::*; - #[test] fn t1() { - let a = fresh_name("ghi"); - printfln!("interned name: %u,\ntextual name: %s\n", - a, interner_get(a)); - } + } diff --git a/src/libsyntax/util/parser_testing.rs b/src/libsyntax/util/parser_testing.rs index ca1e53f7fcd..51fd5be71ab 100644 --- a/src/libsyntax/util/parser_testing.rs +++ b/src/libsyntax/util/parser_testing.rs @@ -40,12 +40,19 @@ fn with_error_checking_parse(s: @str, f: &fn(&mut Parser) -> T) -> T { x } +// parse a string, return a crate. pub fn string_to_crate (source_str : @str) -> @ast::Crate { do with_error_checking_parse(source_str) |p| { p.parse_crate_mod() } } +// parse a string, return a crate and the ParseSess +pub fn string_to_crate_and_sess (source_str : @str) -> (@ast::Crate,@mut ParseSess) { + let (p,ps) = string_to_parser_and_sess(source_str); + (p.parse_crate_mod(),ps) +} + // parse a string, return an expr pub fn string_to_expr (source_str : @str) -> @ast::Expr { do with_error_checking_parse(source_str) |p| { @@ -60,14 +67,6 @@ pub fn string_to_item (source_str : @str) -> Option<@ast::item> { } } -// parse a string, return an item and the ParseSess -pub fn string_to_item_and_sess (source_str : @str) -> (Option<@ast::item>,@mut ParseSess) { - let (p,ps) = string_to_parser_and_sess(source_str); - let io = p.parse_item(~[]); - p.abort_if_errors(); - (io,ps) -} - // parse a string, return a stmt pub fn string_to_stmt(source_str : @str) -> @ast::Stmt { do with_error_checking_parse(source_str) |p| { -- cgit 1.4.1-3-g733a5 From 91d3c364303c3a057feadd40adef0880531e08cc Mon Sep 17 00:00:00 2001 From: John Clements Date: Tue, 25 Jun 2013 11:43:52 -0700 Subject: adding test case to check marking/unmarking --- src/libsyntax/ext/expand.rs | 26 +++++++++++++++++--------- src/libsyntax/fold.rs | 4 ++-- src/libsyntax/parse/token.rs | 7 ++++++- 3 files changed, 25 insertions(+), 12 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index fbaeab03e8a..25edcf63faa 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -23,7 +23,7 @@ use opt_vec; use parse; use parse::{parse_item_from_source_str}; use parse::token; -use parse::token::{fresh_name, ident_to_str, intern}; +use parse::token::{fresh_mark, fresh_name, ident_to_str, intern}; use visit; use visit::Visitor; @@ -67,7 +67,9 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, }, }); - let expanded = match expandfun(cx, mac.span, *tts) { + let fm = fresh_mark(); + let marked_tts = mark_tts(*tts,fm); + let expanded = match expandfun(cx, mac.span, marked_tts) { MRExpr(e) => e, MRAny(expr_maker,_,_) => expr_maker(), _ => { @@ -259,6 +261,12 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, } } +// apply a fresh mark to the given token trees. Used prior to expansion of a macro. +fn mark_tts(tts : &[token_tree], m : Mrk) -> ~[token_tree] { + fold_tts(tts,new_ident_marker(m)) +} + + // This is a secondary mechanism for invoking syntax extensions on items: // "decorator" attributes, such as #[auto_encode]. These are invoked by an // attribute prefixing an item, and are interpreted by feeding the item @@ -1285,7 +1293,7 @@ pub fn new_ident_renamer(from: ast::Ident, // update the ctxts in a path to get a mark node -pub fn new_ident_marker(mark: uint) -> +pub fn new_ident_marker(mark: Mrk) -> @fn(ast::Ident)->ast::Ident { |id : ast::Ident| ast::Ident{ @@ -1461,18 +1469,18 @@ mod test { #[test] fn automatic_renaming () { - // "fn a() -> int { let b = 13; let c = b; b+c }" - // --> b & c should get new names, in the expr too. - // "macro_rules! f (($x:ident) => ($x + b)) fn a() -> int { let b = 13; f!(b)}" - // --> one should be renamed, one should not. - let teststrs = ~[// b & c should get new names throughout, in the expr too: @"fn a() -> int { let b = 13; let c = b; b+c }", // the use of b before the + should be renamed, the other one not: @"macro_rules! f (($x:ident) => ($x + b)) fn a() -> int { let b = 13; f!(b)}", // the b before the plus should not be renamed (requires marks) - @"macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}"]; + @"macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}", + // the z flows into and out of two macros (g & f) along one path, and one (just g) along the + // other, so the result of the whole thing should be "let z_123 = 3; z_123" + @"macro_rules! g (($x:ident) => ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)})) + fn a(){g!(z)}" + ]; for s in teststrs.iter() { // we need regexps to test these! std::io::println(expand_and_resolve_and_pretty_print(*s)); diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 750ec0be984..52c148e7ba2 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -126,7 +126,7 @@ fn fold_mac_(m: &mac, fld: @ast_fold) -> mac { // build a new vector of tts by appling the given function to // all of the identifiers in the token trees. -pub fn fold_tts(tts : &[token_tree], f: @fn(ident)->ident) -> ~[token_tree] { +pub fn fold_tts(tts : &[token_tree], f: @fn(Ident)->Ident) -> ~[token_tree] { do tts.map |tt| { match *tt { tt_tok(span, ref tok) => @@ -145,7 +145,7 @@ pub fn fold_tts(tts : &[token_tree], f: @fn(ident)->ident) -> ~[token_tree] { } // apply ident folder if it's an ident, otherwise leave it alone -fn maybe_fold_ident(t : &token::Token, f: @fn(ident)->ident) -> token::Token { +fn maybe_fold_ident(t : &token::Token, f: @fn(Ident)->Ident) -> token::Token { match *t { token::IDENT(id,followed_by_colons) => token::IDENT(f(id),followed_by_colons), diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 8de597733ae..66f121727af 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -9,7 +9,7 @@ // except according to those terms. use ast; -use ast::Name; +use ast::{Name, Mrk}; use ast_util; use parse::token; use util::interner::StrInterner; @@ -557,6 +557,11 @@ pub fn fresh_name(src_name : &ast::Ident) -> Name { gensym(fmt!("%s_%u",ident_to_str(src_name),num)) } +// create a fresh mark. +pub fn fresh_mark() -> Mrk { + gensym("mark") +} + /** * All the valid words that have meaning in the Rust language. * -- cgit 1.4.1-3-g733a5 From d1c01734f3a34958333fefb2fe91cea5e753ddd7 Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 7 Jun 2013 10:41:18 -0700 Subject: drop back to a simple gensym approach for fresh-name. this is necessary so that the new idents are connected to the original strings. this is important both for error messages, and so that top-level refs get connected to the right things. --- src/libsyntax/parse/token.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 66f121727af..6cdde3708a6 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -543,18 +543,9 @@ pub fn gensym_ident(str : &str) -> ast::Ident { ast::Ident::new(gensym(str)) } - -// create a fresh name. In principle, this is just a -// gensym, but for debugging purposes, you'd like the -// resulting name to have a suggestive stringify, without -// paying the cost of guaranteeing that the name is -// truly unique. I'm going to try to strike a balance -// by using a gensym with a name that has a random number -// at the end. So, the gensym guarantees the uniqueness, -// and the int helps to avoid confusion. -pub fn fresh_name(src_name : &ast::Ident) -> Name { - let num = rand::rng().gen_uint_range(0,0xffff); - gensym(fmt!("%s_%u",ident_to_str(src_name),num)) +// create a fresh name that maps to the same string as the old one. +pub fn fresh_name(src : &ast::Ident) -> Name { + gensym(ident_to_str(src)) } // create a fresh mark. -- cgit 1.4.1-3-g733a5 From 9071ac60b621020c08bc74cd15c363a7780a2230 Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 7 Jun 2013 14:53:53 -0700 Subject: re-add debug version --- src/libsyntax/parse/token.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 6cdde3708a6..132ad95e639 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -546,6 +546,11 @@ pub fn gensym_ident(str : &str) -> ast::Ident { // create a fresh name that maps to the same string as the old one. pub fn fresh_name(src : &ast::Ident) -> Name { gensym(ident_to_str(src)) + // following: debug version. Could work in final except that it's incompatible with + // good error messages and uses of struct names in ambiguous could-be-binding + // locations. + /*let num = rand::rng().gen_uint_range(0,0xffff); + gensym(fmt!("%s_%u",ident_to_str(src),num))*/ } // create a fresh mark. -- cgit 1.4.1-3-g733a5 From 9d33001a90319fc242dcf43ec3c7e1fa1c11d847 Mon Sep 17 00:00:00 2001 From: John Clements Date: Tue, 25 Jun 2013 16:48:03 -0700 Subject: added test for ptr_eq on fresh_name-generated idents --- src/libsyntax/parse/token.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 132ad95e639..29c460c5c3d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -20,6 +20,7 @@ use std::cmp::Equiv; use std::local_data; use std::rand; use std::rand::RngUtil; +use std::ptr::to_unsafe_ptr; #[deriving(Clone, Encodable, Decodable, Eq, IterBytes)] pub enum binop { @@ -544,15 +545,31 @@ pub fn gensym_ident(str : &str) -> ast::Ident { } // create a fresh name that maps to the same string as the old one. +// note that this guarantees that ptr_eq(ident_to_str(src),interner_get(fresh_name(src))); +// that is, that the new name and the old one are connected to ptr_eq strings. pub fn fresh_name(src : &ast::Ident) -> Name { gensym(ident_to_str(src)) // following: debug version. Could work in final except that it's incompatible with // good error messages and uses of struct names in ambiguous could-be-binding - // locations. + // locations. Also definitely destroys the guarantee given above about ptr_eq. /*let num = rand::rng().gen_uint_range(0,0xffff); gensym(fmt!("%s_%u",ident_to_str(src),num))*/ } +// it looks like there oughta be a str_ptr_eq fn, but no one bothered to implement it? +pub fn str_ptr_eq(a: @str, b: @str) -> bool { + // doesn't compile! ...because of rebase mangling. this should be fixed + // in the commit that follows this. + let (a_ptr, b_ptr): (*uint, *uint) = (to_unsafe_ptr(a), to_unsafe_ptr(b)); + a_ptr == b_ptr +} + + + +// return true when two identifiers refer (through the intern table) to the same ptr_eq +// string. This is used to compare identifiers in places where hygienic comparison is +// not wanted (i.e. not lexical vars). + // create a fresh mark. pub fn fresh_mark() -> Mrk { gensym("mark") @@ -698,5 +715,19 @@ pub fn is_reserved_keyword(tok: &Token) -> bool { #[cfg(test)] mod test { use super::*; + use std::io; + use std::managed; + use ast; + use ast_util; + + + #[test] fn t1() { + let ghi = str_to_ident("ghi"); + assert_eq!(ident_to_str(&ghi),@"ghi"); + let fresh = ast::Ident::new(fresh_name(&ghi)); + assert_eq!(ident_to_str(&fresh),@"ghi"); + assert!(str_ptr_eq(ident_to_str(&ghi),ident_to_str(&fresh))); + assert_eq!(3,4); + } } -- cgit 1.4.1-3-g733a5 From 58e7598c2e1265a0f1292ed6f93bfb29abb93504 Mon Sep 17 00:00:00 2001 From: John Clements Date: Wed, 26 Jun 2013 10:11:19 -0700 Subject: added gensym_copy mechanism to ensure sharing of pointers in the interner this makes comparisons constant-time, and enables spelling-comparison of identifiers, crucial in many parts of resolve. --- src/libsyntax/parse/token.rs | 73 +++++++++++++++++++----------------------- src/libsyntax/util/interner.rs | 63 +++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 48 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 29c460c5c3d..17928338f37 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -15,12 +15,12 @@ use parse::token; use util::interner::StrInterner; use util::interner; +use std::cast; use std::char; use std::cmp::Equiv; use std::local_data; use std::rand; use std::rand::RngUtil; -use std::ptr::to_unsafe_ptr; #[deriving(Clone, Encodable, Decodable, Eq, IterBytes)] pub enum binop { @@ -382,30 +382,8 @@ pub fn token_to_binop(tok: &Token) -> Option { } } -pub struct ident_interner { - priv interner: StrInterner, -} - -impl ident_interner { - pub fn intern(&self, val: &str) -> Name { - self.interner.intern(val) - } - pub fn gensym(&self, val: &str) -> Name { - self.interner.gensym(val) - } - pub fn get(&self, idx: Name) -> @str { - self.interner.get(idx) - } - // is this really something that should be exposed? - pub fn len(&self) -> uint { - self.interner.len() - } - pub fn find_equiv>(&self, val: &Q) - -> Option { - self.interner.find_equiv(val) - } -} - +// looks like we can get rid of this completely... +pub type ident_interner = StrInterner; // return a fresh interner, preloaded with special identifiers. fn mk_fresh_ident_interner() -> @ident_interner { @@ -486,9 +464,7 @@ fn mk_fresh_ident_interner() -> @ident_interner { "typeof", // 67 ]; - @ident_interner { - interner: interner::StrInterner::prefill(init_vec) - } + @interner::StrInterner::prefill(init_vec) } // if an interner exists in TLS, return it. Otherwise, prepare a @@ -509,7 +485,7 @@ pub fn get_ident_interner() -> @ident_interner { /* for when we don't care about the contents; doesn't interact with TLD or serialization */ pub fn mk_fake_ident_interner() -> @ident_interner { - @ident_interner { interner: interner::StrInterner::new() } + @interner::StrInterner::new() } // maps a string to its interned representation @@ -545,10 +521,11 @@ pub fn gensym_ident(str : &str) -> ast::Ident { } // create a fresh name that maps to the same string as the old one. -// note that this guarantees that ptr_eq(ident_to_str(src),interner_get(fresh_name(src))); +// note that this guarantees that str_ptr_eq(ident_to_str(src),interner_get(fresh_name(src))); // that is, that the new name and the old one are connected to ptr_eq strings. pub fn fresh_name(src : &ast::Ident) -> Name { - gensym(ident_to_str(src)) + let interner = get_ident_interner(); + interner.gensym_copy(src.name) // following: debug version. Could work in final except that it's incompatible with // good error messages and uses of struct names in ambiguous could-be-binding // locations. Also definitely destroys the guarantee given above about ptr_eq. @@ -557,18 +534,26 @@ pub fn fresh_name(src : &ast::Ident) -> Name { } // it looks like there oughta be a str_ptr_eq fn, but no one bothered to implement it? -pub fn str_ptr_eq(a: @str, b: @str) -> bool { - // doesn't compile! ...because of rebase mangling. this should be fixed - // in the commit that follows this. - let (a_ptr, b_ptr): (*uint, *uint) = (to_unsafe_ptr(a), to_unsafe_ptr(b)); - a_ptr == b_ptr -} - +// determine whether two @str values are pointer-equal +pub fn str_ptr_eq(a : @str, b : @str) -> bool { + unsafe { + let p : uint = cast::transmute(a); + let q : uint = cast::transmute(b); + let result = p == q; + // got to transmute them back, to make sure the ref count is correct: + let junk1 : @str = cast::transmute(p); + let junk2 : @str = cast::transmute(q); + result + } +} // return true when two identifiers refer (through the intern table) to the same ptr_eq // string. This is used to compare identifiers in places where hygienic comparison is // not wanted (i.e. not lexical vars). +pub fn ident_spelling_eq(a : &ast::Ident, b : &ast::Ident) -> bool { + str_ptr_eq(interner_get(a.name),interner_get(b.name)) +} // create a fresh mark. pub fn fresh_mark() -> Mrk { @@ -721,13 +706,21 @@ mod test { use ast_util; - #[test] fn t1() { + #[test] fn str_ptr_eq_tests(){ + let a = @"abc"; + let b = @"abc"; + let c = a; + assert!(str_ptr_eq(a,c)); + assert!(!str_ptr_eq(a,b)); + } + + #[test] fn fresh_name_pointer_sharing() { let ghi = str_to_ident("ghi"); assert_eq!(ident_to_str(&ghi),@"ghi"); + assert!(str_ptr_eq(ident_to_str(&ghi),ident_to_str(&ghi))) let fresh = ast::Ident::new(fresh_name(&ghi)); assert_eq!(ident_to_str(&fresh),@"ghi"); assert!(str_ptr_eq(ident_to_str(&ghi),ident_to_str(&fresh))); - assert_eq!(3,4); } } diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs index 46676ce1093..2b1e7eaa9b2 100644 --- a/src/libsyntax/util/interner.rs +++ b/src/libsyntax/util/interner.rs @@ -117,6 +117,23 @@ impl StrInterner { new_idx } + // I want these gensyms to share name pointers + // with existing entries. This would be automatic, + // except that the existing gensym creates its + // own managed ptr using to_managed. I think that + // adding this utility function is the most + // lightweight way to get what I want, though not + // necessarily the cleanest. + + // create a gensym with the same name as an existing + // entry. + pub fn gensym_copy(&self, idx : uint) -> uint { + let new_idx = self.len(); + // leave out of map to avoid colliding + self.vect.push(self.vect[idx]); + new_idx + } + // this isn't "pure" in the traditional sense, because it can go from // failing to returning a value as items are interned. But for typestate, // where we first check a pred and then rely on it, ceasing to fail is ok. @@ -144,23 +161,23 @@ mod tests { } #[test] - fn i2 () { + fn interner_tests () { let i : Interner<@str> = Interner::new(); // first one is zero: - assert_eq!(i.intern (@"dog"), 0); + assert_eq!(i.intern(@"dog"), 0); // re-use gets the same entry: - assert_eq!(i.intern (@"dog"), 0); + assert_eq!(i.intern(@"dog"), 0); // different string gets a different #: - assert_eq!(i.intern (@"cat"), 1); - assert_eq!(i.intern (@"cat"), 1); + assert_eq!(i.intern(@"cat"), 1); + assert_eq!(i.intern(@"cat"), 1); // dog is still at zero - assert_eq!(i.intern (@"dog"), 0); + assert_eq!(i.intern(@"dog"), 0); // gensym gets 3 - assert_eq!(i.gensym (@"zebra" ), 2); + assert_eq!(i.gensym(@"zebra" ), 2); // gensym of same string gets new number : assert_eq!(i.gensym (@"zebra" ), 3); // gensym of *existing* string gets new number: - assert_eq!(i.gensym (@"dog"), 4); + assert_eq!(i.gensym(@"dog"), 4); assert_eq!(i.get(0), @"dog"); assert_eq!(i.get(1), @"cat"); assert_eq!(i.get(2), @"zebra"); @@ -176,4 +193,34 @@ mod tests { assert_eq!(i.get(2), @"Carol"); assert_eq!(i.intern(@"Bob"), 1); } + + #[test] + fn string_interner_tests() { + let i : StrInterner = StrInterner::new(); + // first one is zero: + assert_eq!(i.intern("dog"), 0); + // re-use gets the same entry: + assert_eq!(i.intern ("dog"), 0); + // different string gets a different #: + assert_eq!(i.intern("cat"), 1); + assert_eq!(i.intern("cat"), 1); + // dog is still at zero + assert_eq!(i.intern("dog"), 0); + // gensym gets 3 + assert_eq!(i.gensym("zebra"), 2); + // gensym of same string gets new number : + assert_eq!(i.gensym("zebra"), 3); + // gensym of *existing* string gets new number: + assert_eq!(i.gensym("dog"), 4); + // gensym tests again with gensym_copy: + assert_eq!(i.gensym_copy(2), 5); + assert_eq!(i.get(5), @"zebra"); + assert_eq!(i.gensym_copy(2), 6); + assert_eq!(i.get(6), @"zebra"); + assert_eq!(i.get(0), @"dog"); + assert_eq!(i.get(1), @"cat"); + assert_eq!(i.get(2), @"zebra"); + assert_eq!(i.get(3), @"zebra"); + assert_eq!(i.get(4), @"dog"); + } } -- cgit 1.4.1-3-g733a5 From 3621c674cc20e666cfa3fdef559f516b39a1189a Mon Sep 17 00:00:00 2001 From: John Clements Date: Wed, 3 Jul 2013 15:15:45 -0700 Subject: comments --- src/libsyntax/parse/token.rs | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 17928338f37..311d498eec2 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -310,22 +310,23 @@ pub fn is_bar(t: &Token) -> bool { match *t { BINOP(OR) | OROR => true, _ => false } } - pub mod special_idents { use ast::Ident; - pub static underscore : Ident = Ident { name: 0, ctxt: 0}; + pub static underscore : Ident = Ident { name: 0, ctxt: 0}; // apparently unused? pub static anon : Ident = Ident { name: 1, ctxt: 0}; pub static invalid : Ident = Ident { name: 2, ctxt: 0}; // '' - pub static unary : Ident = Ident { name: 3, ctxt: 0}; - pub static not_fn : Ident = Ident { name: 4, ctxt: 0}; - pub static idx_fn : Ident = Ident { name: 5, ctxt: 0}; - pub static unary_minus_fn : Ident = Ident { name: 6, ctxt: 0}; + pub static unary : Ident = Ident { name: 3, ctxt: 0}; // apparently unused? + pub static not_fn : Ident = Ident { name: 4, ctxt: 0}; // apparently unused? + pub static idx_fn : Ident = Ident { name: 5, ctxt: 0}; // apparently unused? + pub static unary_minus_fn : Ident = Ident { name: 6, ctxt: 0}; // apparently unused? pub static clownshoes_extensions : Ident = Ident { name: 7, ctxt: 0}; pub static self_ : Ident = Ident { name: 8, ctxt: 0}; // 'self' /* for matcher NTs */ + // none of these appear to be used, but perhaps references to + // these are artificially fabricated by the macro system.... pub static item : Ident = Ident { name: 9, ctxt: 0}; pub static block : Ident = Ident { name: 10, ctxt: 0}; pub static stmt : Ident = Ident { name: 11, ctxt: 0}; @@ -337,7 +338,7 @@ pub mod special_idents { pub static tt : Ident = Ident { name: 17, ctxt: 0}; pub static matchers : Ident = Ident { name: 18, ctxt: 0}; - pub static str : Ident = Ident { name: 19, ctxt: 0}; // for the type + pub static str : Ident = Ident { name: 19, ctxt: 0}; // for the type // apparently unused? /* outside of libsyntax */ pub static arg : Ident = Ident { name: 20, ctxt: 0}; @@ -350,10 +351,32 @@ pub mod special_idents { pub static statik : Ident = Ident { name: 27, ctxt: 0}; pub static clownshoes_foreign_mod: Ident = Ident { name: 28, ctxt: 0}; pub static unnamed_field: Ident = Ident { name: 29, ctxt: 0}; - pub static c_abi: Ident = Ident { name: 30, ctxt: 0}; + pub static c_abi: Ident = Ident { name: 30, ctxt: 0}; // apparently unused? pub static type_self: Ident = Ident { name: 31, ctxt: 0}; // `Self` } +// here are the ones that actually occur in the source. Maybe the rest +// should be removed? +/* +special_idents::anon +special_idents::arg +special_idents::blk +special_idents::clownshoe_abi +special_idents::clownshoe_stack_shim +special_idents::clownshoes_extensions +special_idents::clownshoes_foreign_mod +special_idents::descrim +special_idents::invalid +special_idents::main +special_idents::matchers +special_idents::opaque +special_idents::self_ +special_idents::statik +special_idents::tt +special_idents::type_self +special_idents::unnamed_field +*/ + /** * Maps a token to a record specifying the corresponding binary * operator -- cgit 1.4.1-3-g733a5 From 09e6dda4f268e24c5d1f0804f5c1e57d1fcc158d Mon Sep 17 00:00:00 2001 From: John Clements Date: Wed, 3 Jul 2013 15:16:04 -0700 Subject: add temporarily unused ctxt field to mac_invoc_tt --- src/libsyntax/ast.rs | 2 +- src/libsyntax/ext/expand.rs | 6 +++--- src/libsyntax/fold.rs | 5 +++-- src/libsyntax/parse/parser.rs | 10 +++++----- src/libsyntax/print/pprust.rs | 4 ++-- 5 files changed, 14 insertions(+), 13 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index b3fb8859f65..c2f257643cd 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -659,7 +659,7 @@ pub type mac = Spanned; // There's only one flavor, now, so this could presumably be simplified. #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)] pub enum mac_ { - mac_invoc_tt(Path,~[token_tree]), // new macro-invocation + mac_invoc_tt(Path,~[token_tree],SyntaxContext), // new macro-invocation } pub type lit = Spanned; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d072f2a7812..5e68a75bffa 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -43,7 +43,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, ExprMac(ref mac) => { match (*mac).node { // Token-tree macros: - mac_invoc_tt(ref pth, ref tts) => { + mac_invoc_tt(ref pth, ref tts, ctxt) => { if (pth.segments.len() > 1u) { cx.span_fatal( pth.span, @@ -368,7 +368,7 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv, fld: @ast_fold) -> Option<@ast::item> { let (pth, tts) = match it.node { - item_mac(codemap::Spanned { node: mac_invoc_tt(ref pth, ref tts), _}) => { + item_mac(codemap::Spanned { node: mac_invoc_tt(ref pth, ref tts, ctxt), _}) => { (pth, (*tts).clone()) } _ => cx.span_bug(it.span, "invalid item macro invocation") @@ -471,7 +471,7 @@ pub fn expand_stmt(extsbox: @mut SyntaxEnv, let (mac, pth, tts, semi) = match *s { StmtMac(ref mac, semi) => { match mac.node { - mac_invoc_tt(ref pth, ref tts) => { + mac_invoc_tt(ref pth, ref tts, ctxt) => { ((*mac).clone(), pth, (*tts).clone(), semi) } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 85519bd2671..799ff855cbe 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -116,9 +116,10 @@ fn fold_arg_(a: arg, fld: @ast_fold) -> arg { fn fold_mac_(m: &mac, fld: @ast_fold) -> mac { Spanned { node: match m.node { - mac_invoc_tt(ref p,ref tts) => + mac_invoc_tt(ref p,ref tts,ctxt) => mac_invoc_tt(fld.fold_path(p), - fold_tts(*tts,|id|{fld.fold_ident(id)})) + fold_tts(*tts,|id|{fld.fold_ident(id)}), + ctxt) }, span: fld.new_span(m.span) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 62abe3850c9..8b11a25f13c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -21,7 +21,7 @@ use ast::{_mod, BiAdd, arg, Arm, Attribute, BindByRef, BindInfer}; use ast::{BiBitAnd, BiBitOr, BiBitXor, Block}; use ast::{BlockCheckMode, UnBox}; use ast::{Crate, CrateConfig, Decl, DeclItem}; -use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, enum_def, explicit_self}; +use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, enum_def, explicit_self}; use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock}; use ast::{ExprBreak, ExprCall, ExprCast, ExprDoBody}; @@ -1875,7 +1875,7 @@ impl Parser { |p| p.parse_token_tree()); let hi = self.span.hi; - return self.mk_mac_expr(lo, hi, mac_invoc_tt(pth, tts)); + return self.mk_mac_expr(lo, hi, mac_invoc_tt(pth, tts, EMPTY_CTXT)); } else if *self.token == token::LBRACE { // This might be a struct literal. if self.looking_at_record_literal() { @@ -3197,14 +3197,14 @@ impl Parser { if id == token::special_idents::invalid { return @spanned(lo, hi, StmtMac( - spanned(lo, hi, mac_invoc_tt(pth, tts)), false)); + spanned(lo, hi, mac_invoc_tt(pth, tts, EMPTY_CTXT)), false)); } else { // if it has a special ident, it's definitely an item return @spanned(lo, hi, StmtDecl( @spanned(lo, hi, DeclItem( self.mk_item( lo, hi, id /*id is good here*/, - item_mac(spanned(lo, hi, mac_invoc_tt(pth, tts))), + item_mac(spanned(lo, hi, mac_invoc_tt(pth, tts, EMPTY_CTXT))), inherited, ~[/*no attrs*/]))), self.get_id())); } @@ -4809,7 +4809,7 @@ impl Parser { _ => self.fatal("expected open delimiter") }; // single-variant-enum... : - let m = ast::mac_invoc_tt(pth, tts); + let m = ast::mac_invoc_tt(pth, tts, EMPTY_CTXT); let m: ast::mac = codemap::Spanned { node: m, span: mk_sp(self.span.lo, self.span.hi) }; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 32cf30fd3a0..4d464706d6f 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -619,7 +619,7 @@ pub fn print_item(s: @ps, item: &ast::item) { } bclose(s, item.span); } - ast::item_mac(codemap::Spanned { node: ast::mac_invoc_tt(ref pth, ref tts), + ast::item_mac(codemap::Spanned { node: ast::mac_invoc_tt(ref pth, ref tts, ctxt), _}) => { print_visibility(s, item.vis); print_path(s, pth, false); @@ -1021,7 +1021,7 @@ pub fn print_if(s: @ps, test: &ast::Expr, blk: &ast::Block, pub fn print_mac(s: @ps, m: &ast::mac) { match m.node { - ast::mac_invoc_tt(ref pth, ref tts) => { + ast::mac_invoc_tt(ref pth, ref tts, ctxt) => { print_path(s, pth, false); word(s.s, "!"); popen(s); -- cgit 1.4.1-3-g733a5 From 0954e66442a169be40f1e65de68a85d7e3dacf3a Mon Sep 17 00:00:00 2001 From: John Clements Date: Thu, 5 Sep 2013 14:15:00 -0700 Subject: uncomment mtwt_resolve calls --- src/librustc/middle/resolve.rs | 14 +++++++++---- src/libsyntax/ast_util.rs | 38 ++++++++++++++++++++---------------- src/libsyntax/ext/tt/macro_parser.rs | 4 ++-- src/libsyntax/parse/token.rs | 20 +++++++++++++++++++ 4 files changed, 53 insertions(+), 23 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 8c8dedeef32..292047d885d 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -23,7 +23,7 @@ use middle::pat_util::pat_bindings; 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, mtwt_resolve}; use syntax::ast_util::{path_to_ident, walk_pat, trait_method_to_ty_method}; use syntax::ast_util::{Privacy, Public, Private}; use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy}; @@ -4067,10 +4067,14 @@ impl Resolver { None, visitor); } + // build a map from pattern identifiers to binding-info's. + // this is done hygienically. This could arise for a macro + // that expands into an or-pattern where one 'x' was from the + // user and one 'x' came from the macro. pub fn binding_mode_map(@mut self, pat: @Pat) -> BindingMap { let mut result = HashMap::new(); do pat_bindings(self.def_map, pat) |binding_mode, _id, sp, path| { - let name = path_to_ident(path).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}); @@ -4078,6 +4082,8 @@ impl Resolver { return result; } + // check that all of the arms in an or-pattern have exactly the + // same set of bindings, with the same binding modes for each. pub fn check_consistent_bindings(@mut self, arm: &Arm) { if arm.pats.len() == 0 { return; } let map_0 = self.binding_mode_map(arm.pats[0]); @@ -4293,7 +4299,7 @@ impl Resolver { // what you want). let ident = path.segments[0].identifier; - let renamed = ident.name; // mtwt_resolve(ident); + let renamed = mtwt_resolve(ident); match self.resolve_bare_identifier_pattern(ident) { FoundStructOrEnumVariant(def) @@ -4833,7 +4839,7 @@ impl Resolver { let search_result; match namespace { ValueNS => { - let renamed = ident.name; // mtwt_resolve(ident); + let renamed = mtwt_resolve(ident); search_result = self.search_ribs(self.value_ribs, renamed, span, DontAllowCapturingSelf); diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index dfac782929d..321ac9428ea 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -991,20 +991,19 @@ pub fn getLast(arr: &~[Mrk]) -> uint { pub fn path_name_eq(a : &ast::Path, b : &ast::Path) -> bool { (a.span == b.span) && (a.global == b.global) - // NOTE: ident->name in lifetimes! - && (a.rp == b.rp) - // NOTE: can a type contain an ident? - && (a.types == b.types) - && (idents_name_eq(a.idents, b.idents)) + && (segments_name_eq(a.segments, b.segments)) } -// are two arrays of idents equal when compared unhygienically? -pub fn idents_name_eq(a : &[ast::ident], b : &[ast::ident]) -> bool { +// are two arrays of segments equal when compared unhygienically? +pub fn segments_name_eq(a : &[ast::PathSegment], b : &[ast::PathSegment]) -> bool { if (a.len() != b.len()) { false } else { - for a.iter().enumerate().advance |(idx,id)|{ - if (id.name != b[idx].name) { + for (idx,seg) in a.iter().enumerate() { + if (seg.identifier.name != b[idx].identifier.name) + // ident -> name problems in lifetime comparison? + || (seg.lifetime != b[idx].lifetime) + || (seg.types != b[idx].types) { return false; } } @@ -1017,16 +1016,21 @@ mod test { use ast::*; use super::*; use std::io; + use opt_vec; + + fn ident_to_segment(id : &ident) -> PathSegment { + PathSegment{identifier:id.clone(), lifetime: None, types: opt_vec::Empty} + } #[test] fn idents_name_eq_test() { - assert!(idents_name_eq(~[ident{name:3,ctxt:4}, - ident{name:78,ctxt:82}], - ~[ident{name:3,ctxt:104}, - ident{name:78,ctxt:182}])); - assert!(!idents_name_eq(~[ident{name:3,ctxt:4}, - ident{name:78,ctxt:82}], - ~[ident{name:3,ctxt:104}, - ident{name:77,ctxt:182}])); + assert!(segments_name_eq([ident{name:3,ctxt:4}, + ident{name:78,ctxt:82}].map(ident_to_segment), + [ident{name:3,ctxt:104}, + ident{name:78,ctxt:182}].map(ident_to_segment))); + assert!(!segments_name_eq([ident{name:3,ctxt:4}, + ident{name:78,ctxt:82}].map(ident_to_segment), + [ident{name:3,ctxt:104}, + ident{name:77,ctxt:182}].map(ident_to_segment))); } #[test] fn xorpush_test () { diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index ee7750bfd57..aa4183837e3 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -355,8 +355,8 @@ pub fn parse( match_nonterminal(_,_,_) => { bb_eis.push(ei) } match_tok(ref t) => { let mut ei_t = ei.clone(); - if (token_name_eq(t,&tok)) { - //if (token::mtwt_token_eq(t,&tok)) { + //if (token_name_eq(t,&tok)) { + if (token::mtwt_token_eq(t,&tok)) { ei_t.idx += 1; next_eis.push(ei_t); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 311d498eec2..39e0f85920c 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -720,6 +720,15 @@ 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,_)) => + ast_util::mtwt_resolve(id1) == ast_util::mtwt_resolve(id2), + _ => *t1 == *t2 + } +} + + #[cfg(test)] mod test { use super::*; @@ -728,6 +737,17 @@ mod test { use ast; use ast_util; + fn mark_ident(id : ast::ident, m : ast::Mrk) -> ast::ident { + ast::ident{name:id.name,ctxt:ast_util::new_mark(m,id.ctxt)} + } + + #[test] fn mtwt_token_eq_test() { + assert!(mtwt_token_eq(>,>)); + let a = str_to_ident("bac"); + let a1 = mark_ident(a,92); + assert!(mtwt_token_eq(&IDENT(a,true),&IDENT(a1,false))); + } + #[test] fn str_ptr_eq_tests(){ let a = @"abc"; -- cgit 1.4.1-3-g733a5 From ec0a64def54e0b381ede187bfe199b7620b56c45 Mon Sep 17 00:00:00 2001 From: John Clements Date: Wed, 10 Jul 2013 16:40:09 -0700 Subject: memoization for resolve --- src/libsyntax/ast_util.rs | 129 ++++++++++++++++++++++++++++++------------- src/libsyntax/parse/token.rs | 4 +- 2 files changed, 93 insertions(+), 40 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 321ac9428ea..e0d58c14a9a 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -915,30 +915,63 @@ fn idx_push(vec: &mut ~[T], val: T) -> uint { /// Resolve a syntax object to a name, per MTWT. pub fn mtwt_resolve(id : Ident) -> Name { - resolve_internal(id, get_sctable()) + resolve_internal(id, get_sctable(), get_resolve_table()) +} + +// FIXME #4536: 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() -> @mut ResolveTable { + static resolve_table_key: local_data::Key<@@mut ResolveTable> = &local_data::Key; + match local_data::get(resolve_table_key, |k| k.map(|&k| *k)) { + None => { + let new_table = @@mut 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 #4536 : currently pub to allow testing -pub fn resolve_internal(id : Ident, table : &mut SCTable) -> Name { - match table.table[id.ctxt] { - EmptyCtxt => id.name, - // ignore marks here: - Mark(_,subctxt) => resolve_internal(Ident{name:id.name, ctxt: subctxt},table), - // do the rename if necessary: - Rename(Ident{name,ctxt},toname,subctxt) => { - // this could be cached or computed eagerly: - let resolvedfrom = resolve_internal(Ident{name:name,ctxt:ctxt},table); - let resolvedthis = resolve_internal(Ident{name:id.name,ctxt:subctxt},table); - if ((resolvedthis == resolvedfrom) - && (marksof(ctxt,resolvedthis,table) - == marksof(subctxt,resolvedthis,table))) { - toname - } else { - resolvedthis - } +pub fn resolve_internal(id : Ident, + table : &mut SCTable, + resolve_table : &mut ResolveTable) -> Name { + let key = (id.name,id.ctxt); + match resolve_table.contains_key(&key) { + false => { + let resolved = { + match table.table[id.ctxt] { + 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()) } - IllegalCtxt() => fail!(~"expected resolvable context, got IllegalCtxt") } } @@ -1017,20 +1050,21 @@ mod test { use super::*; use std::io; use opt_vec; + use std::hash::HashMap; - fn ident_to_segment(id : &ident) -> PathSegment { + fn ident_to_segment(id : &Ident) -> PathSegment { PathSegment{identifier:id.clone(), lifetime: None, types: opt_vec::Empty} } #[test] fn idents_name_eq_test() { - assert!(segments_name_eq([ident{name:3,ctxt:4}, - ident{name:78,ctxt:82}].map(ident_to_segment), - [ident{name:3,ctxt:104}, - ident{name:78,ctxt:182}].map(ident_to_segment))); - assert!(!segments_name_eq([ident{name:3,ctxt:4}, - ident{name:78,ctxt:82}].map(ident_to_segment), - [ident{name:3,ctxt:104}, - ident{name:77,ctxt:182}].map(ident_to_segment))); + assert!(segments_name_eq([Ident{name:3,ctxt:4}, + Ident{name:78,ctxt:82}].map(ident_to_segment), + [Ident{name:3,ctxt:104}, + Ident{name:78,ctxt:182}].map(ident_to_segment))); + assert!(!segments_name_eq([Ident{name:3,ctxt:4}, + Ident{name:78,ctxt:82}].map(ident_to_segment), + [Ident{name:3,ctxt:104}, + Ident{name:77,ctxt:182}].map(ident_to_segment))); } #[test] fn xorpush_test () { @@ -1162,29 +1196,30 @@ mod test { #[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),a); + assert_eq!(resolve_internal(id(a,EMPTY_CTXT),&mut t, &mut rt),a); // - simple ignored marks { let sc = unfold_marks(~[1,2,3],EMPTY_CTXT,&mut t); - assert_eq!(resolve_internal(id(a,sc),&mut t),a);} + assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);} // - orthogonal rename where names don't match { let sc = unfold_test_sc(~[R(id(50,EMPTY_CTXT),51),M(12)],EMPTY_CTXT,&mut t); - assert_eq!(resolve_internal(id(a,sc),&mut t),a);} + 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(~[R(id(a,sc1),50), M(1), M(2)], EMPTY_CTXT,&mut t); - assert_eq!(resolve_internal(id(a,sc),&mut t), a);} + assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), a);} // - rename where names and marks match { let sc1 = unfold_test_sc(~[M(1),M(2)],EMPTY_CTXT,&mut t); let sc = unfold_test_sc(~[R(id(a,sc1),50),M(1),M(2)],EMPTY_CTXT,&mut t); - assert_eq!(resolve_internal(id(a,sc),&mut t), 50); } + 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(~[M(1),M(2)],EMPTY_CTXT,&mut t); let sc = unfold_test_sc(~[R(id(a,sc1),50)],sc1,&mut t); - assert_eq!(resolve_internal(id(a,sc),&mut t), 50); } + 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: @@ -1192,22 +1227,28 @@ mod test { { let sc = unfold_test_sc(~[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), 51); } + 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),51); + 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),51); + 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(~[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),50);} + 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); @@ -1217,4 +1258,16 @@ mod test { // 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); + } + } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 39e0f85920c..aa26feec28c 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -737,8 +737,8 @@ mod test { use ast; use ast_util; - fn mark_ident(id : ast::ident, m : ast::Mrk) -> ast::ident { - ast::ident{name:id.name,ctxt:ast_util::new_mark(m,id.ctxt)} + fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident { + ast::Ident{name:id.name,ctxt:ast_util::new_mark(m,id.ctxt)} } #[test] fn mtwt_token_eq_test() { -- cgit 1.4.1-3-g733a5 From dbf4e19ea5946723acc1495ac82ed73f784821dd Mon Sep 17 00:00:00 2001 From: John Clements Date: Thu, 11 Jul 2013 23:07:34 -0700 Subject: remove unneeded imports, clean up unused var warnings --- src/libsyntax/ext/base.rs | 4 ++-- src/libsyntax/ext/expand.rs | 14 +++++++------- src/libsyntax/parse/token.rs | 7 ++----- src/libsyntax/print/pprust.rs | 6 ++++-- 4 files changed, 15 insertions(+), 16 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 24f70bfeaa8..afb8802968c 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -135,13 +135,13 @@ pub fn syntax_expander_table() -> SyntaxEnv { // utility function to simplify creating NormalTT syntax extensions // that ignore their contexts fn builtin_normal_tt_no_ctxt(f: SyntaxExpanderTTFunNoCtxt) -> @Transformer { - let wrapped_expander : SyntaxExpanderTTFun = |a,b,c,d|{f(a,b,c)}; + let wrapped_expander : SyntaxExpanderTTFun = |a,b,c,_d|{f(a,b,c)}; @SE(NormalTT(wrapped_expander, None)) } // utility function to simplify creating IdentTT syntax extensions // that ignore their contexts fn builtin_item_tt_no_ctxt(f: SyntaxExpanderTTItemFunNoCtxt) -> @Transformer { - let wrapped_expander : SyntaxExpanderTTItemFun = |a,b,c,d,e|{f(a,b,c,d)}; + let wrapped_expander : SyntaxExpanderTTItemFun = |a,b,c,d,_e|{f(a,b,c,d)}; @SE(IdentTT(wrapped_expander, None)) } let mut syntax_expanders = HashMap::new(); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8ee045ba8c1..120f75406c7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{Block, Crate, NodeId, DeclLocal, EMPTY_CTXT, Expr_, ExprMac, SyntaxContext}; +use ast::{Block, Crate, NodeId, DeclLocal, Expr_, ExprMac, SyntaxContext}; use ast::{Local, Ident, mac_invoc_tt}; use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi}; -use ast::{ILLEGAL_CTXT, SCTable, token_tree}; +use ast::{token_tree}; use ast; -use ast_util::{new_rename, new_mark, mtwt_resolve}; +use ast_util::{new_rename, new_mark}; use attr; use attr::AttrMetaMethods; use codemap; @@ -585,7 +585,7 @@ fn expand_non_macro_stmt (exts: SyntaxEnv, let new_name = fresh_name(ident); new_pending_renames.push((*ident,new_name)); } - let mut rename_fld = renames_to_fold(new_pending_renames); + let rename_fld = renames_to_fold(new_pending_renames); // rewrite the pattern using the new names (the old ones // have already been applied): let rewritten_pat = rename_fld.fold_pat(expanded_pat); @@ -906,7 +906,7 @@ pub fn expand_block(extsbox: @mut SyntaxEnv, _cx: @ExtCtxt, blk: &Block, fld: @ast_fold, - orig: @fn(&Block, @ast_fold) -> Block) + _orig: @fn(&Block, @ast_fold) -> Block) -> Block { // see note below about treatment of exts table with_exts_frame!(extsbox,false, @@ -917,7 +917,7 @@ pub fn expand_block(extsbox: @mut SyntaxEnv, pub fn expand_block_elts(exts: SyntaxEnv, b: &Block, fld: @ast_fold) -> Block { let block_info = get_block_info(exts); let pending_renames = block_info.pending_renames; - let mut rename_fld = renames_to_fold(pending_renames); + let rename_fld = renames_to_fold(pending_renames); let new_view_items = b.view_items.map(|x| fld.fold_view_item(x)); let mut new_stmts = ~[]; for x in b.stmts.iter() { @@ -1456,7 +1456,7 @@ impl CtxtFn for Marker { pub struct Repainter { ctxt : SyntaxContext } impl CtxtFn for Repainter { - fn f(&self, ctxt : ast::SyntaxContext) -> ast::SyntaxContext { + fn f(&self, _ctxt : ast::SyntaxContext) -> ast::SyntaxContext { self.ctxt } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index aa26feec28c..d0372191084 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -17,10 +17,7 @@ use util::interner; use std::cast; use std::char; -use std::cmp::Equiv; use std::local_data; -use std::rand; -use std::rand::RngUtil; #[deriving(Clone, Encodable, Decodable, Eq, IterBytes)] pub enum binop { @@ -565,8 +562,8 @@ pub fn str_ptr_eq(a : @str, b : @str) -> bool { let q : uint = cast::transmute(b); let result = p == q; // got to transmute them back, to make sure the ref count is correct: - let junk1 : @str = cast::transmute(p); - let junk2 : @str = cast::transmute(q); + let _junk1 : @str = cast::transmute(p); + let _junk2 : @str = cast::transmute(q); result } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 4d464706d6f..8871413a46d 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -619,7 +619,8 @@ pub fn print_item(s: @ps, item: &ast::item) { } bclose(s, item.span); } - ast::item_mac(codemap::Spanned { node: ast::mac_invoc_tt(ref pth, ref tts, ctxt), + // I think it's reasonable to hide the context here: + ast::item_mac(codemap::Spanned { node: ast::mac_invoc_tt(ref pth, ref tts, _), _}) => { print_visibility(s, item.vis); print_path(s, pth, false); @@ -1021,7 +1022,8 @@ pub fn print_if(s: @ps, test: &ast::Expr, blk: &ast::Block, pub fn print_mac(s: @ps, m: &ast::mac) { match m.node { - ast::mac_invoc_tt(ref pth, ref tts, ctxt) => { + // I think it's reasonable to hide the ctxt here: + ast::mac_invoc_tt(ref pth, ref tts, _) => { print_path(s, pth, false); word(s.s, "!"); popen(s); -- cgit 1.4.1-3-g733a5 From 6c294ba538b987d15fba10f7237ca52654fde64c Mon Sep 17 00:00:00 2001 From: John Clements Date: Thu, 5 Sep 2013 09:29:31 -0700 Subject: add test case, cleanup --- src/libsyntax/ext/expand.rs | 29 ++++++++++++++--------------- src/libsyntax/parse/token.rs | 2 -- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src/libsyntax/parse') diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index ad1420f35cd..9c491c7dfce 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -14,7 +14,6 @@ use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi}; use ast::{token_tree}; use ast; use ast_util::{mtwt_outer_mark, new_rename, new_mark}; -use ast_util; use attr; use attr::AttrMetaMethods; use codemap; @@ -1549,6 +1548,7 @@ mod test { use ast; use ast::{Attribute_, AttrOuter, MetaWord, EMPTY_CTXT}; use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve, new_rename}; + use ast_util; use codemap; use codemap::Spanned; use fold; @@ -1557,8 +1557,7 @@ mod test { use parse::token; use print::pprust; use std; - use std::vec; - use util::parser_testing::{string_to_crate, string_to_crate_and_sess, string_to_item}; + use util::parser_testing::{string_to_crate, string_to_crate_and_sess}; use util::parser_testing::{string_to_pat, string_to_tts, strs_to_idents}; use visit; @@ -1779,9 +1778,11 @@ mod test { macro_rules! user(($x:ident) => ({letty!($x); $x})) fn main() -> int {user!(z)}", ~[~[0]], false), - // can't believe I missed this one : a macro def that refers to a local var: - ("fn main() {let x = 19; macro_rules! getx(()=>(x)); getx!();}", - ~[~[0]], true) + // FIXME #8062: this test exposes a *potential* bug; our system does + // not behave exactly like MTWT, but I haven't thought of a way that + // this could cause a bug in Rust, yet. + // ("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}", + // ~[~[0]], true) // FIXME #6994: the next string exposes the bug referred to in issue 6994, so I'm // commenting it out. // the z flows into and out of two macros (g & f) along one path, and one @@ -1800,6 +1801,7 @@ mod test { // run one of the renaming tests fn run_renaming_test(t : &renaming_test) { + let invalid_name = token::special_idents::invalid.name; let (teststr, bound_connections, bound_ident_check) = match *t { (ref str,ref conns, bic) => (str.to_managed(), conns.clone(), bic) }; @@ -1814,7 +1816,7 @@ mod test { assert_eq!(bindings.len(),bound_connections.len()); for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() { let binding_name = mtwt_resolve(bindings[binding_idx]); - let binding_marks = mtwt_marksof(bindings[binding_idx].ctxt,binding_name); + let binding_marks = mtwt_marksof(bindings[binding_idx].ctxt,invalid_name); // shouldmatch can't name varrefs that don't exist: assert!((shouldmatch.len() == 0) || (varrefs.len() > *shouldmatch.iter().max().unwrap())); @@ -1825,20 +1827,17 @@ mod test { assert_eq!(varref.segments.len(),1); let varref_name = mtwt_resolve(varref.segments[0].identifier); let varref_marks = mtwt_marksof(varref.segments[0].identifier.ctxt, - binding_name); + invalid_name); if (!(varref_name==binding_name)){ std::io::println("uh oh, should match but doesn't:"); std::io::println(fmt!("varref: %?",varref)); std::io::println(fmt!("binding: %?", bindings[binding_idx])); - let table = get_sctable(); - std::io::println("SC table:"); - for (idx,val) in table.table.iter().enumerate() { - std::io::println(fmt!("%4u : %?",idx,val)); - } + ast_util::display_sctable(get_sctable()); } assert_eq!(varref_name,binding_name); if (bound_ident_check) { - // we need to check the marks, too: + // we're checking bound-identifier=?, and the marks + // should be the same, too: assert_eq!(varref_marks,binding_marks.clone()); } } else { @@ -1849,7 +1848,7 @@ mod test { std::io::println("uh oh, matches but shouldn't:"); std::io::println(fmt!("varref: %?",varref)); std::io::println(fmt!("binding: %?", bindings[binding_idx])); - std::io::println(fmt!("sc_table: %?",get_sctable())); + ast_util::display_sctable(get_sctable()); } assert!(!fail); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index d0372191084..6b3a95a14f8 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -729,8 +729,6 @@ pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool { #[cfg(test)] mod test { use super::*; - use std::io; - use std::managed; use ast; use ast_util; -- cgit 1.4.1-3-g733a5