From 6ecdc04788334420db05d9894e18d1d7a605ab4f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 25 Mar 2011 16:28:16 +0100 Subject: [PATCH] Add support for break and cont to rustc Testing proper cleanup is hampered by https://github.com/graydon/rust/issues/293 --- src/comp/front/ast.rs | 2 + src/comp/front/lexer.rs | 2 + src/comp/front/parser.rs | 10 +++++ src/comp/front/token.rs | 6 +++ src/comp/middle/fold.rs | 22 +++++++++++ src/comp/middle/trans.rs | 81 +++++++++++++++++++++++++++++++++----- src/comp/middle/typeck.rs | 10 +++++ src/test/run-pass/break.rs | 40 +++++++++++++++++++ 8 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 src/test/run-pass/break.rs diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs index c1e363b76e637..b1dbd80e54ad1 100644 --- a/src/comp/front/ast.rs +++ b/src/comp/front/ast.rs @@ -246,6 +246,8 @@ tag expr_ { expr_path(path, option.t[def], ann); expr_ext(path, vec[@expr], option.t[@expr], @expr, ann); expr_fail; + expr_break; + expr_cont; expr_ret(option.t[@expr]); expr_put(option.t[@expr]); expr_be(@expr); diff --git a/src/comp/front/lexer.rs b/src/comp/front/lexer.rs index a793a9205bba1..878940b73bc98 100644 --- a/src/comp/front/lexer.rs +++ b/src/comp/front/lexer.rs @@ -111,6 +111,8 @@ impure fn new_reader(io.reader rdr, str filename) -> reader keywords.insert("for", token.FOR); keywords.insert("each", token.EACH); + keywords.insert("break", token.BREAK); + keywords.insert("cont", token.CONT); keywords.insert("put", token.PUT); keywords.insert("ret", token.RET); keywords.insert("be", token.BE); diff --git a/src/comp/front/parser.rs b/src/comp/front/parser.rs index dad41e2e75fdd..c8130b0b96539 100644 --- a/src/comp/front/parser.rs +++ b/src/comp/front/parser.rs @@ -829,6 +829,16 @@ impure fn parse_bottom_expr(parser p) -> @ast.expr { } } + case (token.BREAK) { + p.bump(); + ex = ast.expr_break; + } + + case (token.CONT) { + p.bump(); + ex = ast.expr_cont; + } + case (token.PUT) { p.bump(); alt (p.peek()) { diff --git a/src/comp/front/token.rs b/src/comp/front/token.rs index 9d5f0dfd69abb..bb0cea80bb812 100644 --- a/src/comp/front/token.rs +++ b/src/comp/front/token.rs @@ -74,6 +74,9 @@ tag token { ALT; CASE; + BREAK; + CONT; + FAIL; DROP; @@ -242,6 +245,9 @@ fn to_str(token t) -> str { case (ALT) { ret "alt"; } case (CASE) { ret "case"; } + case (BREAK) { ret "break"; } + case (CONT) { ret "cont"; } + case (FAIL) { ret "fail"; } case (DROP) { ret "drop"; } diff --git a/src/comp/middle/fold.rs b/src/comp/middle/fold.rs index 27fad370c9f58..76715a28a9b7b 100644 --- a/src/comp/middle/fold.rs +++ b/src/comp/middle/fold.rs @@ -170,6 +170,10 @@ type ast_fold[ENV] = (fn(&ENV e, &span sp) -> @expr) fold_expr_fail, + (fn(&ENV e, &span sp) -> @expr) fold_expr_break, + + (fn(&ENV e, &span sp) -> @expr) fold_expr_cont, + (fn(&ENV e, &span sp, &option.t[@expr] rv) -> @expr) fold_expr_ret, @@ -695,6 +699,14 @@ fn fold_expr[ENV](&ENV env, ast_fold[ENV] fld, &@expr e) -> @expr { ret fld.fold_expr_fail(env_, e.span); } + case (ast.expr_break) { + ret fld.fold_expr_break(env_, e.span); + } + + case (ast.expr_cont) { + ret fld.fold_expr_cont(env_, e.span); + } + case (ast.expr_ret(?oe)) { auto oee = none[@expr]; alt (oe) { @@ -1266,6 +1278,14 @@ fn identity_fold_expr_fail[ENV](&ENV env, &span sp) -> @expr { ret @respan(sp, ast.expr_fail); } +fn identity_fold_expr_break[ENV](&ENV env, &span sp) -> @expr { + ret @respan(sp, ast.expr_break); +} + +fn identity_fold_expr_cont[ENV](&ENV env, &span sp) -> @expr { + ret @respan(sp, ast.expr_cont); +} + fn identity_fold_expr_ret[ENV](&ENV env, &span sp, &option.t[@expr] rv) -> @expr { ret @respan(sp, ast.expr_ret(rv)); @@ -1565,6 +1585,8 @@ fn new_identity_fold[ENV]() -> ast_fold[ENV] { fold_expr_path = bind identity_fold_expr_path[ENV](_,_,_,_,_), fold_expr_ext = bind identity_fold_expr_ext[ENV](_,_,_,_,_,_,_), fold_expr_fail = bind identity_fold_expr_fail[ENV](_,_), + fold_expr_break = bind identity_fold_expr_break[ENV](_,_), + fold_expr_cont = bind identity_fold_expr_cont[ENV](_,_), fold_expr_ret = bind identity_fold_expr_ret[ENV](_,_,_), fold_expr_put = bind identity_fold_expr_put[ENV](_,_,_), fold_expr_be = bind identity_fold_expr_be[ENV](_,_,_), diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 72ffb734f5672..9eb208597e0d4 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -130,6 +130,7 @@ tag cleanup { tag block_kind { SCOPE_BLOCK; + LOOP_SCOPE_BLOCK(option.t[@block_ctxt], @block_ctxt); NON_SCOPE_BLOCK; } @@ -990,7 +991,7 @@ fn trans_non_gc_free(@block_ctxt cx, ValueRef v) -> result { } fn find_scope_cx(@block_ctxt cx) -> @block_ctxt { - if (cx.kind == SCOPE_BLOCK) { + if (cx.kind != NON_SCOPE_BLOCK) { ret cx; } alt (cx.parent) { @@ -3043,13 +3044,15 @@ fn trans_for(@block_ctxt cx, @ast.decl decl, @ast.expr seq, &ast.block body) -> result { - fn inner(@block_ctxt cx, @ast.local local, ValueRef curr, - @ty.t t, ast.block body) -> result { + @ty.t t, ast.block body, + @block_ctxt outer_next_cx) -> result { - auto scope_cx = new_scope_block_ctxt(cx, "for loop scope"); auto next_cx = new_sub_block_ctxt(cx, "next"); + auto scope_cx = + new_loop_scope_block_ctxt(cx, option.some[@block_ctxt](next_cx), + outer_next_cx, "for loop scope"); cx.build.Br(scope_cx.llbb); auto local_res = alloc_local(scope_cx, local); @@ -3069,10 +3072,13 @@ fn trans_for(@block_ctxt cx, } } + auto next_cx = new_sub_block_ctxt(cx, "next"); auto seq_ty = ty.expr_ty(seq); auto seq_res = trans_expr(cx, seq); - ret iter_sequence(seq_res.bcx, seq_res.val, seq_ty, - bind inner(_, local, _, _, body)); + auto it = iter_sequence(seq_res.bcx, seq_res.val, seq_ty, + bind inner(_, local, _, _, body, next_cx)); + it.bcx.build.Br(next_cx.llbb); + ret res(next_cx, it.val); } @@ -3308,8 +3314,9 @@ fn trans_while(@block_ctxt cx, @ast.expr cond, &ast.block body) -> result { auto cond_cx = new_scope_block_ctxt(cx, "while cond"); - auto body_cx = new_scope_block_ctxt(cx, "while loop body"); auto next_cx = new_sub_block_ctxt(cx, "next"); + auto body_cx = new_loop_scope_block_ctxt(cx, option.none[@block_ctxt], + next_cx, "while loop body"); auto body_res = trans_block(body_cx, body); auto cond_res = trans_expr(cond_cx, cond); @@ -3326,8 +3333,9 @@ fn trans_while(@block_ctxt cx, @ast.expr cond, fn trans_do_while(@block_ctxt cx, &ast.block body, @ast.expr cond) -> result { - auto body_cx = new_scope_block_ctxt(cx, "do-while loop body"); auto next_cx = new_sub_block_ctxt(cx, "next"); + auto body_cx = new_loop_scope_block_ctxt(cx, option.none[@block_ctxt], + next_cx, "do-while loop body"); auto body_res = trans_block(body_cx, body); auto cond_res = trans_expr(body_res.bcx, cond); @@ -4599,6 +4607,14 @@ fn trans_expr(@block_ctxt cx, @ast.expr e) -> result { ret trans_check_expr(cx, a); } + case (ast.expr_break) { + ret trans_break(cx); + } + + case (ast.expr_cont) { + ret trans_cont(cx); + } + case (ast.expr_ret(?e)) { ret trans_ret(cx, e); } @@ -4770,6 +4786,47 @@ fn trans_put(@block_ctxt cx, &option.t[@ast.expr] e) -> result { ret res(bcx, bcx.build.FastCall(llcallee, llargs)); } +fn trans_break_cont(@block_ctxt cx, bool to_end) -> result { + auto bcx = cx; + // Locate closest loop block, outputting cleanup as we go. + auto cleanup_cx = cx; + while (true) { + bcx = trans_block_cleanups(bcx, cleanup_cx); + alt (cleanup_cx.kind) { + case (LOOP_SCOPE_BLOCK(?_cont, ?_break)) { + if (to_end) { + bcx.build.Br(_break.llbb); + } else { + alt (_cont) { + case (option.some[@block_ctxt](?_cont)) { + bcx.build.Br(_cont.llbb); + } + case (_) { + bcx.build.Br(cleanup_cx.llbb); + } + } + } + ret res(new_sub_block_ctxt(cx, "unreachable"), C_nil()); + } + case (_) { + alt (cleanup_cx.parent) { + case (parent_some(?cx)) { cleanup_cx = cx; } + } + } + } + } + ret res(cx, C_nil()); // Never reached. Won't compile otherwise. +} + +fn trans_break(@block_ctxt cx) -> result { + ret trans_break_cont(cx, true); +} + +fn trans_cont(@block_ctxt cx) -> result { + ret trans_break_cont(cx, false); +} + + fn trans_ret(@block_ctxt cx, &option.t[@ast.expr] e) -> result { auto bcx = cx; auto val = C_nil(); @@ -5033,6 +5090,12 @@ fn new_scope_block_ctxt(@block_ctxt bcx, str n) -> @block_ctxt { ret new_block_ctxt(bcx.fcx, parent_some(bcx), SCOPE_BLOCK, n); } +fn new_loop_scope_block_ctxt(@block_ctxt bcx, option.t[@block_ctxt] _cont, + @block_ctxt _break, str n) -> @block_ctxt { + ret new_block_ctxt(bcx.fcx, parent_some(bcx), + LOOP_SCOPE_BLOCK(_cont, _break), n); +} + // Use this when you're making a general CFG BB within a scope. fn new_sub_block_ctxt(@block_ctxt bcx, str n) -> @block_ctxt { ret new_block_ctxt(bcx.fcx, parent_some(bcx), NON_SCOPE_BLOCK, n); @@ -5043,7 +5106,7 @@ fn trans_block_cleanups(@block_ctxt cx, @block_ctxt cleanup_cx) -> @block_ctxt { auto bcx = cx; - if (cleanup_cx.kind != SCOPE_BLOCK) { + if (cleanup_cx.kind == NON_SCOPE_BLOCK) { check (_vec.len[cleanup](cleanup_cx.cleanups) == 0u); } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 596d9d4bd4de9..f7d9e564c11e0 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -1413,6 +1413,8 @@ fn demand_expr_full(&@fn_ctxt fcx, @ty.t expected, @ast.expr e, } case (ast.expr_fail) { e_1 = e.node; } case (ast.expr_log(_)) { e_1 = e.node; } + case (ast.expr_break) { e_1 = e.node; } + case (ast.expr_cont) { e_1 = e.node; } case (ast.expr_ret(_)) { e_1 = e.node; } case (ast.expr_put(_)) { e_1 = e.node; } case (ast.expr_be(_)) { e_1 = e.node; } @@ -1806,6 +1808,14 @@ fn check_expr(&@fn_ctxt fcx, @ast.expr expr) -> @ast.expr { ret expr; } + case (ast.expr_break) { + ret expr; + } + + case (ast.expr_cont) { + ret expr; + } + case (ast.expr_ret(?expr_opt)) { alt (expr_opt) { case (none[@ast.expr]) { diff --git a/src/test/run-pass/break.rs b/src/test/run-pass/break.rs new file mode 100644 index 0000000000000..48c3b091f08b0 --- /dev/null +++ b/src/test/run-pass/break.rs @@ -0,0 +1,40 @@ +// xfail-boot + +fn main() { + auto i = 0; + while (i < 20) { + i += 1; + if (i == 10) { break; } + } + check(i == 10); + + do { + i += 1; + if (i == 20) { break; } + } while (i < 30); + check(i == 20); + + for (int x in vec(1, 2, 3, 4, 5, 6)) { + if (x == 3) { break; } + check(x <= 3); + } + + i = 0; + while (i < 10) { + i += 1; + if (i % 2 == 0) { cont; } + check(i % 2 != 0); + } + + i = 0; + do { + i += 1; + if (i % 2 == 0) { cont; } + check(i % 2 != 0); + } while (i < 10); + + for (int x in vec(1, 2, 3, 4, 5, 6)) { + if (x % 2 == 0) { cont; } + check(x % 2 != 0); + } +}