diff --git a/src/rustc/middle/check_loop.rs b/src/rustc/middle/check_loop.rs index b145ae0d75a..4d69154aac8 100644 --- a/src/rustc/middle/check_loop.rs +++ b/src/rustc/middle/check_loop.rs @@ -25,7 +25,8 @@ fn check_crate(tcx: ty::ctxt, crate: @crate) { v.visit_block(b, {in_loop: false, can_ret: false}, v); } 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 { if !cx.in_loop { diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index e1aeb2abc5e..75416b6dae9 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -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); } +fn trans_loop_body(bcx: block, e: @ast::expr, ret_flag: option, + 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 // call takes place: fn trans_arg_expr(cx: block, arg: ty::arg, lldestty: TypeRef, e: @ast::expr, - &temp_cleanups: [ValueRef]) -> result { + &temp_cleanups: [ValueRef], ret_flag: option) + -> result { let _icx = cx.insn_ctxt("trans_arg_expr"); let ccx = cx.ccx(); let e_ty = expr_ty(cx, e); 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 val = lv.val; let arg_mode = ty::resolved_mode(ccx.tcx, arg.mode); @@ -2595,7 +2623,7 @@ enum call_args { // - new_fn_ctxt // - trans_args fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t, - dest: dest) + dest: dest, ret_flag: option) -> {bcx: block, args: [ValueRef], retslot: ValueRef} { let _icx = cx.insn_ctxt("trans_args"); let mut temp_cleanups = []; @@ -2630,13 +2658,13 @@ fn trans_args(cx: block, llenv: ValueRef, args: call_args, fn_ty: ty::t, alt args { arg_exprs(es) { let llarg_tys = type_of_explicit_args(ccx, arg_tys); - let mut i = 0u; - for e: @ast::expr in es { + let last = es.len() - 1u; + vec::iteri(es) {|i, e| 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; llargs += [r.val]; - i += 1u; } } arg_vals(vs) { @@ -2664,14 +2692,44 @@ fn trans_call(in_cx: block, f: @ast::expr, {|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, get_callee: fn(block) -> lval_maybe_callee, args: call_args, dest: dest) -> 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| let f_res = get_callee(cx); let mut bcx = f_res.bcx; 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 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 = { - trans_args(bcx, llenv, args, fn_expr_ty, dest) + trans_args(bcx, llenv, args, fn_expr_ty, dest, ret_flag) }; bcx = args_res.bcx; 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); } } - 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 } } @@ -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_fn(proto, decl, body, cap_clause) { 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) { 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", 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, - {copies: [], moves: []}, false, dest); + {copies: [], moves: []}, none, dest); } } } - 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, _}) { - ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, b.id, - {copies: [], moves: []}, true, dest); - } - } + ast::expr_loop_body(blk) { + ret trans_loop_body(bcx, e, none, dest); } ast::expr_bind(f, args) { ret closure::trans_bind( @@ -3406,8 +3471,25 @@ fn trans_cont(cx: block) -> block { fn trans_ret(bcx: block, e: option<@ast::expr>) -> block { let _icx = bcx.insn_ctxt("trans_ret"); 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 { - 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)); @@ -3793,6 +3875,7 @@ fn new_fn_ctxt_w_id(ccx: @crate_ctxt, path: path, mut llreturn: llbbs.rt, mut llself: none, mut personality: none, + mut loop_ret: none, llargs: int_hash::(), lllocals: int_hash::(), llupvars: int_hash::(), diff --git a/src/rustc/middle/trans/closure.rs b/src/rustc/middle/trans/closure.rs index 0ac132562fe..e614c4e8f0f 100644 --- a/src/rustc/middle/trans/closure.rs +++ b/src/rustc/middle/trans/closure.rs @@ -287,7 +287,8 @@ fn store_environment(bcx: block, fn build_closure(bcx0: block, cap_vars: [capture::capture_var], ck: ty::closure_kind, - id: ast::node_id) -> closure_result { + id: ast::node_id, + include_ret_handle: option) -> closure_result { let _icx = bcx0.insn_ctxt("closure::build_closure"); // If we need to, package up the iterator body to call 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); } @@ -333,6 +344,7 @@ fn build_closure(bcx0: block, fn load_environment(fcx: fn_ctxt, cdata_ty: ty::t, cap_vars: [capture::capture_var], + load_ret_handle: bool, ck: ty::closure_kind) { let _icx = fcx.insn_ctxt("closure::load_environment"); 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); // Populate the upvars from the environment. - let mut i = 0u; + let mut i = 0; vec::iter(cap_vars) { |cap_var| alt cap_var.mode { capture::cap_drop { /* ignore */ } _ { 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 { ty::ck_block { upvarptr = Load(bcx, upvarptr); } ty::ck_uniq | ty::ck_box { } } let def_id = ast_util::def_id_of_def(cap_var.def); 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, @@ -367,7 +386,7 @@ fn trans_expr_fn(bcx: block, sp: span, id: ast::node_id, cap_clause: ast::capture_clause, - is_loop_body: bool, + is_loop_body: option>, dest: dest) -> block { let _icx = bcx.insn_ctxt("closure::trans_expr_fn"); 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 cap_vars = capture::compute_capture_vars( 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, 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| - 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 }; diff --git a/src/rustc/middle/trans/common.rs b/src/rustc/middle/trans/common.rs index 5714ef23a5b..af3dd12690e 100644 --- a/src/rustc/middle/trans/common.rs +++ b/src/rustc/middle/trans/common.rs @@ -165,6 +165,9 @@ type fn_ctxt = @{ // The a value alloca'd for calls to upcalls.rust_personality. Used when // outputting the resume instruction. mut personality: option, + // 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. llargs: hashmap, diff --git a/src/rustc/middle/trans/impl.rs b/src/rustc/middle/trans/impl.rs index 2f2aaeaef14..8cfa7c8433c 100644 --- a/src/rustc/middle/trans/impl.rs +++ b/src/rustc/middle/trans/impl.rs @@ -36,7 +36,7 @@ fn trans_self_arg(bcx: block, base: @ast::expr) -> result { let mut temp_cleanups = []; let result = trans_arg_expr(bcx, {mode: m_by_ref, ty: 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 // other arguments failing: diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs index 86cf9d34815..721b846e052 100644 --- a/src/rustc/middle/typeck.rs +++ b/src/rustc/middle/typeck.rs @@ -71,6 +71,8 @@ type fn_ctxt = // with any nested functions that capture the environment // (and with any functions whose environment is being captured). {ret_ty: ty::t, + // Used by loop bodies that return from the outer function + indirect_ret_ty: option, purity: ast::purity, proto: ast::proto, infcx: infer::infer_ctxt, @@ -2286,7 +2288,8 @@ fn check_expr_fn_with_unifier(fcx: @fn_ctxt, decl: ast::fn_decl, body: ast::blk, unify: unifier, - expected: ty::t) { + expected: ty::t, + is_loop_body: bool) { let tcx = fcx.ccx.tcx; let fty = ty::mk_fn(tcx, 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. 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 = { @@ -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_ret(expr_opt) { bot = true; + let ret_ty = alt fcx.indirect_ret_ty { + some(t) { t } none { fcx.ret_ty } + }; alt expr_opt { none { 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, "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); } @@ -2895,7 +2901,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier, } ast::expr_fn(proto, decl, body, captures) { check_expr_fn_with_unifier(fcx, expr, proto, decl, body, - unify, expected); + unify, expected, false); capture::check_capture_clause(tcx, expr.id, proto, *captures); } 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), ty_to_str(tcx, expected)); 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 inner_ty = alt check ty::get(rty).struct { + let (inner_ty, proto) = alt check ty::get(rty).struct { ty::ty_fn(fty) { 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( - 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 { ty::ty_fn(fty) { 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); } ast::expr_rec(fields, base) { - alt base { none {/* no-op */ } some(b_0) { check_expr(fcx, b_0); } } - let mut fields_t: [spanned] = []; - for f: ast::field in fields { + option::may(base) {|b| check_expr(fcx, b); } + let fields_t = vec::map(fields, {|f| bot |= check_expr(fcx, f.node.expr); let expr_t = expr_ty(tcx, f.node.expr); let expr_mt = {ty: expr_t, mutbl: f.node.mutbl}; // for the most precise error message, // 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 { none { fn get_node(f: spanned) -> 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 fcx: @fn_ctxt = @{ret_ty: rty, + indirect_ret_ty: none, purity: ast::pure_fn, proto: ast::proto_box, 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 fcx: @fn_ctxt = @{ret_ty: rty, + indirect_ret_ty: none, purity: ast::pure_fn, proto: ast::proto_box, infcx: infer::new_infer_ctxt(ccx.tcx), @@ -3570,6 +3581,7 @@ fn check_fn(ccx: @crate_ctxt, decl: ast::fn_decl, body: ast::blk, id: ast::node_id, + indirect_ret: bool, old_fcx: option<@fn_ctxt>) { // If old_fcx is some(...), this is a block fn { |x| ... }. // 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 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 = @{ret_ty: ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id)), + indirect_ret_ty: indirect_ret_ty, purity: purity, proto: proto, infcx: gather_result.infcx, @@ -3619,7 +3639,8 @@ fn check_fn(ccx: @crate_ctxt, } 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 { @@ -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_enum(vs, _) { check_enum_variants(ccx, it.span, vs, it.id); } 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, _) { - 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) { 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}; // typecheck the ctor 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 for m in members { check_class_member(class_ccx, m.node.decl); } } diff --git a/src/rustc/syntax/ast.rs b/src/rustc/syntax/ast.rs index e657b6a6fe4..95c5b607cd4 100644 --- a/src/rustc/syntax/ast.rs +++ b/src/rustc/syntax/ast.rs @@ -303,6 +303,9 @@ enum expr_ { expr_alt(@expr, [arm], alt_mode), expr_fn(proto, fn_decl, blk, @capture_clause), 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_block(blk), diff --git a/src/test/run-pass/ret-break-cont-in-block.rs b/src/test/run-pass/ret-break-cont-in-block.rs new file mode 100644 index 00000000000..e93ce8a32e9 --- /dev/null +++ b/src/test/run-pass/ret-break-cont-in-block.rs @@ -0,0 +1,57 @@ +fn iter(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(n: T, h: [T]) -> option { + 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"; +}