Support an alternate for syntax that calls a higher-order function
The last argument of the call must be a block, and the type of this argument must a function returning bool. `break` and `cont` are supported in the body of the block, and return `false` or `true` from the function. When the end of the function is reached, `true` is implicitly returned. for vec::all([1, 2, 3]) {|elt| if elt == 2 { break; } log(error, elt); } Issue #1619
This commit is contained in:
parent
9638e7fece
commit
f6e3738b9c
@ -156,7 +156,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
|
||||
time(time_passes, "block-use checking",
|
||||
bind middle::block_use::check_crate(ty_cx, crate));
|
||||
time(time_passes, "loop checking",
|
||||
bind middle::check_loop::check_crate(sess, crate));
|
||||
bind middle::check_loop::check_crate(ty_cx, crate));
|
||||
time(time_passes, "function usage",
|
||||
bind fn_usage::check_crate_fn_usage(ty_cx, crate));
|
||||
time(time_passes, "alt checking",
|
||||
|
@ -33,6 +33,10 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
i += 1u;
|
||||
}
|
||||
}
|
||||
expr_loop_body(body) {
|
||||
cx.allow_block = true;
|
||||
v.visit_expr(body, cx, v);
|
||||
}
|
||||
_ {
|
||||
cx.allow_block = false;
|
||||
visit::visit_expr(ex, cx, v);
|
||||
|
@ -4,7 +4,7 @@ import driver::session::session;
|
||||
|
||||
type ctx = {in_loop: bool, can_ret: bool};
|
||||
|
||||
fn check_crate(sess: session, crate: @crate) {
|
||||
fn check_crate(tcx: ty::ctxt, crate: @crate) {
|
||||
visit::visit_crate(*crate, {in_loop: false,can_ret: true}, visit::mk_vt(@{
|
||||
visit_item: {|i, _cx, v|
|
||||
visit::visit_item(i, {in_loop: false, can_ret: true}, v);
|
||||
@ -21,28 +21,31 @@ fn check_crate(sess: session, crate: @crate) {
|
||||
expr_fn(_, _, _, _) {
|
||||
visit::visit_expr(e, {in_loop: false, can_ret: true}, v);
|
||||
}
|
||||
expr_fn_block(fn_decl, blk) {
|
||||
visit::visit_expr(e, {in_loop: false, can_ret: false}, v);
|
||||
expr_fn_block(_, b) {
|
||||
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);
|
||||
}
|
||||
expr_break {
|
||||
if !cx.in_loop {
|
||||
sess.span_err(e.span, "`break` outside of loop");
|
||||
tcx.sess.span_err(e.span, "`break` outside of loop");
|
||||
}
|
||||
}
|
||||
expr_cont {
|
||||
if !cx.in_loop {
|
||||
sess.span_err(e.span, "`cont` outside of loop");
|
||||
tcx.sess.span_err(e.span, "`cont` outside of loop");
|
||||
}
|
||||
}
|
||||
expr_ret(oe) {
|
||||
if !cx.can_ret {
|
||||
sess.span_err(e.span, "`ret` in block function");
|
||||
tcx.sess.span_err(e.span, "`ret` in block function");
|
||||
}
|
||||
visit::visit_expr_opt(oe, cx, v);
|
||||
}
|
||||
expr_be(re) {
|
||||
if !cx.can_ret {
|
||||
sess.span_err(e.span, "`be` in block function");
|
||||
tcx.sess.span_err(e.span, "`be` in block function");
|
||||
}
|
||||
v.visit_expr(re, cx, v);
|
||||
}
|
||||
|
@ -2990,20 +2990,24 @@ 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, dest);
|
||||
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
|
||||
*cap_clause, false, dest);
|
||||
}
|
||||
ast::expr_fn_block(decl, body) {
|
||||
alt ty::get(expr_ty(bcx, e)).struct {
|
||||
alt check ty::get(expr_ty(bcx, e)).struct {
|
||||
ty::ty_fn({proto, _}) {
|
||||
#debug("translating fn_block %s with type %s",
|
||||
expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e)));
|
||||
let cap_clause = { copies: [], moves: [] };
|
||||
ret closure::trans_expr_fn(
|
||||
bcx, proto, decl, body, e.span, e.id, cap_clause, dest);
|
||||
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
|
||||
{copies: [], moves: []}, false, dest);
|
||||
}
|
||||
_ {
|
||||
fail "type of fn block is not a function!";
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3375,7 +3379,16 @@ fn trans_break_cont(bcx: block, to_end: bool)
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
unwind = alt check unwind.parent { parent_some(cx) { cx } };
|
||||
unwind = alt unwind.parent {
|
||||
parent_some(cx) { cx }
|
||||
// This is a return from a loop body block
|
||||
parent_none {
|
||||
Store(bcx, C_bool(!to_end), bcx.fcx.llretptr);
|
||||
cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn));
|
||||
Unreachable(bcx);
|
||||
ret bcx;
|
||||
}
|
||||
};
|
||||
}
|
||||
cleanup_and_Br(bcx, unwind, target.llbb);
|
||||
Unreachable(bcx);
|
||||
@ -3895,7 +3908,8 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
|
||||
ty_self: self_arg,
|
||||
param_substs: option<param_substs>,
|
||||
id: ast::node_id,
|
||||
maybe_load_env: fn(fn_ctxt)) {
|
||||
maybe_load_env: fn(fn_ctxt),
|
||||
finish: fn(block)) {
|
||||
let _icx = ccx.insn_ctxt("trans_closure");
|
||||
set_uwtable(llfndecl);
|
||||
|
||||
@ -3932,6 +3946,7 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
|
||||
} else {
|
||||
bcx = trans_block(bcx, body, save_in(fcx.llretptr));
|
||||
}
|
||||
finish(bcx);
|
||||
cleanup_and_Br(bcx, bcx_top, fcx.llreturn);
|
||||
|
||||
// Insert the mandatory first few basic blocks before lltop.
|
||||
@ -3957,7 +3972,7 @@ fn trans_fn(ccx: @crate_ctxt,
|
||||
if ccx.sess.opts.extra_debuginfo {
|
||||
debuginfo::create_function(fcx);
|
||||
}
|
||||
});
|
||||
}, {|_bcx|});
|
||||
if do_time {
|
||||
let end = time::get_time();
|
||||
log_fn_time(ccx, path_str(path), start, end);
|
||||
|
@ -367,6 +367,7 @@ fn trans_expr_fn(bcx: block,
|
||||
sp: span,
|
||||
id: ast::node_id,
|
||||
cap_clause: ast::capture_clause,
|
||||
is_loop_body: bool,
|
||||
dest: dest) -> block {
|
||||
let _icx = bcx.insn_ctxt("closure::trans_expr_fn");
|
||||
if dest == ignore { ret bcx; }
|
||||
@ -385,6 +386,8 @@ fn trans_expr_fn(bcx: block,
|
||||
trans_closure(ccx, sub_path, decl, body, llfn, no_self,
|
||||
bcx.fcx.param_substs, id, {|fcx|
|
||||
load_environment(fcx, cdata_ty, cap_vars, ck);
|
||||
}, {|bcx|
|
||||
if is_loop_body { Store(bcx, C_bool(true), bcx.fcx.llretptr); }
|
||||
});
|
||||
llbox
|
||||
};
|
||||
@ -395,7 +398,7 @@ fn trans_expr_fn(bcx: block,
|
||||
ast::proto_uniq { trans_closure_env(ty::ck_uniq) }
|
||||
ast::proto_bare {
|
||||
trans_closure(ccx, sub_path, decl, body, llfn, no_self, none,
|
||||
id, {|_fcx|});
|
||||
id, {|_fcx|}, {|_bcx|});
|
||||
C_null(T_opaque_box_ptr(ccx))
|
||||
}
|
||||
};
|
||||
|
@ -718,9 +718,7 @@ fn C_nil() -> ValueRef {
|
||||
}
|
||||
|
||||
fn C_bool(b: bool) -> ValueRef {
|
||||
if b {
|
||||
ret C_integral(T_bool(), 1u64, False);
|
||||
} else { ret C_integral(T_bool(), 0u64, False); }
|
||||
C_integral(T_bool(), if b { 1u64 } else { 0u64 }, False)
|
||||
}
|
||||
|
||||
fn C_i32(i: i32) -> ValueRef {
|
||||
|
@ -190,7 +190,7 @@ fn mark_for_expr(cx: ctx, e: @expr) {
|
||||
expr_fail(_) | expr_break | expr_cont | expr_unary(_, _) |
|
||||
expr_lit(_) | expr_assert(_) | expr_check(_, _) |
|
||||
expr_if_check(_, _, _) | expr_mac(_) | expr_addr_of(_, _) |
|
||||
expr_ret(_) | expr_loop(_) | expr_bind(_, _) {}
|
||||
expr_ret(_) | expr_loop(_) | expr_bind(_, _) | expr_loop_body(_) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -360,10 +360,6 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
|
||||
find_pre_post_exprs(fcx, es, e.id);
|
||||
}
|
||||
expr_tup(elts) { find_pre_post_exprs(fcx, elts, e.id); }
|
||||
expr_copy(a) {
|
||||
find_pre_post_expr(fcx, a);
|
||||
copy_pre_post(fcx.ccx, e.id, a);
|
||||
}
|
||||
expr_move(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_move); }
|
||||
expr_swap(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_swap); }
|
||||
expr_assign(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_assign); }
|
||||
@ -408,17 +404,10 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
|
||||
expr_postcond(fcx.ccx, l));
|
||||
} else { find_pre_post_exprs(fcx, [l, r], e.id); }
|
||||
}
|
||||
expr_unary(_, operand) {
|
||||
find_pre_post_expr(fcx, operand);
|
||||
copy_pre_post(fcx.ccx, e.id, operand);
|
||||
}
|
||||
expr_addr_of(_, operand) {
|
||||
find_pre_post_expr(fcx, operand);
|
||||
copy_pre_post(fcx.ccx, e.id, operand);
|
||||
}
|
||||
expr_cast(operand, _) {
|
||||
find_pre_post_expr(fcx, operand);
|
||||
copy_pre_post(fcx.ccx, e.id, operand);
|
||||
expr_addr_of(_, x) | expr_cast(x, _) | expr_unary(_, x) |
|
||||
expr_loop_body(x) | expr_assert(x) | expr_copy(x) {
|
||||
find_pre_post_expr(fcx, x);
|
||||
copy_pre_post(fcx.ccx, e.id, x);
|
||||
}
|
||||
expr_while(test, body) {
|
||||
find_pre_post_expr(fcx, test);
|
||||
@ -512,10 +501,6 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
|
||||
then everything is true! */
|
||||
prestate, false_postcond(num_local_vars));
|
||||
}
|
||||
expr_assert(p) {
|
||||
find_pre_post_expr(fcx, p);
|
||||
copy_pre_post(fcx.ccx, e.id, p);
|
||||
}
|
||||
expr_check(_, p) {
|
||||
find_pre_post_expr(fcx, p);
|
||||
copy_pre_post(fcx.ccx, e.id, p);
|
||||
@ -527,12 +512,6 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
|
||||
expr_if_check(p, conseq, maybe_alt) {
|
||||
join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
expr_bind(operator, maybe_args) {
|
||||
let mut args = [];
|
||||
let mut cmodes = callee_modes(fcx, operator.id);
|
||||
|
@ -422,7 +422,6 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
|
||||
init_assign), elts,
|
||||
return_val);
|
||||
}
|
||||
expr_copy(a) { ret find_pre_post_state_sub(fcx, pres, a, e.id, none); }
|
||||
expr_move(lhs, rhs) {
|
||||
ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_move);
|
||||
}
|
||||
@ -598,17 +597,10 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
|
||||
}
|
||||
ret changed | set_poststate_ann(fcx.ccx, e.id, a_post);
|
||||
}
|
||||
expr_field(val, _, _) {
|
||||
ret find_pre_post_state_sub(fcx, pres, val, e.id, none);
|
||||
}
|
||||
expr_unary(_, operand) {
|
||||
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
|
||||
}
|
||||
expr_addr_of(_, operand) {
|
||||
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
|
||||
}
|
||||
expr_cast(operand, _) {
|
||||
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
|
||||
expr_field(x, _, _) | expr_loop_body(x) | expr_unary(_, x) |
|
||||
expr_addr_of(_, x) | expr_assert(x) | expr_cast(x, _) |
|
||||
expr_copy(x) {
|
||||
ret find_pre_post_state_sub(fcx, pres, x, e.id, none);
|
||||
}
|
||||
expr_fail(maybe_fail_val) {
|
||||
// FIXME Should factor out this code,
|
||||
@ -622,9 +614,6 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
|
||||
option::maybe(maybe_fail_val, false, {|fail_val|
|
||||
find_pre_post_state_expr(fcx, pres, fail_val)});
|
||||
}
|
||||
expr_assert(p) {
|
||||
ret find_pre_post_state_sub(fcx, pres, p, e.id, none);
|
||||
}
|
||||
expr_check(_, p) {
|
||||
/* predicate p holds after this expression executes */
|
||||
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p);
|
||||
|
@ -2910,6 +2910,24 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
||||
check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
|
||||
unify, expected);
|
||||
}
|
||||
ast::expr_loop_body(block) {
|
||||
let rty = structurally_resolved_type(fcx, expr.span, expected);
|
||||
let inner_ty = 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})
|
||||
}
|
||||
};
|
||||
check_expr_with(fcx, block, inner_ty);
|
||||
let block_ty = structurally_resolved_type(
|
||||
fcx, expr.span, ty::node_id_to_type(tcx, block.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)
|
||||
with fty}));
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::expr_block(b) {
|
||||
// If this is an unchecked block, turn off purity-checking
|
||||
bot = check_block(fcx, b);
|
||||
|
@ -303,6 +303,7 @@ enum expr_ {
|
||||
expr_alt(@expr, [arm], alt_mode),
|
||||
expr_fn(proto, fn_decl, blk, @capture_clause),
|
||||
expr_fn_block(fn_decl, blk),
|
||||
expr_loop_body(@expr),
|
||||
expr_block(blk),
|
||||
|
||||
/*
|
||||
|
@ -407,6 +407,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
|
||||
expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs))
|
||||
}
|
||||
expr_unary(binop, ohs) { expr_unary(binop, fld.fold_expr(ohs)) }
|
||||
expr_loop_body(f) { expr_loop_body(fld.fold_expr(f)) }
|
||||
expr_lit(_) { e }
|
||||
expr_cast(expr, ty) { expr_cast(fld.fold_expr(expr), ty) }
|
||||
expr_addr_of(m, ohs) { expr_addr_of(m, fld.fold_expr(ohs)) }
|
||||
|
@ -1403,13 +1403,37 @@ fn parse_else_expr(p: parser) -> @ast::expr {
|
||||
}
|
||||
|
||||
fn parse_for_expr(p: parser) -> @ast::expr {
|
||||
let lo = p.last_span.lo;
|
||||
let decl = parse_local(p, false, false);
|
||||
expect_word(p, "in");
|
||||
let seq = parse_expr(p);
|
||||
let body = parse_block_no_value(p);
|
||||
let mut hi = body.span.hi;
|
||||
ret mk_expr(p, lo, hi, ast::expr_for(decl, seq, body));
|
||||
let lo = p.last_span;
|
||||
// FIXME remove this kludge after migration and snapshotting (#1619)
|
||||
let new_style = alt p.token {
|
||||
token::IDENT(_, false) { alt p.look_ahead(1u) {
|
||||
token::DOT | token::LPAREN { true }
|
||||
_ { false }
|
||||
} }
|
||||
token::IDENT(_, true) { true }
|
||||
_ { false }
|
||||
};
|
||||
if new_style {
|
||||
let call = parse_expr(p);
|
||||
alt call.node {
|
||||
ast::expr_call(f, args, true) {
|
||||
let b_arg = vec::last(args);
|
||||
let last = mk_expr(p, b_arg.span.lo, b_arg.span.hi,
|
||||
ast::expr_loop_body(b_arg));
|
||||
@{node: ast::expr_call(f, vec::init(args) + [last], true)
|
||||
with *call}
|
||||
}
|
||||
_ {
|
||||
p.span_fatal(lo, "`for` must be followed by a block call");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let decl = parse_local(p, false, false);
|
||||
expect_word(p, "in");
|
||||
let seq = parse_expr(p);
|
||||
let body = parse_block_no_value(p);
|
||||
mk_expr(p, lo.lo, body.span.hi, ast::expr_for(decl, seq, body))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_while_expr(p: parser) -> @ast::expr {
|
||||
|
@ -855,9 +855,16 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||
pclose(s);
|
||||
}
|
||||
ast::expr_call(func, args, has_block) {
|
||||
let mut base_args = args;
|
||||
let blk = if has_block {
|
||||
let blk_arg = vec::pop(base_args);
|
||||
alt blk_arg.node {
|
||||
ast::expr_loop_body(_) { word_nbsp(s, "for"); }
|
||||
_ {}
|
||||
}
|
||||
some(blk_arg)
|
||||
} else { none };
|
||||
print_expr_parens_if_not_bot(s, func);
|
||||
let mut base_args = args, blk = none;
|
||||
if has_block { blk = some(vec::pop(base_args)); }
|
||||
if !has_block || vec::len(base_args) > 0u {
|
||||
popen(s);
|
||||
commasep_exprs(s, inconsistent, base_args);
|
||||
@ -989,6 +996,9 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||
print_fn_block_args(s, decl);
|
||||
print_possibly_embedded_block(s, body, block_block_fn, indent_unit);
|
||||
}
|
||||
ast::expr_loop_body(body) {
|
||||
print_expr(s, body);
|
||||
}
|
||||
ast::expr_block(blk) {
|
||||
// containing cbox, will be closed by print-block at }
|
||||
cbox(s, indent_unit);
|
||||
|
@ -338,10 +338,12 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||
for eo: option<@expr> in args { visit_expr_opt(eo, e, v); }
|
||||
}
|
||||
expr_binary(_, a, b) { v.visit_expr(a, e, v); v.visit_expr(b, e, v); }
|
||||
expr_unary(_, a) { v.visit_expr(a, e, v); }
|
||||
expr_addr_of(_, x) | expr_unary(_, x) | expr_loop_body(x) |
|
||||
expr_check(_, x) | expr_assert(x) {
|
||||
v.visit_expr(x, e, v);
|
||||
}
|
||||
expr_lit(_) { }
|
||||
expr_cast(x, t) { v.visit_expr(x, e, v); v.visit_ty(t, e, v); }
|
||||
expr_addr_of(_, x) { v.visit_expr(x, e, v); }
|
||||
expr_if(x, b, eo) {
|
||||
v.visit_expr(x, e, v);
|
||||
v.visit_block(b, e, v);
|
||||
@ -353,7 +355,7 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||
visit_expr_opt(eo, e, v);
|
||||
}
|
||||
expr_while(x, b) { v.visit_expr(x, e, v); v.visit_block(b, e, v); }
|
||||
expr_loop(b) { v.visit_block(b, e, v); }
|
||||
expr_loop(b) { v.visit_block(b, e, v); }
|
||||
expr_for(dcl, x, b) {
|
||||
v.visit_local(dcl, e, v);
|
||||
v.visit_expr(x, e, v);
|
||||
@ -394,8 +396,6 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||
v.visit_expr(lv, e, v);
|
||||
v.visit_expr(x, e, v);
|
||||
}
|
||||
expr_check(_, x) { v.visit_expr(x, e, v); }
|
||||
expr_assert(x) { v.visit_expr(x, e, v); }
|
||||
expr_mac(mac) { visit_mac(mac, e, v); }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user