From 2ed473487323bb4e5a600a3318e0981981214210 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 10 Jun 2014 13:54:13 -0700 Subject: [PATCH] librustc: Fix the issue with labels shadowing variable names by making the leading quote part of the identifier for the purposes of hygiene. This adopts @jbclements' solution to #14539. I'm not sure if this is a breaking change or not. Closes #12512. [breaking-change] --- src/librustc/middle/resolve_lifetime.rs | 8 +- .../middle/typeck/infer/error_reporting.rs | 3 +- src/librustc/util/ppaux.rs | 2 +- src/libsyntax/ext/build.rs | 31 +++++- src/libsyntax/ext/bytes.rs | 14 ++- src/libsyntax/ext/env.rs | 2 +- src/libsyntax/ext/format.rs | 2 +- src/libsyntax/parse/lexer/mod.rs | 39 +++++-- src/libsyntax/parse/parser.rs | 2 +- src/libsyntax/parse/token.rs | 103 +++++++++--------- src/libsyntax/print/pprust.rs | 11 +- src/test/compile-fail/hygienic-label-1.rs | 2 +- src/test/compile-fail/hygienic-label-2.rs | 2 +- src/test/compile-fail/hygienic-label-3.rs | 2 +- src/test/compile-fail/hygienic-label-4.rs | 2 +- src/test/compile-fail/regions-name-static.rs | 2 +- src/test/run-pass/loop-label-shadowing.rs | 19 ++++ 17 files changed, 158 insertions(+), 88 deletions(-) create mode 100644 src/test/run-pass/loop-label-shadowing.rs diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index f416686efd8..8ff5331cec2 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -165,7 +165,7 @@ fn visit_block(&mut self, fn visit_lifetime_ref(&mut self, lifetime_ref: &ast::Lifetime, scope: Scope<'a>) { - if lifetime_ref.name == special_idents::statik.name { + if lifetime_ref.name == special_idents::static_lifetime.name { self.insert_lifetime(lifetime_ref, DefStaticRegion); return; } @@ -330,7 +330,7 @@ fn unresolved_lifetime_ref(&self, lifetime_ref: &ast::Lifetime) { self.sess.span_err( lifetime_ref.span, - format!("use of undeclared lifetime name `'{}`", + format!("use of undeclared lifetime name `{}`", token::get_name(lifetime_ref.name)).as_slice()); } @@ -338,7 +338,7 @@ fn check_lifetime_names(&self, lifetimes: &Vec) { for i in range(0, lifetimes.len()) { let lifetime_i = lifetimes.get(i); - let special_idents = [special_idents::statik]; + let special_idents = [special_idents::static_lifetime]; for lifetime in lifetimes.iter() { if special_idents.iter().any(|&i| i.name == lifetime.name) { self.sess.span_err( @@ -354,7 +354,7 @@ fn check_lifetime_names(&self, lifetimes: &Vec) { if lifetime_i.name == lifetime_j.name { self.sess.span_err( lifetime_j.span, - format!("lifetime name `'{}` declared twice in \ + format!("lifetime name `{}` declared twice in \ the same scope", token::get_name(lifetime_j.name)).as_slice()); } diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index bcd66ed4d66..2e02c798339 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -1505,7 +1505,8 @@ fn inc_counter(&self) { fn give_lifetime(&self) -> ast::Lifetime { let mut lifetime; loop { - let s = num_to_str(self.counter.get()); + let mut s = String::from_str("'"); + s.push_str(num_to_str(self.counter.get()).as_slice()); if !self.taken.contains(&s) { lifetime = name_to_dummy_lifetime( token::str_to_ident(s.as_slice()).name); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 31994d08d23..80f3508d0cd 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -162,7 +162,7 @@ pub fn bound_region_to_str(cx: &ctxt, match br { BrNamed(_, name) => { - format!("{}'{}{}", prefix, token::get_name(name), space_str) + format!("{}{}{}", prefix, token::get_name(name), space_str) } BrAnon(_) => prefix.to_string(), BrFresh(_) => prefix.to_string(), diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 148b653b61c..4ef7796c454 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -85,6 +85,7 @@ fn stmt_let_typed(&self, typ: P, ex: Gc) -> Gc; + fn stmt_item(&self, sp: Span, item: Gc) -> Gc; // blocks fn block(&self, span: Span, stmts: Vec>, @@ -239,6 +240,14 @@ fn item_mod(&self, span: Span, inner_span: Span, vi: Vec, items: Vec>) -> Gc; + fn item_static(&self, + span: Span, + name: Ident, + ty: P, + mutbl: ast::Mutability, + expr: Gc) + -> Gc; + fn item_ty_poly(&self, span: Span, name: Ident, @@ -484,11 +493,19 @@ fn stmt_let_typed(&self, box(GC) respan(sp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)) } - fn block(&self, span: Span, stmts: Vec>, - expr: Option>) -> P { + fn block(&self, + span: Span, + stmts: Vec>, + expr: Option>) + -> P { self.block_all(span, Vec::new(), stmts, expr) } + fn stmt_item(&self, sp: Span, item: Gc) -> Gc { + let decl = respan(sp, ast::DeclItem(item)); + box(GC) respan(sp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)) + } + fn block_expr(&self, expr: Gc) -> P { self.block_all(expr.span, Vec::new(), Vec::new(), Some(expr)) } @@ -942,6 +959,16 @@ fn item_mod(&self, span: Span, inner_span: Span, name: Ident, ) } + fn item_static(&self, + span: Span, + name: Ident, + ty: P, + mutbl: ast::Mutability, + expr: Gc) + -> Gc { + self.item(span, name, Vec::new(), ast::ItemStatic(ty, mutbl, expr)) + } + fn item_ty_poly(&self, span: Span, name: Ident, ty: P, generics: Generics) -> Gc { self.item(span, name, Vec::new(), ast::ItemTy(ty, generics)) diff --git a/src/libsyntax/ext/bytes.rs b/src/libsyntax/ext/bytes.rs index b2088d2bc82..b87a25d4a44 100644 --- a/src/libsyntax/ext/bytes.rs +++ b/src/libsyntax/ext/bytes.rs @@ -94,6 +94,18 @@ pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) } let e = cx.expr_vec_slice(sp, bytes); - let e = quote_expr!(cx, { static BYTES: &'static [u8] = $e; BYTES}); + let ty = cx.ty(sp, ast::TyVec(cx.ty_ident(sp, cx.ident_of("u8")))); + let lifetime = cx.lifetime(sp, cx.ident_of("'static").name); + let item = cx.item_static(sp, + cx.ident_of("BYTES"), + cx.ty_rptr(sp, + ty, + Some(lifetime), + ast::MutImmutable), + ast::MutImmutable, + e); + let e = cx.expr_block(cx.block(sp, + vec!(cx.stmt_item(sp, item)), + Some(cx.expr_ident(sp, cx.ident_of("BYTES"))))); MacExpr::new(e) } diff --git a/src/libsyntax/ext/env.rs b/src/libsyntax/ext/env.rs index d1a4d2f3ee3..9ef7241ca24 100644 --- a/src/libsyntax/ext/env.rs +++ b/src/libsyntax/ext/env.rs @@ -43,7 +43,7 @@ pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) cx.ident_of("str")), Some(cx.lifetime(sp, cx.ident_of( - "static").name)), + "'static").name)), ast::MutImmutable)))) } Some(s) => { diff --git a/src/libsyntax/ext/format.rs b/src/libsyntax/ext/format.rs index d3b73cbe33a..cfce4b1e0fc 100644 --- a/src/libsyntax/ext/format.rs +++ b/src/libsyntax/ext/format.rs @@ -465,7 +465,7 @@ fn to_expr(&self, invocation: Invocation) -> Gc { self.ecx.ident_of("rt"), self.ecx.ident_of("Piece")), vec!(self.ecx.lifetime(self.fmtsp, - self.ecx.ident_of("static").name)), + self.ecx.ident_of("'static").name)), Vec::new() ), None); let ty = ast::TyFixedLengthVec( diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index bb23fe50bd9..459cb6d31ed 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -757,19 +757,34 @@ fn next_token_inner(&mut self) -> token::Token { while ident_continue(self.curr) { self.bump(); } + + // Include the leading `'` in the real identifier, for macro + // expansion purposes. See #12512 for the gory details of why + // this is necessary. let ident = self.with_str_from(start, |lifetime_name| { - str_to_ident(lifetime_name) + str_to_ident(format!("'{}", lifetime_name).as_slice()) }); - let tok = &token::IDENT(ident, false); - - if token::is_keyword(token::keywords::Self, tok) { - self.err_span(start, self.last_pos, - "invalid lifetime name: 'self \ - is no longer a special lifetime"); - } else if token::is_any_keyword(tok) && - !token::is_keyword(token::keywords::Static, tok) { - self.err_span(start, self.last_pos, - "invalid lifetime name"); + + // Conjure up a "keyword checking ident" to make sure that + // the lifetime name is not a keyword. + let keyword_checking_ident = + self.with_str_from(start, |lifetime_name| { + str_to_ident(lifetime_name) + }); + let keyword_checking_token = + &token::IDENT(keyword_checking_ident, false); + if token::is_keyword(token::keywords::Self, + keyword_checking_token) { + self.err_span(start, + self.last_pos, + "invalid lifetime name: 'self \ + is no longer a special lifetime"); + } else if token::is_any_keyword(keyword_checking_token) && + !token::is_keyword(token::keywords::Static, + keyword_checking_token) { + self.err_span(start, + self.last_pos, + "invalid lifetime name"); } return token::LIFETIME(ident); } @@ -1128,7 +1143,7 @@ fn mk_ident (id: &str, is_mod_name: bool) -> token::Token { #[test] fn lifetime_name() { assert_eq!(setup(&mk_sh(), "'abc".to_string()).next_token().tok, - token::LIFETIME(token::str_to_ident("abc"))); + token::LIFETIME(token::str_to_ident("'abc"))); } #[test] fn raw_string() { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 437b06e3df6..aaedb570955 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3452,7 +3452,7 @@ fn parse_optional_ty_param_bounds(&mut self, allow_any_lifetime: bool) match self.token { token::LIFETIME(lifetime) => { let lifetime_interned_string = token::get_ident(lifetime); - if lifetime_interned_string.equiv(&("static")) { + if lifetime_interned_string.equiv(&("'static")) { result.push(StaticRegionTyParamBound); if allow_any_lifetime && ret_lifetime.is_none() { ret_lifetime = Some(ast::Lifetime { diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index fa70261a7d7..a4a022708d9 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -232,7 +232,7 @@ pub fn to_str(t: &Token) -> String { /* Name components */ IDENT(s, _) => get_ident(s).get().to_string(), LIFETIME(s) => { - (format!("'{}", get_ident(s))).to_string() + (format!("{}", get_ident(s))).to_string() } UNDERSCORE => "_".to_string(), @@ -433,71 +433,72 @@ pub mod special_idents { (0, invalid, ""); (super::SELF_KEYWORD_NAME, self_, "self"); (super::STATIC_KEYWORD_NAME, statik, "static"); + (3, static_lifetime, "'static"); // for matcher NTs - (3, tt, "tt"); - (4, matchers, "matchers"); + (4, tt, "tt"); + (5, matchers, "matchers"); // outside of libsyntax - (5, clownshoe_abi, "__rust_abi"); - (6, opaque, ""); - (7, unnamed_field, ""); - (8, type_self, "Self"); + (6, clownshoe_abi, "__rust_abi"); + (7, opaque, ""); + (8, unnamed_field, ""); + (9, type_self, "Self"); } pub mod keywords { // These ones are variants of the Keyword enum 'strict: - (9, As, "as"); - (10, Break, "break"); - (11, Crate, "crate"); - (12, Else, "else"); - (13, Enum, "enum"); - (14, Extern, "extern"); - (15, False, "false"); - (16, Fn, "fn"); - (17, For, "for"); - (18, If, "if"); - (19, Impl, "impl"); - (20, In, "in"); - (21, Let, "let"); - (22, Loop, "loop"); - (23, Match, "match"); - (24, Mod, "mod"); - (25, Mut, "mut"); - (26, Once, "once"); - (27, Pub, "pub"); - (28, Ref, "ref"); - (29, Return, "return"); + (10, As, "as"); + (11, Break, "break"); + (12, Crate, "crate"); + (13, Else, "else"); + (14, Enum, "enum"); + (15, Extern, "extern"); + (16, False, "false"); + (17, Fn, "fn"); + (18, For, "for"); + (19, If, "if"); + (20, Impl, "impl"); + (21, In, "in"); + (22, Let, "let"); + (23, Loop, "loop"); + (24, Match, "match"); + (25, Mod, "mod"); + (26, Mut, "mut"); + (27, Once, "once"); + (28, Pub, "pub"); + (29, Ref, "ref"); + (30, Return, "return"); // Static and Self are also special idents (prefill de-dupes) (super::STATIC_KEYWORD_NAME, Static, "static"); (super::SELF_KEYWORD_NAME, Self, "self"); - (30, Struct, "struct"); - (31, Super, "super"); - (32, True, "true"); - (33, Trait, "trait"); - (34, Type, "type"); - (35, Unsafe, "unsafe"); - (36, Use, "use"); - (37, Virtual, "virtual"); - (38, While, "while"); - (39, Continue, "continue"); - (40, Proc, "proc"); - (41, Box, "box"); + (31, Struct, "struct"); + (32, Super, "super"); + (33, True, "true"); + (34, Trait, "trait"); + (35, Type, "type"); + (36, Unsafe, "unsafe"); + (37, Use, "use"); + (38, Virtual, "virtual"); + (39, While, "while"); + (40, Continue, "continue"); + (41, Proc, "proc"); + (42, Box, "box"); 'reserved: - (42, Alignof, "alignof"); - (43, Be, "be"); - (44, Const, "const"); - (45, Offsetof, "offsetof"); - (46, Priv, "priv"); - (47, Pure, "pure"); - (48, Sizeof, "sizeof"); - (49, Typeof, "typeof"); - (50, Unsized, "unsized"); - (51, Yield, "yield"); - (52, Do, "do"); + (43, Alignof, "alignof"); + (44, Be, "be"); + (45, Const, "const"); + (46, Offsetof, "offsetof"); + (47, Priv, "priv"); + (48, Pure, "pure"); + (49, Sizeof, "sizeof"); + (50, Typeof, "typeof"); + (51, Unsized, "unsized"); + (52, Yield, "yield"); + (53, Do, "do"); } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 33b7086d7ae..77bc967b92d 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -9,8 +9,8 @@ // except according to those terms. use abi; -use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound, - TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided}; +use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound}; +use ast::{TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided}; use ast; use ast_util; use owned_slice::OwnedSlice; @@ -1325,7 +1325,6 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> { } ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => { for ident in opt_ident.iter() { - try!(word(&mut self.s, "'")); try!(self.print_ident(*ident)); try!(self.word_space(":")); } @@ -1339,7 +1338,6 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> { } ast::ExprLoop(ref blk, opt_ident) => { for ident in opt_ident.iter() { - try!(word(&mut self.s, "'")); try!(self.print_ident(*ident)); try!(self.word_space(":")); } @@ -1504,7 +1502,6 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> { try!(word(&mut self.s, "break")); try!(space(&mut self.s)); for ident in opt_ident.iter() { - try!(word(&mut self.s, "'")); try!(self.print_ident(*ident)); try!(space(&mut self.s)); } @@ -1513,7 +1510,6 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> IoResult<()> { try!(word(&mut self.s, "continue")); try!(space(&mut self.s)); for ident in opt_ident.iter() { - try!(word(&mut self.s, "'")); try!(self.print_ident(*ident)); try!(space(&mut self.s)) } @@ -1943,7 +1939,7 @@ pub fn print_bounds(&mut self, match *region { Some(ref lt) => { let token = token::get_name(lt.name); - if token.get() != "static" { + if token.get() != "'static" { try!(self.nbsp()); first = false; try!(self.print_lifetime(lt)); @@ -1988,7 +1984,6 @@ pub fn print_bounds(&mut self, pub fn print_lifetime(&mut self, lifetime: &ast::Lifetime) -> IoResult<()> { - try!(word(&mut self.s, "'")); self.print_name(lifetime.name) } diff --git a/src/test/compile-fail/hygienic-label-1.rs b/src/test/compile-fail/hygienic-label-1.rs index 010cde769d7..0e87dc97c26 100644 --- a/src/test/compile-fail/hygienic-label-1.rs +++ b/src/test/compile-fail/hygienic-label-1.rs @@ -15,5 +15,5 @@ macro_rules! foo { } pub fn main() { - 'x: loop { foo!() } //~ ERROR use of undeclared label `x` + 'x: loop { foo!() } //~ ERROR use of undeclared label `'x` } diff --git a/src/test/compile-fail/hygienic-label-2.rs b/src/test/compile-fail/hygienic-label-2.rs index 78d8fce38d5..fe87e32459b 100644 --- a/src/test/compile-fail/hygienic-label-2.rs +++ b/src/test/compile-fail/hygienic-label-2.rs @@ -15,5 +15,5 @@ macro_rules! foo { } pub fn main() { - foo!(break 'x); //~ ERROR use of undeclared label `x` + foo!(break 'x); //~ ERROR use of undeclared label `'x` } diff --git a/src/test/compile-fail/hygienic-label-3.rs b/src/test/compile-fail/hygienic-label-3.rs index 439132fa152..b5954ac9930 100644 --- a/src/test/compile-fail/hygienic-label-3.rs +++ b/src/test/compile-fail/hygienic-label-3.rs @@ -16,6 +16,6 @@ macro_rules! foo { pub fn main() { 'x: for _ in range(0,1) { - foo!() //~ ERROR use of undeclared label `x` + foo!() //~ ERROR use of undeclared label `'x` }; } diff --git a/src/test/compile-fail/hygienic-label-4.rs b/src/test/compile-fail/hygienic-label-4.rs index dfda4586527..67fa56b1306 100644 --- a/src/test/compile-fail/hygienic-label-4.rs +++ b/src/test/compile-fail/hygienic-label-4.rs @@ -15,5 +15,5 @@ macro_rules! foo { } pub fn main() { - foo!(break 'x); //~ ERROR use of undeclared label `x` + foo!(break 'x); //~ ERROR use of undeclared label `'x` } diff --git a/src/test/compile-fail/regions-name-static.rs b/src/test/compile-fail/regions-name-static.rs index c1170654dd2..9f50ad36660 100644 --- a/src/test/compile-fail/regions-name-static.rs +++ b/src/test/compile-fail/regions-name-static.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `static` +struct Foo<'static> { //~ ERROR illegal lifetime parameter name: `'static` x: &'static int } diff --git a/src/test/run-pass/loop-label-shadowing.rs b/src/test/run-pass/loop-label-shadowing.rs new file mode 100644 index 00000000000..cfe51fe7758 --- /dev/null +++ b/src/test/run-pass/loop-label-shadowing.rs @@ -0,0 +1,19 @@ +// Copyright 2012 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. + +// Issue #12512. + +fn main() { + let mut foo = Vec::new(); + 'foo: for i in [1, 2, 3].iter() { + foo.push(i); + } +} + -- 2.44.0