Skip to content

Commit 064f82d

Browse files
committed
Support returning from loop blocks
The code is somewhat invasive, but it seems hard to do this in a clean way, since the design itself involves a bunch of 'action at a distance'. Issue #1819
1 parent f6e3738 commit 064f82d

File tree

8 files changed

+243
-51
lines changed

8 files changed

+243
-51
lines changed

src/rustc/middle/check_loop.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) {
2525
v.visit_block(b, {in_loop: false, can_ret: false}, v);
2626
}
2727
expr_loop_body(@{node: expr_fn_block(_, b), _}) {
28-
v.visit_block(b, {in_loop: true, can_ret: false}, v);
28+
let blk = is_blockish(ty::ty_fn_proto(ty::expr_ty(tcx, e)));
29+
v.visit_block(b, {in_loop: true, can_ret: blk}, v);
2930
}
3031
expr_break {
3132
if !cx.in_loop {

src/rustc/middle/trans/base.rs

Lines changed: 102 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2516,15 +2516,43 @@ fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id,
25162516
ret store_in_dest(e_res.bcx, newval, dest);
25172517
}
25182518

2519+
fn trans_loop_body(bcx: block, e: @ast::expr, ret_flag: option<ValueRef>,
2520+
dest: dest) -> block {
2521+
alt check e.node {
2522+
ast::expr_loop_body(b@@{node: ast::expr_fn_block(decl, body), _}) {
2523+
alt check ty::get(expr_ty(bcx, e)).struct {
2524+
ty::ty_fn({proto, _}) {
2525+
closure::trans_expr_fn(bcx, proto, decl, body, e.span, b.id,
2526+
{copies: [], moves: []}, some(ret_flag),
2527+
dest)
2528+
}
2529+
}
2530+
}
2531+
}
2532+
}
2533+
25192534
// temp_cleanups: cleanups that should run only if failure occurs before the
25202535
// call takes place:
25212536
fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
2522-
&temp_cleanups: [ValueRef]) -> result {
2537+
&temp_cleanups: [ValueRef], ret_flag: option<ValueRef>)
2538+
-> result {
25232539
let _icx = cx.insn_ctxt("trans_arg_expr");
25242540
let ccx = cx.ccx();
25252541
let e_ty = expr_ty(cx, e);
25262542
let is_bot = ty::type_is_bot(e_ty);
2527-
let lv = trans_temp_lval(cx, e);
2543+
let lv = alt ret_flag {
2544+
// If there is a ret_flag, this *must* be a loop body
2545+
some(ptr) {
2546+
alt check e.node {
2547+
ast::expr_loop_body(blk) {
2548+
let scratch = alloc_ty(cx, expr_ty(cx, blk));
2549+
let bcx = trans_loop_body(cx, e, ret_flag, save_in(scratch));
2550+
{bcx: bcx, val: scratch, kind: temporary}
2551+
}
2552+
}
2553+
}
2554+
none { trans_temp_lval(cx, e) }
2555+
};
25282556
let mut bcx = lv.bcx;
25292557
let mut val = lv.val;
25302558
let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode);
@@ -2595,7 +2623,7 @@ enum call_args {
25952623
// - new_fn_ctxt
25962624
// - trans_args
25972625
fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t,
2598-
dest: dest)
2626+
dest: dest, ret_flag: option<ValueRef>)
25992627
-> {bcx: block, args: [ValueRef], retslot: ValueRef} {
26002628
let _icx = cx.insn_ctxt("trans_args");
26012629
let mut temp_cleanups = [];
@@ -2630,13 +2658,13 @@ fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t,
26302658
alt args {
26312659
arg_exprs(es) {
26322660
let llarg_tys = type_of_explicit_args(ccx, arg_tys);
2633-
let mut i = 0u;
2634-
for e: @ast::expr in es {
2661+
let last = es.len() - 1u;
2662+
vec::iteri(es) {|i, e|
26352663
let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i],
2636-
e, temp_cleanups);
2664+
e, temp_cleanups, if i == last { ret_flag }
2665+
else { none });
26372666
bcx = r.bcx;
26382667
llargs += [r.val];
2639-
i += 1u;
26402668
}
26412669
}
26422670
arg_vals(vs) {
@@ -2664,14 +2692,44 @@ fn trans_call(in_cx: block, f: @ast::expr,
26642692
{|cx| trans_callee(cx, f)}, args, dest)
26652693
}
26662694

