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
This commit is contained in:
Marijn Haverbeke
2012-03-27 12:33:13 +02:00
parent f6e3738b9c
commit 064f82d68d
8 changed files with 243 additions and 51 deletions

View File

@@ -25,7 +25,8 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) {
v.visit_block(b, {in_loop: false, can_ret: false}, v); v.visit_block(b, {in_loop: false, can_ret: false}, v);
} }
expr_loop_body(@{node: expr_fn_block(_, b), _}) { expr_loop_body(@{node: expr_fn_block(_, b), _}) {
v.visit_block(b, {in_loop: true, can_ret: false}, v); let blk = is_blockish(ty::ty_fn_proto(ty::expr_ty(tcx, e)));
v.visit_block(b, {in_loop: true, can_ret: blk}, v);
} }
expr_break { expr_break {
if !cx.in_loop { if !cx.in_loop {

View File

@@ -2516,15 +2516,43 @@ fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id,
ret store_in_dest(e_res.bcx, newval, dest); ret store_in_dest(e_res.bcx, newval, dest);
} }
fn trans_loop_body(bcx: block, e: @ast::expr, ret_flag: option<ValueRef>,
dest: dest) -> block {
alt check e.node {
ast::expr_loop_body(b@@{node: ast::expr_fn_block(decl, body), _}) {
alt check ty::get(expr_ty(bcx, e)).struct {
ty::ty_fn({proto, _}) {
closure::trans_expr_fn(bcx, proto, decl, body, e.span, b.id,
{copies: [], moves: []}, some(ret_flag),
dest)
}
}
}
}
}
// temp_cleanups: cleanups that should run only if failure occurs before the // temp_cleanups: cleanups that should run only if failure occurs before the
// call takes place: // call takes place:
fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr, fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr,
&temp_cleanups: [ValueRef]) -> result { &temp_cleanups: [ValueRef], ret_flag: option<ValueRef>)
-> result {
let _icx = cx.insn_ctxt("trans_arg_expr"); let _icx = cx.insn_ctxt("trans_arg_expr");
let ccx = cx.ccx(); let ccx = cx.ccx();
let e_ty = expr_ty(cx, e); let e_ty = expr_ty(cx, e);
let is_bot = ty::type_is_bot(e_ty); let is_bot = ty::type_is_bot(e_ty);
let lv = trans_temp_lval(cx, e); let lv = alt ret_flag {
// If there is a ret_flag, this *must* be a loop body
some(ptr) {
alt check e.node {
ast::expr_loop_body(blk) {
let scratch = alloc_ty(cx, expr_ty(cx, blk));
let bcx = trans_loop_body(cx, e, ret_flag, save_in(scratch));
{bcx: bcx, val: scratch, kind: temporary}
}
}
}
none { trans_temp_lval(cx, e) }
};
let mut bcx = lv.bcx; let mut bcx = lv.bcx;
let mut val = lv.val; let mut val = lv.val;
let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode); let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode);
@@ -2595,7 +2623,7 @@ enum call_args {
// - new_fn_ctxt // - new_fn_ctxt
// - trans_args // - trans_args
fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t, fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t,
dest: dest) dest: dest, ret_flag: option<ValueRef>)
-> {bcx: block, args: [ValueRef], retslot: ValueRef} { -> {bcx: block, args: [ValueRef], retslot: ValueRef} {
let _icx = cx.insn_ctxt("trans_args"); let _icx = cx.insn_ctxt("trans_args");
let mut temp_cleanups = []; let mut temp_cleanups = [];
@@ -2630,13 +2658,13 @@ fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t,
alt args { alt args {
arg_exprs(es) { arg_exprs(es) {
let llarg_tys = type_of_explicit_args(ccx, arg_tys); let llarg_tys = type_of_explicit_args(ccx, arg_tys);
let mut i = 0u; let last = es.len() - 1u;
for e: @ast::expr in es { vec::iteri(es) {|i, e|
let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i], let r = trans_arg_expr(bcx, arg_tys[i], llarg_tys[i],
e, temp_cleanups); e, temp_cleanups, if i == last { ret_flag }
else { none });
bcx = r.bcx; bcx = r.bcx;
llargs += [r.val]; llargs += [r.val];
i += 1u;
} }
} }
arg_vals(vs) { arg_vals(vs) {
@@ -2664,14 +2692,44 @@ fn trans_call(in_cx: block, f: @ast::expr,
{|cx| trans_callee(cx, f)}, args, dest) {|cx| trans_callee(cx, f)}, args, dest)
} }
fn body_contains_ret(body: ast::blk) -> bool {
let cx = {mut found: false};
visit::visit_block(body, cx, visit::mk_vt(@{
visit_item: {|_i, _cx, _v|},
visit_expr: {|e: @ast::expr, cx: {mut found: bool}, v|
if !cx.found {
alt e.node {
ast::expr_ret(_) { cx.found = true; }
_ { visit::visit_expr(e, cx, v); }
}
}
} with *visit::default_visitor()
}));
cx.found
}
fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t, fn trans_call_inner(in_cx: block, fn_expr_ty: ty::t, ret_ty: ty::t,
get_callee: fn(block) -> lval_maybe_callee, get_callee: fn(block) -> lval_maybe_callee,
args: call_args, dest: dest) args: call_args, dest: dest)
-> block { -> block {
let ret_in_loop = alt args {
arg_exprs(args) { args.len() > 0u && alt vec::last(args).node {
ast::expr_loop_body(@{node: ast::expr_fn_block(_, body), _}) {
body_contains_ret(body)
}
_ { false }
} }
_ { false }
};
with_scope(in_cx, "call") {|cx| with_scope(in_cx, "call") {|cx|
let f_res = get_callee(cx); let f_res = get_callee(cx);
let mut bcx = f_res.bcx; let mut bcx = f_res.bcx;
let ccx = cx.ccx(); let ccx = cx.ccx();
let ret_flag = if ret_in_loop {
let flag = alloca(in_cx, T_bool());
Store(cx, C_bool(false), flag);
some(flag)
} else { none };
let mut faddr = f_res.val; let mut faddr = f_res.val;
let llenv = alt f_res.env { 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,
}; };
let args_res = { let args_res = {
trans_args(bcx, llenv, args, fn_expr_ty, dest) trans_args(bcx, llenv, args, fn_expr_ty, dest, ret_flag)
}; };
bcx = args_res.bcx; bcx = args_res.bcx;
let mut llargs = args_res.args; 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,
*cell = Load(bcx, llretslot); *cell = Load(bcx, llretslot);
} }
} }
if ty::type_is_bot(ret_ty) { Unreachable(bcx); } if ty::type_is_bot(ret_ty) {
Unreachable(bcx);
} else if ret_in_loop {
bcx = with_cond(bcx, Load(bcx, option::get(ret_flag))) {|bcx|
option::may(bcx.fcx.loop_ret) {|lret|
Store(bcx, C_bool(true), lret.flagptr);
Store(bcx, C_bool(false), bcx.fcx.llretptr);
}
cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn));
Unreachable(bcx);
bcx
}
}
bcx bcx
} }
} }
@@ -2991,7 +3061,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); } ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); }
ast::expr_fn(proto, decl, body, cap_clause) { ast::expr_fn(proto, decl, body, cap_clause) {
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id, ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
*cap_clause, false, dest); *cap_clause, none, dest);
} }
ast::expr_fn_block(decl, body) { ast::expr_fn_block(decl, body) {
alt check ty::get(expr_ty(bcx, e)).struct { alt check ty::get(expr_ty(bcx, e)).struct {
@@ -2999,17 +3069,12 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
#debug("translating fn_block %s with type %s", #debug("translating fn_block %s with type %s",
expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e))); expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e)));
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id, ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
{copies: [], moves: []}, false, dest); {copies: [], moves: []}, none, dest);
} }
} }
} }
ast::expr_loop_body(b@@{node: ast::expr_fn_block(decl, body), _}) { ast::expr_loop_body(blk) {
alt check ty::get(expr_ty(bcx, e)).struct { ret trans_loop_body(bcx, e, none, dest);
ty::ty_fn({proto, _}) {
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, b.id,
{copies: [], moves: []}, true, dest);
}
}
} }
ast::expr_bind(f, args) { ast::expr_bind(f, args) {
ret closure::trans_bind( ret closure::trans_bind(
@@ -3406,8 +3471,25 @@ fn trans_cont(cx: block) -> block {
fn trans_ret(bcx: block, e: option<@ast::expr>) -> block { fn trans_ret(bcx: block, e: option<@ast::expr>) -> block {
let _icx = bcx.insn_ctxt("trans_ret"); let _icx = bcx.insn_ctxt("trans_ret");
let mut bcx = bcx; let mut bcx = bcx;
let retptr = alt bcx.fcx.loop_ret {
some({flagptr, retptr}) {
// This is a loop body return. Must set continue flag (our retptr)
// to false, return flag to true, and then store the value in the
// parent's retptr.
Store(bcx, C_bool(true), flagptr);
Store(bcx, C_bool(false), bcx.fcx.llretptr);
alt e {
some(x) { PointerCast(bcx, retptr,
T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))) }
none { retptr }
}
}
none { bcx.fcx.llretptr }
};
alt e { alt e {
some(x) { bcx = trans_expr_save_in(bcx, x, bcx.fcx.llretptr); } some(x) {
bcx = trans_expr_save_in(bcx, x, retptr);
}
_ {} _ {}
} }
cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn)); cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn));
@@ -3793,6 +3875,7 @@ fn new_fn_ctxt_w_id(ccx: @crate_ctxt, path: path,
mut llreturn: llbbs.rt, mut llreturn: llbbs.rt,
mut llself: none, mut llself: none,
mut personality: none, mut personality: none,
mut loop_ret: none,
llargs: int_hash::<local_val>(), llargs: int_hash::<local_val>(),
lllocals: int_hash::<local_val>(), lllocals: int_hash::<local_val>(),
llupvars: int_hash::<ValueRef>(), llupvars: int_hash::<ValueRef>(),

View File

@@ -287,7 +287,8 @@ fn store_environment(bcx: block,
fn build_closure(bcx0: block, fn build_closure(bcx0: block,
cap_vars: [capture::capture_var], cap_vars: [capture::capture_var],
ck: ty::closure_kind, ck: ty::closure_kind,
id: ast::node_id) -> closure_result { id: ast::node_id,
include_ret_handle: option<ValueRef>) -> closure_result {
let _icx = bcx0.insn_ctxt("closure::build_closure"); let _icx = bcx0.insn_ctxt("closure::build_closure");
// If we need to, package up the iterator body to call // If we need to, package up the iterator body to call
let mut env_vals = []; let mut env_vals = [];
@@ -324,6 +325,16 @@ fn build_closure(bcx0: block,
} }
} }
} }
option::may(include_ret_handle) {|flagptr|
let our_ret = alt bcx.fcx.loop_ret {
some({retptr, _}) { retptr }
none { bcx.fcx.llretptr }
};
let nil_ret = PointerCast(bcx, our_ret, T_ptr(T_nil()));
env_vals +=
[env_ref(flagptr, ty::mk_mut_ptr(tcx, ty::mk_bool(tcx)), owned),
env_ref(nil_ret, ty::mk_nil_ptr(tcx), owned)];
}
ret store_environment(bcx, env_vals, ck); ret store_environment(bcx, env_vals, ck);
} }
@@ -333,6 +344,7 @@ fn build_closure(bcx0: block,
fn load_environment(fcx: fn_ctxt, fn load_environment(fcx: fn_ctxt,
cdata_ty: ty::t, cdata_ty: ty::t,
cap_vars: [capture::capture_var], cap_vars: [capture::capture_var],
load_ret_handle: bool,
ck: ty::closure_kind) { ck: ty::closure_kind) {
let _icx = fcx.insn_ctxt("closure::load_environment"); let _icx = fcx.insn_ctxt("closure::load_environment");
let bcx = raw_block(fcx, fcx.llloadenv); let bcx = raw_block(fcx, fcx.llloadenv);
@@ -341,23 +353,30 @@ fn load_environment(fcx: fn_ctxt,
let llcdata = base::opaque_box_body(bcx, cdata_ty, fcx.llenv); let llcdata = base::opaque_box_body(bcx, cdata_ty, fcx.llenv);
// Populate the upvars from the environment. // Populate the upvars from the environment.
let mut i = 0u; let mut i = 0;
vec::iter(cap_vars) { |cap_var| vec::iter(cap_vars) { |cap_var|
alt cap_var.mode { alt cap_var.mode {
capture::cap_drop { /* ignore */ } capture::cap_drop { /* ignore */ }
_ { _ {
let mut upvarptr = let mut upvarptr =
GEPi(bcx, llcdata, [0, abi::closure_body_bindings, i as int]); GEPi(bcx, llcdata, [0, abi::closure_body_bindings, i]);
alt ck { alt ck {
ty::ck_block { upvarptr = Load(bcx, upvarptr); } ty::ck_block { upvarptr = Load(bcx, upvarptr); }
ty::ck_uniq | ty::ck_box { } ty::ck_uniq | ty::ck_box { }
} }
let def_id = ast_util::def_id_of_def(cap_var.def); let def_id = ast_util::def_id_of_def(cap_var.def);
fcx.llupvars.insert(def_id.node, upvarptr); fcx.llupvars.insert(def_id.node, upvarptr);
i += 1u; i += 1;
} }
} }
} }
if load_ret_handle {
let flagptr = Load(bcx, GEPi(bcx, llcdata,
[0, abi::closure_body_bindings, i]));
let retptr = Load(bcx, GEPi(bcx, llcdata,
[0, abi::closure_body_bindings, i+1]));
fcx.loop_ret = some({flagptr: flagptr, retptr: retptr});
}
} }
fn trans_expr_fn(bcx: block, fn trans_expr_fn(bcx: block,
@@ -367,7 +386,7 @@ fn trans_expr_fn(bcx: block,
sp: span, sp: span,
id: ast::node_id, id: ast::node_id,
cap_clause: ast::capture_clause, cap_clause: ast::capture_clause,
is_loop_body: bool, is_loop_body: option<option<ValueRef>>,
dest: dest) -> block { dest: dest) -> block {
let _icx = bcx.insn_ctxt("closure::trans_expr_fn"); let _icx = bcx.insn_ctxt("closure::trans_expr_fn");
if dest == ignore { ret bcx; } if dest == ignore { ret bcx; }
@@ -382,12 +401,17 @@ fn trans_expr_fn(bcx: block,
let trans_closure_env = fn@(ck: ty::closure_kind) -> ValueRef { let trans_closure_env = fn@(ck: ty::closure_kind) -> ValueRef {
let cap_vars = capture::compute_capture_vars( let cap_vars = capture::compute_capture_vars(
ccx.tcx, id, proto, cap_clause); ccx.tcx, id, proto, cap_clause);
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck, id); let ret_handle = alt is_loop_body { some(x) { x } none { none } };
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck, id,
ret_handle);
trans_closure(ccx, sub_path, decl, body, llfn, no_self, trans_closure(ccx, sub_path, decl, body, llfn, no_self,
bcx.fcx.param_substs, id, {|fcx| bcx.fcx.param_substs, id, {|fcx|
load_environment(fcx, cdata_ty, cap_vars, ck); load_environment(fcx, cdata_ty, cap_vars,
option::is_some(ret_handle), ck);
}, {|bcx| }, {|bcx|
if is_loop_body { Store(bcx, C_bool(true), bcx.fcx.llretptr); } if option::is_some(is_loop_body) {
Store(bcx, C_bool(true), bcx.fcx.llretptr);
}
}); });
llbox llbox
}; };

View File

@@ -165,6 +165,9 @@ type fn_ctxt = @{
// The a value alloca'd for calls to upcalls.rust_personality. Used when // The a value alloca'd for calls to upcalls.rust_personality. Used when
// outputting the resume instruction. // outputting the resume instruction.
mut personality: option<ValueRef>, mut personality: option<ValueRef>,
// If this is a for-loop body that returns, this holds the pointers needed
// for that
mut loop_ret: option<{flagptr: ValueRef, retptr: ValueRef}>,
// Maps arguments to allocas created for them in llallocas. // Maps arguments to allocas created for them in llallocas.
llargs: hashmap<ast::node_id, local_val>, llargs: hashmap<ast::node_id, local_val>,

View File

@@ -36,7 +36,7 @@ fn trans_self_arg(bcx: block, base: @ast::expr) -> result {
let mut temp_cleanups = []; let mut temp_cleanups = [];
let result = trans_arg_expr(bcx, {mode: m_by_ref, ty: basety}, let result = trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
T_ptr(type_of::type_of(bcx.ccx(), basety)), T_ptr(type_of::type_of(bcx.ccx(), basety)),
base, temp_cleanups); base, temp_cleanups, none);
// by-ref self argument should not require cleanup in the case of // by-ref self argument should not require cleanup in the case of
// other arguments failing: // other arguments failing:

View File

@@ -71,6 +71,8 @@ type fn_ctxt =
// with any nested functions that capture the environment // with any nested functions that capture the environment
// (and with any functions whose environment is being captured). // (and with any functions whose environment is being captured).
{ret_ty: ty::t, {ret_ty: ty::t,
// Used by loop bodies that return from the outer function
indirect_ret_ty: option<ty::t>,
purity: ast::purity, purity: ast::purity,
proto: ast::proto, proto: ast::proto,
infcx: infer::infer_ctxt, infcx: infer::infer_ctxt,
@@ -2286,7 +2288,8 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
decl: ast::fn_decl, decl: ast::fn_decl,
body: ast::blk, body: ast::blk,
unify: unifier, unify: unifier,
expected: ty::t) { expected: ty::t,
is_loop_body: bool) {
let tcx = fcx.ccx.tcx; let tcx = fcx.ccx.tcx;
let fty = ty::mk_fn(tcx, let fty = ty::mk_fn(tcx,
ty_of_fn_decl(tcx, m_check_tyvar(fcx), proto, decl)); ty_of_fn_decl(tcx, m_check_tyvar(fcx), proto, decl));
@@ -2303,7 +2306,7 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt,
// record projection work on type inferred arguments. // record projection work on type inferred arguments.
unify(fcx, expr.span, expected, fty); unify(fcx, expr.span, expected, fty);
check_fn(fcx.ccx, proto, decl, body, expr.id, some(fcx)); check_fn(fcx.ccx, proto, decl, body, expr.id, is_loop_body, some(fcx));
} }
type check_call_or_bind_result = { type check_call_or_bind_result = {
@@ -2766,15 +2769,18 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
ast::expr_cont { write_bot(tcx, id); bot = true; } ast::expr_cont { write_bot(tcx, id); bot = true; }
ast::expr_ret(expr_opt) { ast::expr_ret(expr_opt) {
bot = true; bot = true;
let ret_ty = alt fcx.indirect_ret_ty {
some(t) { t } none { fcx.ret_ty }
};
alt expr_opt { alt expr_opt {
none { none {
let nil = ty::mk_nil(tcx); let nil = ty::mk_nil(tcx);
if !are_compatible(fcx, fcx.ret_ty, nil) { if !are_compatible(fcx, ret_ty, nil) {
tcx.sess.span_err(expr.span, tcx.sess.span_err(expr.span,
"ret; in function returning non-nil"); "ret; in function returning non-nil");
} }
} }
some(e) { check_expr_with(fcx, e, fcx.ret_ty); } some(e) { check_expr_with(fcx, e, ret_ty); }
} }
write_bot(tcx, id); write_bot(tcx, id);
} }
@@ -2895,7 +2901,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
} }
ast::expr_fn(proto, decl, body, captures) { ast::expr_fn(proto, decl, body, captures) {
check_expr_fn_with_unifier(fcx, expr, proto, decl, body, check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
unify, expected); unify, expected, false);
capture::check_capture_clause(tcx, expr.id, proto, *captures); capture::check_capture_clause(tcx, expr.id, proto, *captures);
} }
ast::expr_fn_block(decl, body) { ast::expr_fn_block(decl, body) {
@@ -2908,19 +2914,25 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
expr_to_str(expr), expr_to_str(expr),
ty_to_str(tcx, expected)); ty_to_str(tcx, expected));
check_expr_fn_with_unifier(fcx, expr, proto, decl, body, check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
unify, expected); unify, expected, false);
} }
ast::expr_loop_body(block) { ast::expr_loop_body(b) {
let rty = structurally_resolved_type(fcx, expr.span, expected); let rty = structurally_resolved_type(fcx, expr.span, expected);
let inner_ty = alt check ty::get(rty).struct { let (inner_ty, proto) = alt check ty::get(rty).struct {
ty::ty_fn(fty) { ty::ty_fn(fty) {
demand::simple(fcx, expr.span, fty.output, ty::mk_bool(tcx)); demand::simple(fcx, expr.span, fty.output, ty::mk_bool(tcx));
ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty}) (ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty}),
fty.proto)
} }
}; };
check_expr_with(fcx, block, inner_ty); alt check b.node {
ast::expr_fn_block(decl, body) {
check_expr_fn_with_unifier(fcx, b, proto, decl, body,
unify, inner_ty, true);
}
}
let block_ty = structurally_resolved_type( let block_ty = structurally_resolved_type(
fcx, expr.span, ty::node_id_to_type(tcx, block.id)); fcx, expr.span, ty::node_id_to_type(tcx, b.id));
alt check ty::get(block_ty).struct { alt check ty::get(block_ty).struct {
ty::ty_fn(fty) { ty::ty_fn(fty) {
write_ty(tcx, expr.id, ty::mk_fn(tcx, {output: ty::mk_bool(tcx) write_ty(tcx, expr.id, ty::mk_fn(tcx, {output: ty::mk_bool(tcx)
@@ -3045,18 +3057,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
write_ty(tcx, id, typ); write_ty(tcx, id, typ);
} }
ast::expr_rec(fields, base) { ast::expr_rec(fields, base) {
alt base { none {/* no-op */ } some(b_0) { check_expr(fcx, b_0); } } option::may(base) {|b| check_expr(fcx, b); }
let mut fields_t: [spanned<field>] = []; let fields_t = vec::map(fields, {|f|
for f: ast::field in fields {
bot |= check_expr(fcx, f.node.expr); bot |= check_expr(fcx, f.node.expr);
let expr_t = expr_ty(tcx, f.node.expr); let expr_t = expr_ty(tcx, f.node.expr);
let expr_mt = {ty: expr_t, mutbl: f.node.mutbl}; let expr_mt = {ty: expr_t, mutbl: f.node.mutbl};
// for the most precise error message, // for the most precise error message,
// should be f.node.expr.span, not f.span // should be f.node.expr.span, not f.span
fields_t += respan(f.node.expr.span, {ident: f.node.ident, mt: expr_mt})
[respan(f.node.expr.span, });
{ident: f.node.ident, mt: expr_mt})];
}
alt base { alt base {
none { none {
fn get_node(f: spanned<field>) -> field { f.node } fn get_node(f: spanned<field>) -> field { f.node }
@@ -3384,6 +3393,7 @@ fn check_const(ccx: @crate_ctxt, _sp: span, e: @ast::expr, id: ast::node_id) {
let rty = node_id_to_type(ccx.tcx, id); let rty = node_id_to_type(ccx.tcx, id);
let fcx: @fn_ctxt = let fcx: @fn_ctxt =
@{ret_ty: rty, @{ret_ty: rty,
indirect_ret_ty: none,
purity: ast::pure_fn, purity: ast::pure_fn,
proto: ast::proto_box, proto: ast::proto_box,
infcx: infer::new_infer_ctxt(ccx.tcx), infcx: infer::new_infer_ctxt(ccx.tcx),
@@ -3403,6 +3413,7 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
let rty = node_id_to_type(ccx.tcx, id); let rty = node_id_to_type(ccx.tcx, id);
let fcx: @fn_ctxt = let fcx: @fn_ctxt =
@{ret_ty: rty, @{ret_ty: rty,
indirect_ret_ty: none,
purity: ast::pure_fn, purity: ast::pure_fn,
proto: ast::proto_box, proto: ast::proto_box,
infcx: infer::new_infer_ctxt(ccx.tcx), infcx: infer::new_infer_ctxt(ccx.tcx),
@@ -3570,6 +3581,7 @@ fn check_fn(ccx: @crate_ctxt,
decl: ast::fn_decl, decl: ast::fn_decl,
body: ast::blk, body: ast::blk,
id: ast::node_id, id: ast::node_id,
indirect_ret: bool,
old_fcx: option<@fn_ctxt>) { old_fcx: option<@fn_ctxt>) {
// If old_fcx is some(...), this is a block fn { |x| ... }. // If old_fcx is some(...), this is a block fn { |x| ... }.
// In that case, the purity is inherited from the context. // In that case, the purity is inherited from the context.
@@ -3579,8 +3591,16 @@ fn check_fn(ccx: @crate_ctxt,
}; };
let gather_result = gather_locals(ccx, decl, body, id, old_fcx); let gather_result = gather_locals(ccx, decl, body, id, old_fcx);
let indirect_ret_ty = if indirect_ret {
let ofcx = option::get(old_fcx);
alt ofcx.indirect_ret_ty {
some(t) { some(t) }
none { some(ofcx.ret_ty) }
}
} else { none };
let fcx: @fn_ctxt = let fcx: @fn_ctxt =
@{ret_ty: ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)), @{ret_ty: ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)),
indirect_ret_ty: indirect_ret_ty,
purity: purity, purity: purity,
proto: proto, proto: proto,
infcx: gather_result.infcx, infcx: gather_result.infcx,
@@ -3619,7 +3639,8 @@ fn check_fn(ccx: @crate_ctxt,
} }
fn check_method(ccx: @crate_ctxt, method: @ast::method) { fn check_method(ccx: @crate_ctxt, method: @ast::method) {
check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id, none); check_fn(ccx, ast::proto_bare, method.decl, method.body, method.id,
false, none);
} }
fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map { fn class_types(ccx: @crate_ctxt, members: [@ast::class_item]) -> class_map {
@@ -3651,10 +3672,10 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); } ast::item_const(_, e) { check_const(ccx, it.span, e, it.id); }
ast::item_enum(vs, _) { check_enum_variants(ccx, it.span, vs, it.id); } ast::item_enum(vs, _) { check_enum_variants(ccx, it.span, vs, it.id); }
ast::item_fn(decl, tps, body) { ast::item_fn(decl, tps, body) {
check_fn(ccx, ast::proto_bare, decl, body, it.id, none); check_fn(ccx, ast::proto_bare, decl, body, it.id, false, none);
} }
ast::item_res(decl, tps, body, dtor_id, _) { ast::item_res(decl, tps, body, dtor_id, _) {
check_fn(ccx, ast::proto_bare, decl, body, dtor_id, none); check_fn(ccx, ast::proto_bare, decl, body, dtor_id, false, none);
} }
ast::item_impl(tps, _, ty, ms) { ast::item_impl(tps, _, ty, ms) {
let mut self_ty = ast_ty_to_ty(ccx.tcx, m_check, ty); let mut self_ty = ast_ty_to_ty(ccx.tcx, m_check, ty);
@@ -3671,7 +3692,7 @@ fn check_item(ccx: @crate_ctxt, it: @ast::item) {
enclosing_class:members_info with *ccx}; enclosing_class:members_info with *ccx};
// typecheck the ctor // typecheck the ctor
check_fn(class_ccx, ast::proto_bare, ctor.node.dec, check_fn(class_ccx, ast::proto_bare, ctor.node.dec,
ctor.node.body, ctor.node.id, none); ctor.node.body, ctor.node.id, false, none);
// typecheck the members // typecheck the members
for m in members { check_class_member(class_ccx, m.node.decl); } for m in members { check_class_member(class_ccx, m.node.decl); }
} }

View File

@@ -303,6 +303,9 @@ enum expr_ {
expr_alt(@expr, [arm], alt_mode), expr_alt(@expr, [arm], alt_mode),
expr_fn(proto, fn_decl, blk, @capture_clause), expr_fn(proto, fn_decl, blk, @capture_clause),
expr_fn_block(fn_decl, blk), expr_fn_block(fn_decl, blk),
// Inner expr is always an expr_fn_block. We need the wrapping node to
// sanely type this (a function returning nil on the inside but bool on
// the outside).
expr_loop_body(@expr), expr_loop_body(@expr),
expr_block(blk), expr_block(blk),

View File

@@ -0,0 +1,57 @@
fn iter<T>(v: [T], it: fn(T) -> bool) {
let mut i = 0u, l = v.len();
while i < l {
if !it(v[i]) { break; }
i += 1u;
}
}
fn find_pos<T>(n: T, h: [T]) -> option<uint> {
let mut i = 0u;
for iter(h) {|e|
if e == n { ret some(i); }
i += 1u;
}
none
}
fn bail_deep(x: [[bool]]) {
let mut seen = false;
for iter(x) {|x|
for iter(x) {|x|
assert !seen;
if x { seen = true; ret; }
}
}
assert !seen;
}
fn ret_deep() -> str {
for iter([1, 2]) {|e|
for iter([3, 4]) {|x|
if e + x > 4 { ret "hi"; }
}
}
ret "bye";
}
fn main() {
let mut last = 0;
for vec::all([1, 2, 3, 4, 5, 6, 7]) {|e|
last = e;
if e == 5 { break; }
if e % 2 == 1 { cont; }
assert e % 2 == 0;
};
assert last == 5;
assert find_pos(1, [0, 1, 2, 3]) == some(1u);
assert find_pos(1, [0, 4, 2, 3]) == none;
assert find_pos("hi", ["foo", "bar", "baz", "hi"]) == some(3u);
bail_deep([[false, false], [true, true], [false, true]]);
bail_deep([[true]]);
bail_deep([[false, false, false]]);
assert ret_deep() == "hi";
}