Skip to content

Commit

Permalink
auto merge of #17634 : jakub-/rust/if_let, r=kballard
Browse files Browse the repository at this point in the history
Continuation of #16741.
  • Loading branch information
bors committed Sep 30, 2014
2 parents 88d1a22 + e723051 commit 57a05cf
Show file tree
Hide file tree
Showing 28 changed files with 402 additions and 37 deletions.
17 changes: 16 additions & 1 deletion src/doc/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2441,6 +2441,8 @@ The currently implemented features of the reference compiler are:
* `default_type_params` - Allows use of default type parameters. The future of
this feature is uncertain.

* `if_let` - Allows use of the `if 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 @@ -3229,7 +3231,7 @@ for i in range(0u, 256) {
if_expr : "if" no_struct_literal_expr '{' block '}'
else_tail ? ;
else_tail : "else" [ if_expr
else_tail : "else" [ if_expr | if_let_expr
| '{' block '}' ] ;
```

Expand Down Expand Up @@ -3434,6 +3436,19 @@ let message = match maybe_digit {
};
```

### If let expressions

```{.ebnf .gram}
if_let_expr : "if" "let" pat '=' expr '{' block '}'
else_tail ? ;
else_tail : "else" [ if_expr | if_let_expr | '{' block '}' ] ;
```

An `if let` expression is semantically identical to an `if` expression 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 corresponding
block will execute, otherwise flow proceeds to the first `else` block that follows.

### 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 @@ -151,5 +151,6 @@ register_diagnostics!(
E0157,
E0158,
E0159,
E0161
E0161,
E0162
)
7 changes: 5 additions & 2 deletions src/librustc/lint/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,10 @@ impl LintPass for UnnecessaryParens {
let (value, msg, struct_lit_needs_parens) = match e.node {
ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
ast::ExprMatch(ref head, _) => (head, "`match` head expression", true),
ast::ExprMatch(ref head, _, source) => match source {
ast::MatchNormal => (head, "`match` head expression", true),
ast::MatchIfLetDesugar => (head, "`if let` head expression", true)
},
ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
ast::ExprAssign(_, ref value) => (value, "assigned value", false),
ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
Expand Down Expand Up @@ -1242,7 +1245,7 @@ impl LintPass for UnusedMut {

fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
match e.node {
ast::ExprMatch(_, ref arms) => {
ast::ExprMatch(_, ref arms, _) => {
for a in arms.iter() {
self.check_unused_mut_pat(cx, a.pats.as_slice())
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustc/middle/cfg/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
self.add_node(expr.id, [then_exit, else_exit]) // 4, 5
}

ast::ExprIfLet(..) => {
self.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}

ast::ExprWhile(ref cond, ref body, _) => {
//
// [pred]
Expand Down Expand Up @@ -322,7 +326,7 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
expr_exit
}

ast::ExprMatch(ref discr, ref arms) => {
ast::ExprMatch(ref discr, ref arms, _) => {
//
// [pred]
// |
Expand Down
26 changes: 22 additions & 4 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ pub fn check_crate(tcx: &ty::ctxt) {
fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
visit::walk_expr(cx, ex);
match ex.node {
ExprMatch(ref scrut, ref arms) => {
ExprMatch(ref scrut, ref arms, source) => {
// First, check legality of move bindings.
for arm in arms.iter() {
check_legality_of_move_bindings(cx,
Expand Down Expand Up @@ -184,7 +184,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
}

// Fourth, check for unreachable arms.
check_arms(cx, inlined_arms.as_slice());
check_arms(cx, inlined_arms.as_slice(), source);

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

// Check for unreachable patterns
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)]) {
fn check_arms(cx: &MatchCheckCtxt, arms: &[(Vec<P<Pat>>, Option<&Expr>)], source: MatchSource) {
let mut seen = Matrix(vec![]);
let mut printed_if_let_err = false;
for &(ref pats, guard) in arms.iter() {
for pat in pats.iter() {
let v = vec![&**pat];

match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
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 {
// 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");
}
}
Useful => (),
UsefulWithWitness(_) => unreachable!()
}
Expand Down
6 changes: 5 additions & 1 deletion src/librustc/middle/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,11 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,TYPER> {
}
}

ast::ExprMatch(ref discr, ref arms) => {
ast::ExprIfLet(..) => {
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
}

ast::ExprMatch(ref discr, ref arms, _) => {
let discr_cmt = return_if_err!(self.mc.cat_expr(&**discr));
self.borrow_expr(&**discr, ty::ReEmpty, ty::ImmBorrow, MatchDiscriminant);

Expand Down
12 changes: 11 additions & 1 deletion src/librustc/middle/liveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,9 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) {
ir.add_live_node_for_node(expr.id, ExprNode(expr.span));
visit::walk_expr(ir, expr);
}
ExprIfLet(..) => {
ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
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 @@ -1011,6 +1014,10 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&**cond, ln)
}

ExprIfLet(..) => {
self.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}

ExprWhile(ref cond, ref blk, _) => {
self.propagate_through_loop(expr, WhileLoop(&**cond), &**blk, succ)
}
Expand All @@ -1026,7 +1033,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_loop(expr, LoopLoop, &**blk, succ)
}

ExprMatch(ref e, ref arms) => {
ExprMatch(ref e, ref arms, _) => {
//
// (e)
// |
Expand Down Expand Up @@ -1470,6 +1477,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) {
ExprPath(..) | ExprBox(..) => {
visit::walk_expr(this, expr);
}
ExprIfLet(..) => {
this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,10 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
ast::ExprForLoop(..) => {
Ok(self.cat_rvalue_node(expr.id(), expr.span(), expr_ty))
}

ast::ExprIfLet(..) => {
self.tcx().sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/librustc/middle/trans/debuginfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3576,6 +3576,11 @@ fn populate_scope_map(cx: &CrateContext,
}
}

ast::ExprIfLet(..) => {
cx.sess().span_bug(exp.span, "debuginfo::populate_scope_map() - \
Found unexpanded if-let.");
}

ast::ExprWhile(ref cond_exp, ref loop_body, _) => {
walk_expr(cx, &**cond_exp, scope_stack, scope_map);

Expand Down Expand Up @@ -3654,7 +3659,7 @@ fn populate_scope_map(cx: &CrateContext,
}
}

ast::ExprMatch(ref discriminant_exp, ref arms) => {
ast::ExprMatch(ref discriminant_exp, ref arms, _) => {
walk_expr(cx, &**discriminant_exp, scope_stack, scope_map);

// For each arm we have to first walk the pattern as these might
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ast::ExprIf(ref cond, ref thn, ref els) => {
controlflow::trans_if(bcx, expr.id, &**cond, &**thn, els.as_ref().map(|e| &**e), dest)
}
ast::ExprMatch(ref discr, ref arms) => {
ast::ExprMatch(ref discr, ref arms, _) => {
_match::trans_match(bcx, expr, &**discr, arms.as_slice(), dest)
}
ast::ExprBlock(ref blk) => {
Expand Down
4 changes: 4 additions & 0 deletions src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3635,6 +3635,10 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind {
RvalueDpsExpr
}

ast::ExprIfLet(..) => {
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}

ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
RvalueDpsExpr
}
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4106,6 +4106,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.as_ref().map(|e| &**e),
id, expr.span, expected);
}
ast::ExprIfLet(..) => {
tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet");
}
ast::ExprWhile(ref cond, ref body, _) => {
check_expr_has_type(fcx, &**cond, ty::mk_bool());
check_block_no_value(fcx, &**body);
Expand Down Expand Up @@ -4143,7 +4146,7 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
fcx.write_nil(id);
}
}
ast::ExprMatch(ref discrim, ref arms) => {
ast::ExprMatch(ref discrim, ref arms, _) => {
_match::check_match(fcx, expr, &**discrim, arms.as_slice());
}
ast::ExprFnBlock(_, ref decl, ref body) => {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/typeck/check/regionck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
visit::walk_expr(rcx, expr);
}

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

visit::walk_expr(rcx, expr);
Expand Down
1 change: 1 addition & 0 deletions src/librustc/util/ppaux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub fn explain_region_and_span(cx: &ctxt, region: ty::Region)
ast::ExprMethodCall(..) => {
explain_span(cx, "method call", expr.span)
},
ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => explain_span(cx, "if 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 @@ -293,6 +293,7 @@ mod svh_visitor {
ExprForLoop(..) => SawExprForLoop,

// just syntactic artifacts, expanded away by time of SVH.
ExprIfLet(..) => unreachable!(),
ExprMac(..) => unreachable!(),
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,14 +521,15 @@ pub enum Expr_ {
ExprLit(P<Lit>),
ExprCast(P<Expr>, P<Ty>),
ExprIf(P<Expr>, P<Block>, Option<P<Expr>>),
ExprIfLet(P<Pat>, P<Expr>, P<Block>, Option<P<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.
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.
ExprLoop(P<Block>, Option<Ident>),
ExprMatch(P<Expr>, Vec<Arm>),
ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
ExprFnBlock(CaptureClause, P<FnDecl>, P<Block>),
ExprProc(P<FnDecl>, P<Block>),
ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P<FnDecl>, P<Block>),
Expand Down Expand Up @@ -576,6 +577,12 @@ pub struct QPath {
pub item_name: Ident,
}

#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
pub enum MatchSource {
MatchNormal,
MatchIfLetDesugar
}

#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Show)]
pub enum CaptureClause {
CaptureByValue,
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ fn fold_expr(cx: &mut Context, expr: P<ast::Expr>) -> P<ast::Expr> {
fold::noop_fold_expr(ast::Expr {
id: id,
node: match node {
ast::ExprMatch(m, arms) => {
ast::ExprMatch(m, arms, source) => {
ast::ExprMatch(m, arms.into_iter()
.filter(|a| (cx.in_cfg)(a.attrs.as_slice()))
.collect())
.collect(), source)
}
_ => node
},
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
}

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

fn expr_if(&self, span: Span, cond: P<ast::Expr>,
Expand Down
Loading

0 comments on commit 57a05cf

Please sign in to comment.