Skip to content

Commit c7e0724

Browse files
committed
auto merge of #17733 : jgallagher/rust/while-let, r=alexcrichton
This is *heavily* based on `if let` (#17634) by @jakub- and @kballard This should close #17687
2 parents daa71e4 + 16ccdba commit c7e0724

25 files changed

+266
-15
lines changed

src/doc/reference.md

+14
Original file line numberDiff line numberDiff line change
@@ -2488,6 +2488,8 @@ The currently implemented features of the reference compiler are:
24882488

24892489
* `if_let` - Allows use of the `if let` syntax.
24902490

2491+
* `while_let` - Allows use of the `while let` syntax.
2492+
24912493
* `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
24922494
are inherently unstable and no promise about them is made.
24932495

@@ -3494,6 +3496,18 @@ of a condition expression it expects a refutable let statement. If the value of
34943496
expression on the right hand side of the let statement matches the pattern, the corresponding
34953497
block will execute, otherwise flow proceeds to the first `else` block that follows.
34963498

3499+
### While let loops
3500+
3501+
```{.ebnf .gram}
3502+
while_let_expr : "while" "let" pat '=' expr '{' block '}' ;
3503+
```
3504+
3505+
A `while let` loop is semantically identical to a `while` loop but in place of a
3506+
condition expression it expects a refutable let statement. If the value of the
3507+
expression on the right hand side of the let statement matches the pattern, the
3508+
loop body block executes and control returns to the pattern matching statement.
3509+
Otherwise, the while expression completes.
3510+
34973511
### Return expressions
34983512

34993513
```{.ebnf .gram}

src/librustc/diagnostics.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -145,5 +145,6 @@ register_diagnostics!(
145145
E0161,
146146
E0162,
147147
E0163,
148-
E0164
148+
E0164,
149+
E0165
149150
)

src/librustc/lint/builtin.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,8 @@ impl LintPass for UnnecessaryParens {
10821082
ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
10831083
ast::ExprMatch(ref head, _, source) => match source {
10841084
ast::MatchNormal => (head, "`match` head expression", true),
1085-
ast::MatchIfLetDesugar => (head, "`if let` head expression", true)
1085+
ast::MatchIfLetDesugar => (head, "`if let` head expression", true),
1086+
ast::MatchWhileLetDesugar => (head, "`while let` head expression", true),
10861087
},
10871088
ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
10881089
ast::ExprAssign(_, ref value) => (value, "assigned value", false),

src/librustc/middle/cfg/construct.rs

+4
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
259259
expr_exit
260260
}
261261

262+
ast::ExprWhileLet(..) => {
263+
self.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
264+
}
265+
262266
ast::ExprForLoop(ref pat, ref head, ref body, _) => {
263267
//
264268
// [pred]

src/librustc/middle/check_match.rs

+22-10
Original file line numberDiff line numberDiff line change
@@ -261,20 +261,32 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)], source
261261

262262
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
263263
NotUseful => {
264-
if source == MatchIfLetDesugar {
265-
if printed_if_let_err {
266-
// we already printed an irrefutable if-let pattern error.
267-
// We don't want two, that's just confusing.
268-
} else {
264+
match source {
265+
MatchIfLetDesugar => {
266+
if printed_if_let_err {
267+
// we already printed an irrefutable if-let pattern error.
268+
// We don't want two, that's just confusing.
269+
} else {
270+
// find the first arm pattern so we can use its span
271+
let &(ref first_arm_pats, _) = &arms[0];
272+
let first_pat = first_arm_pats.get(0);
273+
let span = first_pat.span;
274+
span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern");
275+
printed_if_let_err = true;
276+
}
277+
},
278+
279+
MatchWhileLetDesugar => {
269280
// find the first arm pattern so we can use its span
270281
let &(ref first_arm_pats, _) = &arms[0];
271282
let first_pat = first_arm_pats.get(0);
272283
let span = first_pat.span;
273-
span_err!(cx.tcx.sess, span, E0162, "irrefutable if-let pattern");
274-
printed_if_let_err = true;
275-
}
276-
} else {
277-
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern");
284+
span_err!(cx.tcx.sess, span, E0165, "irrefutable while-let pattern");
285+
},
286+
287+
MatchNormal => {
288+
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
289+
},
278290
}
279291
}
280292
Useful => (),

src/librustc/middle/expr_use_visitor.rs

+4
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
429429
self.walk_block(&**blk);
430430
}
431431