2695+
fn body_contains_ret(body: ast::blk) -> bool {
2696+
let cx = {mut found: false};
2697+
visit::visit_block(body, cx, visit::mk_vt(@{
2698+
visit_item: {|_i, _cx, _v|},
2699+
visit_expr: {|e: @ast::expr, cx: {mut found: bool}, v|
2700+
if !cx.found {
2701+
alt e.node {
2702+
ast::expr_ret(_) { cx.found = true; }
2703+
_ { visit::visit_expr(e, cx, v); }
2704+
}
2705+
}
2706+
} with *visit::default_visitor()
2707+
}));
2708+
cx.found
2709+
}
2710+
26672711
fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t,
26682712
get_callee: fn(block) -> lval_maybe_callee,
26692713
args: call_args, dest: dest)
26702714
-> block {
2715+
let ret_in_loop = alt args {
2716+
arg_exprs(args) { args.len() > 0u && alt vec::last(args).node {
2717+
ast::expr_loop_body(@{node: ast::expr_fn_block(_, body), _}) {
2718+
body_contains_ret(body)
2719+
}
2720+
_ { false }
2721+
} }
2722+
_ { false }
2723+
};
26712724
with_scope(in_cx, "call") {|cx|
26722725
let f_res = get_callee(cx);
26732726
let mut bcx = f_res.bcx;
26742727
let ccx = cx.ccx();
2728+
let ret_flag = if ret_in_loop {
2729+
let flag = alloca(in_cx, T_bool());
2730+
Store(cx, C_bool(false), flag);
2731+
some(flag)
2732+
} else { none };
26752733

26762734
let mut faddr = f_res.val;
26772735
let llenv = alt f_res.env {
@@ -2695,7 +2753,7 @@ fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t,
26952753
};
26962754

26972755
let args_res = {
2698-
trans_args(bcx, llenv, args, fn_expr_ty, dest)
2756+
trans_args(bcx, llenv, args, fn_expr_ty, dest, ret_flag)
26992757
};
27002758
bcx = args_res.bcx;
27012759
let mut llargs = args_res.args;
@@ -2718,7 +2776,19 @@ fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t,
27182776
*cell = Load(bcx, llretslot);
27192777
}
27202778
}
2721-
if ty::type_is_bot(ret_ty) { Unreachable(bcx); }
2779+
if ty::type_is_bot(ret_ty) {
2780+
Unreachable(bcx);
2781+
} else if ret_in_loop {
2782+
bcx = with_cond(bcx, Load(bcx, option::get(ret_flag))) {|bcx|
2783+
option::may(bcx.fcx.loop_ret) {|lret|
2784+
Store(bcx, C_bool(true), lret.flagptr);
2785+
Store(bcx, C_bool(false), bcx.fcx.llretptr);
2786+
}
2787+
cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn));
2788+
Unreachable(bcx);
2789+
bcx
2790+
}
2791+
}
27222792
bcx
27232793
}
27242794
}
@@ -2991,25 +3061,20 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
29913061
ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); }
29923062
ast::expr_fn(proto, decl, body, cap_clause) {
29933063
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
2994-
*cap_clause, false, dest);
3064+
*cap_clause, none, dest);
29953065
}
29963066
ast::expr_fn_block(decl, body) {
29973067
alt check ty::get(expr_ty(bcx, e)).struct {
29983068
ty::ty_fn({proto, _}) {
29993069
#debug("translating fn_block %s with type %s",
30003070
expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e)));
30013071
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
3002-
{copies: [], moves: []}, false, dest);
3072+
{copies: [], moves: []}, none, dest);
30033073
}
30043074
}
30053075
}
3006-
ast::expr_loop_body(b@@{node: ast::expr_fn_block(decl, body), _}) {
3007-
alt check ty::get(expr_ty(bcx, e)).struct {
3008-
ty::ty_fn({proto, _}) {
3009-
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, b.id,
3010-
{copies: [], moves: []}, true, dest);
3011-
}
3012-
}
3076+
ast::expr_loop_body(blk) {
3077+
ret trans_loop_body(bcx, e, none, dest);
30133078
}
30143079
ast::expr_bind(f, args) {
30153080
ret closure::trans_bind(
@@ -3406,8 +3471,25 @@ fn trans_cont(cx: block) -> block {
34063471
fn trans_ret(bcx: block, e: option<@ast::expr>) -> block {
34073472
let _icx = bcx.insn_ctxt("trans_ret");
34083473
let mut bcx = bcx;
3474+
let retptr = alt bcx.fcx.loop_ret {
3475+
some({flagptr, retptr}) {
3476+
// This is a loop body return. Must set continue flag (our retptr)
3477+
// to false, return flag to true, and then store the value in the
3478+
// parent's retptr.
3479+
Store(bcx, C_bool(true), flagptr);
3480+
Store(bcx, C_bool(false), bcx.fcx.llretptr);
3481+
alt e {
3482+
some(x) { PointerCast(bcx, retptr,
3483+
T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))) }
3484+
none { retptr }
3485+
}
3486+
}
3487+
none { bcx.fcx.llretptr }
3488+
};
34093489
alt e {
3410-
some(x) { bcx = trans_expr_save_in(bcx, x, bcx.fcx.llretptr); }
3490+
some(x) {
3491+
bcx = trans_expr_save_in(bcx, x, retptr);
3492+
}
34113493
_ {}
34123494
}
34133495
cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn));
@@ -3793,6 +3875,7 @@ fn new_fn_ctxt_w_id(ccx: @crate_ctxt, path: path,
37933875
mut llreturn: llbbs.rt,
37943876
mut llself: none,
37953877
mut personality: none,
3878+
mut loop_ret: none,
37963879
llargs: int_hash::<local_val>(),
37973880
lllocals: int_hash::<local_val>(),
37983881
llupvars: int_hash::<ValueRef>(),

src/rustc/middle/trans/closure.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,8 @@ fn store_environment(bcx: block,
287287
fn build_closure(bcx0: block,
288288
cap_vars: [capture::capture_var],
289289
ck: ty::closure_kind,
290-
id: ast::node_id) -> closure_result {
290+
id: ast::node_id,
291+
include_ret_handle: option<ValueRef>) -> closure_result {
291292
let _icx = bcx0.insn_ctxt("closure::build_closure");
292293
// If we need to, package up the iterator body to call
293294
let mut env_vals = [];
@@ -324,6 +325,16 @@ fn build_closure(bcx0: block,
324325
}
325326
}
326327
}
328+
option::may(include_ret_handle) {|flagptr|
329+
let our_ret = alt bcx.fcx.loop_ret {
330+
some({retptr, _}) { retptr }
331+
none { bcx.fcx.llretptr }
332+
};
333+
let nil_ret = PointerCast(bcx, our_ret, T_ptr(T_nil()));
334+
env_vals +=
335+
[env_ref(flagptr, ty::mk_mut_ptr(tcx, ty::mk_bool(tcx)), owned),
336+
env_ref(nil_ret, ty::mk_nil_ptr(tcx), owned)];
337+
}
327338
ret store_environment(bcx, env_vals, ck);
328339
}
329340

