about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-08-30 09:01:20 +0000
committerbors <bors@rust-lang.org>2014-08-30 09:01:20 +0000
commit43c26e6041b811b322f49933e8d0f9455cb7ea2b (patch)
tree1d6a7a8293538c92f5883659ab872d550a790690
parentc8e86e977f5f95bd2f533c12ebf8107fb6f21a0f (diff)
parente76db8ebc2955ce19605a9c73d2b26b977406803 (diff)
downloadrust-43c26e6041b811b322f49933e8d0f9455cb7ea2b.tar.gz
rust-43c26e6041b811b322f49933e8d0f9455cb7ea2b.zip
auto merge of #16190 : Pythoner6/rust/labeled-while-loop, r=alexcrichton
Fixes #12643

> Say!
> I like labelled breaks/continues!

I will use them with a `for` loop.
And I will use with a `loop` loop.
Say! I will use them ANYWHERE!
… _even_ in a `while` loop.

Because they're now supported there.
-rw-r--r--src/librustc/lint/builtin.rs4
-rw-r--r--src/librustc/middle/cfg/construct.rs4
-rw-r--r--src/librustc/middle/check_loop.rs4
-rw-r--r--src/librustc/middle/expr_use_visitor.rs2
-rw-r--r--src/librustc/middle/liveness.rs2
-rw-r--r--src/librustc/middle/region.rs4
-rw-r--r--src/librustc/middle/resolve.rs4
-rw-r--r--src/librustc/middle/trans/debuginfo.rs2
-rw-r--r--src/librustc/middle/trans/expr.rs2
-rw-r--r--src/librustc/middle/typeck/check/mod.rs2
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs4
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/ext/expand.rs6
-rw-r--r--src/libsyntax/fold.rs14
-rw-r--r--src/libsyntax/parse/parser.rs11
-rw-r--r--src/libsyntax/print/pprust.rs8
-rw-r--r--src/libsyntax/visit.rs4
-rw-r--r--src/test/run-pass/hygienic-labels-in-let.rs17
-rw-r--r--src/test/run-pass/hygienic-labels.rs12
-rw-r--r--src/test/run-pass/labeled-break.rs8
-rw-r--r--src/test/run-pass/while-label.rs22
21 files changed, 106 insertions, 33 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 4f4824a2219..4b43c685fa9 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -58,7 +58,7 @@ impl LintPass for WhileTrue {
 
     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
         match e.node {
-            ast::ExprWhile(cond, _) => {
+            ast::ExprWhile(cond, _, _) => {
                 match cond.node {
                     ast::ExprLit(lit) => {
                         match lit.node {
@@ -1073,7 +1073,7 @@ impl LintPass for UnnecessaryParens {
     fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
         let (value, msg, struct_lit_needs_parens) = match e.node {
             ast::ExprIf(cond, _, _) => (cond, "`if` condition", true),
-            ast::ExprWhile(cond, _) => (cond, "`while` condition", true),
+            ast::ExprWhile(cond, _, _) => (cond, "`while` condition", true),
             ast::ExprMatch(head, _) => (head, "`match` head expression", true),
             ast::ExprRet(Some(value)) => (value, "`return` value", false),
             ast::ExprAssign(_, value) => (value, "assigned value", false),
diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs
index 2acc92cd227..776067d49a2 100644
--- a/src/librustc/middle/cfg/construct.rs
+++ b/src/librustc/middle/cfg/construct.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -227,7 +227,7 @@ impl<'a> CFGBuilder<'a> {
                 self.add_node(expr.id, [then_exit, else_exit])           // 4, 5
             }
 
-            ast::ExprWhile(ref cond, ref body) => {
+            ast::ExprWhile(ref cond, ref body, _) => {
                 //
                 //         [pred]
                 //           |
diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs
index 12841fb20d2..a40294328c7 100644
--- a/src/librustc/middle/check_loop.rs
+++ b/src/librustc/middle/check_loop.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -35,7 +35,7 @@ impl<'a> Visitor<Context> for CheckLoopVisitor<'a> {
 
     fn visit_expr(&mut self, e: &ast::Expr, cx:Context) {
         match e.node {
-            ast::ExprWhile(ref e, ref b) => {
+            ast::ExprWhile(ref e, ref b, _) => {
                 self.visit_expr(&**e, cx);
                 self.visit_block(&**b, Loop);
             }
diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs
index ae9a3fa6a67..6e522034147 100644
--- a/src/librustc/middle/expr_use_visitor.rs
+++ b/src/librustc/middle/expr_use_visitor.rs
@@ -410,7 +410,7 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
                 self.walk_block(&**blk);
             }
 
-            ast::ExprWhile(ref cond_expr, ref blk) => {
+            ast::ExprWhile(ref cond_expr, ref blk, _) => {
                 self.consume_expr(&**cond_expr);
                 self.walk_block(&**blk);
             }
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 38288bf3011..6ff09916b35 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -1017,7 +1017,7 @@ impl<'a> Liveness<'a> {
             self.propagate_through_expr(&**cond, ln)
           }
 
-          ExprWhile(ref cond, ref blk) => {
+          ExprWhile(ref cond, ref blk, _) => {
             self.propagate_through_loop(expr,
                                         WhileLoop(cond.clone()),
                                         &**blk,
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index 11dd3eee88e..21bfcfeec70 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -496,7 +496,7 @@ fn resolve_expr(visitor: &mut RegionResolutionVisitor,
             visitor.region_maps.mark_as_terminating_scope(body.id);
         }
 
-        ast::ExprWhile(expr, body) => {
+        ast::ExprWhile(expr, body, _) => {
             visitor.region_maps.mark_as_terminating_scope(expr.id);
             visitor.region_maps.mark_as_terminating_scope(body.id);
         }
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 58be2c73bd9..ed2f1db2aa8 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -23,7 +23,7 @@ use util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
 
 use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate};
 use syntax::ast::{DeclItem, DefId, Expr, ExprAgain, ExprBreak, ExprField};
-use syntax::ast::{ExprFnBlock, ExprForLoop, ExprLoop, ExprMethodCall};
+use syntax::ast::{ExprFnBlock, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall};
 use syntax::ast::{ExprPath, ExprProc, ExprStruct, ExprUnboxedFn, FnDecl};
 use syntax::ast::{ForeignItem, ForeignItemFn, ForeignItemStatic, Generics};
 use syntax::ast::{Ident, ImplItem, Item, ItemEnum, ItemFn, ItemForeignMod};
@@ -5622,7 +5622,7 @@ impl<'a> Resolver<'a> {
                 visit::walk_expr(self, expr, ());
             }
 
-            ExprLoop(_, Some(label)) => {
+            ExprLoop(_, Some(label)) | ExprWhile(_, _, Some(label)) => {
                 self.with_label_rib(|this| {
                     let def_like = DlDef(DefLabel(expr.id));
 
diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs
index 222cd92742f..62cd38c1e90 100644
--- a/src/librustc/middle/trans/debuginfo.rs
+++ b/src/librustc/middle/trans/debuginfo.rs
@@ -3512,7 +3512,7 @@ fn populate_scope_map(cx: &CrateContext,
                 }
             }
 
-            ast::ExprWhile(ref cond_exp, ref loop_body) => {
+            ast::ExprWhile(ref cond_exp, ref loop_body, _) => {
                 walk_expr(cx, &**cond_exp, scope_stack, scope_map);
 
                 with_new_scope(cx,
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index c49bb7f4e6b..72f99a3802d 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -900,7 +900,7 @@ fn trans_rvalue_stmt_unadjusted<'a>(bcx: &'a Block<'a>,
         ast::ExprRet(ex) => {
             controlflow::trans_ret(bcx, ex)
         }
-        ast::ExprWhile(ref cond, ref body) => {
+        ast::ExprWhile(ref cond, ref body, _) => {
             controlflow::trans_while(bcx, expr.id, &**cond, &**body)
         }
         ast::ExprForLoop(ref pat, ref head, ref body, _) => {
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index cfd2ee2b441..71075297b07 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -3757,7 +3757,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
         check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.clone(),
                         id, expr.span, expected);
       }
-      ast::ExprWhile(ref cond, ref body) => {
+      ast::ExprWhile(ref cond, ref body, _) => {
         check_expr_has_type(fcx, &**cond, ty::mk_bool());
         check_block_no_value(fcx, &**body);
         let cond_ty = fcx.expr_ty(&**cond);
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index f54e650a173..db9d52814b6 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -749,7 +749,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
             rcx.set_repeating_scope(repeating_scope);
         }
 
-        ast::ExprWhile(ref cond, ref body) => {
+        ast::ExprWhile(ref cond, ref body, _) => {
             let repeating_scope = rcx.set_repeating_scope(cond.id);
             rcx.visit_expr(&**cond, ());
 
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 8df6b65cd16..68a1c521f19 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -524,7 +524,8 @@ pub enum Expr_ {
     ExprLit(Gc<Lit>),
     ExprCast(Gc<Expr>, P<Ty>),
     ExprIf(Gc<Expr>, P<Block>, Option<Gc<Expr>>),
-    ExprWhile(Gc<Expr>, P<Block>),
+    // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
+    ExprWhile(Gc<Expr>, P<Block>, Option<Ident>),
     // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprForLoop(Gc<Pat>, Gc<Expr>, P<Block>, Option<Ident>),
     // Conditionless loop (can be exited with break, cont, or ret)
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 9dbea1c9ac2..d0f3cf6f9d7 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -66,6 +66,12 @@ fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
             }
         }
 
+        ast::ExprWhile(cond, body, opt_ident) => {
+            let cond = fld.fold_expr(cond);
+            let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
+            fld.cx.expr(e.span, ast::ExprWhile(cond, body, opt_ident))
+        }
+
         ast::ExprLoop(loop_block, opt_ident) => {
             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index be1c0d96711..7deabed04b8 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -1132,18 +1132,20 @@ pub fn noop_fold_expr<T: Folder>(e: Gc<Expr>, folder: &mut T) -> Gc<Expr> {
                    folder.fold_block(tr),
                    fl.map(|x| folder.fold_expr(x)))
         }
-        ExprWhile(cond, body) => {
-            ExprWhile(folder.fold_expr(cond), folder.fold_block(body))
+        ExprWhile(cond, body, opt_ident) => {
+            ExprWhile(folder.fold_expr(cond),
+                      folder.fold_block(body),
+                      opt_ident.map(|i| folder.fold_ident(i)))
         }
-        ExprForLoop(pat, iter, body, ref maybe_ident) => {
+        ExprForLoop(pat, iter, body, ref opt_ident) => {
             ExprForLoop(folder.fold_pat(pat),
                         folder.fold_expr(iter),
                         folder.fold_block(body),
-                        maybe_ident.map(|i| folder.fold_ident(i)))
+                        opt_ident.map(|i| folder.fold_ident(i)))
         }
         ExprLoop(body, opt_ident) => {
             ExprLoop(folder.fold_block(body),
-                     opt_ident.map(|x| folder.fold_ident(x)))
+                     opt_ident.map(|i| folder.fold_ident(i)))
         }
         ExprMatch(expr, ref arms) => {
             ExprMatch(folder.fold_expr(expr),
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 37bda15ac2c..60f24401152 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -2094,19 +2094,22 @@ impl<'a> Parser<'a> {
                     return self.parse_for_expr(None);
                 }
                 if self.eat_keyword(keywords::While) {
-                    return self.parse_while_expr();
+                    return self.parse_while_expr(None);
                 }
                 if Parser::token_is_lifetime(&self.token) {
                     let lifetime = self.get_lifetime();
                     self.bump();
                     self.expect(&token::COLON);
+                    if self.eat_keyword(keywords::While) {
+                        return self.parse_while_expr(Some(lifetime))
+                    }
                     if self.eat_keyword(keywords::For) {
                         return self.parse_for_expr(Some(lifetime))
                     }
                     if self.eat_keyword(keywords::Loop) {
                         return self.parse_loop_expr(Some(lifetime))
                     }
-                    self.fatal("expected `for` or `loop` after a label")
+                    self.fatal("expected `while`, `for`, or `loop` after a label")
                 }
                 if self.eat_keyword(keywords::Loop) {
                     return self.parse_loop_expr(None);
@@ -2762,12 +2765,12 @@ impl<'a> Parser<'a> {
         self.mk_expr(lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident))
     }
 
-    pub fn parse_while_expr(&mut self) -> Gc<Expr> {
+    pub fn parse_while_expr(&mut self, opt_ident: Option<ast::Ident>) -> Gc<Expr> {
         let lo = self.last_span.lo;
         let cond = self.parse_expr_res(RESTRICT_NO_STRUCT_LITERAL);
         let body = self.parse_block();
         let hi = body.span.hi;
-        return self.mk_expr(lo, hi, ExprWhile(cond, body));
+        return self.mk_expr(lo, hi, ExprWhile(cond, body, opt_ident));
     }
 
     pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>) -> Gc<Expr> {
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index da265d81250..6b94048eab7 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -1438,7 +1438,11 @@ impl<'a> State<'a> {
             ast::ExprIf(ref test, ref blk, elseopt) => {
                 try!(self.print_if(&**test, &**blk, elseopt, false));
             }
-            ast::ExprWhile(ref test, ref blk) => {
+            ast::ExprWhile(ref test, ref blk, opt_ident) => {
+                for ident in opt_ident.iter() {
+                    try!(self.print_ident(*ident));
+                    try!(self.word_space(":"));
+                }
                 try!(self.head("while"));
                 try!(self.print_expr(&**test));
                 try!(space(&mut self.s));
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 7a35d82b0e4..65e192e8437 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -1,4 +1,4 @@
-// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -775,7 +775,7 @@ pub fn walk_expr<E: Clone, V: Visitor<E>>(visitor: &mut V, expression: &Expr, en
             visitor.visit_block(&**if_block, env.clone());
             walk_expr_opt(visitor, optional_else, env.clone())
         }
-        ExprWhile(ref subexpression, ref block) => {
+        ExprWhile(ref subexpression, ref block, _) => {
             visitor.visit_expr(&**subexpression, env.clone());
             visitor.visit_block(&**block, env.clone())
         }
diff --git a/src/test/run-pass/hygienic-labels-in-let.rs b/src/test/run-pass/hygienic-labels-in-let.rs
index e6c1046d1fa..397ce75b6b9 100644
--- a/src/test/run-pass/hygienic-labels-in-let.rs
+++ b/src/test/run-pass/hygienic-labels-in-let.rs
@@ -19,6 +19,13 @@ macro_rules! loop_x {
     }
 }
 
+macro_rules! while_true {
+    ($e: expr) => {
+        // $e shouldn't be able to interact with this 'x
+        'x: while 1i + 1 == 2 { $e }
+    }
+}
+
 macro_rules! run_once {
     ($e: expr) => {
         // ditto
@@ -49,6 +56,16 @@ pub fn main() {
     };
     assert_eq!(k, 1i);
 
+    let l: int = {
+        'x: for _ in range(0i, 1) {
+            // ditto
+            while_true!(break 'x);
+            i += 1;
+        }
+        i + 1
+    };
+    assert_eq!(l, 1i);
+
     let n: int = {
         'x: for _ in range(0i, 1) {
             // ditto
diff --git a/src/test/run-pass/hygienic-labels.rs b/src/test/run-pass/hygienic-labels.rs
index 320441204df..abb0bdab71f 100644
--- a/src/test/run-pass/hygienic-labels.rs
+++ b/src/test/run-pass/hygienic-labels.rs
@@ -24,6 +24,13 @@ macro_rules! run_once {
     }
 }
 
+macro_rules! while_x {
+    ($e: expr) => {
+        // ditto
+        'x: while 1i + 1 == 2 { $e }
+    }
+}
+
 pub fn main() {
     'x: for _ in range(0i, 1) {
         // this 'x should refer to the outer loop, lexically
@@ -37,6 +44,11 @@ pub fn main() {
         fail!("break doesn't act hygienically inside infinite loop");
     }
 
+    'x: while 1i + 1 == 2 {
+        while_x!(break 'x);
+        fail!("break doesn't act hygienically inside infinite while loop");
+    }
+
     'x: for _ in range(0i, 1) {
         // ditto
         run_once!(continue 'x);
diff --git a/src/test/run-pass/labeled-break.rs b/src/test/run-pass/labeled-break.rs
index 091a57620f0..9f12cd79876 100644
--- a/src/test/run-pass/labeled-break.rs
+++ b/src/test/run-pass/labeled-break.rs
@@ -1,4 +1,4 @@
-// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// 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.
 //
@@ -20,4 +20,10 @@ pub fn main() {
             break 'bar;
         }
     }
+
+    'foobar: while 1i + 1 == 2 {
+        loop {
+            break 'foobar;
+        }
+    }
 }
diff --git a/src/test/run-pass/while-label.rs b/src/test/run-pass/while-label.rs
new file mode 100644
index 00000000000..dd53ac889f5
--- /dev/null
+++ b/src/test/run-pass/while-label.rs
@@ -0,0 +1,22 @@
+// Copyright 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 <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.
+
+
+pub fn main() {
+    let mut i = 100i;
+    'w: while 1i + 1 == 2 {
+        i -= 1;
+        if i == 95 {
+            break 'w;
+            fail!("Should have broken out of loop");
+        }
+    }
+    assert_eq!(i, 95);
+}