432+
ast::ExprWhileLet(..) => {
433+
self.tcx().sess.span_bug(expr.span, "non-desugared ExprWhileLet");
434+
}
435+
432436
ast::ExprForLoop(ref pat, ref head, ref blk, _) => {
433437
// The pattern lives as long as the block.
434438
debug!("walk_expr for loop case: blk id={}", blk.id);

src/librustc/middle/liveness.rs

+10
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
484484
ExprIfLet(..) => {
485485
ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
486486
}
487+
ExprWhileLet(..) => {
488+
ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
489+
}
487490
ExprForLoop(ref pat, _, _, _) => {
488491
pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| {
489492
debug!("adding local variable {} from for loop with bm {:?}",
@@ -1022,6 +1025,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
10221025
self.propagate_through_loop(expr, WhileLoop(&**cond), &**blk, succ)
10231026
}
10241027

1028+
ExprWhileLet(..) => {
1029+
self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
1030+
}
1031+
10251032
ExprForLoop(ref pat, ref head, ref blk, _) => {
10261033
let ln = self.propagate_through_loop(expr, ForLoop(&**pat), &**blk, succ);
10271034
self.propagate_through_expr(&**head, ln)
@@ -1480,6 +1487,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
14801487
ExprIfLet(..) => {
14811488
this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
14821489
}
1490+
ExprWhileLet(..) => {
1491+
this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
1492+
}
14831493
}
14841494
}
14851495

src/librustc/middle/mem_categorization.rs

+3
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,9 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
530530
ast::ExprIfLet(..) => {
531531
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
532532
}
533+
ast::ExprWhileLet(..) => {
534+
self.tcx().sess.span_bug(expr.span, "non-desugared ExprWhileLet");
535+
}
533536
}
534537
}
535538

src/librustc/middle/trans/debuginfo.rs

+5
Original file line numberDiff line numberDiff line change
@@ -3496,6 +3496,11 @@ fn populate_scope_map(cx: &CrateContext,
34963496
})
34973497
}
34983498

3499+
ast::ExprWhileLet(..) => {
3500+
cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \
3501+
Found unexpanded while-let.");
3502+
}
3503+
34993504
ast::ExprForLoop(ref pattern, ref head, ref body, _) => {
35003505
walk_expr(cx, &**head, scope_stack, scope_map);
35013506

src/librustc/middle/ty.rs

+3
Original file line numberDiff line numberDiff line change
@@ -3605,6 +3605,9 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
36053605
ast::ExprIfLet(..) => {
36063606
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
36073607
}
3608+
ast::ExprWhileLet(..) => {
3609+
tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
3610+
}
36083611

36093612
ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
36103613
RvalueDpsExpr

src/librustc/middle/typeck/check/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4058,6 +4058,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
40584058
fcx.write_nil(id);
40594059
}
40604060
}
4061+
ast::ExprWhileLet(..) => {
4062+
tcx.sess.span_bug(expr.span, "non-desugared ExprWhileLet");
4063+
}
40614064
ast::ExprForLoop(ref pat, ref head, ref block, _) => {
40624065
check_expr(fcx, &**head);
40634066
let typ = lookup_method_for_for_loop(fcx, &**head, expr.id);

src/librustc/util/ppaux.rs

+3
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
9393
explain_span(cx, "method call", expr.span)
9494
},
9595
ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if let", expr.span),
96+
ast::ExprMatch(_, _, ast::MatchWhileLetDesugar) => {
97+
explain_span(cx, "while let", expr.span)
98+
},
9699
ast::ExprMatch(..) => explain_span(cx, "match", expr.span),
97100
_ => explain_span(cx, "expression", expr.span)
98101
}

src/librustc_back/svh.rs

+1
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ mod svh_visitor {
294294

295295
// just syntactic artifacts, expanded away by time of SVH.
296296
ExprIfLet(..) => unreachable!(),
297+
ExprWhileLet(..) => unreachable!(),
297298
ExprMac(..) => unreachable!(),
298299
}
299300
}

