diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index c8f5f89b6691..d9b834fe9fca 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -786,10 +786,28 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { ExprAddrOf(m, ref ohs) => { hir::ExprAddrOf(lower_mutability(lctx, m), lower_expr(lctx, ohs)) } - ExprIf(ref cond, ref tr, ref fl) => { + // More complicated than you might expect because the else branch + // might be `if let`. + ExprIf(ref cond, ref blk, ref else_opt) => { + let else_opt = else_opt.as_ref().map(|els| match els.node { + ExprIfLet(..) => { + // wrap the if-let expr in a block + let span = els.span; + let blk = P(hir::Block { + stmts: vec![], + expr: Some(lower_expr(lctx, els)), + id: lctx.next_id(), + rules: hir::DefaultBlock, + span: span + }); + expr_block(lctx, blk) + } + _ => lower_expr(lctx, els) + }); + hir::ExprIf(lower_expr(lctx, cond), - lower_block(lctx, tr), - fl.as_ref().map(|x| lower_expr(lctx, x))) + lower_block(lctx, blk), + else_opt) } ExprWhile(ref cond, ref body, opt_ident) => { hir::ExprWhile(lower_expr(lctx, cond), @@ -880,16 +898,123 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { ExprInPlace(..) => { panic!("todo"); } - ExprIfLet(..) => { - panic!("todo"); + + // Desugar ExprIfLet + // From: `if let = []` + ExprIfLet(ref pat, ref sub_expr, ref body, ref else_opt) => { + // to: + // + // match { + // => , + // [_ if => ,] + // _ => [ | ()] + // } + + // ` => ` + let pat_arm = { + let body_expr = expr_block(lctx, lower_block(lctx, body)); + arm(vec![lower_pat(lctx, pat)], body_expr) + }; + + // `[_ if => ,]` + let mut else_opt = else_opt.as_ref().map(|e| lower_expr(lctx, e)); + let else_if_arms = { + let mut arms = vec![]; + loop { + let else_opt_continue = else_opt + .and_then(|els| els.and_then(|els| match els.node { + // else if + hir::ExprIf(cond, then, else_opt) => { + let pat_under = pat_wild(lctx, e.span); + arms.push(hir::Arm { + attrs: vec![], + pats: vec![pat_under], + guard: Some(cond), + body: expr_block(lctx, then) + }); + else_opt.map(|else_opt| (else_opt, true)) + } + _ => Some((P(els), false)) + })); + match else_opt_continue { + Some((e, true)) => { + else_opt = Some(e); + } + Some((e, false)) => { + else_opt = Some(e); + break; + } + None => { + else_opt = None; + break; + } + } + } + arms + }; + + let contains_else_clause = else_opt.is_some(); + + // `_ => [ | ()]` + let else_arm = { + let pat_under = pat_wild(lctx, e.span); + let else_expr = else_opt.unwrap_or_else(|| expr_tuple(lctx, e.span, vec![])); + arm(vec![pat_under], else_expr) + }; + + let mut arms = Vec::with_capacity(else_if_arms.len() + 2); + arms.push(pat_arm); + arms.extend(else_if_arms); + arms.push(else_arm); + + let match_expr = expr(lctx, + e.span, + hir::ExprMatch(lower_expr(lctx, sub_expr), arms, + hir::MatchSource::IfLetDesugar { + contains_else_clause: contains_else_clause, + })); + return match_expr; } - ExprWhileLet(..) => { - panic!("todo"); + + // Desugar ExprWhileLet + // From: `[opt_ident]: while let = ` + ExprWhileLet(ref pat, ref sub_expr, ref body, opt_ident) => { + // to: + // + // [opt_ident]: loop { + // match { + // => , + // _ => break + // } + // } + + // ` => ` + let pat_arm = { + let body_expr = expr_block(lctx, lower_block(lctx, body)); + arm(vec![lower_pat(lctx, pat)], body_expr) + }; + + // `_ => break` + let break_arm = { + let pat_under = pat_wild(lctx, e.span); + let break_expr = expr_break(lctx, e.span); + arm(vec![pat_under], break_expr) + }; + + // // `match { ... }` + let arms = vec![pat_arm, break_arm]; + let match_expr = expr(lctx, + e.span, + hir::ExprMatch(lower_expr(lctx, sub_expr), arms, hir::MatchSource::WhileLetDesugar)); + + // `[opt_ident]: loop { ... }` + let loop_block = block_expr(lctx, match_expr); + return expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident)); } // Desugar ExprForLoop // From: `[opt_ident]: for in ` - ExprForLoop(ref pat, ref head, ref body, ref opt_ident) => { + ExprForLoop(ref pat, ref head, ref body, opt_ident) => { // to: // // { @@ -952,7 +1077,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { // `[opt_ident]: loop { ... }` let loop_block = block_expr(lctx, match_expr); - let loop_expr = expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident.clone())); + let loop_expr = expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident)); // `mut iter => { ... }` let iter_arm = { @@ -976,16 +1101,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { // `{ let result = ...; result }` let result_ident = token::gensym_ident("result"); - let result = expr_block(lctx, - block_all(lctx, - e.span, - vec![stmt_let(lctx, - e.span, - false, - result_ident, - match_expr)], - Some(expr_ident(lctx, e.span, result_ident)))); - return result; + return expr_block(lctx, + block_all(lctx, + e.span, + vec![stmt_let(lctx, e.span, + false, + result_ident, + match_expr)], + Some(expr_ident(lctx, e.span, result_ident)))) } ExprMac(_) => panic!("Shouldn't exist here"), @@ -1137,6 +1260,10 @@ fn expr_block(lctx: &LoweringContext, b: P) -> P { expr(lctx, b.span, hir::ExprBlock(b)) } +fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec>) -> P { + expr(lctx, sp, hir::ExprTup(exprs)) +} + fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P { P(hir::Expr { id: lctx.next_id(), @@ -1208,6 +1335,10 @@ fn pat_ident_binding_mode(lctx: &LoweringContext, pat(lctx, span, pat_ident) } +fn pat_wild(lctx: &LoweringContext, span: Span) -> P { + pat(lctx, span, hir::PatWild(hir::PatWildSingle)) +} + fn pat(lctx: &LoweringContext, span: Span, pat: hir::Pat_) -> P { P(hir::Pat { id: lctx.next_id(), node: pat, span: span }) } diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 8cfad6341de0..f6767bc4e476 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -212,147 +212,11 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) } - // Desugar ExprWhileLet - // From: `[opt_ident]: while let = ` ast::ExprWhileLet(pat, expr, body, opt_ident) => { - // to: - // - // [opt_ident]: loop { - // match { - // => , - // _ => break - // } - // } - - push_compiler_expansion(fld, span, CompilerExpansionFormat::WhileLet); - - // ` => ` - 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 { ... }` - let arms = vec![pat_arm, break_arm]; - let match_expr = fld.cx.expr(span, - ast::ExprMatch(expr, arms, ast::MatchSource::WhileLetDesugar)); - - // `[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); - let result = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)); - fld.cx.bt_pop(); - result - } - - // Desugar ExprIfLet - // From: `if let = []` - ast::ExprIfLet(pat, expr, body, mut elseopt) => { - // to: - // - // match { - // => , - // [_ if => ,] - // _ => [ | ()] - // } - - push_compiler_expansion(fld, span, CompilerExpansionFormat::IfLet); - - // ` => ` - 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 { - let elseopt_continue = elseopt - .and_then(|els| els.and_then(|els| match els.node { - // else if - ast::ExprIf(cond, then, elseopt) => { - let pat_under = fld.cx.pat_wild(span); - arms.push(ast::Arm { - attrs: vec![], - pats: vec![pat_under], - guard: Some(cond), - body: fld.cx.expr_block(then) - }); - elseopt.map(|elseopt| (elseopt, true)) - } - _ => Some((P(els), false)) - })); - match elseopt_continue { - Some((e, true)) => { - elseopt = Some(e); - } - Some((e, false)) => { - elseopt = Some(e); - break; - } - None => { - elseopt = None; - break; - } - } - } - arms - }; - - let contains_else_clause = elseopt.is_some(); - - // `_ => [ | ()]` - let else_arm = { - let pat_under = fld.cx.pat_wild(span); - let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_tuple(span, vec![])); - 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.extend(else_if_arms); - arms.push(else_arm); - - let match_expr = fld.cx.expr(span, - ast::ExprMatch(expr, arms, - ast::MatchSource::IfLetDesugar { - contains_else_clause: contains_else_clause, - })); - let result = fld.fold_expr(match_expr); - fld.cx.bt_pop(); - result - } - - // Desugar support for ExprIfLet in the ExprIf else position - ast::ExprIf(cond, blk, elseopt) => { - let elseopt = elseopt.map(|els| els.and_then(|els| match els.node { - ast::ExprIfLet(..) => { - push_compiler_expansion(fld, span, CompilerExpansionFormat::IfLet); - // wrap the if-let expr in a block - let span = els.span; - let blk = P(ast::Block { - stmts: vec![], - expr: Some(P(els)), - id: ast::DUMMY_NODE_ID, - rules: ast::DefaultBlock, - span: span - }); - let result = fld.cx.expr_block(blk); - fld.cx.bt_pop(); - result - } - _ => P(els) - })); - let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt)); - if_expr.map(|e| noop_fold_expr(e, fld)) + let pat = fld.fold_pat(pat); + let expr = fld.fold_expr(expr); + let (body, opt_ident) = expand_loop_block(body, opt_ident, fld); + fld.cx.expr(span, ast::ExprWhileLet(pat, expr, body, opt_ident)) } ast::ExprLoop(loop_block, opt_ident) => {