Add support for break and cont to rustc

Testing proper cleanup is hampered by
https://github.com/graydon/rust/issues/293
This commit is contained in:
Marijn Haverbeke 2011-03-25 16:28:16 +01:00
parent 9c5affda1a
commit 6ecdc04788
8 changed files with 164 additions and 9 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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()) {

View File

@ -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"; }

View File

@ -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](_,_,_),

View File

@ -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);
}

View File

@ -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]) {

View File

@ -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);
}
}