1
Fork 0

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]
This commit is contained in:
Patrick Walton 2014-06-10 13:54:13 -07:00 committed by Alex Crichton
parent e7f11f20e5
commit 2ed4734873
17 changed files with 159 additions and 89 deletions

View file

@ -165,7 +165,7 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
fn visit_lifetime_ref(&mut self, fn visit_lifetime_ref(&mut self,
lifetime_ref: &ast::Lifetime, lifetime_ref: &ast::Lifetime,
scope: Scope<'a>) { 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); self.insert_lifetime(lifetime_ref, DefStaticRegion);
return; return;
} }
@ -330,7 +330,7 @@ impl<'a> LifetimeContext<'a> {
lifetime_ref: &ast::Lifetime) { lifetime_ref: &ast::Lifetime) {
self.sess.span_err( self.sess.span_err(
lifetime_ref.span, lifetime_ref.span,
format!("use of undeclared lifetime name `'{}`", format!("use of undeclared lifetime name `{}`",
token::get_name(lifetime_ref.name)).as_slice()); token::get_name(lifetime_ref.name)).as_slice());
} }
@ -338,7 +338,7 @@ impl<'a> LifetimeContext<'a> {
for i in range(0, lifetimes.len()) { for i in range(0, lifetimes.len()) {
let lifetime_i = lifetimes.get(i); let lifetime_i = lifetimes.get(i);
let special_idents = [special_idents::statik]; let special_idents = [special_idents::static_lifetime];
for lifetime in lifetimes.iter() { for lifetime in lifetimes.iter() {
if special_idents.iter().any(|&i| i.name == lifetime.name) { if special_idents.iter().any(|&i| i.name == lifetime.name) {
self.sess.span_err( self.sess.span_err(
@ -354,7 +354,7 @@ impl<'a> LifetimeContext<'a> {
if lifetime_i.name == lifetime_j.name { if lifetime_i.name == lifetime_j.name {
self.sess.span_err( self.sess.span_err(
lifetime_j.span, lifetime_j.span,
format!("lifetime name `'{}` declared twice in \ format!("lifetime name `{}` declared twice in \
the same scope", the same scope",
token::get_name(lifetime_j.name)).as_slice()); token::get_name(lifetime_j.name)).as_slice());
} }

View file

@ -1505,7 +1505,8 @@ impl LifeGiver {
fn give_lifetime(&self) -> ast::Lifetime { fn give_lifetime(&self) -> ast::Lifetime {
let mut lifetime; let mut lifetime;
loop { 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) { if !self.taken.contains(&s) {
lifetime = name_to_dummy_lifetime( lifetime = name_to_dummy_lifetime(
token::str_to_ident(s.as_slice()).name); token::str_to_ident(s.as_slice()).name);

View file

@ -162,7 +162,7 @@ pub fn bound_region_to_str(cx: &ctxt,
match br { match br {
BrNamed(_, name) => { BrNamed(_, name) => {
format!("{}'{}{}", prefix, token::get_name(name), space_str) format!("{}{}{}", prefix, token::get_name(name), space_str)
} }
BrAnon(_) => prefix.to_string(), BrAnon(_) => prefix.to_string(),
BrFresh(_) => prefix.to_string(), BrFresh(_) => prefix.to_string(),

View file

@ -85,6 +85,7 @@ pub trait AstBuilder {
typ: P<ast::Ty>, typ: P<ast::Ty>,
ex: Gc<ast::Expr>) ex: Gc<ast::Expr>)
-> Gc<ast::Stmt>; -> Gc<ast::Stmt>;
fn stmt_item(&self, sp: Span, item: Gc<ast::Item>) -> Gc<ast::Stmt>;
// blocks // blocks
fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>, fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>,
@ -239,6 +240,14 @@ pub trait AstBuilder {
vi: Vec<ast::ViewItem>, vi: Vec<ast::ViewItem>,
items: Vec<Gc<ast::Item>>) -> Gc<ast::Item>; items: Vec<Gc<ast::Item>>) -> Gc<ast::Item>;
fn item_static(&self,
span: Span,
name: Ident,
ty: P<ast::Ty>,
mutbl: ast::Mutability,
expr: Gc<ast::Expr>)
-> Gc<ast::Item>;
fn item_ty_poly(&self, fn item_ty_poly(&self,
span: Span, span: Span,
name: Ident, name: Ident,
@ -484,11 +493,19 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
box(GC) respan(sp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID)) box(GC) respan(sp, ast::StmtDecl(box(GC) decl, ast::DUMMY_NODE_ID))
} }
fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>, fn block(&self,
expr: Option<Gc<Expr>>) -> P<ast::Block> { span: Span,
stmts: Vec<Gc<ast::Stmt>>,
expr: Option<Gc<Expr>>)
-> P<ast::Block> {
self.block_all(span, Vec::new(), stmts, expr) self.block_all(span, Vec::new(), stmts, expr)
} }
fn stmt_item(&self, sp: Span, item: Gc<ast::Item>) -> Gc<ast::Stmt> {
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<ast::Expr>) -> P<ast::Block> { fn block_expr(&self, expr: Gc<ast::Expr>) -> P<ast::Block> {
self.block_all(expr.span, Vec::new(), Vec::new(), Some(expr)) self.block_all(expr.span, Vec::new(), Vec::new(), Some(expr))
} }
@ -942,6 +959,16 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
) )
} }
fn item_static(&self,
span: Span,
name: Ident,
ty: P<ast::Ty>,
mutbl: ast::Mutability,
expr: Gc<ast::Expr>)
-> Gc<ast::Item> {
self.item(span, name, Vec::new(), ast::ItemStatic(ty, mutbl, expr))
}
fn item_ty_poly(&self, span: Span, name: Ident, ty: P<ast::Ty>, fn item_ty_poly(&self, span: Span, name: Ident, ty: P<ast::Ty>,
generics: Generics) -> Gc<ast::Item> { generics: Generics) -> Gc<ast::Item> {
self.item(span, name, Vec::new(), ast::ItemTy(ty, generics)) self.item(span, name, Vec::new(), ast::ItemTy(ty, generics))

View file

@ -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 = 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) MacExpr::new(e)
} }

View file

@ -43,7 +43,7 @@ pub fn expand_option_env(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
cx.ident_of("str")), cx.ident_of("str")),
Some(cx.lifetime(sp, Some(cx.lifetime(sp,
cx.ident_of( cx.ident_of(
"static").name)), "'static").name)),
ast::MutImmutable)))) ast::MutImmutable))))
} }
Some(s) => { Some(s) => {

View file

@ -465,7 +465,7 @@ impl<'a, 'b> Context<'a, 'b> {
self.ecx.ident_of("rt"), self.ecx.ident_of("rt"),
self.ecx.ident_of("Piece")), self.ecx.ident_of("Piece")),
vec!(self.ecx.lifetime(self.fmtsp, vec!(self.ecx.lifetime(self.fmtsp,
self.ecx.ident_of("static").name)), self.ecx.ident_of("'static").name)),
Vec::new() Vec::new()
), None); ), None);
let ty = ast::TyFixedLengthVec( let ty = ast::TyFixedLengthVec(

View file

@ -757,19 +757,34 @@ impl<'a> StringReader<'a> {
while ident_continue(self.curr) { while ident_continue(self.curr) {
self.bump(); self.bump();
} }
let ident = self.with_str_from(start, |lifetime_name| {
str_to_ident(lifetime_name)
});
let tok = &token::IDENT(ident, false);
if token::is_keyword(token::keywords::Self, tok) { // Include the leading `'` in the real identifier, for macro
self.err_span(start, self.last_pos, // expansion purposes. See #12512 for the gory details of why
"invalid lifetime name: 'self \ // this is necessary.
is no longer a special lifetime"); let ident = self.with_str_from(start, |lifetime_name| {
} else if token::is_any_keyword(tok) && str_to_ident(format!("'{}", lifetime_name).as_slice())
!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); return token::LIFETIME(ident);
} }
@ -1128,7 +1143,7 @@ mod test {
#[test] fn lifetime_name() { #[test] fn lifetime_name() {
assert_eq!(setup(&mk_sh(), "'abc".to_string()).next_token().tok, 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() { #[test] fn raw_string() {

View file

@ -3452,7 +3452,7 @@ impl<'a> Parser<'a> {
match self.token { match self.token {
token::LIFETIME(lifetime) => { token::LIFETIME(lifetime) => {
let lifetime_interned_string = token::get_ident(lifetime); let lifetime_interned_string = token::get_ident(lifetime);
if lifetime_interned_string.equiv(&("static")) { if lifetime_interned_string.equiv(&("'static")) {
result.push(StaticRegionTyParamBound); result.push(StaticRegionTyParamBound);
if allow_any_lifetime && ret_lifetime.is_none() { if allow_any_lifetime && ret_lifetime.is_none() {
ret_lifetime = Some(ast::Lifetime { ret_lifetime = Some(ast::Lifetime {

View file

@ -232,7 +232,7 @@ pub fn to_str(t: &Token) -> String {
/* Name components */ /* Name components */
IDENT(s, _) => get_ident(s).get().to_string(), IDENT(s, _) => get_ident(s).get().to_string(),
LIFETIME(s) => { LIFETIME(s) => {
(format!("'{}", get_ident(s))).to_string() (format!("{}", get_ident(s))).to_string()
} }
UNDERSCORE => "_".to_string(), UNDERSCORE => "_".to_string(),
@ -433,71 +433,72 @@ declare_special_idents_and_keywords! {
(0, invalid, ""); (0, invalid, "");
(super::SELF_KEYWORD_NAME, self_, "self"); (super::SELF_KEYWORD_NAME, self_, "self");
(super::STATIC_KEYWORD_NAME, statik, "static"); (super::STATIC_KEYWORD_NAME, statik, "static");
(3, static_lifetime, "'static");
// for matcher NTs // for matcher NTs
(3, tt, "tt"); (4, tt, "tt");
(4, matchers, "matchers"); (5, matchers, "matchers");
// outside of libsyntax // outside of libsyntax
(5, clownshoe_abi, "__rust_abi"); (6, clownshoe_abi, "__rust_abi");
(6, opaque, "<opaque>"); (7, opaque, "<opaque>");
(7, unnamed_field, "<unnamed_field>"); (8, unnamed_field, "<unnamed_field>");
(8, type_self, "Self"); (9, type_self, "Self");
} }
pub mod keywords { pub mod keywords {
// These ones are variants of the Keyword enum // These ones are variants of the Keyword enum
'strict: 'strict:
(9, As, "as"); (10, As, "as");
(10, Break, "break"); (11, Break, "break");
(11, Crate, "crate"); (12, Crate, "crate");
(12, Else, "else"); (13, Else, "else");
(13, Enum, "enum"); (14, Enum, "enum");
(14, Extern, "extern"); (15, Extern, "extern");
(15, False, "false"); (16, False, "false");
(16, Fn, "fn"); (17, Fn, "fn");
(17, For, "for"); (18, For, "for");
(18, If, "if"); (19, If, "if");
(19, Impl, "impl"); (20, Impl, "impl");
(20, In, "in"); (21, In, "in");
(21, Let, "let"); (22, Let, "let");
(22, Loop, "loop"); (23, Loop, "loop");
(23, Match, "match"); (24, Match, "match");
(24, Mod, "mod"); (25, Mod, "mod");
(25, Mut, "mut"); (26, Mut, "mut");
(26, Once, "once"); (27, Once, "once");
(27, Pub, "pub"); (28, Pub, "pub");
(28, Ref, "ref"); (29, Ref, "ref");
(29, Return, "return"); (30, Return, "return");
// Static and Self are also special idents (prefill de-dupes) // Static and Self are also special idents (prefill de-dupes)
(super::STATIC_KEYWORD_NAME, Static, "static"); (super::STATIC_KEYWORD_NAME, Static, "static");
(super::SELF_KEYWORD_NAME, Self, "self"); (super::SELF_KEYWORD_NAME, Self, "self");
(30, Struct, "struct"); (31, Struct, "struct");
(31, Super, "super"); (32, Super, "super");
(32, True, "true"); (33, True, "true");
(33, Trait, "trait"); (34, Trait, "trait");
(34, Type, "type"); (35, Type, "type");
(35, Unsafe, "unsafe"); (36, Unsafe, "unsafe");
(36, Use, "use"); (37, Use, "use");
(37, Virtual, "virtual"); (38, Virtual, "virtual");
(38, While, "while"); (39, While, "while");
(39, Continue, "continue"); (40, Continue, "continue");
(40, Proc, "proc"); (41, Proc, "proc");
(41, Box, "box"); (42, Box, "box");
'reserved: 'reserved:
(42, Alignof, "alignof"); (43, Alignof, "alignof");
(43, Be, "be"); (44, Be, "be");
(44, Const, "const"); (45, Const, "const");
(45, Offsetof, "offsetof"); (46, Offsetof, "offsetof");
(46, Priv, "priv"); (47, Priv, "priv");
(47, Pure, "pure"); (48, Pure, "pure");
(48, Sizeof, "sizeof"); (49, Sizeof, "sizeof");
(49, Typeof, "typeof"); (50, Typeof, "typeof");
(50, Unsized, "unsized"); (51, Unsized, "unsized");
(51, Yield, "yield"); (52, Yield, "yield");
(52, Do, "do"); (53, Do, "do");
} }
} }

View file

@ -9,8 +9,8 @@
// except according to those terms. // except according to those terms.
use abi; use abi;
use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound, use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound};
TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided}; use ast::{TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided};
use ast; use ast;
use ast_util; use ast_util;
use owned_slice::OwnedSlice; use owned_slice::OwnedSlice;
@ -1325,7 +1325,6 @@ impl<'a> State<'a> {
} }
ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => { ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => {
for ident in opt_ident.iter() { for ident in opt_ident.iter() {
try!(word(&mut self.s, "'"));
try!(self.print_ident(*ident)); try!(self.print_ident(*ident));
try!(self.word_space(":")); try!(self.word_space(":"));
} }
@ -1339,7 +1338,6 @@ impl<'a> State<'a> {
} }
ast::ExprLoop(ref blk, opt_ident) => { ast::ExprLoop(ref blk, opt_ident) => {
for ident in opt_ident.iter() { for ident in opt_ident.iter() {
try!(word(&mut self.s, "'"));
try!(self.print_ident(*ident)); try!(self.print_ident(*ident));
try!(self.word_space(":")); try!(self.word_space(":"));
} }
@ -1504,7 +1502,6 @@ impl<'a> State<'a> {
try!(word(&mut self.s, "break")); try!(word(&mut self.s, "break"));
try!(space(&mut self.s)); try!(space(&mut self.s));
for ident in opt_ident.iter() { for ident in opt_ident.iter() {
try!(word(&mut self.s, "'"));
try!(self.print_ident(*ident)); try!(self.print_ident(*ident));
try!(space(&mut self.s)); try!(space(&mut self.s));
} }
@ -1513,7 +1510,6 @@ impl<'a> State<'a> {
try!(word(&mut self.s, "continue")); try!(word(&mut self.s, "continue"));
try!(space(&mut self.s)); try!(space(&mut self.s));
for ident in opt_ident.iter() { for ident in opt_ident.iter() {
try!(word(&mut self.s, "'"));
try!(self.print_ident(*ident)); try!(self.print_ident(*ident));
try!(space(&mut self.s)) try!(space(&mut self.s))
} }
@ -1943,7 +1939,7 @@ impl<'a> State<'a> {
match *region { match *region {
Some(ref lt) => { Some(ref lt) => {
let token = token::get_name(lt.name); let token = token::get_name(lt.name);
if token.get() != "static" { if token.get() != "'static" {
try!(self.nbsp()); try!(self.nbsp());
first = false; first = false;
try!(self.print_lifetime(lt)); try!(self.print_lifetime(lt));
@ -1988,7 +1984,6 @@ impl<'a> State<'a> {
pub fn print_lifetime(&mut self, pub fn print_lifetime(&mut self,
lifetime: &ast::Lifetime) -> IoResult<()> { lifetime: &ast::Lifetime) -> IoResult<()> {
try!(word(&mut self.s, "'"));
self.print_name(lifetime.name) self.print_name(lifetime.name)
} }

View file

@ -15,5 +15,5 @@ macro_rules! foo {
} }
pub fn main() { pub fn main() {
'x: loop { foo!() } //~ ERROR use of undeclared label `x` 'x: loop { foo!() } //~ ERROR use of undeclared label `'x`
} }

View file

@ -15,5 +15,5 @@ macro_rules! foo {
} }
pub fn main() { pub fn main() {
foo!(break 'x); //~ ERROR use of undeclared label `x` foo!(break 'x); //~ ERROR use of undeclared label `'x`
} }

View file

@ -16,6 +16,6 @@ macro_rules! foo {
pub fn main() { pub fn main() {
'x: for _ in range(0,1) { 'x: for _ in range(0,1) {
foo!() //~ ERROR use of undeclared label `x` foo!() //~ ERROR use of undeclared label `'x`
}; };
} }

View file

@ -15,5 +15,5 @@ macro_rules! foo {
} }
pub fn main() { pub fn main() {
foo!(break 'x); //~ ERROR use of undeclared label `x` foo!(break 'x); //~ ERROR use of undeclared label `'x`
} }

View file

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // 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 x: &'static int
} }

View file

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Issue #12512.
fn main() {
let mut foo = Vec::new();
'foo: for i in [1, 2, 3].iter() {
foo.push(i);
}
}