aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/comp/front/ast.rs2
-rw-r--r--src/comp/front/lexer.rs2
-rw-r--r--src/comp/front/parser.rs10
-rw-r--r--src/comp/front/token.rs6
-rw-r--r--src/comp/middle/fold.rs22
-rw-r--r--src/comp/middle/trans.rs81
-rw-r--r--src/comp/middle/typeck.rs10
-rw-r--r--src/test/run-pass/break.rs40
8 files changed, 164 insertions, 9 deletions
diff --git a/src/comp/front/ast.rs b/src/comp/front/ast.rs
index c1e363b7..b1dbd80e 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 a793a920..878940b7 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 dad41e2e..c8130b0b 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 9d5f0dfd..bb0cea80 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 27fad370..76715a28 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 72ffb734..9eb20859 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 596d9d4b..f7d9e564 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 00000000..48c3b091
--- /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);
+ }
+}