Skip to content

Commit bc364b4

Browse files
committed
if let and while let
1 parent 04a7675 commit bc364b4

File tree

2 files changed

+154
-159
lines changed

2 files changed

+154
-159
lines changed

src/librustc_front/lowering.rs

Lines changed: 150 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -786,10 +786,28 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
786786
ExprAddrOf(m, ref ohs) => {
787787
hir::ExprAddrOf(lower_mutability(lctx, m), lower_expr(lctx, ohs))
788788
}
789-
ExprIf(ref cond, ref tr, ref fl) => {
789+
// More complicated than you might expect because the else branch
790+
// might be `if let`.
791+
ExprIf(ref cond, ref blk, ref else_opt) => {
792+
let else_opt = else_opt.as_ref().map(|els| match els.node {
793+
ExprIfLet(..) => {
794+
// wrap the if-let expr in a block
795+
let span = els.span;
796+
let blk = P(hir::Block {
797+
stmts: vec![],
798+
expr: Some(lower_expr(lctx, els)),
799+
id: lctx.next_id(),
800+
rules: hir::DefaultBlock,
801+
span: span
802+
});
803+
expr_block(lctx, blk)
804+
}
805+
_ => lower_expr(lctx, els)
806+
});
807+
790808
hir::ExprIf(lower_expr(lctx, cond),
791-
lower_block(lctx, tr),
792-
fl.as_ref().map(|x| lower_expr(lctx, x)))
809+
lower_block(lctx, blk),
810+
else_opt)
793811
}
794812
ExprWhile(ref cond, ref body, opt_ident) => {
795813
hir::ExprWhile(lower_expr(lctx, cond),
@@ -880,16 +898,123 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
880898
ExprInPlace(..) => {
881899
panic!("todo");
882900
}
883-
ExprIfLet(..) => {
884-
panic!("todo");
901+
902+
// Desugar ExprIfLet
903+
// From: `if let <pat> = <sub_expr> <body> [<else_opt>]`
904+
ExprIfLet(ref pat, ref sub_expr, ref body, ref else_opt) => {
905+
// to:
906+
//
907+
// match <sub_expr> {
908+
// <pat> => <body>,
909+
// [_ if <else_opt_if_cond> => <else_opt_if_body>,]
910+
// _ => [<else_opt> | ()]
911+
// }
912+
913+
// `<pat> => <body>`
914+
let pat_arm = {
915+
let body_expr = expr_block(lctx, lower_block(lctx, body));
916+
arm(vec![lower_pat(lctx, pat)], body_expr)
917+
};
918+
919+
// `[_ if <else_opt_if_cond> => <else_opt_if_body>,]`
920+
let mut else_opt = else_opt.as_ref().map(|e| lower_expr(lctx, e));
921+
let else_if_arms = {
922+
let mut arms = vec![];
923+
loop {
924+
let else_opt_continue = else_opt
925+
.and_then(|els| els.and_then(|els| match els.node {
926+
// else if
927+
hir::ExprIf(cond, then, else_opt) => {
928+
let pat_under = pat_wild(lctx, e.span);
929+
arms.push(hir::Arm {
930+
attrs: vec![],
931+
pats: vec![pat_under],
932+
guard: Some(cond),
933+
body: expr_block(lctx, then)
934+
});
935+
else_opt.map(|else_opt| (else_opt, true))
936+
}
937+
_ => Some((P(els), false))
938+
}));
939+
match else_opt_continue {
940+
Some((e, true)) => {
941+
else_opt = Some(e);
942+
}
943+
Some((e, false)) => {
944+
else_opt = Some(e);
945+
break;
946+
}
947+
None => {
948+
else_opt = None;
949+
break;
950+
}
951+
}
952+
}
953+
arms
954+
};
955+
956+
let contains_else_clause = else_opt.is_some();
957+
958+
// `_ => [<else_opt> | ()]`
959+
let else_arm = {
960+
let pat_under = pat_wild(lctx, e.span);
961+
let else_expr = else_opt.unwrap_or_else(|| expr_tuple(lctx, e.span, vec![]));
962+
arm(vec![pat_under], else_expr)
963+
};
964+
965+
let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
966+
arms.push(pat_arm);
967+
arms.extend(else_if_arms);
968+
arms.push(else_arm);
969+
970+
let match_expr = expr(lctx,
971+
e.span,
972+
hir::ExprMatch(lower_expr(lctx, sub_expr), arms,
973+
hir::MatchSource::IfLetDesugar {
974+
contains_else_clause: contains_else_clause,
975+
}));
976+
return match_expr;
885977
}
886-
ExprWhileLet(..) => {
887-
panic!("todo");
978+
979+
// Desugar ExprWhileLet
980+
// From: `[opt_ident]: while let <pat> = <sub_expr> <body>`
981+
ExprWhileLet(ref pat, ref sub_expr, ref body, opt_ident) => {
982+
// to:
983+
//
984+
// [opt_ident]: loop {
985+
// match <sub_expr> {
986+
// <pat> => <body>,
987+
// _ => break
988+
// }
989+
// }
990+
991+
// `<pat> => <body>`
992+
let pat_arm = {
993+
let body_expr = expr_block(lctx, lower_block(lctx, body));
994+
arm(vec![lower_pat(lctx, pat)], body_expr)
995+
};
996+
997+
// `_ => break`
998+
let break_arm = {
999+
let pat_under = pat_wild(lctx, e.span);
1000+
let break_expr = expr_break(lctx, e.span);
1001+
arm(vec![pat_under], break_expr)
1002+
};
1003+
1004+
// // `match <sub_expr> { ... }`
1005+
let arms = vec![pat_arm, break_arm];
1006+
let match_expr = expr(lctx,
1007+
e.span,
1008+
hir::ExprMatch(lower_expr(lctx, sub_expr), arms, hir::MatchSource::WhileLetDesugar));
1009+
1010+
// `[opt_ident]: loop { ... }`
1011+
let loop_block = block_expr(lctx, match_expr);
1012+
return expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident));
8881013
}
8891014

8901015
// Desugar ExprForLoop
8911016
// From: `[opt_ident]: for <pat> in <head> <body>`
892-
ExprForLoop(ref pat, ref head, ref body, ref opt_ident) => {
1017+
ExprForLoop(ref pat, ref head, ref body, opt_ident) => {
8931018
// to:
8941019
//
8951020
// {
@@ -952,7 +1077,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
9521077

9531078
// `[opt_ident]: loop { ... }`
9541079
let loop_block = block_expr(lctx, match_expr);
955-
let loop_expr = expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident.clone()));
1080+
let loop_expr = expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident));
9561081

