From 211012bc4bdf032f34d9291aaf314b2984d1a5c7 Mon Sep 17 00:00:00 2001 From: Edward Wang Date: Tue, 25 Feb 2014 04:47:19 +0800 Subject: [PATCH] Add namespace support for rename operation Maintains two renaming tables, one for regular idents and one for lifetime idents (a loop label is of lifetime kind) so that let bindings and loop labels with same name won't clash. Closes #12512. --- src/librustc/middle/resolve.rs | 10 ++--- src/libsyntax/ast.rs | 11 +++++ src/libsyntax/ext/base.rs | 8 +++- src/libsyntax/ext/expand.rs | 62 ++++++++++++++++------------ src/libsyntax/ext/mtwt.rs | 71 +++++++++++++++++++++----------- src/libsyntax/fold.rs | 36 ++++++++-------- src/libsyntax/parse/token.rs | 8 ++-- src/test/run-pass/issue-12512.rs | 20 +++++++++ src/test/run-pass/loop-labels.rs | 39 ++++++++++++++++++ 9 files changed, 188 insertions(+), 77 deletions(-) create mode 100644 src/test/run-pass/issue-12512.rs create mode 100644 src/test/run-pass/loop-labels.rs diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 0d3586266e869..0dafc412de3e1 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -4177,7 +4177,7 @@ impl Resolver { fn binding_mode_map(&mut self, pat: @Pat) -> BindingMap { let mut result = HashMap::new(); pat_bindings(self.def_map, pat, |binding_mode, _id, sp, path| { - let name = mtwt::resolve(path_to_ident(path)); + let name = mtwt::resolve(path_to_ident(path), PlainIdent); result.insert(name, binding_info {span: sp, binding_mode: binding_mode}); @@ -4412,7 +4412,7 @@ impl Resolver { // what you want). let ident = path.segments.get(0).identifier; - let renamed = mtwt::resolve(ident); + let renamed = mtwt::resolve(ident, PlainIdent); match self.resolve_bare_identifier_pattern(ident) { FoundStructOrEnumVariant(def, lp) @@ -4966,7 +4966,7 @@ impl Resolver { let search_result; match namespace { ValueNS => { - let renamed = mtwt::resolve(ident); + let renamed = mtwt::resolve(ident, PlainIdent); let mut value_ribs = self.value_ribs.borrow_mut(); search_result = self.search_ribs(value_ribs.get(), renamed, @@ -5214,7 +5214,7 @@ impl Resolver { let rib = label_ribs.get()[label_ribs.get().len() - 1]; let mut bindings = rib.bindings.borrow_mut(); - let renamed = mtwt::resolve(label); + let renamed = mtwt::resolve(label, LifetimeIdent); bindings.get().insert(renamed, def_like); } @@ -5226,7 +5226,7 @@ impl Resolver { ExprBreak(Some(label)) | ExprAgain(Some(label)) => { let mut label_ribs = self.label_ribs.borrow_mut(); - let renamed = mtwt::resolve(label); + let renamed = mtwt::resolve(label, LifetimeIdent); match self.search_ribs(label_ribs.get(), renamed, expr.span) { None => self.resolve_error(expr.span, diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 544d9f2d66935..c0e8c199e175c 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -75,6 +75,17 @@ impl Eq for Ident { } } +// This can not be more fine-grained because of loop label. It starts +// as a `token::LIFETIME` then becomes part of `ExprLoop`, `ExprForLoop`, +// `ExprBreak` or `ExprAgain` and is represented as an `ast::Ident`, not +// an `ast::Lifetime` as others do. Even so, it is still considered to +// be one of LifetimeIdent. +#[deriving(Clone, Eq, Encodable, Decodable, Hash)] +pub enum IdentKind { + PlainIdent, + LifetimeIdent +} + /// A SyntaxContext represents a chain of macro-expandings /// and renamings. Each macro expansion corresponds to /// a fresh uint diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 79068d4046965..44e364ff965ea 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -148,20 +148,24 @@ pub struct BlockInfo { // should macros escape from this scope? macros_escape: bool, // what are the pending renames? - pending_renames: RenameList, + pending_renames: RenameLists, } impl BlockInfo { pub fn new() -> BlockInfo { BlockInfo { macros_escape: false, - pending_renames: Vec::new(), + pending_renames: RenameLists { plain: Vec::new(), lifetime: Vec::new() }, } } } // a list of ident->name renamings pub type RenameList = Vec<(ast::Ident, Name)>; +pub struct RenameLists { + plain: RenameList, + lifetime: RenameList +} // The base map of methods for expanding syntax extension // AST nodes into full ASTs diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8b23de235b804..5d9adcba3a1c7 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -11,7 +11,7 @@ use ast::{P, Block, Crate, DeclLocal, ExprMac}; use ast::{Local, Ident, MacInvocTT}; use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi}; -use ast::TokenTree; +use ast::{IdentKind, LifetimeIdent, PlainIdent, TokenTree}; use ast; use ext::mtwt; use ext::build::AstBuilder; @@ -228,15 +228,15 @@ fn expand_loop_block(loop_block: P, // syntax context otherwise an unrelated `break` or `continue` in // the same context will pick that up in the deferred renaming pass // and be renamed incorrectly. - let mut rename_list = vec!(rename); + let mut rename_list = RenameLists { plain: Vec::new(), lifetime: vec!(rename)}; let mut rename_fld = renames_to_fold(&mut rename_list); - let renamed_ident = rename_fld.fold_ident(label); + let renamed_ident = rename_fld.fold_ident(label, LifetimeIdent); // The rename *must* be added to the enclosed syntax context for // `break` or `continue` to pick up because by definition they are // in a block enclosed by loop head. fld.extsbox.push_frame(); - fld.extsbox.info().pending_renames.push(rename); + fld.extsbox.info().pending_renames.lifetime.push(rename); let expanded_block = expand_block_elts(loop_block, fld); fld.extsbox.pop_frame(); @@ -627,10 +627,11 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander) let mut name_finder = new_name_finder(Vec::new()); name_finder.visit_pat(expanded_pat,()); // generate fresh names, push them to a new pending list - let mut new_pending_renames = Vec::new(); + let mut new_pending_renames = RenameLists { plain: Vec::new(), + lifetime: Vec::new() }; for ident in name_finder.ident_accumulator.iter() { let new_name = fresh_name(ident); - new_pending_renames.push((*ident,new_name)); + new_pending_renames.plain.push((*ident,new_name)); } let rewritten_pat = { let mut rename_fld = @@ -640,7 +641,8 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander) rename_fld.fold_pat(expanded_pat) }; // add them to the existing pending renames: - fld.extsbox.info().pending_renames.push_all_move(new_pending_renames); + fld.extsbox.info().pending_renames + .plain.push_all_move(new_pending_renames.plain); // also, don't forget to expand the init: let new_init_opt = init.map(|e| fld.fold_expr(e)); let rewritten_local = @@ -760,13 +762,17 @@ pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P { } pub struct IdentRenamer<'a> { - renames: &'a mut RenameList, + renames: &'a mut RenameLists, } 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) + fn fold_ident(&mut self, id: Ident, ns: IdentKind) -> Ident { + let rs = match ns { + PlainIdent => &self.renames.plain, + LifetimeIdent => &self.renames.lifetime + }; + let new_ctxt = rs.iter().fold(id.ctxt, |ctxt, &(from, to)| { + mtwt::new_rename(from, to, ctxt, ns) }); Ident { name: id.name, @@ -777,7 +783,7 @@ impl<'a> Folder for IdentRenamer<'a> { // given a mutable list of renames, return a tree-folder that applies those // renames. -pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> { +pub fn renames_to_fold<'a>(renames: &'a mut RenameLists) -> IdentRenamer<'a> { IdentRenamer { renames: renames, } @@ -849,10 +855,10 @@ pub fn expand_crate(parse_sess: @parse::ParseSess, struct Marker { mark: Mrk } impl Folder for Marker { - fn fold_ident(&mut self, id: Ident) -> Ident { + fn fold_ident(&mut self, id: Ident, ns: IdentKind) -> Ident { ast::Ident { name: id.name, - ctxt: mtwt::new_mark(self.mark, id.ctxt) + ctxt: mtwt::new_mark(self.mark, id.ctxt, ns) } } fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac { @@ -860,7 +866,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::new_mark(self.mark, ctxt, PlainIdent)) } }; Spanned { @@ -1148,8 +1154,9 @@ mod test { // must be one check clause for each binding: assert_eq!(bindings.len(),bound_connections.len()); for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() { - let binding_name = mtwt::resolve(*bindings.get(binding_idx)); - let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name); + let binding_name = mtwt::resolve(*bindings.get(binding_idx), ast::PlainIdent); + let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, + invalid_name, ast::PlainIdent); // shouldmatch can't name varrefs that don't exist: assert!((shouldmatch.len() == 0) || (varrefs.len() > *shouldmatch.iter().max().unwrap())); @@ -1160,17 +1167,18 @@ mod test { assert_eq!(varref.segments.len(),1); let varref_name = mtwt::resolve(varref.segments .get(0) - .identifier); + .identifier, + ast::PlainIdent); let varref_marks = mtwt::marksof(varref.segments .get(0) .identifier .ctxt, - invalid_name); + invalid_name, ast::PlainIdent); if !(varref_name==binding_name) { println!("uh oh, should match but doesn't:"); println!("varref: {:?}",varref); println!("binding: {:?}", *bindings.get(binding_idx)); - mtwt::with_sctable(|x| mtwt::display_sctable(x)); + mtwt::with_sctable(ast::PlainIdent, |x| mtwt::display_sctable(x)); } assert_eq!(varref_name,binding_name); if bound_ident_check { @@ -1180,7 +1188,7 @@ mod test { } } else { let fail = (varref.segments.len() == 1) - && (mtwt::resolve(varref.segments.get(0).identifier) + && (mtwt::resolve(varref.segments.get(0).identifier, ast::PlainIdent) == binding_name); // temp debugging: if fail { @@ -1197,7 +1205,7 @@ mod test { varref.segments.get(0).identifier.name, string.get()); println!("binding: {:?}", *bindings.get(binding_idx)); - mtwt::with_sctable(|x| mtwt::display_sctable(x)); + mtwt::with_sctable(ast::PlainIdent, |x| mtwt::display_sctable(x)); } assert!(!fail); } @@ -1227,7 +1235,7 @@ foo_module!() [b] => b, _ => fail!("expected just one binding for ext_cx") }; - let resolved_binding = mtwt::resolve(*cxbind); + let resolved_binding = mtwt::resolve(*cxbind, ast::PlainIdent); // find all the xx varrefs: let mut path_finder = new_path_finder(Vec::new()); visit::walk_crate(&mut path_finder, &cr, ()); @@ -1238,17 +1246,17 @@ foo_module!() p.segments.len() == 1 && "xx" == token::get_ident(p.segments.get(0).identifier).get() }).enumerate() { - if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding { + if mtwt::resolve(v.segments.get(0).identifier, ast::PlainIdent) != resolved_binding { println!("uh oh, xx binding didn't match xx varref:"); println!("this is xx varref \\# {:?}",idx); println!("binding: {:?}",cxbind); println!("resolves to: {:?}",resolved_binding); println!("varref: {:?}",v.segments.get(0).identifier); println!("resolves to: {:?}", - mtwt::resolve(v.segments.get(0).identifier)); - mtwt::with_sctable(|x| mtwt::display_sctable(x)); + mtwt::resolve(v.segments.get(0).identifier, ast::PlainIdent)); + mtwt::with_sctable(ast::PlainIdent, |x| mtwt::display_sctable(x)); } - assert_eq!(mtwt::resolve(v.segments.get(0).identifier), + assert_eq!(mtwt::resolve(v.segments.get(0).identifier, ast::PlainIdent), resolved_binding); }; } diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs index b0ed215f3e103..df3e312368d74 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/mtwt.rs @@ -15,7 +15,8 @@ //! and definition contexts*. J. Funct. Program. 22, 2 (March 2012), 181-216. //! DOI=10.1017/S0956796812000093 http://dx.doi.org/10.1017/S0956796812000093 -use ast::{Ident, Mrk, Name, SyntaxContext}; +use ast::{Ident, IdentKind, PlainIdent, LifetimeIdent}; +use ast::{Mrk, Name, SyntaxContext}; use std::cell::RefCell; use std::local_data; @@ -57,8 +58,8 @@ pub enum SyntaxContext_ { } /// 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 new_mark(m: Mrk, tail: SyntaxContext, ns: IdentKind) -> SyntaxContext { + with_sctable(ns, |table| new_mark_internal(m, tail, table)) } // Extend a syntax context with a given mark and table @@ -73,8 +74,9 @@ fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable) -> SyntaxCont /// 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)) + tail: SyntaxContext, + ns: IdentKind) -> SyntaxContext { + with_sctable(ns, |table| new_rename_internal(id, to, tail, table)) } // Extend a syntax context with a given rename and sctable @@ -90,20 +92,32 @@ fn new_rename_internal(id: Ident, *rename_memo.get().find_or_insert_with(key, new_ctxt) } +struct SCTables { + plain: SCTable, + lifetime: SCTable, +} + /// 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) +pub fn with_sctable(ns: IdentKind, + op: |&SCTable| -> T) -> T { + local_data_key!(sctable_key: Rc) local_data::get(sctable_key, |opt_ts| { - let table = match opt_ts { + let tables = match opt_ts { None => { - let ts = Rc::new(new_sctable_internal()); + let ts = Rc::new(SCTables { + plain: new_sctable_internal(), + lifetime: new_sctable_internal(), + }); local_data::set(sctable_key, ts.clone()); ts } Some(ts) => ts.clone() }; - op(table.borrow()) + match ns { + PlainIdent => op(&tables.borrow().plain), + LifetimeIdent => op(&tables.borrow().lifetime) + } }) } @@ -134,31 +148,42 @@ fn idx_push(vec: &mut Vec , val: T) -> u32 { } /// Resolve a syntax object to a name, per MTWT. -pub fn resolve(id: Ident) -> Name { - with_sctable(|sctable| { - with_resolve_table_mut(|resolve_table| { +pub fn resolve(id: Ident, ns: IdentKind) -> Name { + with_sctable(ns, |sctable| { + with_resolve_table_mut(ns, |resolve_table| { resolve_internal(id, sctable, resolve_table) }) }) } type ResolveTable = HashMap<(Name,SyntaxContext),Name>; +struct ResolveTables { + plain: RefCell, + lifetime: RefCell, +} // okay, I admit, putting this in TLS is not so nice: // fetch the SCTable from TLS, create one if it doesn't yet exist. -fn with_resolve_table_mut(op: |&mut ResolveTable| -> T) -> T { - local_data_key!(resolve_table_key: Rc>) +fn with_resolve_table_mut(ns: IdentKind, + op: |&mut ResolveTable| -> T) -> T { + local_data_key!(resolve_table_key: Rc) local_data::get(resolve_table_key, |opt_ts| { - let table = match opt_ts { + let tables = match opt_ts { None => { - let ts = Rc::new(RefCell::new(HashMap::new())); + let ts = Rc::new(ResolveTables { + plain: RefCell::new(HashMap::new()), + lifetime: RefCell::new(HashMap::new()), + }); local_data::set(resolve_table_key, ts.clone()); ts } Some(ts) => ts.clone() }; - op(table.borrow().borrow_mut().get()) + match ns { + PlainIdent => op(tables.borrow().plain.borrow_mut().get()), + LifetimeIdent => op(tables.borrow().lifetime.borrow_mut().get()) + } }) } @@ -206,8 +231,8 @@ fn resolve_internal(id: Ident, } /// Compute the marks associated with a syntax context. -pub fn marksof(ctxt: SyntaxContext, stopname: Name) -> Vec { - with_sctable(|table| marksof_internal(ctxt, stopname, table)) +pub fn marksof(ctxt: SyntaxContext, stopname: Name, ns: IdentKind) -> Vec { + with_sctable(ns, |table| marksof_internal(ctxt, stopname, table)) } // the internal function for computing marks @@ -247,8 +272,8 @@ fn marksof_internal(ctxt: SyntaxContext, /// Return the outer mark for a context with a mark at the outside. /// FAILS when outside is not a mark. -pub fn outer_mark(ctxt: SyntaxContext) -> Mrk { - with_sctable(|sctable| { +pub fn outer_mark(ctxt: SyntaxContext, ns: IdentKind) -> Mrk { + with_sctable(ns, |sctable| { match *sctable.table.borrow().get().get(ctxt as uint) { Mark(mrk, _) => mrk, _ => fail!("can't retrieve outer mark when outside is not a mark") @@ -455,7 +480,7 @@ mod tests { #[test] fn mtwt_resolve_test(){ let a = 40; - assert_eq!(resolve(id(a,EMPTY_CTXT)),a); + assert_eq!(resolve(id(a,EMPTY_CTXT), PlainIdent),a); } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index e5b90393e0ad3..3865646f314c4 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -246,7 +246,7 @@ pub trait Folder { }) } - fn fold_ident(&mut self, i: Ident) -> Ident { + fn fold_ident(&mut self, i: Ident, _: IdentKind) -> Ident { i } @@ -255,7 +255,7 @@ pub trait Folder { span: self.new_span(p.span), global: p.global, segments: p.segments.map(|segment| ast::PathSegment { - identifier: self.fold_ident(segment.identifier), + identifier: self.fold_ident(segment.identifier, PlainIdent), lifetimes: segment.lifetimes.map(|l| fold_lifetime(l, self)), types: segment.types.map(|&typ| self.fold_ty(typ)), }) @@ -384,7 +384,7 @@ pub fn fold_tts(tts: &[TokenTree], fld: &mut T) -> Vec { sep.as_ref().map(|tok|maybe_fold_ident(tok,fld)), is_optional), TTNonterminal(sp,ref ident) => - TTNonterminal(sp,fld.fold_ident(*ident)) + TTNonterminal(sp,fld.fold_ident(*ident, PlainIdent)) } }).collect() } @@ -393,9 +393,9 @@ pub fn fold_tts(tts: &[TokenTree], fld: &mut T) -> Vec { fn maybe_fold_ident(t: &token::Token, fld: &mut T) -> token::Token { match *t { token::IDENT(id, followed_by_colons) => { - token::IDENT(fld.fold_ident(id), followed_by_colons) + token::IDENT(fld.fold_ident(id, PlainIdent), followed_by_colons) } - token::LIFETIME(id) => token::LIFETIME(fld.fold_ident(id)), + token::LIFETIME(id) => token::LIFETIME(fld.fold_ident(id, LifetimeIdent)), _ => (*t).clone() } } @@ -482,7 +482,7 @@ fn fold_struct_field(f: &StructField, fld: &mut T) -> StructField { fn fold_field_(field: Field, folder: &mut T) -> Field { ast::Field { - ident: respan(field.ident.span, folder.fold_ident(field.ident.node)), + ident: respan(field.ident.span, folder.fold_ident(field.ident.node, PlainIdent)), expr: folder.fold_expr(field.expr), span: folder.new_span(field.span), } @@ -601,7 +601,7 @@ pub fn noop_fold_item_underscore(i: &Item_, folder: &mut T) -> Item_ pub fn noop_fold_type_method(m: &TypeMethod, fld: &mut T) -> TypeMethod { TypeMethod { id: fld.new_id(m.id), // Needs to be first, for ast_map. - ident: fld.fold_ident(m.ident), + ident: fld.fold_ident(m.ident, PlainIdent), attrs: m.attrs.map(|a| fold_attribute_(*a, fld)), purity: m.purity, decl: fld.fold_fn_decl(m.decl), @@ -642,7 +642,7 @@ pub fn noop_fold_item(i: &Item, folder: &mut T) -> SmallVector<@Item> SmallVector::one(@Item { id: id, - ident: folder.fold_ident(ident), + ident: folder.fold_ident(ident, PlainIdent), attrs: i.attrs.map(|e| fold_attribute_(*e, folder)), node: node, vis: i.vis, @@ -653,7 +653,7 @@ pub fn noop_fold_item(i: &Item, folder: &mut T) -> SmallVector<@Item> pub fn noop_fold_foreign_item(ni: &ForeignItem, folder: &mut T) -> @ForeignItem { @ForeignItem { id: folder.new_id(ni.id), // Needs to be first, for ast_map. - ident: folder.fold_ident(ni.ident), + ident: folder.fold_ident(ni.ident, PlainIdent), attrs: ni.attrs.map(|x| fold_attribute_(*x, folder)), node: match ni.node { ForeignItemFn(ref fdec, ref generics) => { @@ -676,7 +676,7 @@ pub fn noop_fold_foreign_item(ni: &ForeignItem, folder: &mut T) -> @F pub fn noop_fold_method(m: &Method, folder: &mut T) -> @Method { @Method { id: folder.new_id(m.id), // Needs to be first, for ast_map. - ident: folder.fold_ident(m.ident), + ident: folder.fold_ident(m.ident, PlainIdent), attrs: m.attrs.map(|a| fold_attribute_(*a, folder)), generics: fold_generics(&m.generics, folder), explicit_self: folder.fold_explicit_self(&m.explicit_self), @@ -753,7 +753,7 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { } ExprMethodCall(i, ref tps, ref args) => { ExprMethodCall( - folder.fold_ident(i), + folder.fold_ident(i, PlainIdent), tps.map(|&x| folder.fold_ty(x)), args.map(|&x| folder.fold_expr(x))) } @@ -782,11 +782,11 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { ExprForLoop(folder.fold_pat(pat), folder.fold_expr(iter), folder.fold_block(body), - maybe_ident.map(|i| folder.fold_ident(i))) + maybe_ident.map(|i| folder.fold_ident(i, LifetimeIdent))) } ExprLoop(body, opt_ident) => { ExprLoop(folder.fold_block(body), - opt_ident.map(|x| folder.fold_ident(x))) + opt_ident.map(|x| folder.fold_ident(x, LifetimeIdent))) } ExprMatch(expr, ref arms) => { ExprMatch(folder.fold_expr(expr), @@ -809,7 +809,7 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { } ExprField(el, id, ref tys) => { ExprField(folder.fold_expr(el), - folder.fold_ident(id), + folder.fold_ident(id, PlainIdent), tys.map(|&x| folder.fold_ty(x))) } ExprIndex(el, er) => { @@ -817,8 +817,10 @@ pub fn noop_fold_expr(e: @Expr, folder: &mut T) -> @Expr { } ExprPath(ref pth) => ExprPath(folder.fold_path(pth)), ExprLogLevel => ExprLogLevel, - ExprBreak(opt_ident) => ExprBreak(opt_ident.map(|x| folder.fold_ident(x))), - ExprAgain(opt_ident) => ExprAgain(opt_ident.map(|x| folder.fold_ident(x))), + ExprBreak(opt_ident) => + ExprBreak(opt_ident.map(|x| folder.fold_ident(x, LifetimeIdent))), + ExprAgain(opt_ident) => + ExprAgain(opt_ident.map(|x| folder.fold_ident(x, LifetimeIdent))), ExprRet(ref e) => { ExprRet(e.map(|x| folder.fold_expr(x))) } @@ -890,7 +892,7 @@ mod test { struct ToZzIdentFolder; impl Folder for ToZzIdentFolder { - fn fold_ident(&mut self, _: ast::Ident) -> ast::Ident { + fn fold_ident(&mut self, _: ast::Ident, _: ast::IdentKind) -> ast::Ident { token::str_to_ident("zz") } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 71ee32b4aade7..52d639de6a969 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -722,8 +722,10 @@ pub fn is_reserved_keyword(tok: &Token) -> bool { pub fn mtwt_token_eq(t1 : &Token, t2 : &Token) -> bool { match (t1,t2) { - (&IDENT(id1,_),&IDENT(id2,_)) | (&LIFETIME(id1),&LIFETIME(id2)) => - mtwt::resolve(id1) == mtwt::resolve(id2), + (&IDENT(id1,_),&IDENT(id2,_)) => + mtwt::resolve(id1, ast::PlainIdent) == mtwt::resolve(id2, ast::PlainIdent), + (&LIFETIME(id1),&LIFETIME(id2)) => + mtwt::resolve(id1, ast::LifetimeIdent) == mtwt::resolve(id2, ast::LifetimeIdent), _ => *t1 == *t2 } } @@ -736,7 +738,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::new_mark(m,id.ctxt,ast::PlainIdent)} } #[test] fn mtwt_token_eq_test() { diff --git a/src/test/run-pass/issue-12512.rs b/src/test/run-pass/issue-12512.rs new file mode 100644 index 0000000000000..e5ab6c39a99df --- /dev/null +++ b/src/test/run-pass/issue-12512.rs @@ -0,0 +1,20 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that variables and loop labels with the same name don't +// shadow each other. + +pub fn main() { + let mut foo = 0i; + 'foo: for _ in range(0, 1) { + foo += 1; + assert_eq!(foo, 1); + } +} diff --git a/src/test/run-pass/loop-labels.rs b/src/test/run-pass/loop-labels.rs new file mode 100644 index 0000000000000..1fd0374967092 --- /dev/null +++ b/src/test/run-pass/loop-labels.rs @@ -0,0 +1,39 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that variables and loop labels with the same name don't +// shadow each other. + +// Tests that loop labels honor their lexical scopes and don't clash with +// each other. + +#[allow(unreachable_code)]; + +pub fn main() { + let mut outer = 0i; + 'bar: loop { + let mut bar = 0i; + 'bar: for _ in range(0, 1) { + bar += 1; + // This should refer to the inner loop + continue 'bar; + unreachable!(); + } + assert_eq!(bar, 1); + outer += 1; + // This should break out of the outer loop + break 'bar; + + 'bar: loop { + unreachable!(); + } + } + assert_eq!(outer, 1); +}