From 07351b44c657225d45e2aa0a302ccd28f96839a8 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 8 Sep 2013 22:08:01 +1000 Subject: [PATCH 1/2] syntax: implement labelled breaks for `for`. `for` desugars to `loop` so it is trivial to just desugar to `loop` while retaining any label. --- src/librustc/middle/trans/debuginfo.rs | 2 +- src/libsyntax/ast.rs | 2 +- src/libsyntax/ext/expand.rs | 4 ++-- src/libsyntax/fold.rs | 7 ++++--- src/libsyntax/oldvisit.rs | 2 +- src/libsyntax/parse/parser.rs | 15 ++++++++++----- src/libsyntax/print/pprust.rs | 7 ++++++- src/libsyntax/visit.rs | 3 +-- src/test/run-pass/labeled-break.rs | 6 ++++++ 9 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 21bde8e388b80..b89414a6a53a0 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -2064,7 +2064,7 @@ fn populate_scope_map(cx: &mut CrateContext, } } - ast::ExprForLoop(_, _, _) => { + ast::ExprForLoop(_, _, _, _) => { cx.sess.span_bug(exp.span, "debuginfo::populate_scope_map() - \ Found unexpanded for-loop."); } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index b993f98ec82b1..6957f9ae3519d 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -528,7 +528,7 @@ pub enum Expr_ { ExprCast(@Expr, Ty), ExprIf(@Expr, Block, Option<@Expr>), ExprWhile(@Expr, Block), - ExprForLoop(@Pat, @Expr, Block), + ExprForLoop(@Pat, @Expr, Block, Option), /* Conditionless loop (can be exited with break, cont, or ret) Same semantics as while(true) { body }, but typestate knows that the (implicit) condition is always true. */ diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index cde418ca991fa..abade04fedf81 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -113,7 +113,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, // Desugar expr_for_loop // From: `for in ` - ast::ExprForLoop(src_pat, src_expr, ref src_loop_block) => { + ast::ExprForLoop(src_pat, src_expr, ref src_loop_block, opt_ident) => { let src_pat = src_pat.clone(); let src_expr = src_expr.clone(); @@ -257,7 +257,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, // `loop { ... }` let loop_block = { let loop_body_block = mk_block(cx, [match_stmt], None, span); - let loop_body_expr = mk_expr(cx, span, ast::ExprLoop(loop_body_block, None)); + let loop_body_expr = mk_expr(cx, span, ast::ExprLoop(loop_body_block, opt_ident)); let loop_body_stmt = @spanned(lo, hi, ast::StmtExpr(loop_body_expr, cx.next_id())); mk_block(cx, [iter_decl_stmt, loop_body_stmt], diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index aad992706fd01..11e39163992d8 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -557,10 +557,11 @@ pub fn noop_fold_expr(e: &Expr_, fld: @ast_fold) -> Expr_ { ExprWhile(cond, ref body) => { ExprWhile(fld.fold_expr(cond), fld.fold_block(body)) } - ExprForLoop(pat, iter, ref body) => { + ExprForLoop(pat, iter, ref body, opt_ident) => { ExprForLoop(fld.fold_pat(pat), - fld.fold_expr(iter), - fld.fold_block(body)) + fld.fold_expr(iter), + fld.fold_block(body), + opt_ident.map_move(|x| fld.fold_ident(x))) } ExprLoop(ref body, opt_ident) => { ExprLoop( diff --git a/src/libsyntax/oldvisit.rs b/src/libsyntax/oldvisit.rs index e9d7b5c4a0dd1..850f73096edbb 100644 --- a/src/libsyntax/oldvisit.rs +++ b/src/libsyntax/oldvisit.rs @@ -523,7 +523,7 @@ pub fn visit_expr(ex: @Expr, (e, v): (E, vt)) { (v.visit_expr)(x, (e.clone(), v)); (v.visit_block)(b, (e.clone(), v)); } - ExprForLoop(pattern, subexpression, ref block) => { + ExprForLoop(pattern, subexpression, ref block, _) => { (v.visit_pat)(pattern, (e.clone(), v)); (v.visit_expr)(subexpression, (e.clone(), v)); (v.visit_block)(block, (e.clone(), v)) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8b11a25f13ce3..b86153525047e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1771,7 +1771,7 @@ impl Parser { } else if self.eat_keyword(keywords::If) { return self.parse_if_expr(); } else if self.eat_keyword(keywords::For) { - return self.parse_for_expr(); + return self.parse_for_expr(None); } else if self.eat_keyword(keywords::Do) { return self.parse_sugary_call_expr(lo, ~"do", DoSugar, ExprDoBody); @@ -1781,8 +1781,13 @@ impl Parser { let lifetime = self.get_lifetime(&*self.token); self.bump(); self.expect(&token::COLON); - self.expect_keyword(keywords::Loop); - return self.parse_loop_expr(Some(lifetime)); + if self.eat_keyword(keywords::For) { + return self.parse_for_expr(Some(lifetime)) + } else if self.eat_keyword(keywords::Loop) { + return self.parse_loop_expr(Some(lifetime)) + } else { + self.fatal("expected `for` or `loop` after a label") + } } else if self.eat_keyword(keywords::Loop) { return self.parse_loop_expr(None); } else if self.eat_keyword(keywords::Match) { @@ -2467,7 +2472,7 @@ impl Parser { } // parse a 'for' .. 'in' expression ('for' token already eaten) - pub fn parse_for_expr(&self) -> @Expr { + pub fn parse_for_expr(&self, opt_ident: Option) -> @Expr { // Parse: `for in ` let lo = self.last_span.lo; @@ -2477,7 +2482,7 @@ impl Parser { let loop_block = self.parse_block(); let hi = self.span.hi; - self.mk_expr(lo, hi, ExprForLoop(pat, expr, loop_block)) + self.mk_expr(lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident)) } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index f440e0a177110..282b66b2c59ef 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1232,7 +1232,12 @@ pub fn print_expr(s: @ps, expr: &ast::Expr) { space(s.s); print_block(s, blk); } - ast::ExprForLoop(pat, iter, ref blk) => { + ast::ExprForLoop(pat, iter, ref blk, opt_ident) => { + for ident in opt_ident.iter() { + word(s.s, "'"); + print_ident(s, *ident); + word_space(s, ":"); + } head(s, "for"); print_pat(s, pat); space(s.s); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ae2044cb97952..191b4509a80b3 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -599,7 +599,7 @@ pub fn walk_expr>(visitor: &mut V, expression: @Expr, env: visitor.visit_expr(subexpression, env.clone()); visitor.visit_block(block, env.clone()) } - ExprForLoop(pattern, subexpression, ref block) => { + ExprForLoop(pattern, subexpression, ref block, _) => { visitor.visit_pat(pattern, env.clone()); visitor.visit_expr(subexpression, env.clone()); visitor.visit_block(block, env.clone()) @@ -812,4 +812,3 @@ impl Visitor<()> for SimpleVisitorVisitor { walk_struct_field(self, struct_field, env) } } - diff --git a/src/test/run-pass/labeled-break.rs b/src/test/run-pass/labeled-break.rs index b6b6e0e143795..d7d210c752414 100644 --- a/src/test/run-pass/labeled-break.rs +++ b/src/test/run-pass/labeled-break.rs @@ -14,4 +14,10 @@ pub fn main() { break 'foo; } } + + 'bar: for _ in range(0, 100) { + loop { + break 'bar; + } + } } From 14183114e1048097869a5c56ebfa4a6d71e1a492 Mon Sep 17 00:00:00 2001 From: Huon Wilson Date: Sun, 8 Sep 2013 22:46:20 +1000 Subject: [PATCH 2/2] syntax: aesthetic improvements to the `for` desugaring. --- src/libsyntax/ext/expand.rs | 168 ++++++++---------------------------- 1 file changed, 38 insertions(+), 130 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index abade04fedf81..099fa54c0c299 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -14,13 +14,13 @@ use ast::{item_mac, Mrk, Stmt_, StmtDecl, StmtMac, StmtExpr, StmtSemi}; use ast::{token_tree}; use ast; use ast_util::{mtwt_outer_mark, new_rename, new_mark}; +use ext::build::AstBuilder; use attr; use attr::AttrMetaMethods; use codemap; -use codemap::{Span, Spanned, spanned, ExpnInfo, NameAndSpan}; +use codemap::{Span, Spanned, ExpnInfo, NameAndSpan}; use ext::base::*; use fold::*; -use opt_vec; use parse; use parse::{parse_item_from_source_str}; use parse::token; @@ -33,7 +33,7 @@ use std::vec; pub fn expand_expr(extsbox: @mut SyntaxEnv, cx: @ExtCtxt, e: &Expr_, - s: Span, + span: Span, fld: @ast_fold, orig: @fn(&Expr_, Span, @ast_fold) -> (Expr_, Span)) -> (Expr_, Span) { @@ -66,7 +66,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, } Some(@SE(NormalTT(expandfun, exp_span))) => { cx.bt_push(ExpnInfo { - call_site: s, + call_site: span, callee: NameAndSpan { name: extnamestr, span: exp_span, @@ -98,7 +98,7 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, fld.fold_expr(marked_after).node.clone(); cx.bt_pop(); - (fully_expanded, s) + (fully_expanded, span) } _ => { cx.span_fatal( @@ -112,62 +112,18 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, } // Desugar expr_for_loop - // From: `for in ` + // From: `[':] for in ` ast::ExprForLoop(src_pat, src_expr, ref src_loop_block, opt_ident) => { - let src_pat = src_pat.clone(); - let src_expr = src_expr.clone(); - // Expand any interior macros etc. // NB: we don't fold pats yet. Curious. - let src_expr = fld.fold_expr(src_expr).clone(); - let src_loop_block = fld.fold_block(src_loop_block).clone(); - - let span = s; - let lo = s.lo; - let hi = s.hi; - - pub fn mk_expr(cx: @ExtCtxt, span: Span, - node: Expr_) -> @ast::Expr { - @ast::Expr { - id: cx.next_id(), - node: node, - span: span, - } - } - - fn mk_block(cx: @ExtCtxt, - stmts: &[@ast::Stmt], - expr: Option<@ast::Expr>, - span: Span) -> ast::Block { - ast::Block { - view_items: ~[], - stmts: stmts.to_owned(), - expr: expr, - id: cx.next_id(), - rules: ast::DefaultBlock, - span: span, - } - } - - fn mk_simple_path(ident: ast::Ident, span: Span) -> ast::Path { - ast::Path { - span: span, - global: false, - segments: ~[ - ast::PathSegment { - identifier: ident, - lifetime: None, - types: opt_vec::Empty, - } - ], - } - } + let src_expr = fld.fold_expr(src_expr); + let src_loop_block = fld.fold_block(src_loop_block); // to: // // { // let _i = &mut ; - // loop { + // [':] loop { // match i.next() { // None => break, // Some() => @@ -176,98 +132,50 @@ pub fn expand_expr(extsbox: @mut SyntaxEnv, // } let local_ident = token::gensym_ident("i"); - let some_ident = token::str_to_ident("Some"); - let none_ident = token::str_to_ident("None"); - let next_ident = token::str_to_ident("next"); + let next_ident = cx.ident_of("next"); + let none_ident = cx.ident_of("None"); - let local_path_1 = mk_simple_path(local_ident, span); - let local_path_2 = mk_simple_path(local_ident, span); - let some_path = mk_simple_path(some_ident, span); - let none_path = mk_simple_path(none_ident, span); + let local_path = cx.path_ident(span, local_ident); + let some_path = cx.path_ident(span, cx.ident_of("Some")); // `let i = &mut ` - let iter_decl_stmt = { - let ty = ast::Ty { - id: cx.next_id(), - node: ast::ty_infer, - span: span - }; - let local = @ast::Local { - is_mutbl: false, - ty: ty, - pat: @ast::Pat { - id: cx.next_id(), - node: ast::PatIdent(ast::BindInfer, local_path_1, None), - span: src_expr.span - }, - init: Some(mk_expr(cx, src_expr.span, - ast::ExprAddrOf(ast::MutMutable, src_expr))), - id: cx.next_id(), - span: src_expr.span, - }; - let e = @spanned(src_expr.span.lo, - src_expr.span.hi, - ast::DeclLocal(local)); - @spanned(lo, hi, ast::StmtDecl(e, cx.next_id())) - }; + let iter_decl_stmt = cx.stmt_let(span, false, local_ident, + cx.expr_mut_addr_of(span, src_expr)); - // `None => break;` + // `None => break ['];` let none_arm = { - let break_expr = mk_expr(cx, span, ast::ExprBreak(None)); - let break_stmt = @spanned(lo, hi, ast::StmtExpr(break_expr, cx.next_id())); - let none_block = mk_block(cx, [break_stmt], None, span); - let none_pat = @ast::Pat { - id: cx.next_id(), - node: ast::PatIdent(ast::BindInfer, none_path, None), - span: span - }; - ast::Arm { - pats: ~[none_pat], - guard: None, - body: none_block - } + let break_expr = cx.expr(span, ast::ExprBreak(opt_ident)); + let none_pat = cx.pat_ident(span, none_ident); + cx.arm(span, ~[none_pat], break_expr) }; // `Some() => ` - let some_arm = { - let pat = @ast::Pat { - id: cx.next_id(), - node: ast::PatEnum(some_path, Some(~[src_pat])), - span: src_pat.span - }; - ast::Arm { - pats: ~[pat], - guard: None, - body: src_loop_block - } - }; + let some_arm = + cx.arm(span, + ~[cx.pat_enum(span, some_path, ~[src_pat])], + cx.expr_block(src_loop_block)); // `match i.next() { ... }` - let match_stmt = { - let local_expr = mk_expr(cx, span, ast::ExprPath(local_path_2)); - let next_call_expr = mk_expr(cx, span, - ast::ExprMethodCall(cx.next_id(), - local_expr, next_ident, - ~[], ~[], ast::NoSugar)); - let match_expr = mk_expr(cx, span, ast::ExprMatch(next_call_expr, - ~[none_arm, some_arm])); - @spanned(lo, hi, ast::StmtExpr(match_expr, cx.next_id())) - }; + let match_expr = { + let next_call_expr = + cx.expr_method_call(span, cx.expr_path(local_path), next_ident, ~[]); - // `loop { ... }` - let loop_block = { - let loop_body_block = mk_block(cx, [match_stmt], None, span); - let loop_body_expr = mk_expr(cx, span, ast::ExprLoop(loop_body_block, opt_ident)); - let loop_body_stmt = @spanned(lo, hi, ast::StmtExpr(loop_body_expr, cx.next_id())); - mk_block(cx, [iter_decl_stmt, - loop_body_stmt], - None, span) + cx.expr_match(span, next_call_expr, ~[none_arm, some_arm]) }; - (ast::ExprBlock(loop_block), span) + // ['ident:] loop { ... } + let loop_expr = cx.expr(span, + ast::ExprLoop(cx.block_expr(match_expr), opt_ident)); + + // `{ let ... ; loop { ... } }` + let block = cx.block(span, + ~[iter_decl_stmt], + Some(loop_expr)); + + (ast::ExprBlock(block), span) } - _ => orig(e, s, fld) + _ => orig(e, span, fld) } }