From 0c2c8116a307e88f8327e0ea846d2c9c135193b7 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 2 Oct 2014 22:45:46 -0400 Subject: [PATCH 1/7] Teach libsyntax about `while let` --- src/libsyntax/ast.rs | 2 ++ src/libsyntax/ext/expand.rs | 36 +++++++++++++++++++++++++++++++++ src/libsyntax/fold.rs | 6 ++++++ src/libsyntax/parse/classify.rs | 1 + src/libsyntax/parse/parser.rs | 18 ++++++++++++++++- src/libsyntax/print/pprust.rs | 13 ++++++++++++ src/libsyntax/visit.rs | 5 +++++ 7 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 274bb2e39e019..675249b259148 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -524,6 +524,8 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprWhile(P, P, Option), // FIXME #6993: change to Option ... or not, if these are hygienic. + ExprWhileLet(P, P, P, Option), + // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(P, P, P, Option), // Conditionless loop (can be exited with break, cont, or ret) // FIXME #6993: change to Option ... or not, if these are hygienic. diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index efe4b76354f56..575dcf32dd6cb 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -67,6 +67,42 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) } + // Desugar ExprWhileLet + // From: `[opt_ident]: while let = ` + ast::ExprWhileLet(pat, expr, body, opt_ident) => { + // to: + // + // [opt_ident]: loop { + // match { + // => , + // _ => break + // } + // } + + // ` => ` + let pat_arm = { + let body_expr = fld.cx.expr_block(body); + fld.cx.arm(pat.span, vec![pat], body_expr) + }; + + // `_ => break` + let break_arm = { + let pat_under = fld.cx.pat_wild(span); + let break_expr = fld.cx.expr_break(span); + fld.cx.arm(span, vec![pat_under], break_expr) + }; + + // `match { ... }` + let arms = vec![pat_arm, break_arm]; + let match_expr = fld.cx.expr(span, + ast::ExprMatch(expr, arms, ast::MatchWhileLetDesugar)); + + // `[opt_ident]: loop { ... }` + let loop_block = fld.cx.block_expr(match_expr); + let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); + fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)) + } + // Desugar ExprIfLet // From: `if let = []` ast::ExprIfLet(pat, expr, body, mut elseopt) => { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 32e226361e9d4..6ecb4d3208d14 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1218,6 +1218,12 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> folder.fold_block(body), opt_ident.map(|i| folder.fold_ident(i))) } + ExprWhileLet(pat, expr, body, opt_ident) => { + ExprWhileLet(folder.fold_pat(pat), + folder.fold_expr(expr), + folder.fold_block(body), + opt_ident.map(|i| folder.fold_ident(i))) + } ExprForLoop(pat, iter, body, opt_ident) => { ExprForLoop(folder.fold_pat(pat), folder.fold_expr(iter), diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index cb57318445e41..d46d078c776be 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { | ast::ExprMatch(..) | ast::ExprBlock(_) | ast::ExprWhile(..) + | ast::ExprWhileLet(..) | ast::ExprLoop(..) | ast::ExprForLoop(..) => false, _ => true diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e7f40cf072296..5f304fb0aeb82 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -26,7 +26,7 @@ use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, Ex use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; -use ast::{ExprVec, ExprWhile, ExprForLoop, Field, FnDecl}; +use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; use ast::{Once, Many}; use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind}; use ast::{FnOnceUnboxedClosureKind}; @@ -2935,7 +2935,11 @@ impl<'a> Parser<'a> { self.mk_expr(lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident)) } + /// Parse a 'while' or 'while let' expression ('while' token already eaten) pub fn parse_while_expr(&mut self, opt_ident: Option) -> P { + if self.is_keyword(keywords::Let) { + return self.parse_while_let_expr(opt_ident); + } let lo = self.last_span.lo; let cond = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL); let body = self.parse_block(); @@ -2943,6 +2947,18 @@ impl<'a> Parser<'a> { return self.mk_expr(lo, hi, ExprWhile(cond, body, opt_ident)); } + /// Parse a 'while let' expression ('while' token already eaten) + pub fn parse_while_let_expr(&mut self, opt_ident: Option) -> P { + let lo = self.last_span.lo; + self.expect_keyword(keywords::Let); + let pat = self.parse_pat(); + self.expect(&token::EQ); + let expr = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL); + let body = self.parse_block(); + let hi = body.span.hi; + return self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident)); + } + pub fn parse_loop_expr(&mut self, opt_ident: Option) -> P { let lo = self.last_span.lo; let body = self.parse_block(); diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e1a2b2aeefeea..fd3dd1c5a2cee 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1525,6 +1525,19 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } + ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => { + for ident in opt_ident.iter() { + try!(self.print_ident(*ident)); + try!(self.word_space(":")); + } + try!(self.head("while let")); + try!(self.print_pat(&**pat)); + try!(space(&mut self.s)); + try!(self.word_space("=")); + try!(self.print_expr(&**expr)); + try!(space(&mut self.s)); + try!(self.print_block(&**blk)); + } ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => { for ident in opt_ident.iter() { try!(self.print_ident(*ident)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5c7b144f4ab6e..ce316ef57668a 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -737,6 +737,11 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_block(&**if_block); walk_expr_opt(visitor, optional_else); } + ExprWhileLet(ref pattern, ref subexpression, ref block, _) => { + visitor.visit_pat(&**pattern); + visitor.visit_expr(&**subexpression); + visitor.visit_block(&**block); + } ExprForLoop(ref pattern, ref subexpression, ref block, _) => { visitor.visit_pat(&**pattern); visitor.visit_expr(&**subexpression); From b003f104490599c49d27842c9a25b158e7cccdcb Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 2 Oct 2014 23:28:15 -0400 Subject: [PATCH 2/7] Desugar `while let` into `loop { match { ... } }` --- src/libsyntax/ast.rs | 3 ++- src/libsyntax/ext/build.rs | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 675249b259148..4ed3ee0159884 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -581,7 +581,8 @@ pub struct QPath { #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] pub enum MatchSource { MatchNormal, - MatchIfLetDesugar + MatchIfLetDesugar, + MatchWhileLetDesugar, } #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)] diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 87983e1aea3a9..98ac6fe6a6c80 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -147,6 +147,8 @@ pub trait AstBuilder { fn expr_some(&self, sp: Span, expr: P) -> P; fn expr_none(&self, sp: Span) -> P; + fn expr_break(&self, sp: Span) -> P; + fn expr_tuple(&self, sp: Span, exprs: Vec>) -> P; fn expr_fail(&self, span: Span, msg: InternedString) -> P; @@ -688,6 +690,12 @@ impl<'a> AstBuilder for ExtCtxt<'a> { self.expr_path(none) } + + fn expr_break(&self, sp: Span) -> P { + self.expr(sp, ast::ExprBreak(None)) + } + + fn expr_tuple(&self, sp: Span, exprs: Vec>) -> P { self.expr(sp, ast::ExprTup(exprs)) } From 660cf180701d5fc961a900d626c1a1d314852bb1 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 2 Oct 2014 23:32:32 -0400 Subject: [PATCH 3/7] Move `while let` behind `while_let` feature gate --- src/doc/reference.md | 2 ++ src/libsyntax/feature_gate.rs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/doc/reference.md b/src/doc/reference.md index c34a136a68e88..261e5edcf665b 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2503,6 +2503,8 @@ The currently implemented features of the reference compiler are: * `if_let` - Allows use of the `if let` syntax. +* `while_let` - Allows use of the `while let` syntax. + * `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics are inherently unstable and no promise about them is made. diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index c3c36d0444272..28b20133a1f2b 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -73,6 +73,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("slicing_syntax", Active), ("if_let", Active), + ("while_let", Active), // if you change this list without updating src/doc/reference.md, cmr will be sad @@ -356,6 +357,10 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { e.span, "slicing syntax is experimental"); } + ast::ExprWhileLet(..) => { + self.gate_feature("while_let", e.span, + "`while let` syntax is experimental"); + } _ => {} } visit::walk_expr(self, e); From 45fd623762aca093ab913ed3a8fcd13c5798005d Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 3 Oct 2014 00:41:24 -0400 Subject: [PATCH 4/7] Handle `while let` desugaring --- src/librustc/diagnostics.rs | 3 ++- src/librustc/lint/builtin.rs | 3 ++- src/librustc/middle/cfg/construct.rs | 4 +++ src/librustc/middle/check_match.rs | 30 +++++++++++++++-------- src/librustc/middle/expr_use_visitor.rs | 4 +++ src/librustc/middle/liveness.rs | 10 ++++++++ src/librustc/middle/mem_categorization.rs | 3 +++ src/librustc/middle/trans/debuginfo.rs | 5 ++++ src/librustc/middle/ty.rs | 3 +++ src/librustc/middle/typeck/check/mod.rs | 3 +++ src/librustc/util/ppaux.rs | 3 +++ src/librustc_back/svh.rs | 1 + 12 files changed, 60 insertions(+), 12 deletions(-) diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index b39c415216837..0ccfc7e80f606 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -150,5 +150,6 @@ register_diagnostics!( E0161, E0162, E0163, - E0164 + E0164, + E0165 ) diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index e0e16390e353f..988b128e31d5c 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1082,7 +1082,8 @@ impl LintPass for UnnecessaryParens { ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true), ast::ExprMatch(ref head, _, source) => match source { ast::MatchNormal => (head, "`match` head expression", true), - ast::MatchIfLetDesugar => (head, "`if let` head expression", true) + ast::MatchIfLetDesugar => (head, "`if let` head expression", true), + ast::MatchWhileLetDesugar => (head, "`while let` head expression", true), }, ast::ExprRet(Some(ref value)) => (value, "`return` value", false), ast::ExprAssign(_, ref value) => (value, "assigned value", false), diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index a9a3981ab5fc8..b573c4e59487a 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -259,6 +259,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { expr_exit } + ast::ExprWhileLet(..) => { + self.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } + ast::ExprForLoop(ref pat, ref head, ref body, _) => { // // [pred] diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 297640707687c..dde3f3281f09f 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -261,20 +261,30 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec>, Option<&Expr>)], source match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) { NotUseful => { - if source == MatchIfLetDesugar { - if printed_if_let_err { - // we already printed an irrefutable if-let pattern error. - // We don't want two, that's just confusing. - } else { + match source { + MatchIfLetDesugar => { + if printed_if_let_err { + // we already printed an irrefutable if-let pattern error. + // We don't want two, that's just confusing. + } else { + // find the first arm pattern so we can use its span + let &(ref first_arm_pats, _) = &arms[0]; + let first_pat = first_arm_pats.get(0); + let span = first_pat.span; + span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern"); + printed_if_let_err = true; + } + }, + + MatchWhileLetDesugar => { // find the first arm pattern so we can use its span let &(ref first_arm_pats, _) = &arms[0]; let first_pat = first_arm_pats.get(0); let span = first_pat.span; - span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern"); - printed_if_let_err = true; - } - } else { - span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"); + span_err!(cx.tcx.sess, span, E0165, "irrefutable while-let pattern"); + }, + + _ => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern") } } Useful => (), diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 6cfdac93efc8a..6f179e0624fe2 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -429,6 +429,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> { self.walk_block(&**blk); } + ast::ExprWhileLet(..) => { + self.tcx().sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } + ast::ExprForLoop(ref pat, ref head, ref blk, _) => { // The pattern lives as long as the block. debug!("walk_expr for loop case: blk id={}", blk.id); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 2176cd5658943..90973dfaa0028 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -484,6 +484,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { ExprIfLet(..) => { ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet"); } + ExprWhileLet(..) => { + ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } ExprForLoop(ref pat, _, _, _) => { pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| { debug!("adding local variable {} from for loop with bm {:?}", @@ -1022,6 +1025,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_loop(expr, WhileLoop(&**cond), &**blk, succ) } + ExprWhileLet(..) => { + self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } + ExprForLoop(ref pat, ref head, ref blk, _) => { let ln = self.propagate_through_loop(expr, ForLoop(&**pat), &**blk, succ); self.propagate_through_expr(&**head, ln) @@ -1480,6 +1487,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ExprIfLet(..) => { this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet"); } + ExprWhileLet(..) => { + this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index fa494b357c19d..4a1c4aaa89514 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -530,6 +530,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::ExprIfLet(..) => { self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet"); } + ast::ExprWhileLet(..) => { + self.tcx().sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 43c989982996f..d81f78f23ffaf 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -3496,6 +3496,11 @@ fn populate_scope_map(cx: &CrateContext, }) } + ast::ExprWhileLet(..) => { + cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \ + Found unexpanded while-let."); + } + ast::ExprForLoop(ref pattern, ref head, ref body, _) => { walk_expr(cx, &**head, scope_stack, scope_map); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index cea74c6573d54..9a51b258ae469 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3615,6 +3615,9 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprIfLet(..) => { tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet"); } + ast::ExprWhileLet(..) => { + tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } ast::ExprLit(ref lit) if lit_is_str(&**lit) => { RvalueDpsExpr diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 68bb3fcf94544..07a56ad46203c 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -4116,6 +4116,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, fcx.write_nil(id); } } + ast::ExprWhileLet(..) => { + tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet"); + } ast::ExprForLoop(ref pat, ref head, ref block, _) => { check_expr(fcx, &**head); let typ = lookup_method_for_for_loop(fcx, &**head, expr.id); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 6898eb17e9170..459e7eb093e7c 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -93,6 +93,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region) explain_span(cx, "method call", expr.span) }, ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if let", expr.span), + ast::ExprMatch(_, _, ast::MatchWhileLetDesugar) => { + explain_span(cx, "while let", expr.span) + }, ast::ExprMatch(..) => explain_span(cx, "match", expr.span), _ => explain_span(cx, "expression", expr.span) } diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index 35e42e3c54b23..3c28cac6c6f7c 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -294,6 +294,7 @@ mod svh_visitor { // just syntactic artifacts, expanded away by time of SVH. ExprIfLet(..) => unreachable!(), + ExprWhileLet(..) => unreachable!(), ExprMac(..) => unreachable!(), } } From b5db97b354e5b1e1b5d99f513a40493f223fddae Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 3 Oct 2014 00:59:32 -0400 Subject: [PATCH 5/7] Add tests for `while let` --- .../compile-fail/lint-unnecessary-parens.rs | 3 +- src/test/compile-fail/while-let.rs | 37 ++++++++++++ src/test/run-pass/while-let.rs | 56 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/test/compile-fail/while-let.rs create mode 100644 src/test/run-pass/while-let.rs diff --git a/src/test/compile-fail/lint-unnecessary-parens.rs b/src/test/compile-fail/lint-unnecessary-parens.rs index 3c962ebf21670..6c766dec9a3e9 100644 --- a/src/test/compile-fail/lint-unnecessary-parens.rs +++ b/src/test/compile-fail/lint-unnecessary-parens.rs @@ -9,7 +9,7 @@ // except according to those terms. #![deny(unnecessary_parens)] -#![feature(if_let)] +#![feature(if_let,while_let)] #[deriving(Eq, PartialEq)] struct X { y: bool } @@ -34,6 +34,7 @@ fn main() { _ => {} } if let 1i = (1i) {} //~ ERROR unnecessary parentheses around `if let` head expression + while let 1i = (2i) {} //~ ERROR unnecessary parentheses around `while let` head expression let v = X { y: false }; // struct lits needs parens, so these shouldn't warn. if (v == X { y: true }) {} diff --git a/src/test/compile-fail/while-let.rs b/src/test/compile-fail/while-let.rs new file mode 100644 index 0000000000000..0dd442ec3f66a --- /dev/null +++ b/src/test/compile-fail/while-let.rs @@ -0,0 +1,37 @@ +// 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. + +#![feature(macro_rules,while_let)] + +fn macros() { + macro_rules! foo{ + ($p:pat, $e:expr, $b:block) => {{ + while let $p = $e $b + }} + } + macro_rules! bar{ + ($p:pat, $e:expr, $b:block) => {{ + foo!($p, $e, $b) + }} + } + + foo!(a, 1i, { //~ ERROR irrefutable while-let + println!("irrefutable pattern"); + }); + bar!(a, 1i, { //~ ERROR irrefutable while-let + println!("irrefutable pattern"); + }); +} + +pub fn main() { + while let a = 1i { //~ ERROR irrefutable while-let + println!("irrefutable pattern"); + } +} diff --git a/src/test/run-pass/while-let.rs b/src/test/run-pass/while-let.rs new file mode 100644 index 0000000000000..41f54d47ad42c --- /dev/null +++ b/src/test/run-pass/while-let.rs @@ -0,0 +1,56 @@ +// 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. + +#![feature(while_let)] + +use std::collections::PriorityQueue; + +fn make_pq() -> PriorityQueue { + PriorityQueue::from_vec(vec![1i,2,3]) +} + +pub fn main() { + let mut pq = make_pq(); + let mut sum = 0i; + while let Some(x) = pq.pop() { + sum += x; + } + assert_eq!(sum, 6i); + + pq = make_pq(); + sum = 0; + 'a: while let Some(x) = pq.pop() { + sum += x; + if x == 2 { + break 'a; + } + } + assert_eq!(sum, 5i); + + pq = make_pq(); + sum = 0; + 'a: while let Some(x) = pq.pop() { + if x == 3 { + continue 'a; + } + sum += x; + } + assert_eq!(sum, 3i); + + let mut pq1 = make_pq(); + sum = 0; + while let Some(x) = pq1.pop() { + let mut pq2 = make_pq(); + while let Some(y) = pq2.pop() { + sum += x * y; + } + } + assert_eq!(sum, 6i + 12 + 18); +} From 0d6dafa51fb6d5a1a2bee788801f384b9355b08a Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 3 Oct 2014 01:09:01 -0400 Subject: [PATCH 6/7] Add `while let` to the reference Closes #17687 --- src/doc/reference.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/doc/reference.md b/src/doc/reference.md index 261e5edcf665b..4ad64576e1588 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -3511,6 +3511,18 @@ of a condition expression it expects a refutable let statement. If the value of expression on the right hand side of the let statement matches the pattern, the corresponding block will execute, otherwise flow proceeds to the first `else` block that follows. +### While let loops + +```{.ebnf .gram} +while_let_expr : "while" "let" pat '=' expr '{' block '}' ; +``` + +A `while let` loop is semantically identical to a `while` loop but in place of a +condition expression it expects a refutable let statement. If the value of the +expression on the right hand side of the let statement matches the pattern, the +loop body block executes and control returns to the pattern matching statement. +Otherwise, the while expression completes. + ### Return expressions ```{.ebnf .gram} From 16ccdba8512fc7ce397095cf5d62858ef2234c47 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 11 Oct 2014 12:03:15 -0400 Subject: [PATCH 7/7] Use explicit case rather than catch-all --- src/librustc/middle/check_match.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index dde3f3281f09f..3976574aef9c0 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -284,7 +284,9 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec>, Option<&Expr>)], source span_err!(cx.tcx.sess, span, E0165, "irrefutable while-let pattern"); }, - _ => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern") + MatchNormal => { + span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern") + }, } } Useful => (),