diff --git a/src/doc/rust.md b/src/doc/rust.md index 4bbcba6c2f300..fe1a65c2c163c 100644 --- a/src/doc/rust.md +++ b/src/doc/rust.md @@ -2457,6 +2457,8 @@ The currently implemented features of the reference compiler are: whether this will continue as a feature or not. For these reasons, the glob import statement has been hidden behind this feature flag. +* `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. diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index a789049d4de8c..c4275613b162c 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -17,6 +17,11 @@ register_diagnostic!(E0001, r##" one is too specific or the ordering is incorrect. "##) +register_diagnostic!(E0159, r##" + This error is produced by an `if let` expression where the pattern is irrefutable. + An `if let` that can never fail is considered an error. +"##) + register_diagnostics!( E0002, E0003, diff --git a/src/librustc/front/config.rs b/src/librustc/front/config.rs index 93320caf5f281..ce97020571022 100644 --- a/src/librustc/front/config.rs +++ b/src/librustc/front/config.rs @@ -206,7 +206,7 @@ fn fold_block(cx: &mut Context, b: ast::P) -> ast::P { fn fold_expr(cx: &mut Context, expr: Gc) -> Gc { let expr = match expr.node { - ast::ExprMatch(ref m, ref arms) => { + ast::ExprMatch(ref m, ref arms, source) => { let arms = arms.iter() .filter(|a| (cx.in_cfg)(a.attrs.as_slice())) .map(|a| a.clone()) @@ -214,7 +214,7 @@ fn fold_expr(cx: &mut Context, expr: Gc) -> Gc { box(GC) ast::Expr { id: expr.id, span: expr.span.clone(), - node: ast::ExprMatch(m.clone(), arms), + node: ast::ExprMatch(m.clone(), arms, source), } } _ => expr.clone() diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 7c19b25e01cd7..108553aa38bd5 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -70,6 +70,8 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("unboxed_closures", Active), ("import_shadowing", Active), + ("if_let", Active), + // if you change this list without updating src/doc/rust.md, cmr will be sad // A temporary feature gate used to enable parser extensions needed @@ -339,6 +341,14 @@ impl<'a> Visitor<()> for Context<'a> { "unboxed closures are a work-in-progress \ feature with known bugs"); } + ast::ExprIfLet(..) | + ast::ExprMatch(_, _, ast::MatchIfLetDesugar) => { + // note: at the point where we check feature-gates, the AST desugaring + // should not have happened, but the ExprMatch check is included just + // in case. + self.gate_feature("if_let", e.span, + "`if let` syntax is experimental"); + } _ => {} } visit::walk_expr(self, e, ()); diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index 80b4764014673..98f9052ee1c39 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -1080,7 +1080,10 @@ impl LintPass for UnnecessaryParens { let (value, msg, struct_lit_needs_parens) = match e.node { ast::ExprIf(cond, _, _) => (cond, "`if` condition", true), ast::ExprWhile(cond, _) => (cond, "`while` condition", true), - ast::ExprMatch(head, _) => (head, "`match` head expression", true), + ast::ExprMatch(head, _, source) => match source { + ast::MatchNormal => (head, "`match` head expression", true), + ast::MatchIfLetDesugar => (head, "`if let` head expression", true) + }, ast::ExprRet(Some(value)) => (value, "`return` value", false), ast::ExprAssign(_, value) => (value, "assigned value", false), ast::ExprAssignOp(_, _, value) => (value, "assigned value", false), @@ -1192,7 +1195,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()) } diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index fd3074e130e70..fb2e3d6b22d54 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -225,6 +225,10 @@ impl<'a> CFGBuilder<'a> { 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] @@ -325,7 +329,7 @@ impl<'a> CFGBuilder<'a> { expr_exit } - ast::ExprMatch(ref discr, ref arms) => { + ast::ExprMatch(ref discr, ref arms, _) => { // // [pred] // | diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 230668e706653..53411d8ed2cd9 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -139,7 +139,7 @@ pub fn check_crate(tcx: &ty::ctxt, krate: &Crate) { fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) { visit::walk_expr(cx, ex, ()); match ex.node { - ExprMatch(scrut, ref arms) => { + ExprMatch(scrut, ref arms, source) => { // First, check legality of move bindings. for arm in arms.iter() { check_legality_of_move_bindings(cx, @@ -178,7 +178,7 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) { check_for_static_nan(cx, inlined_arms.as_slice()); // 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. @@ -251,13 +251,30 @@ fn check_for_static_nan(cx: &MatchCheckCtxt, arms: &[Arm]) { } // Check for unreachable patterns -fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) { +fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm], source: MatchSource) { let mut seen = Matrix(vec!()); + let mut printed_if_let_err = false; for arm in arms.iter() { for &pat in arm.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 first_arm = &arms[0]; // we know there's at least 1 arm + let first_pat = first_arm.pats.get(0); // and it's safe to assume 1 pat + let span = first_pat.span; + span_err!(cx.tcx.sess, span, E0159, "irrefutable if-let pattern"); + printed_if_let_err = true; + } + } else { + span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"); + } + } Useful => (), UsefulWithWitness(_) => unreachable!() } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 84b879227ae6c..8798d50f4e7f0 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -356,7 +356,11 @@ impl<'d,'t,TYPER:mc::Typer> 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, _) => { // treatment of the discriminant is handled while // walking the arms: self.walk_expr(&**discr); diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 2062fa97777d5..f3aa9f2f26baa 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -491,6 +491,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 {:?}", @@ -1017,6 +1020,10 @@ impl<'a> Liveness<'a> { 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.clone()), @@ -1035,7 +1042,7 @@ impl<'a> Liveness<'a> { self.propagate_through_loop(expr, LoopLoop, &**blk, succ) } - ExprMatch(ref e, ref arms) => { + ExprMatch(ref e, ref arms, _) => { // // (e) // | @@ -1458,6 +1465,9 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ExprPath(..) | ExprBox(..) | ExprForLoop(..) => { visit::walk_expr(this, expr, ()); } + ExprIfLet(..) => { + this.ir.tcx.sess.span_bug(expr.span, "non-desugared ExprIfLet"); + } } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index ef1e0515156c0..05ab519ca9497 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -514,6 +514,10 @@ impl<'t,TYPER:Typer> 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"); + } } } diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 88bdb00a7c983..0c8640fb6ce0a 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -3603,6 +3603,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); @@ -3681,7 +3686,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 diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 748274b1201e5..863fb994e0157 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -739,7 +739,7 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>, ast::ExprIf(ref cond, ref thn, els) => { controlflow::trans_if(bcx, expr.id, &**cond, thn.clone(), els, 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) => { diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 2b768c811e715..86355bc223115 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3429,6 +3429,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(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 8a9c147f60f9c..394fe293d222c 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3430,6 +3430,9 @@ fn check_expr_with_unifier(fcx: &FnCtxt, check_then_else(fcx, &**cond, &**then_blk, opt_else_expr.clone(), 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); @@ -3468,7 +3471,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) => { diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 3813dd7964244..7b867dfc54398 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -591,7 +591,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, ()); diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index e0e9f0e6910b2..901bfdadd1315 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -80,6 +80,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) } diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index 29668795ed7ba..f2749cb45742b 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -291,6 +291,7 @@ mod svh_visitor { ExprForLoop(..) => SawExprForLoop, // just syntactic artifacts, expanded away by time of SVH. + ExprIfLet(..) => unreachable!(), ExprMac(..) => unreachable!(), } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 42d9430d7320e..2478d3b274d4a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -534,13 +534,14 @@ pub enum Expr_ { ExprLit(Gc), ExprCast(Gc, P), ExprIf(Gc, P, Option>), + ExprIfLet(P, P, P, Option>), ExprWhile(Gc, P), // FIXME #6993: change to Option ... or not, if these are hygienic. ExprForLoop(Gc, Gc, P, Option), // Conditionless loop (can be exited with break, cont, or ret) // FIXME #6993: change to Option ... or not, if these are hygienic. ExprLoop(P, Option), - ExprMatch(Gc, Vec), + ExprMatch(Gc, Vec, MatchSource), ExprFnBlock(CaptureClause, P, P), ExprProc(P, P), ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P, P), @@ -574,6 +575,12 @@ pub enum Expr_ { ExprParen(Gc) } +#[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, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index f7eddca4b7aed..8b3ad1df1f9bc 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -860,7 +860,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn expr_match(&self, span: Span, arg: Gc, arms: Vec) -> Gc { - self.expr(span, ast::ExprMatch(arg, arms)) + self.expr(span, ast::ExprMatch(arg, arms, ast::MatchNormal)) } fn expr_if(&self, span: Span, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d918b28d4dc8f..6f2c290421574 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -62,6 +62,91 @@ fn expand_expr(e: Gc, fld: &mut MacroExpander) -> Gc { } } + // Desugar ExprIfLet + // From: `if let = []` + ast::ExprIfLet(pat, expr, body, mut elseopt) => { + let span = e.span; + + // to: + // + // match { + // => , + // [_ if => ,] + // _ => [ | ()] + // } + + // ` => ` + let pat_arm = { + let body_expr = fld.cx.expr_block(body); + fld.cx.arm(pat.span, vec![pat], body_expr) + }; + + // `[_ if => ,]` + let else_if_arms = { + let mut arms = vec![]; + loop { + // NOTE: replace with 'if let' after snapshot + match elseopt { + Some(els) => match els.node { + // else if + ast::ExprIf(cond, then, elseopt_) => { + let pat_under = fld.cx.pat_wild(span); + elseopt = elseopt_; + arms.push(ast::Arm { + attrs: vec![], + pats: vec![pat_under], + guard: Some(cond), + body: fld.cx.expr_block(then) + }); + } + _ => break + }, + None => break + } + } + arms + }; + + // `_ => [ | ()]` + let else_arm = { + let pat_under = fld.cx.pat_wild(span); + let else_expr = match elseopt { + Some(els) => els, + None => fld.cx.expr_lit(span, ast::LitNil) + }; + fld.cx.arm(span, vec![pat_under], else_expr) + }; + + let mut arms = Vec::with_capacity(else_if_arms.len() + 2); + arms.push(pat_arm); + arms.push_all_move(else_if_arms); + arms.push(else_arm); + + let match_expr = fld.cx.expr(span, ast::ExprMatch(expr, arms, ast::MatchIfLetDesugar)); + fld.fold_expr(match_expr) + } + + // Desugar support for ExprIfLet in the ExprIf else position + ast::ExprIf(cond, blk, elseopt) => { + let elseopt = elseopt.map(|els| match els.node { + ast::ExprIfLet(..) => { + // wrap the if-let expr in a block + let blk = P(ast::Block { + view_items: vec![], + stmts: vec![], + expr: Some(els), + id: ast::DUMMY_NODE_ID, + rules: ast::DefaultBlock, + span: els.span + }); + fld.cx.expr_block(blk) + } + _ => els + }); + let if_expr = fld.cx.expr(e.span, ast::ExprIf(cond, blk, elseopt)); + noop_fold_expr(if_expr, fld) + } + ast::ExprLoop(loop_block, opt_ident) => { let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident)) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index fb96b4b83d7f3..246173974e4d7 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1129,6 +1129,12 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { folder.fold_block(tr), fl.map(|x| folder.fold_expr(x))) } + ExprIfLet(pat, expr, tr, fl) => { + ExprIfLet(folder.fold_pat(pat), + folder.fold_expr(expr), + folder.fold_block(tr), + fl.map(|x| folder.fold_expr(x))) + } ExprWhile(cond, body) => { ExprWhile(folder.fold_expr(cond), folder.fold_block(body)) } @@ -1142,9 +1148,10 @@ pub fn noop_fold_expr(e: Gc, folder: &mut T) -> Gc { ExprLoop(folder.fold_block(body), opt_ident.map(|x| folder.fold_ident(x))) } - ExprMatch(expr, ref arms) => { + ExprMatch(expr, ref arms, source) => { ExprMatch(folder.fold_expr(expr), - arms.iter().map(|x| folder.fold_arm(x)).collect()) + arms.iter().map(|x| folder.fold_arm(x)).collect(), + source) } ExprFnBlock(capture_clause, ref decl, ref body) => { ExprFnBlock(capture_clause, diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs index 516f22cdf4d60..5e1962de686a4 100644 --- a/src/libsyntax/parse/classify.rs +++ b/src/libsyntax/parse/classify.rs @@ -25,6 +25,7 @@ use std::gc::Gc; pub fn expr_requires_semi_to_be_stmt(e: Gc) -> bool { match e.node { ast::ExprIf(..) + | ast::ExprIfLet(..) | ast::ExprMatch(..) | ast::ExprBlock(_) | ast::ExprWhile(..) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9e2829e638050..529a784d5cbdf 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -23,7 +23,7 @@ use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, Explicit use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; use ast::{ExprBreak, ExprCall, ExprCast}; -use ast::{ExprField, ExprFnBlock, ExprIf, ExprIndex}; +use ast::{ExprField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex}; use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; @@ -38,7 +38,7 @@ use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl}; use ast::{ItemMac, ItemMod, ItemStruct, ItemTrait, ItemTy, Lit, Lit_}; use ast::{LitBool, LitChar, LitByte, LitBinary}; use ast::{LitNil, LitStr, LitInt, Local, LocalLet}; -use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal}; +use ast::{MutImmutable, MutMutable, Mac_, MacInvocTT, Matcher, MatchNonterminal, MatchNormal}; use ast::{MatchSeq, MatchTok, Method, MutTy, BiMul, Mutability}; use ast::{MethodImplItem}; use ast::{NamedField, UnNeg, NoReturn, UnNot, P, Pat, PatEnum}; @@ -569,12 +569,11 @@ impl<'a> Parser<'a> { /// If the next token is the given keyword, eat it and return /// true. Otherwise, return false. pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool { - match self.token { - token::IDENT(sid, false) if kw.to_name() == sid.name => { - self.bump(); - true - } - _ => false + if self.is_keyword(kw) { + self.bump(); + true + } else { + false } } @@ -2714,8 +2713,11 @@ impl<'a> Parser<'a> { } } - /// Parse an 'if' expression ('if' token already eaten) + /// Parse an 'if' or 'if let' expression ('if' token already eaten) pub fn parse_if_expr(&mut self) -> Gc { + if self.is_keyword(keywords::Let) { + return self.parse_if_let_expr(); + } let lo = self.last_span.lo; let cond = self.parse_expr_res(RESTRICT_NO_STRUCT_LITERAL); let thn = self.parse_block(); @@ -2729,6 +2731,23 @@ impl<'a> Parser<'a> { self.mk_expr(lo, hi, ExprIf(cond, thn, els)) } + /// Parse an 'if let' expression ('if' token already eaten) + pub fn parse_if_let_expr(&mut self) -> Gc { + 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(RESTRICT_NO_STRUCT_LITERAL); + let thn = self.parse_block(); + let els = if self.eat_keyword(keywords::Else) { + Some(self.parse_else_expr()) + } else { + None + }; + let hi = els.map_or(thn.span.hi, |expr| expr.span.hi); + self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els)) + } + // `|args| expr` pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause) -> Gc { @@ -2810,7 +2829,7 @@ impl<'a> Parser<'a> { } let hi = self.span.hi; self.bump(); - return self.mk_expr(lo, hi, ExprMatch(discriminant, arms)); + return self.mk_expr(lo, hi, ExprMatch(discriminant, arms, MatchNormal)); } pub fn parse_arm(&mut self) -> Arm { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6fe44078447e5..73dbccb28469a 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1281,6 +1281,19 @@ impl<'a> State<'a> { try!(self.print_block(&**t)); self.print_else(e) } + // "another else-if-let" + ast::ExprIfLet(ref pat, ref expr, ref blk, elseopt) => { + try!(self.cbox(indent_unit - 1u)); + try!(self.ibox(0u)); + try!(word(&mut self.s, " else if 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)); + self.print_else(elseopt) + } // "final else" ast::ExprBlock(ref b) => { try!(self.cbox(indent_unit - 1u)); @@ -1299,15 +1312,26 @@ impl<'a> State<'a> { } pub fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, - elseopt: Option>, chk: bool) -> IoResult<()> { + elseopt: Option>) -> IoResult<()> { try!(self.head("if")); - if chk { try!(self.word_nbsp("check")); } try!(self.print_expr(test)); try!(space(&mut self.s)); try!(self.print_block(blk)); self.print_else(elseopt) } + pub fn print_if_let(&mut self, pat: &ast::Pat, expr: &ast::Expr, blk: &ast::Block, + elseopt: Option>) -> IoResult<()> { + try!(self.head("if 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)); + self.print_else(elseopt) + } + pub fn print_mac(&mut self, m: &ast::Mac) -> IoResult<()> { match m.node { // I think it's reasonable to hide the ctxt here: @@ -1462,7 +1486,10 @@ impl<'a> State<'a> { try!(self.print_type(&**ty)); } ast::ExprIf(ref test, ref blk, elseopt) => { - try!(self.print_if(&**test, &**blk, elseopt, false)); + try!(self.print_if(&**test, &**blk, elseopt)); + } + ast::ExprIfLet(ref pat, ref expr, ref blk, elseopt) => { + try!(self.print_if_let(&**pat, &**expr, &**blk, elseopt)); } ast::ExprWhile(ref test, ref blk) => { try!(self.head("while")); @@ -1492,7 +1519,7 @@ impl<'a> State<'a> { try!(space(&mut self.s)); try!(self.print_block(&**blk)); } - ast::ExprMatch(ref expr, ref arms) => { + ast::ExprMatch(ref expr, ref arms, _) => { try!(self.cbox(indent_unit)); try!(self.ibox(4)); try!(self.word_nbsp("match")); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 9e371143311c2..6ddfefaba881a 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -784,6 +784,12 @@ pub fn walk_expr>(visitor: &mut V, expression: &Expr, en visitor.visit_block(&**if_block, env.clone()); walk_expr_opt(visitor, optional_else, env.clone()) } + ExprIfLet(ref pattern, ref subexpression, ref if_block, optional_else) => { + visitor.visit_pat(&**pattern, env.clone()); + visitor.visit_expr(&**subexpression, env.clone()); + visitor.visit_block(&**if_block, env.clone()); + walk_expr_opt(visitor, optional_else, env.clone()) + } ExprWhile(ref subexpression, ref block) => { visitor.visit_expr(&**subexpression, env.clone()); visitor.visit_block(&**block, env.clone()) @@ -794,7 +800,7 @@ pub fn walk_expr>(visitor: &mut V, expression: &Expr, en visitor.visit_block(&**block, env.clone()) } ExprLoop(ref block, _) => visitor.visit_block(&**block, env.clone()), - ExprMatch(ref subexpression, ref arms) => { + ExprMatch(ref subexpression, ref arms, _) => { visitor.visit_expr(&**subexpression, env.clone()); for arm in arms.iter() { visitor.visit_arm(arm, env.clone()) diff --git a/src/test/compile-fail/if-let.rs b/src/test/compile-fail/if-let.rs new file mode 100644 index 0000000000000..0d452181d81b9 --- /dev/null +++ b/src/test/compile-fail/if-let.rs @@ -0,0 +1,58 @@ +// 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,if_let)] + +fn macros() { + macro_rules! foo{ + ($p:pat, $e:expr, $b:block) => {{ + if let $p = $e $b + }} + } + macro_rules! bar{ + ($p:pat, $e:expr, $b:block) => {{ + foo!($p, $e, $b) + }} + } + + foo!(a, 1i, { //~ ERROR irrefutable if-let + println!("irrefutable pattern"); + }); + bar!(a, 1i, { //~ ERROR irrefutable if-let + println!("irrefutable pattern"); + }); +} + +pub fn main() { + if let a = 1i { //~ ERROR irrefutable if-let + println!("irrefutable pattern"); + } + + if let a = 1i { //~ ERROR irrefutable if-let + println!("irrefutable pattern"); + } else if true { + println!("else-if in irrefutable if-let"); + } else { + println!("else in irrefutable if-let"); + } + + if let 1i = 2i { + println!("refutable pattern"); + } else if let a = 1i { //~ ERROR irrefutable if-let + println!("irrefutable pattern"); + } + + if true { + println!("if"); + } else if let a = 1i { //~ ERROR irrefutable if-let + println!("irrefutable pattern"); + } +} + diff --git a/src/test/compile-fail/lint-unnecessary-parens.rs b/src/test/compile-fail/lint-unnecessary-parens.rs index d51d5b4af87a9..3c962ebf21670 100644 --- a/src/test/compile-fail/lint-unnecessary-parens.rs +++ b/src/test/compile-fail/lint-unnecessary-parens.rs @@ -9,6 +9,7 @@ // except according to those terms. #![deny(unnecessary_parens)] +#![feature(if_let)] #[deriving(Eq, PartialEq)] struct X { y: bool } @@ -32,6 +33,7 @@ fn main() { match (true) { //~ ERROR unnecessary parentheses around `match` head expression _ => {} } + if let 1i = (1i) {} //~ ERROR unnecessary parentheses around `if 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/run-pass/if-let.rs b/src/test/run-pass/if-let.rs new file mode 100644 index 0000000000000..4bf3a85677c72 --- /dev/null +++ b/src/test/run-pass/if-let.rs @@ -0,0 +1,69 @@ +// 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(if_let)] + +pub fn main() { + let x = Some(3i); + if let Some(y) = x { + assert_eq!(y, 3i); + } else { + fail!("if-let failed"); + } + let mut worked = false; + if let Some(_) = x { + worked = true; + } + assert!(worked); + let clause: uint; + if let None = Some("test") { + clause = 1; + } else if 4u > 5 { + clause = 2; + } else if let Ok(()) = Err::<(),&'static str>("test") { + clause = 3; + } else { + clause = 4; + } + assert_eq!(clause, 4u); + + if 3i > 4 { + fail!("bad math"); + } else if let 1 = 2i { + fail!("bad pattern match"); + } + + enum Foo { + One, + Two(uint), + Three(String, int) + } + + let foo = Three("three".to_string(), 42i); + if let One = foo { + fail!("bad pattern match"); + } else if let Two(_x) = foo { + fail!("bad pattern match"); + } else if let Three(s, _) = foo { + assert_eq!(s.as_slice(), "three"); + } else { + fail!("bad else"); + } + + if false { + fail!("wat"); + } else if let a@Two(_) = Two(42u) { + if let Two(b) = a { + assert_eq!(b, 42u); + } else { + fail!("fail in nested if-let"); + } + } +}