Skip to content

Commit dd66e75

Browse files
committed
Preliminary support for labeled break/continue for loops
This patch adds preliminary middle-end support (liveness and trans) for breaks and `loop`s to `loop` constructs that have labels. while and for loops can't have labels yet. Progress on #2216
1 parent 46d4bbb commit dd66e75

File tree

10 files changed

+232
-98
lines changed

10 files changed

+232
-98
lines changed

src/libsyntax/print/pprust.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,10 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
11801180
ast::expr_loop(blk, opt_ident) => {
11811181
head(s, ~"loop");
11821182
space(s.s);
1183-
opt_ident.iter(|ident| {print_ident(s, *ident); space(s.s)});
1183+
opt_ident.iter(|ident| {
1184+
print_ident(s, *ident);
1185+
word_space(s, ~":");
1186+
});
11841187
print_block(s, blk);
11851188
}
11861189
ast::expr_match(expr, arms) => {

src/rustc/middle/liveness.rs

Lines changed: 136 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@
9595
use dvec::DVec;
9696
use std::map::HashMap;
9797
use syntax::{visit, ast_util};
98-
use syntax::print::pprust::{expr_to_str};
98+
use syntax::print::pprust::{expr_to_str, block_to_str};
9999
use visit::vt;
100-
use syntax::codemap::span;
100+
use syntax::codemap::{span, span_to_str};
101101
use syntax::ast::*;
102102
use io::WriterUtil;
103103
use capture::{cap_move, cap_drop, cap_copy, cap_ref};
@@ -167,6 +167,16 @@ impl LiveNodeKind : cmp::Eq {
167167
pure fn ne(other: &LiveNodeKind) -> bool { !self.eq(other) }
168168
}
169169

170+
fn live_node_kind_to_str(lnk: LiveNodeKind, cx: ty::ctxt) -> ~str {
171+
let cm = cx.sess.codemap;
172+
match lnk {
173+
FreeVarNode(s) => fmt!("Free var node [%s]", span_to_str(s, cm)),
174+
ExprNode(s) => fmt!("Expr node [%s]", span_to_str(s, cm)),
175+
VarDefNode(s) => fmt!("Var def node [%s]", span_to_str(s, cm)),
176+
ExitNode => ~"Exit node"
177+
}
178+
}
179+
170180
fn check_crate(tcx: ty::ctxt,
171181
method_map: typeck::method_map,
172182
crate: @crate) -> last_use_map {
@@ -277,8 +287,8 @@ fn IrMaps(tcx: ty::ctxt, method_map: typeck::method_map,
277287
tcx: tcx,
278288
method_map: method_map,
279289
last_use_map: last_use_map,
280-
num_live_nodes: 0u,
281-
num_vars: 0u,
290+
num_live_nodes: 0,
291+
num_vars: 0,
282292
live_node_map: HashMap(),
283293
variable_map: HashMap(),
284294
capture_map: HashMap(),
@@ -291,9 +301,10 @@ impl IrMaps {
291301
fn add_live_node(lnk: LiveNodeKind) -> LiveNode {
292302
let ln = LiveNode(self.num_live_nodes);
293303
self.lnks.push(lnk);
294-
self.num_live_nodes += 1u;
304+
self.num_live_nodes += 1;
295305

296-
debug!("%s is of kind %?", ln.to_str(), lnk);
306+
debug!("%s is of kind %s", ln.to_str(),
307+
live_node_kind_to_str(lnk, self.tcx));
297308

298309
ln
299310
}
@@ -308,7 +319,7 @@ impl IrMaps {
308319
fn add_variable(vk: VarKind) -> Variable {
309320
let v = Variable(self.num_vars);
310321
self.var_kinds.push(vk);
311-
self.num_vars += 1u;
322+
self.num_vars += 1;
312323

313324
match vk {
314325
Local(LocalInfo {id:node_id, _}) |
@@ -491,6 +502,10 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
491502
}
492503
expr_fn(_, _, _, cap_clause) |
493504
expr_fn_block(_, _, cap_clause) => {
505+
// Interesting control flow (for loops can contain labeled
506+
// breaks or continues)
507+
self.add_live_node_for_node(expr.id, ExprNode(expr.span));
508+
494509
// Make a live_node for each captured variable, with the span
495510
// being the location that the variable is used. This results
496511
// in better error messages than just pointing at the closure
@@ -571,14 +586,22 @@ const ACC_READ: uint = 1u;
571586
const ACC_WRITE: uint = 2u;
572587
const ACC_USE: uint = 4u;
573588

589+
type LiveNodeMap = HashMap<node_id, LiveNode>;
590+
574591
struct Liveness {
575592
tcx: ty::ctxt,
576593
ir: @IrMaps,
577594
s: Specials,
578595
successors: ~[mut LiveNode],
579596
users: ~[mut users],
580-
mut break_ln: LiveNode,
581-
mut cont_ln: LiveNode,
597+
// The list of node IDs for the nested loop scopes
598+
// we're in.
599+
mut loop_scope: @DVec<node_id>,
600+
// mappings from loop node ID to LiveNode
601+
// ("break" label should map to loop node ID,
602+
// it probably doesn't now)
603+
break_ln: LiveNodeMap,
604+
cont_ln: LiveNodeMap
582605
}
583606

584607
fn Liveness(ir: @IrMaps, specials: Specials) -> Liveness {
@@ -594,8 +617,9 @@ fn Liveness(ir: @IrMaps, specials: Specials) -> Liveness {
594617
vec::to_mut(
595618
vec::from_elem(ir.num_live_nodes * ir.num_vars,
596619
invalid_users())),
597-
break_ln: invalid_node(),
598-
cont_ln: invalid_node()
620+
loop_scope: @DVec(),
621+
break_ln: HashMap(),
622+
cont_ln: HashMap()
599623
}
600624
}
601625

@@ -691,6 +715,9 @@ impl Liveness {
691715
if reader.is_valid() {Some((*self.ir).lnk(reader))} else {None}
692716
}
693717

718+
/*
719+
Is this variable live on entry to any of its successor nodes?
720+
*/
694721
fn live_on_exit(ln: LiveNode, var: Variable)
695722
-> Option<LiveNodeKind> {
696723

@@ -717,8 +744,8 @@ impl Liveness {
717744
}
718745

719746
fn indices(ln: LiveNode, op: fn(uint)) {
720-
let node_base_idx = self.idx(ln, Variable(0u));
721-
for uint::range(0u, self.ir.num_vars) |var_idx| {
747+
let node_base_idx = self.idx(ln, Variable(0));
748+
for uint::range(0, self.ir.num_vars) |var_idx| {
722749
op(node_base_idx + var_idx)
723750
}
724751
}
@@ -735,8 +762,8 @@ impl Liveness {
735762
fn write_vars(wr: io::Writer,
736763
ln: LiveNode,
737764
test: fn(uint) -> LiveNode) {
738-
let node_base_idx = self.idx(ln, Variable(0u));
739-
for uint::range(0u, self.ir.num_vars) |var_idx| {
765+
let node_base_idx = self.idx(ln, Variable(0));
766+
for uint::range(0, self.ir.num_vars) |var_idx| {
740767
let idx = node_base_idx + var_idx;
741768
if test(idx).is_valid() {
742769
wr.write_str(~" ");
@@ -745,6 +772,28 @@ impl Liveness {
745772
}
746773
}
747774
775+
fn find_loop_scope(opt_label: Option<ident>, id: node_id, sp: span)
776+
-> node_id {
777+
match opt_label {
778+
Some(_) => // Refers to a labeled loop. Use the results of resolve
779+
// to find with one
780+
match self.tcx.def_map.find(id) {
781+
Some(def_label(loop_id)) => loop_id,
782+
_ => self.tcx.sess.span_bug(sp, ~"Label on break/loop \
783+
doesn't refer to a loop")
784+
},
785+
None =>
786+
// Vanilla 'break' or 'loop', so use the enclosing
787+
// loop scope
788+
if self.loop_scope.len() == 0 {
789+
self.tcx.sess.span_bug(sp, ~"break outside loop");
790+
}
791+
else {
792+
self.loop_scope.last()
793+
}
794+
}
795+
}
796+
748797
fn ln_str(ln: LiveNode) -> ~str {
749798
do io::with_str_writer |wr| {
750799
wr.write_str(~"[ln(");
@@ -833,18 +882,18 @@ impl Liveness {
833882
let idx = self.idx(ln, var);
834883
let user = &mut self.users[idx];
835884

836-
if (acc & ACC_WRITE) != 0u {
885+
if (acc & ACC_WRITE) != 0 {
837886
user.reader = invalid_node();
838887
user.writer = ln;
839888
}
840889

841890
// Important: if we both read/write, must do read second
842891
// or else the write will override.
843-
if (acc & ACC_READ) != 0u {
892+
if (acc & ACC_READ) != 0 {
844893
user.reader = ln;
845894
}
846895

847-
if (acc & ACC_USE) != 0u {
896+
if (acc & ACC_USE) != 0 {
848897
self.users[idx].used = true;
849898
}
850899

@@ -858,10 +907,13 @@ impl Liveness {
858907
// if there is a `break` or `again` at the top level, then it's
859908
// effectively a return---this only occurs in `for` loops,
860909
// where the body is really a closure.
910+
911+
debug!("compute: using id for block, %s", block_to_str(body,
912+
self.tcx.sess.intr()));
913+
861914
let entry_ln: LiveNode =
862-
self.with_loop_nodes(self.s.exit_ln, self.s.exit_ln, || {
863-
self.propagate_through_fn_block(decl, body)
864-
});
915+
self.with_loop_nodes(body.node.id, self.s.exit_ln, self.s.exit_ln,
916+
|| { self.propagate_through_fn_block(decl, body) });
865917

866918
// hack to skip the loop unless debug! is enabled:
867919
debug!("^^ liveness computation results for body %d (entry=%s)",
@@ -972,6 +1024,9 @@ impl Liveness {
9721024
}
9731025

9741026
fn propagate_through_expr(expr: @expr, succ: LiveNode) -> LiveNode {
1027+
debug!("propagate_through_expr: %s",
1028+
expr_to_str(expr, self.tcx.sess.intr()));
1029+
9751030
match expr.node {
9761031
// Interesting cases with control flow or which gen/kill
9771032

@@ -983,16 +1038,27 @@ impl Liveness {
9831038
self.propagate_through_expr(e, succ)
9841039
}
9851040

986-
expr_fn(*) | expr_fn_block(*) => {
987-
// the construction of a closure itself is not important,
988-
// but we have to consider the closed over variables.
989-
let caps = (*self.ir).captures(expr);
990-
do (*caps).foldr(succ) |cap, succ| {
991-
self.init_from_succ(cap.ln, succ);
992-
let var = self.variable(cap.var_nid, expr.span);
993-
self.acc(cap.ln, var, ACC_READ | ACC_USE);
994-
cap.ln
995-
}
1041+
expr_fn(_, _, blk, _) | expr_fn_block(_, blk, _) => {
1042+
debug!("%s is an expr_fn or expr_fn_block",
1043+
expr_to_str(expr, self.tcx.sess.intr()));
1044+
1045+
/*
1046+
The next-node for a break is the successor of the entire
1047+
loop. The next-node for a continue is the top of this loop.
1048+
*/
1049+
self.with_loop_nodes(blk.node.id, succ,
1050+
self.live_node(expr.id, expr.span), || {
1051+
1052+
// the construction of a closure itself is not important,
1053+
// but we have to consider the closed over variables.
1054+
let caps = (*self.ir).captures(expr);
1055+
do (*caps).foldr(succ) |cap, succ| {
1056+
self.init_from_succ(cap.ln, succ);
1057+
let var = self.variable(cap.var_nid, expr.span);
1058+
self.acc(cap.ln, var, ACC_READ | ACC_USE);
1059+
cap.ln
1060+
}
1061+
})
9961062
}
9971063

9981064
expr_if(cond, then, els) => {
@@ -1021,6 +1087,8 @@ impl Liveness {
10211087
self.propagate_through_loop(expr, Some(cond), blk, succ)
10221088
}
10231089

1090+
// Note that labels have been resolved, so we don't need to look
1091+
// at the label ident
10241092
expr_loop(blk, _) => {
10251093
self.propagate_through_loop(expr, None, blk, succ)
10261094
}
@@ -1062,29 +1130,31 @@ impl Liveness {
10621130
}
10631131

10641132
expr_break(opt_label) => {
1065-
if !self.break_ln.is_valid() {
1066-
self.tcx.sess.span_bug(
1067-
expr.span, ~"break with invalid break_ln");
1068-
}
1133+
// Find which label this break jumps to
1134+
let sc = self.find_loop_scope(opt_label, expr.id, expr.span);
10691135

1070-
if opt_label.is_some() {
1071-
self.tcx.sess.span_unimpl(expr.span, ~"labeled break");
1072-
}
1136+
// Now that we know the label we're going to,
1137+
// look it up in the break loop nodes table
10731138

1074-
self.break_ln
1139+
match self.break_ln.find(sc) {
1140+
Some(b) => b,
1141+
None => self.tcx.sess.span_bug(expr.span,
1142+
~"Break to unknown label")
1143+
}
10751144
}
10761145

10771146
expr_again(opt_label) => {
1078-
if !self.cont_ln.is_valid() {
1079-
self.tcx.sess.span_bug(
1080-
expr.span, ~"cont with invalid cont_ln");
1081-
}
1147+
// Find which label this expr continues to to
1148+
let sc = self.find_loop_scope(opt_label, expr.id, expr.span);
10821149

1083-
if opt_label.is_some() {
1084-
self.tcx.sess.span_unimpl(expr.span, ~"labeled again");
1085-
}
1150+
// Now that we know the label we're going to,
1151+
// look it up in the continue loop nodes table
10861152

1087-
self.cont_ln
1153+
match self.cont_ln.find(sc) {
1154+
Some(b) => b,
1155+
None => self.tcx.sess.span_bug(expr.span,
1156+
~"Loop to unknown label")
1157+
}
10881158
}
10891159

10901160
expr_move(l, r) | expr_assign(l, r) => {
@@ -1314,6 +1384,7 @@ impl Liveness {
13141384
13151385
*/
13161386

1387+
13171388
// first iteration:
13181389
let mut first_merge = true;
13191390
let ln = self.live_node(expr.id, expr.span);
@@ -1325,32 +1396,37 @@ impl Liveness {
13251396
self.merge_from_succ(ln, succ, first_merge);
13261397
first_merge = false;
13271398
}
1399+
debug!("propagate_through_loop: using id for loop body %d %s",
1400+
expr.id, block_to_str(body, self.tcx.sess.intr()));
1401+
13281402
let cond_ln = self.propagate_through_opt_expr(cond, ln);
1329-
let body_ln = self.with_loop_nodes(succ, ln, || {
1403+
let body_ln = self.with_loop_nodes(expr.id, succ, ln, || {
13301404
self.propagate_through_block(body, cond_ln)
13311405
});
13321406

13331407
// repeat until fixed point is reached:
13341408
while self.merge_from_succ(ln, body_ln, first_merge) {
13351409
first_merge = false;
13361410
assert cond_ln == self.propagate_through_opt_expr(cond, ln);
1337-
assert body_ln == self.with_loop_nodes(succ, ln, || {
1411+
assert body_ln == self.with_loop_nodes(expr.id, succ, ln,
1412+
|| {
13381413
self.propagate_through_block(body, cond_ln)
13391414
});
13401415
}
13411416

13421417
cond_ln
13431418
}
13441419

1345-
fn with_loop_nodes<R>(break_ln: LiveNode,
1420+
fn with_loop_nodes<R>(loop_node_id: node_id,
1421+
break_ln: LiveNode,
13461422
cont_ln: LiveNode,
13471423
f: fn() -> R) -> R {
1348-
let bl = self.break_ln, cl = self.cont_ln;
1349-
self.break_ln = break_ln;
1350-
self.cont_ln = cont_ln;
1351-
let r <- f();
1352-
self.break_ln = bl;
1353-
self.cont_ln = cl;
1424+
debug!("with_loop_nodes: %d %u", loop_node_id, *break_ln);
1425+
self.loop_scope.push(loop_node_id);
1426+
self.break_ln.insert(loop_node_id, break_ln);
1427+
self.cont_ln.insert(loop_node_id, cont_ln);
1428+
let r = f();
1429+
self.loop_scope.pop();
13541430
move r
13551431
}
13561432
}
@@ -1526,6 +1602,10 @@ impl @Liveness {
15261602
}
15271603
}
15281604

1605+
/*
1606+
Checks whether <var> is live on entry to any of the successors of <ln>.
1607+
If it is, report an error.
1608+
*/
15291609
fn check_move_from_var(span: span, ln: LiveNode, var: Variable) {
15301610
debug!("check_move_from_var(%s, %s)",
15311611
ln.to_str(), var.to_str());

0 commit comments

Comments
 (0)