src/libsyntax/ast.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,8 @@ pub enum Expr_ {
524524
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
525525
ExprWhile(P<Expr>, P<Block>, Option<Ident>),
526526
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
527+
ExprWhileLet(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
528+
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
527529
ExprForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
528530
// Conditionless loop (can be exited with break, cont, or ret)
529531
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
@@ -579,7 +581,8 @@ pub struct QPath {
579581
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
580582
pub enum MatchSource {
581583
MatchNormal,
582-
MatchIfLetDesugar
584+
MatchIfLetDesugar,
585+
MatchWhileLetDesugar,
583586
}
584587

585588
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]

src/libsyntax/ext/build.rs

+8
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ pub trait AstBuilder {
147147
fn expr_some(&self, sp: Span, expr: P<ast::Expr>) -> P<ast::Expr>;
148148
fn expr_none(&self, sp: Span) -> P<ast::Expr>;
149149

150+
fn expr_break(&self, sp: Span) -> P<ast::Expr>;
151+
150152
fn expr_tuple(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr>;
151153

152154
fn expr_fail(&self, span: Span, msg: InternedString) -> P<ast::Expr>;
@@ -688,6 +690,12 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
688690
self.expr_path(none)
689691
}
690692

693+
694+
fn expr_break(&self, sp: Span) -> P<ast::Expr> {
695+
self.expr(sp, ast::ExprBreak(None))
696+
}
697+
698+
691699
fn expr_tuple(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
692700
self.expr(sp, ast::ExprTup(exprs))
693701
}

src/libsyntax/ext/expand.rs

+36
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,42 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
6767
fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
6868
}
6969

70+
// Desugar ExprWhileLet
71+
// From: `[opt_ident]: while let <pat> = <expr> <body>`
72+
ast::ExprWhileLet(pat, expr, body, opt_ident) => {
73+
// to:
74+
//
75+
// [opt_ident]: loop {
76+
// match <expr> {
77+
// <pat> => <body>,
78+
// _ => break
79+
// }
80+
// }
81+
82+
// `<pat> => <body>`
83+
let pat_arm = {
84+
let body_expr = fld.cx.expr_block(body);
85+
fld.cx.arm(pat.span, vec![pat], body_expr)
86+
};
87+
88+
// `_ => break`
89+
let break_arm = {
90+
let pat_under = fld.cx.pat_wild(span);
91+
let break_expr = fld.cx.expr_break(span);
92+
fld.cx.arm(span, vec![pat_under], break_expr)
93+
};
94+
95+
// `match <expr> { ... }`
96+
let arms = vec![pat_arm, break_arm];
97+
let match_expr = fld.cx.expr(span,
98+
ast::ExprMatch(expr, arms, ast::MatchWhileLetDesugar));
99+
100+
// `[opt_ident]: loop { ... }`
101+
let loop_block = fld.cx.block_expr(match_expr);
102+
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
103+
fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
104+
}
105+
70106
// Desugar ExprIfLet
71107
// From: `if let <pat> = <expr> <body> [<elseopt>]`
72108
ast::ExprIfLet(pat, expr, body, mut elseopt) => {

src/libsyntax/feature_gate.rs

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
7373
("slicing_syntax", Active),
7474

7575
("if_let", Active),
76+
("while_let", Active),
7677

7778
// if you change this list without updating src/doc/reference.md, cmr will be sad
7879

@@ -345,6 +346,10 @@ impl<'a, 'v> Visitor<'v> for Context<'a> {
345346
e.span,
346347
"slicing syntax is experimental");
347348
}
349+
ast::ExprWhileLet(..) => {
350+
self.gate_feature("while_let", e.span,
351+
"`while let` syntax is experimental");
352+
}
348353
_ => {}
349354
}
350355
visit::walk_expr(self, e);

src/libsyntax/fold.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,12 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
12161216
folder.fold_block(body),
12171217
opt_ident.map(|i| folder.fold_ident(i)))
12181218
}
1219+
ExprWhileLet(pat, expr, body, opt_ident) => {
1220+
ExprWhileLet(folder.fold_pat(pat),
1221+
folder.fold_expr(expr),
1222+
folder.fold_block(body),
1223+
opt_ident.map(|i| folder.fold_ident(i)))
1224+
}
12191225
ExprForLoop(pat, iter, body, opt_ident) => {
12201226
ExprForLoop(folder.fold_pat(pat),
12211227
folder.fold_expr(iter),

src/libsyntax/parse/classify.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
2828
| ast::ExprMatch(..)
2929
| ast::ExprBlock(_)
3030
| ast::ExprWhile(..)
31+
| ast::ExprWhileLet(..)
3132
| ast::ExprLoop(..)
3233
| ast::ExprForLoop(..) => false,
3334
_ => true

src/libsyntax/parse/parser.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, Ex
2626
use ast::{ExprLit, ExprLoop, ExprMac};
2727
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc};
2828
use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn};
29-
use ast::{ExprVec, ExprWhile, ExprForLoop, Field, FnDecl};
29+
use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
3030
use ast::{Once, Many};
3131
use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind};
3232
use ast::{FnOnceUnboxedClosureKind};
@@ -2935,14 +2935,30 @@ impl<'a> Parser<'a> {
29352935
self.mk_expr(lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident))
29362936
}
29372937

