about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2014-06-10 13:54:13 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-06-13 13:53:34 -0700
commit2ed473487323bb4e5a600a3318e0981981214210 (patch)
treea48635d0cac054e7045be8d0fbbd506f4f50b74e /src
parente7f11f20e5e72a3b22863a9913df94303321a5ce (diff)
downloadrust-2ed473487323bb4e5a600a3318e0981981214210.tar.gz
rust-2ed473487323bb4e5a600a3318e0981981214210.zip
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]
Diffstat (limited to 'src')
-rw-r--r--src/librustc/middle/resolve_lifetime.rs8
-rw-r--r--src/librustc/middle/typeck/infer/error_reporting.rs3
-rw-r--r--src/librustc/util/ppaux.rs2
-rw-r--r--src/libsyntax/ext/build.rs31
-rw-r--r--src/libsyntax/ext/bytes.rs14
-rw-r--r--src/libsyntax/ext/env.rs2
-rw-r--r--src/libsyntax/ext/format.rs2
-rw-r--r--src/libsyntax/parse/lexer/mod.rs39
-rw-r--r--src/libsyntax/parse/parser.rs2
-rw-r--r--src/libsyntax/parse/token.rs103
-rw-r--r--src/libsyntax/print/pprust.rs11
-rw-r--r--src/test/compile-fail/hygienic-label-1.rs2
-rw-r--r--src/test/compile-fail/hygienic-label-2.rs2
-rw-r--r--src/test/compile-fail/hygienic-label-3.rs2
-rw-r--r--src/test/compile-fail/hygienic-label-4.rs2
-rw-r--r--src/test/compile-fail/regions-name-static.rs2
-rw-r--r--src/test/run-pass/loop-label-shadowing.rs19
17 files changed, 158 insertions, 88 deletions
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 @@ impl<'a, 'b> Visitor<Scope<'a>> for LifetimeContext<'b> {
     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 @@ impl<'a> LifetimeContext<'a> {
                                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 @@ impl<'a> LifetimeContext<'a> {
         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 @@ impl<'a> LifetimeContext<'a> {
                 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 @@ impl LifeGiver {
     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 @@ pub trait AstBuilder {
                       typ: P<ast::Ty>,
                       ex: Gc<ast::Expr>)
                       -> Gc<ast::Stmt>;
+    fn stmt_item(&self, sp: Span, item: Gc<ast::Item>) -> Gc<ast::Stmt>;
 
     // blocks
     fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>,
@@ -239,6 +240,14 @@ pub trait AstBuilder {
                 vi: Vec<ast::ViewItem>,
                 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,
                     span: Span,
                     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))
     }
 
-    fn block(&self, span: Span, stmts: Vec<Gc<ast::Stmt>>,
-             expr: Option<Gc<Expr>>) -> P<ast::Block> {
+    fn block(&self,
+             span: Span,
+             stmts: Vec<Gc<ast::Stmt>>,
+             expr: Option<Gc<Expr>>)
+             -> P<ast::Block> {
         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> {
         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>,
                     generics: Generics) -> Gc<ast::Item> {
         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 @@ impl<'a, 'b> Context<'a, 'b> {
                     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 @@ impl<'a> StringReader<'a> {
                 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 @@ mod test {
 
     #[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 @@ impl<'a> Parser<'a> {
             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 @@ declare_special_idents_and_keywords! {
         (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,                 "<opaque>");
-        (7,                          unnamed_field,          "<unnamed_field>");
-        (8,                          type_self,              "Self");
+        (6,                          clownshoe_abi,          "__rust_abi");
+        (7,                          opaque,                 "<opaque>");
+        (8,                          unnamed_field,          "<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 @@ impl<'a> State<'a> {
             }
             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 @@ impl<'a> State<'a> {
             }
             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 @@ impl<'a> State<'a> {
                 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 @@ impl<'a> State<'a> {
                 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 @@ impl<'a> State<'a> {
             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 @@ impl<'a> State<'a> {
 
     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 <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);
+    }
+}
+