9571082
// `mut iter => { ... }`
9581083
let iter_arm = {
@@ -976,16 +1101,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
9761101

9771102
// `{ let result = ...; result }`
9781103
let result_ident = token::gensym_ident("result");
979-
let result = expr_block(lctx,
980-
block_all(lctx,
981-
e.span,
982-
vec![stmt_let(lctx,
983-
e.span,
984-
false,
985-
result_ident,
986-
match_expr)],
987-
Some(expr_ident(lctx, e.span, result_ident))));
988-
return result;
1104+
return expr_block(lctx,
1105+
block_all(lctx,
1106+
e.span,
1107+
vec![stmt_let(lctx, e.span,
1108+
false,
1109+
result_ident,
1110+
match_expr)],
1111+
Some(expr_ident(lctx, e.span, result_ident))))
9891112
}
9901113

9911114
ExprMac(_) => panic!("Shouldn't exist here"),
@@ -1137,6 +1260,10 @@ fn expr_block(lctx: &LoweringContext, b: P<hir::Block>) -> P<hir::Expr> {
11371260
expr(lctx, b.span, hir::ExprBlock(b))
11381261
}
11391262

1263+
fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>) -> P<hir::Expr> {
1264+
expr(lctx, sp, hir::ExprTup(exprs))
1265+
}
1266+
11401267
fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P<hir::Expr> {
11411268
P(hir::Expr {
11421269
id: lctx.next_id(),
@@ -1208,6 +1335,10 @@ fn pat_ident_binding_mode(lctx: &LoweringContext,
12081335
pat(lctx, span, pat_ident)
12091336
}
12101337

1338+
fn pat_wild(lctx: &LoweringContext, span: Span) -> P<hir::Pat> {
1339+
pat(lctx, span, hir::PatWild(hir::PatWildSingle))
1340+
}
1341+
12111342
fn pat(lctx: &LoweringContext, span: Span, pat: hir::Pat_) -> P<hir::Pat> {
12121343
P(hir::Pat { id: lctx.next_id(), node: pat, span: span })
12131344
}

src/libsyntax/ext/expand.rs

Lines changed: 4 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -212,147 +212,11 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
212212
fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
213213
}
214214

215-
// Desugar ExprWhileLet
216-
// From: `[opt_ident]: while let <pat> = <expr> <body>`
217215
ast::ExprWhileLet(pat, expr, body, opt_ident) => {
218-
// to:
219-
//
220-
// [opt_ident]: loop {
221-
// match <expr> {
222-
// <pat> => <body>,
223-
// _ => break
224-
// }
225-
// }
226-
227-
push_compiler_expansion(fld, span, CompilerExpansionFormat::WhileLet);
228-
229-
// `<pat> => <body>`
230-
let pat_arm = {
231-
let body_expr = fld.cx.expr_block(body);
232-
fld.cx.arm(pat.span, vec![pat], body_expr)
233-
};
234-
235-
// `_ => break`
236-
let break_arm = {
237-
let pat_under = fld.cx.pat_wild(span);
238-
let break_expr = fld.cx.expr_break(span);
239-
fld.cx.arm(span, vec![pat_under], break_expr)
240-
};
241-
242-
// `match <expr> { ... }`
243-
let arms = vec![pat_arm, break_arm];
244-
let match_expr = fld.cx.expr(span,
245-
ast::ExprMatch(expr, arms, ast::MatchSource::WhileLetDesugar));
246-
247-
// `[opt_ident]: loop { ... }`
248-
let loop_block = fld.cx.block_expr(match_expr);
249-
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
250-
let result = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident));
251-
fld.cx.bt_pop();
252-
result
253-
}
254-
255-
// Desugar ExprIfLet
256-
// From: `if let <pat> = <expr> <body> [<elseopt>]`
257-
ast::ExprIfLet(pat, expr, body, mut elseopt) => {
258-
// to:
259-
//
260-
// match <expr> {
261-
// <pat> => <body>,
262-
// [_ if <elseopt_if_cond> => <elseopt_if_body>,]
263-
// _ => [<elseopt> | ()]
264-
// }
265-
266-
push_compiler_expansion(fld, span, CompilerExpansionFormat::IfLet);
267-
268-
// `<pat> => <body>`
269-
let pat_arm = {
270-
let body_expr = fld.cx.expr_block(body);
271-
fld.cx.arm(pat.span, vec![pat], body_expr)
272-
};
273-
274-
// `[_ if <elseopt_if_cond> => <elseopt_if_body>,]`
275-
let else_if_arms = {
276-
let mut arms = vec![];
277-
loop {
278-
let elseopt_continue = elseopt
279-
.and_then(|els| els.and_then(|els| match els.node {
280-
// else if
281-
ast::ExprIf(cond, then, elseopt) => {
282-
let pat_under = fld.cx.pat_wild(span);
283-
arms.push(ast::Arm {
284-
attrs: vec![],
285-
pats: vec![pat_under],
286-
guard: Some(cond),
287-
body: fld.cx.expr_block(then)
288-
});
289-
elseopt.map(|elseopt| (elseopt, true))
290-
}
291-
_ => Some((P(els), false))
292-
}));
293-
match elseopt_continue {
294-
Some((e, true)) => {
295-
elseopt = Some(e);
296-
}
297-
Some((e, false)) => {
298-
elseopt = Some(e);
299-
break;
300-
}
301-
None => {
302-
elseopt = None;
303-
break;
304-
}
305-
}
306-
}
307-
arms
308-
};
309-
310-
let contains_else_clause = elseopt.is_some();
311-
312-
// `_ => [<elseopt> | ()]`
313-
let else_arm = {
314-
let pat_under = fld.cx.pat_wild(span);
315-
let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_tuple(span, vec![]));
316-
fld.cx.arm(span, vec![pat_under], else_expr)
317-
};
318-
319-
let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
320-
arms.push(pat_arm);
321-
arms.extend(else_if_arms);
322-
arms.push(else_arm);
323-
324-
let match_expr = fld.cx.expr(span,
325-
ast::ExprMatch(expr, arms,
326-
ast::MatchSource::IfLetDesugar {
327-
contains_else_clause: contains_else_clause,
328-
}));
329-
let result = fld.fold_expr(match_expr);
330-
fld.cx.bt_pop();
331-
result
332-
}
333-
334-
// Desugar support for ExprIfLet in the ExprIf else position
335-
ast::ExprIf(cond, blk, elseopt) => {
336-
let elseopt = elseopt.map(|els| els.and_then(|els| match els.node {
337-
ast::ExprIfLet(..) => {
338-
push_compiler_expansion(fld, span, CompilerExpansionFormat::IfLet);
339-
// wrap the if-let expr in a block
340-
let span = els.span;
341-
let blk = P(ast::Block {
342-
stmts: vec![],
343-
expr: Some(P(els)),
344-
id: ast::DUMMY_NODE_ID,
345-
rules: ast::DefaultBlock,
346-
span: span
347-
});
348-
let result = fld.cx.expr_block(blk);
349-
fld.cx.bt_pop();
350-
result
351-
}
352-
_ => P(els)
353-
}));
354-
let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt));
355-
if_expr.map(|e| noop_fold_expr(e, fld))
216+
let pat = fld.fold_pat(pat);
217+
let expr = fld.fold_expr(expr);
218+
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
219+
fld.cx.expr(span, ast::ExprWhileLet(pat, expr, body, opt_ident))
356220
}
357221

358222
ast::ExprLoop(loop_block, opt_ident) => {

0 commit comments

Comments
 (0)