2938+
/// Parse a 'while' or 'while let' expression ('while' token already eaten)
29382939
pub fn parse_while_expr(&mut self, opt_ident: Option<ast::Ident>) -> P<Expr> {
2940+
if self.is_keyword(keywords::Let) {
2941+
return self.parse_while_let_expr(opt_ident);
2942+
}
29392943
let lo = self.last_span.lo;
29402944
let cond = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL);
29412945
let body = self.parse_block();
29422946
let hi = body.span.hi;
29432947
return self.mk_expr(lo, hi, ExprWhile(cond, body, opt_ident));
29442948
}
29452949

2950+
/// Parse a 'while let' expression ('while' token already eaten)
2951+
pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::Ident>) -> P<Expr> {
2952+
let lo = self.last_span.lo;
2953+
self.expect_keyword(keywords::Let);
2954+
let pat = self.parse_pat();
2955+
self.expect(&token::EQ);
2956+
let expr = self.parse_expr_res(RESTRICTION_NO_STRUCT_LITERAL);
2957+
let body = self.parse_block();
2958+
let hi = body.span.hi;
2959+
return self.mk_expr(lo, hi, ExprWhileLet(pat, expr, body, opt_ident));
2960+
}
2961+
29462962
pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>) -> P<Expr> {
29472963
let lo = self.last_span.lo;
29482964
let body = self.parse_block();

src/libsyntax/print/pprust.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,19 @@ impl<'a> State<'a> {
15151515
try!(space(&mut self.s));
15161516
try!(self.print_block(&**blk));
15171517
}
1518+
ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => {
1519+
for ident in opt_ident.iter() {
1520+
try!(self.print_ident(*ident));
1521+
try!(self.word_space(":"));
1522+
}
1523+
try!(self.head("while let"));
1524+
try!(self.print_pat(&**pat));
1525+
try!(space(&mut self.s));
1526+
try!(self.word_space("="));
1527+
try!(self.print_expr(&**expr));
1528+
try!(space(&mut self.s));
1529+
try!(self.print_block(&**blk));
1530+
}
15181531
ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => {
15191532
for ident in opt_ident.iter() {
15201533
try!(self.print_ident(*ident));

src/libsyntax/visit.rs

+5
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,11 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
733733
visitor.visit_block(&**if_block);
734734
walk_expr_opt(visitor, optional_else);
735735
}
736+
ExprWhileLet(ref pattern, ref subexpression, ref block, _) => {
737+
visitor.visit_pat(&**pattern);
738+
visitor.visit_expr(&**subexpression);
739+
visitor.visit_block(&**block);
740+
}
736741
ExprForLoop(ref pattern, ref subexpression, ref block, _) => {
737742
visitor.visit_pat(&**pattern);
738743
visitor.visit_expr(&**subexpression);

0 commit comments

Comments
 (0)