@@ -333,6 +344,7 @@ fn build_closure(bcx0: block,
333344
fn load_environment(fcx: fn_ctxt,
334345
cdata_ty: ty::t,
335346
cap_vars: [capture::capture_var],
347+
load_ret_handle: bool,
336348
ck: ty::closure_kind) {
337349
let _icx = fcx.insn_ctxt("closure::load_environment");
338350
let bcx = raw_block(fcx, fcx.llloadenv);
@@ -341,23 +353,30 @@ fn load_environment(fcx: fn_ctxt,
341353
let llcdata = base::opaque_box_body(bcx, cdata_ty, fcx.llenv);
342354

343355
// Populate the upvars from the environment.
344-
let mut i = 0u;
356+
let mut i = 0;
345357
vec::iter(cap_vars) { |cap_var|
346358
alt cap_var.mode {
347359
capture::cap_drop { /* ignore */ }
348360
_ {
349361
let mut upvarptr =
350-
GEPi(bcx, llcdata, [0, abi::closure_body_bindings, i as int]);
362+
GEPi(bcx, llcdata, [0, abi::closure_body_bindings, i]);
351363
alt ck {
352364
ty::ck_block { upvarptr = Load(bcx, upvarptr); }
353365
ty::ck_uniq | ty::ck_box { }
354366
}
355367
let def_id = ast_util::def_id_of_def(cap_var.def);
356368
fcx.llupvars.insert(def_id.node, upvarptr);
357-
i += 1u;
369+
i += 1;
358370
}
359371
}
360372
}
373+
if load_ret_handle {
374+
let flagptr = Load(bcx, GEPi(bcx, llcdata,
375+
[0, abi::closure_body_bindings, i]));
376+
let retptr = Load(bcx, GEPi(bcx, llcdata,
377+
[0, abi::closure_body_bindings, i+1]));
378+
fcx.loop_ret = some({flagptr: flagptr, retptr: retptr});
379+
}
361380
}
362381

363382
fn trans_expr_fn(bcx: block,
@@ -367,7 +386,7 @@ fn trans_expr_fn(bcx: block,
367386
sp: span,
368387
id: ast::node_id,
369388
cap_clause: ast::capture_clause,
370-
is_loop_body: bool,
389+
is_loop_body: option<option<ValueRef>>,
371390
dest: dest) -> block {
372391
let _icx = bcx.insn_ctxt("closure::trans_expr_fn");
373392
if dest == ignore { ret bcx; }
@@ -382,12 +401,17 @@ fn trans_expr_fn(bcx: block,
382401
let trans_closure_env = fn@(ck: ty::closure_kind) -> ValueRef {
383402
let cap_vars = capture::compute_capture_vars(
384403
ccx.tcx, id, proto, cap_clause);
385-
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck, id);
404+
let ret_handle = alt is_loop_body { some(x) { x } none { none } };
405+
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck, id,
406+
ret_handle);
386407
trans_closure(ccx, sub_path, decl, body, llfn, no_self,
387408
bcx.fcx.param_substs, id, {|fcx|
388-
load_environment(fcx, cdata_ty, cap_vars, ck);
409+
load_environment(fcx, cdata_ty, cap_vars,
410+
option::is_some(ret_handle), ck);
389411
}, {|bcx|
390-
if is_loop_body { Store(bcx, C_bool(true), bcx.fcx.llretptr); }
412+
if option::is_some(is_loop_body) {
413+
Store(bcx, C_bool(true), bcx.fcx.llretptr);
414+
}
391415
});
392416
llbox
393417
};

src/rustc/middle/trans/common.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ type fn_ctxt = @{
165165
// The a value alloca'd for calls to upcalls.rust_personality. Used when
166166
// outputting the resume instruction.
167167
mut personality: option<ValueRef>,
168+
// If this is a for-loop body that returns, this holds the pointers needed
169+
// for that
170+
mut loop_ret: option<{flagptr: ValueRef, retptr: ValueRef}>,
168171

169172
// Maps arguments to allocas created for them in llallocas.
170173
llargs: hashmap<ast::node_id, local_val>,

src/rustc/middle/trans/impl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fn trans_self_arg(bcx: block, base: @ast::expr) -> result {
3636
let mut temp_cleanups = [];
3737
let result = trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
3838
T_ptr(type_of::type_of(bcx.ccx(), basety)),
39-
base, temp_cleanups);
39+
base, temp_cleanups, none);
4040

4141
// by-ref self argument should not require cleanup in the case of
4242
// other arguments failing:

0 commit comments

Comments
 (0)