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..bbdfae0f91b 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -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..e030e4bbd4e 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -35,7 +35,7 @@ impl<'a> Visitor 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..9366d722c5e 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -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..f8952454b21 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -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 bd337c4b934..3a9ec40fdbb 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -3489,7 +3489,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..39b1e764eba 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -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), ExprCast(Gc, P), ExprIf(Gc, P, Option>), - ExprWhile(Gc, P), + // FIXME #6993: change to Option ... or not, if these are hygienic. + ExprWhile(Gc, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(Gc, Gc, P, Option), // 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, fld: &mut MacroExpander) -> Gc { } } + 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..946ff7ff3a4 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1132,18 +1132,20 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { 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 { + pub fn parse_while_expr(&mut self, opt_ident: Option) -> Gc { 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) -> Gc { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index da265d81250..c0483f16213 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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..ffe8c0e6e01 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -775,7 +775,7 @@ pub fn walk_expr>(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..5e9bae0884b 100644 --- a/src/test/run-pass/labeled-break.rs +++ b/src/test/run-pass/labeled-break.rs @@ -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 or the MIT license +// , 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); +}