about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2015-09-28 17:24:42 +1300
committerNick Cameron <ncameron@mozilla.com>2015-10-09 11:53:41 +1300
commitbc364b4a0d678b29cba92ce948d948aee9d76b25 (patch)
treedd7ae97421e42072e69df6ca867c9d64b68b0a6b
parent04a7675d222bcba021886bd5f21d7c6b33273811 (diff)
downloadrust-bc364b4a0d678b29cba92ce948d948aee9d76b25.tar.gz
rust-bc364b4a0d678b29cba92ce948d948aee9d76b25.zip
if let and while let
-rw-r--r--src/librustc_front/lowering.rs169
-rw-r--r--src/libsyntax/ext/expand.rs144
2 files changed, 154 insertions, 159 deletions
diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs
index c8f5f89b669..d9b834fe9fc 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<hir::Expr> {
                 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<hir::Expr> {
                 ExprInPlace(..) => {
                     panic!("todo");
                 }
-                ExprIfLet(..) => {
-                    panic!("todo");
+
+                // Desugar ExprIfLet
+                // From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
+                ExprIfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
+                    // to:
+                    //
+                    //   match <sub_expr> {
+                    //     <pat> => <body>,
+                    //     [_ if <else_opt_if_cond> => <else_opt_if_body>,]
+                    //     _ => [<else_opt> | ()]
+                    //   }
+
+                    // `<pat> => <body>`
+                    let pat_arm = {
+                        let body_expr = expr_block(lctx, lower_block(lctx, body));
+                        arm(vec![lower_pat(lctx, pat)], body_expr)
+                    };
+
+                    // `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
+                    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();
+
+                    // `_ => [<else_opt> | ()]`
+                    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 <pat> = <sub_expr> <body>`
+                ExprWhileLet(ref pat, ref sub_expr, ref body, opt_ident) => {
+                    // to:
+                    //
+                    //   [opt_ident]: loop {
+                    //     match <sub_expr> {
+                    //       <pat> => <body>,
+                    //       _ => break
+                    //     }
+                    //   }
+
+                    // `<pat> => <body>`
+                    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 <sub_expr> { ... }`
+                    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 <pat> in <head> <body>`
-                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<hir::Expr> {
 
                     // `[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<hir::Expr> {
 
                     // `{ 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<hir::Block>) -> P<hir::Expr> {
     expr(lctx, b.span, hir::ExprBlock(b))
 }
 
+fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>) -> P<hir::Expr> {
+    expr(lctx, sp, hir::ExprTup(exprs))
+}
+
 fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P<hir::Expr> {
     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<hir::Pat> {
+    pat(lctx, span, hir::PatWild(hir::PatWildSingle))
+}
+
 fn pat(lctx: &LoweringContext, span: Span, pat: hir::Pat_) -> P<hir::Pat> {
     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 8cfad6341de..f6767bc4e47 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -212,147 +212,11 @@ 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
-            //     }
-            //   }
-
-            push_compiler_expansion(fld, span, CompilerExpansionFormat::WhileLet);
-
-            // `<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::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 <pat> = <expr> <body> [<elseopt>]`
-        ast::ExprIfLet(pat, expr, body, mut elseopt) => {
-            // to:
-            //
-            //   match <expr> {
-            //     <pat> => <body>,
-            //     [_ if <elseopt_if_cond> => <elseopt_if_body>,]
-            //     _ => [<elseopt> | ()]
-            //   }
-
-            push_compiler_expansion(fld, span, CompilerExpansionFormat::IfLet);
-
-            // `<pat> => <body>`
-            let pat_arm = {
-                let body_expr = fld.cx.expr_block(body);
-                fld.cx.arm(pat.span, vec![pat], body_expr)
-            };
-
-            // `[_ if <elseopt_if_cond> => <elseopt_if_body>,]`
-            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();
-
-            // `_ => [<elseopt> | ()]`
-            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) => {