Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for while let #17733

Merged
merged 7 commits into from
Oct 13, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -3509,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}
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,6 @@ register_diagnostics!(
E0161,
E0162,
E0163,
E0164
E0164,
E0165
)
3 changes: 2 additions & 1 deletion src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
32 changes: 22 additions & 10 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,32 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, 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");
},

MatchNormal => {
span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern")
},
}
}
Useful => (),
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {:?}",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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");
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/librustc/middle/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions src/librustc_back/svh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ mod svh_visitor {

// just syntactic artifacts, expanded away by time of SVH.
ExprIfLet(..) => unreachable!(),
ExprWhileLet(..) => unreachable!(),
ExprMac(..) => unreachable!(),
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,8 @@ pub enum Expr_ {
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprWhile(P<Expr>, P<Block>, Option<Ident>),
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprWhileLet(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
// Conditionless loop (can be exited with break, cont, or ret)
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
Expand Down Expand Up @@ -579,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)]
Expand Down
8 changes: 8 additions & 0 deletions src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ pub trait AstBuilder {
fn expr_some(&self, sp: Span, expr: P<ast::Expr>) -> P<ast::Expr>;
fn expr_none(&self, sp: Span) -> P<ast::Expr>;

fn expr_break(&self, sp: Span) -> P<ast::Expr>;

fn expr_tuple(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr>;

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


fn expr_break(&self, sp: Span) -> P<ast::Expr> {
self.expr(sp, ast::ExprBreak(None))
}


fn expr_tuple(&self, sp: Span, exprs: Vec<P<ast::Expr>>) -> P<ast::Expr> {
self.expr(sp, ast::ExprTup(exprs))
}
Expand Down
36 changes: 36 additions & 0 deletions src/libsyntax/ext/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,42 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
}

// Desugar ExprWhileLet
// From: `[opt_ident]: while let <pat> = <expr> <body>`
ast::ExprWhileLet(pat, expr, body, opt_ident) => {
// to:
//
// [opt_ident]: loop {
// match <expr> {
// <pat> => <body>,
// _ => break
// }
// }

// `<pat> => <body>`
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 <expr> { ... }`
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 <pat> = <expr> <body> [<elseopt>]`
ast::ExprIfLet(pat, expr, body, mut elseopt) => {
Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,12 @@ pub fn noop_fold_expr<T: Folder>(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),
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 17 additions & 1 deletion src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -2935,14 +2935,30 @@ 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<ast::Ident>) -> P<Expr> {
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();
let hi = body.span.hi;
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<ast::Ident>) -> P<Expr> {
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<ast::Ident>) -> P<Expr> {
let lo = self.last_span.lo;
let body = self.parse_block();
Expand Down
13 changes: 13 additions & 0 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
5 changes: 5 additions & 0 deletions src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading