Skip to content

Commit

Permalink
auto merge of #16190 : Pythoner6/rust/labeled-while-loop, r=alexcrichton
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bors committed Aug 30, 2014
2 parents c8e86e9 + e76db8e commit 43c26e6
Show file tree
Hide file tree
Showing 21 changed files with 106 additions and 33 deletions.
4 changes: 2 additions & 2 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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]
// |
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/check_loop.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/region.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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);
}
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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));

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, _) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/typeck/check/regionck.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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, ());

Expand Down
3 changes: 2 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
14 changes: 8 additions & 6 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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),
Expand Down
11 changes: 7 additions & 4 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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> {
Expand Down
8 changes: 6 additions & 2 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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));
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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())
}
Expand Down
17 changes: 17 additions & 0 deletions src/test/run-pass/hygienic-labels-in-let.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions src/test/run-pass/hygienic-labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion src/test/run-pass/labeled-break.rs
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -20,4 +20,10 @@ pub fn main() {
break 'bar;
}
}

'foobar: while 1i + 1 == 2 {
loop {
break 'foobar;
}
}
}
22 changes: 22 additions & 0 deletions src/test/run-pass/while-label.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 43c26e6

Please sign in to comment.