Skip to content

Commit 57a05cf

Browse files
committed
auto merge of #17634 : jakub-/rust/if_let, r=kballard
Continuation of #16741.
2 parents 88d1a22 + e723051 commit 57a05cf

28 files changed

+402
-37
lines changed

src/doc/reference.md

+16-1
Original file line numberDiff line numberDiff line change
@@ -2441,6 +2441,8 @@ The currently implemented features of the reference compiler are:
24412441
* `default_type_params` - Allows use of default type parameters. The future of
24422442
this feature is uncertain.
24432443

2444+
* `if_let` - Allows use of the `if let` syntax.
2445+
24442446
* `intrinsics` - Allows use of the "rust-intrinsics" ABI. Compiler intrinsics
24452447
are inherently unstable and no promise about them is made.
24462448

@@ -3229,7 +3231,7 @@ for i in range(0u, 256) {
32293231
if_expr : "if" no_struct_literal_expr '{' block '}'
32303232
else_tail ? ;
32313233
3232-
else_tail : "else" [ if_expr
3234+
else_tail : "else" [ if_expr | if_let_expr
32333235
| '{' block '}' ] ;
32343236
```
32353237

@@ -3434,6 +3436,19 @@ let message = match maybe_digit {
34343436
};
34353437
```
34363438

3439+
### If let expressions
3440+
3441+
```{.ebnf .gram}
3442+
if_let_expr : "if" "let" pat '=' expr '{' block '}'
3443+
else_tail ? ;
3444+
else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ;
3445+
```
3446+
3447+
An `if let` expression is semantically identical to an `if` expression but in place
3448+
of a condition expression it expects a refutable let statement. If the value of the
3449+
expression on the right hand side of the let statement matches the pattern, the corresponding
3450+
block will execute, otherwise flow proceeds to the first `else` block that follows.
3451+
34373452
### Return expressions
34383453

34393454
```{.ebnf .gram}

src/librustc/diagnostics.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -151,5 +151,6 @@ register_diagnostics!(
151151
E0157,
152152
E0158,
153153
E0159,
154-
E0161
154+
E0161,
155+
E0162
155156
)

src/librustc/lint/builtin.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,10 @@ impl LintPass for UnnecessaryParens {
10921092
let (value, msg, struct_lit_needs_parens) = match e.node {
10931093
ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
10941094
ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
1095-
ast::ExprMatch(ref head, _) => (head, "`match` head expression", true),
1095+
ast::ExprMatch(ref head, _, source) => match source {
1096+
ast::MatchNormal => (head, "`match` head expression", true),
1097+
ast::MatchIfLetDesugar => (head, "`if let` head expression", true)
1098+
},
10961099
ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
10971100
ast::ExprAssign(_, ref value) => (value, "assigned value", false),
10981101
ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
@@ -1242,7 +1245,7 @@ impl LintPass for UnusedMut {
12421245

12431246
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
12441247
match e.node {
1245-
ast::ExprMatch(_, ref arms) => {
1248+
ast::ExprMatch(_, ref arms, _) => {
12461249
for a in arms.iter() {
12471250
self.check_unused_mut_pat(cx, a.pats.as_slice())
12481251
}

src/librustc/middle/cfg/construct.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
222222
self.add_node(expr.id, [then_exit, else_exit]) // 4, 5
223223
}
224224

225+
ast::ExprIfLet(..) => {
226+
self.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
227+
}
228+
225229
ast::ExprWhile(ref cond, ref body, _) => {
226230
//
227231
// [pred]
@@ -322,7 +326,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
322326
expr_exit
323327
}
324328

325-
ast::ExprMatch(ref discr, ref arms) => {
329+
ast::ExprMatch(ref discr, ref arms, _) => {
326330
//
327331
// [pred]
328332
// |

src/librustc/middle/check_match.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ pub fn check_crate(tcx: &ty::ctxt) {
147147
fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
148148
visit::walk_expr(cx, ex);
149149
match ex.node {
150-
ExprMatch(ref scrut, ref arms) => {
150+
ExprMatch(ref scrut, ref arms, source) => {
151151
// First, check legality of move bindings.
152152
for arm in arms.iter() {
153153
check_legality_of_move_bindings(cx,
@@ -184,7 +184,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
184184
}
185185

186186
// Fourth, check for unreachable arms.
187-
check_arms(cx, inlined_arms.as_slice());
187+
check_arms(cx, inlined_arms.as_slice(), source);
188188

189189
// Finally, check if the whole match expression is exhaustive.
190190
// Check for empty enum, because is_useful only works on inhabited types.
@@ -252,13 +252,31 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, pats: &[P<Pat>]) {
252252
}
253253

254254
// Check for unreachable patterns
255-
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)]) {
255+
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)], source: MatchSource) {
256256
let mut seen = Matrix(vec![]);
257+
let mut printed_if_let_err = false;
257258
for &(ref pats, guard) in arms.iter() {
258259
for pat in pats.iter() {
259260
let v = vec![&**pat];
261+
260262
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
261-
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
263+
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 {
269+
// find the first arm pattern so we can use its span
270+
let &(ref first_arm_pats, _) = &arms[0];
271+
let first_pat = first_arm_pats.get(0);
272+
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");
278+
}
279+
}
262280
Useful => (),
263281
UsefulWithWitness(_) => unreachable!()
264282
}

src/librustc/middle/expr_use_visitor.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,11 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
374374
}
375375
}
376376

377-
ast::ExprMatch(ref discr, ref arms) => {
377+
ast::ExprIfLet(..) => {
378+
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
379+
}
380+
381+
ast::ExprMatch(ref discr, ref arms, _) => {
378382
let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr));
379383
self.borrow_expr(&**discr, ty::ReEmpty, ty::ImmBorrow, MatchDiscriminant);
380384

src/librustc/middle/liveness.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
481481
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
482482
visit::walk_expr(ir, expr);
483483
}
484+
ExprIfLet(..) => {
485+
ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
486+
}
484487
ExprForLoop(ref pat, _, _, _) => {
485488
pat_util::pat_bindings(&ir.tcx.def_map, &**pat, |bm, p_id, sp, path1| {
486489
debug!("adding local variable {} from for loop with bm {:?}",
@@ -1011,6 +1014,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
10111014
self.propagate_through_expr(&**cond, ln)
10121015
}
10131016

1017+
ExprIfLet(..) => {
1018+
self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
1019+
}
1020+
10141021
ExprWhile(ref cond, ref blk, _) => {
10151022
self.propagate_through_loop(expr, WhileLoop(&**cond), &**blk, succ)
10161023
}
@@ -1026,7 +1033,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
10261033
self.propagate_through_loop(expr, LoopLoop, &**blk, succ)
10271034
}
10281035

1029-
ExprMatch(ref e, ref arms) => {
1036+
ExprMatch(ref e, ref arms, _) => {
10301037
//
10311038
// (e)
10321039
// |
@@ -1470,6 +1477,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
14701477
ExprPath(..) | ExprBox(..) => {
14711478
visit::walk_expr(this, expr);
14721479
}
1480+
ExprIfLet(..) => {
1481+
this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
1482+
}
14731483
}
14741484
}
14751485

src/librustc/middle/mem_categorization.rs

+4
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
505505
ast::ExprForLoop(..) => {
506506
Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
507507
}
508+
509+
ast::ExprIfLet(..) => {
510+
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
511+
}
508512
}
509513
}
510514

src/librustc/middle/trans/debuginfo.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -3576,6 +3576,11 @@ fn populate_scope_map(cx: &CrateContext,
35763576
}
35773577
}
35783578

3579+
ast::ExprIfLet(..) => {
3580+
cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \
3581+
Found unexpanded if-let.");
3582+
}
3583+
35793584
ast::ExprWhile(ref cond_exp, ref loop_body, _) => {
35803585
walk_expr(cx, &**cond_exp, scope_stack, scope_map);
35813586

@@ -3654,7 +3659,7 @@ fn populate_scope_map(cx: &CrateContext,
36543659
}
36553660
}
36563661

3657-
ast::ExprMatch(ref discriminant_exp, ref arms) => {
3662+
ast::ExprMatch(ref discriminant_exp, ref arms, _) => {
36583663
walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);
36593664

36603665
// For each arm we have to first walk the pattern as these might

src/librustc/middle/trans/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
10131013
ast::ExprIf(ref cond, ref thn, ref els) => {
10141014
controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest)
10151015
}
1016-
ast::ExprMatch(ref discr, ref arms) => {
1016+
ast::ExprMatch(ref discr, ref arms, _) => {
10171017
_match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest)
10181018
}
10191019
ast::ExprBlock(ref blk) => {

src/librustc/middle/ty.rs

+4
Original file line numberDiff line numberDiff line change
@@ -3635,6 +3635,10 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
36353635
RvalueDpsExpr
36363636
}
36373637

3638+
ast::ExprIfLet(..) => {
3639+
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
3640+
}
3641+
36383642
ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
36393643
RvalueDpsExpr
36403644
}

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -4106,6 +4106,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
41064106
check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.as_ref().map(|e| &**e),
41074107
id, expr.span, expected);
41084108
}
4109+
ast::ExprIfLet(..) => {
4110+
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
4111+
}
41094112
ast::ExprWhile(ref cond, ref body, _) => {
41104113
check_expr_has_type(fcx, &**cond, ty::mk_bool());
41114114
check_block_no_value(fcx, &**body);
@@ -4143,7 +4146,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
41434146
fcx.write_nil(id);
41444147
}
41454148
}
4146-
ast::ExprMatch(ref discrim, ref arms) => {
4149+
ast::ExprMatch(ref discrim, ref arms, _) => {
41474150
_match::check_match(fcx, expr, &**discrim, arms.as_slice());
41484151
}
41494152
ast::ExprFnBlock(_, ref decl, ref body) => {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
725725
visit::walk_expr(rcx, expr);
726726
}
727727

728-
ast::ExprMatch(ref discr, ref arms) => {
728+
ast::ExprMatch(ref discr, ref arms, _) => {
729729
link_match(rcx, &**discr, arms.as_slice());
730730

731731
visit::walk_expr(rcx, expr);

src/librustc/util/ppaux.rs

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
9292
ast::ExprMethodCall(..) => {
9393
explain_span(cx, "method call", expr.span)
9494
},
95+
ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if let", expr.span),
9596
ast::ExprMatch(..) => explain_span(cx, "match", expr.span),
9697
_ => explain_span(cx, "expression", expr.span)
9798
}

src/librustc_back/svh.rs

+1
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ mod svh_visitor {
293293
ExprForLoop(..) => SawExprForLoop,
294294

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

src/libsyntax/ast.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -521,14 +521,15 @@ pub enum Expr_ {
521521
ExprLit(P<Lit>),
522522
ExprCast(P<Expr>, P<Ty>),
523523
ExprIf(P<Expr>, P<Block>, Option<P<Expr>>),
524+
ExprIfLet(P<Pat>, P<Expr>, P<Block>, Option<P<Expr>>),
524525
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
525526
ExprWhile(P<Expr>, P<Block>, Option<Ident>),
526527
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
527528
ExprForLoop(P<Pat>, P<Expr>, P<Block>, Option<Ident>),
528529
// Conditionless loop (can be exited with break, cont, or ret)
529530
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
530531
ExprLoop(P<Block>, Option<Ident>),
531-
ExprMatch(P<Expr>, Vec<Arm>),
532+
ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
532533
ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
533534
ExprProc(P<FnDecl>, P<Block>),
534535
ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>),
@@ -576,6 +577,12 @@ pub struct QPath {
576577
pub item_name: Ident,
577578
}
578579

580+
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
581+
pub enum MatchSource {
582+
MatchNormal,
583+
MatchIfLetDesugar
584+
}
585+
579586
#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
580587
pub enum CaptureClause {
581588
CaptureByValue,

src/libsyntax/config.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ fn fold_expr(cx: &mut Context, expr: P<ast::Expr>) -> P<ast::Expr> {
210210
fold::noop_fold_expr(ast::Expr {
211211
id: id,
212212
node: match node {
213-
ast::ExprMatch(m, arms) => {
213+
ast::ExprMatch(m, arms, source) => {
214214
ast::ExprMatch(m, arms.into_iter()
215215
.filter(|a| (cx.in_cfg)(a.attrs.as_slice()))
216-
.collect())
216+
.collect(), source)
217217
}
218218
_ => node
219219
},

src/libsyntax/ext/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
845845
}
846846

847847
fn expr_match(&self, span: Span, arg: P<ast::Expr>, arms: Vec<ast::Arm>) -> P<Expr> {
848-
self.expr(span, ast::ExprMatch(arg, arms))
848+
self.expr(span, ast::ExprMatch(arg, arms, ast::MatchNormal))
849849
}
850850

851851
fn expr_if(&self, span: Span, cond: P<ast::Expr>,

0 commit comments

Comments
 (0)