Skip to content

Commit f6e3738

Browse files
committed
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
1 parent 9638e7f commit f6e3738

File tree

15 files changed

+123
-78
lines changed

15 files changed

+123
-78
lines changed

src/rustc/driver/driver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
156156
time(time_passes, "block-use checking",
157157
bind middle::block_use::check_crate(ty_cx, crate));
158158
time(time_passes, "loop checking",
159-
bind middle::check_loop::check_crate(sess, crate));
159+
bind middle::check_loop::check_crate(ty_cx, crate));
160160
time(time_passes, "function usage",
161161
bind fn_usage::check_crate_fn_usage(ty_cx, crate));
162162
time(time_passes, "alt checking",

src/rustc/middle/block_use.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
3333
i += 1u;
3434
}
3535
}
36+
expr_loop_body(body) {
37+
cx.allow_block = true;
38+
v.visit_expr(body, cx, v);
39+
}
3640
_ {
3741
cx.allow_block = false;
3842
visit::visit_expr(ex, cx, v);

src/rustc/middle/check_loop.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import driver::session::session;
44

55
type ctx = {in_loop: bool, can_ret: bool};
66

7-
fn check_crate(sess: session, crate: @crate) {
7+
fn check_crate(tcx: ty::ctxt, crate: @crate) {
88
visit::visit_crate(*crate, {in_loop: false,can_ret: true}, visit::mk_vt(@{
99
visit_item: {|i, _cx, v|
1010
visit::visit_item(i, {in_loop: false, can_ret: true}, v);
@@ -21,28 +21,31 @@ fn check_crate(sess: session, crate: @crate) {
2121
expr_fn(_, _, _, _) {
2222
visit::visit_expr(e, {in_loop: false, can_ret: true}, v);
2323
}
24-
expr_fn_block(fn_decl, blk) {
25-
visit::visit_expr(e, {in_loop: false, can_ret: false}, v);
24+
expr_fn_block(_, b) {
25+
v.visit_block(b, {in_loop: false, can_ret: false}, v);
26+
}
27+
expr_loop_body(@{node: expr_fn_block(_, b), _}) {
28+
v.visit_block(b, {in_loop: true, can_ret: false}, v);
2629
}
2730
expr_break {
2831
if !cx.in_loop {
29-
sess.span_err(e.span, "`break` outside of loop");
32+
tcx.sess.span_err(e.span, "`break` outside of loop");
3033
}
3134
}
3235
expr_cont {
3336
if !cx.in_loop {
34-
sess.span_err(e.span, "`cont` outside of loop");
37+
tcx.sess.span_err(e.span, "`cont` outside of loop");
3538
}
3639
}
3740
expr_ret(oe) {
3841
if !cx.can_ret {
39-
sess.span_err(e.span, "`ret` in block function");
42+
tcx.sess.span_err(e.span, "`ret` in block function");
4043
}
4144
visit::visit_expr_opt(oe, cx, v);
4245
}
4346
expr_be(re) {
4447
if !cx.can_ret {
45-
sess.span_err(e.span, "`be` in block function");
48+
tcx.sess.span_err(e.span, "`be` in block function");
4649
}
4750
v.visit_expr(re, cx, v);
4851
}

src/rustc/middle/trans/base.rs

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,20 +2990,24 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
29902990
}
29912991
ast::expr_addr_of(_, x) { ret trans_addr_of(bcx, x, dest); }
29922992
ast::expr_fn(proto, decl, body, cap_clause) {
2993-
ret closure::trans_expr_fn(
2994-
bcx, proto, decl, body, e.span, e.id, *cap_clause, dest);
2993+
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
2994+
*cap_clause, false, dest);
29952995
}
29962996
ast::expr_fn_block(decl, body) {
2997-
alt ty::get(expr_ty(bcx, e)).struct {
2997+
alt check ty::get(expr_ty(bcx, e)).struct {
29982998
ty::ty_fn({proto, _}) {
29992999
#debug("translating fn_block %s with type %s",
30003000
expr_to_str(e), ty_to_str(tcx, expr_ty(bcx, e)));
3001-
let cap_clause = { copies: [], moves: [] };
3002-
ret closure::trans_expr_fn(
3003-
bcx, proto, decl, body, e.span, e.id, cap_clause, dest);
3001+
ret closure::trans_expr_fn(bcx, proto, decl, body, e.span, e.id,
3002+
{copies: [], moves: []}, false, dest);
30043003
}
3005-
_ {
3006-
fail "type of fn block is not a function!";
3004+
}
3005+
}
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);
30073011
}
30083012
}
30093013
}
@@ -3375,7 +3379,16 @@ fn trans_break_cont(bcx: block, to_end: bool)
33753379
}
33763380
_ {}
33773381
}
3378-
unwind = alt check unwind.parent { parent_some(cx) { cx } };
3382+
unwind = alt unwind.parent {
3383+
parent_some(cx) { cx }
3384+
// This is a return from a loop body block
3385+
parent_none {
3386+
Store(bcx, C_bool(!to_end), bcx.fcx.llretptr);
3387+
cleanup_and_leave(bcx, none, some(bcx.fcx.llreturn));
3388+
Unreachable(bcx);
3389+
ret bcx;
3390+
}
3391+
};
33793392
}
33803393
cleanup_and_Br(bcx, unwind, target.llbb);
33813394
Unreachable(bcx);
@@ -3895,7 +3908,8 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
38953908
ty_self: self_arg,
38963909
param_substs: option<param_substs>,
38973910
id: ast::node_id,
3898-
maybe_load_env: fn(fn_ctxt)) {
3911+
maybe_load_env: fn(fn_ctxt),
3912+
finish: fn(block)) {
38993913
let _icx = ccx.insn_ctxt("trans_closure");
39003914
set_uwtable(llfndecl);
39013915

@@ -3932,6 +3946,7 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
39323946
} else {
39333947
bcx = trans_block(bcx, body, save_in(fcx.llretptr));
39343948
}
3949+
finish(bcx);
39353950
cleanup_and_Br(bcx, bcx_top, fcx.llreturn);
39363951

39373952
// Insert the mandatory first few basic blocks before lltop.
@@ -3957,7 +3972,7 @@ fn trans_fn(ccx: @crate_ctxt,
39573972
if ccx.sess.opts.extra_debuginfo {
39583973
debuginfo::create_function(fcx);
39593974
}
3960-
});
3975+
}, {|_bcx|});
39613976
if do_time {
39623977
let end = time::get_time();
39633978
log_fn_time(ccx, path_str(path), start, end);

src/rustc/middle/trans/closure.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,7 @@ fn trans_expr_fn(bcx: block,
367367
sp: span,
368368
id: ast::node_id,
369369
cap_clause: ast::capture_clause,
370+
is_loop_body: bool,
370371
dest: dest) -> block {
371372
let _icx = bcx.insn_ctxt("closure::trans_expr_fn");
372373
if dest == ignore { ret bcx; }
@@ -385,6 +386,8 @@ fn trans_expr_fn(bcx: block,
385386
trans_closure(ccx, sub_path, decl, body, llfn, no_self,
386387
bcx.fcx.param_substs, id, {|fcx|
387388
load_environment(fcx, cdata_ty, cap_vars, ck);
389+
}, {|bcx|
390+
if is_loop_body { Store(bcx, C_bool(true), bcx.fcx.llretptr); }
388391
});
389392
llbox
390393
};
@@ -395,7 +398,7 @@ fn trans_expr_fn(bcx: block,
395398
ast::proto_uniq { trans_closure_env(ty::ck_uniq) }
396399
ast::proto_bare {
397400
trans_closure(ccx, sub_path, decl, body, llfn, no_self, none,
398-
id, {|_fcx|});
401+
id, {|_fcx|}, {|_bcx|});
399402
C_null(T_opaque_box_ptr(ccx))
400403
}
401404
};

src/rustc/middle/trans/common.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,7 @@ fn C_nil() -> ValueRef {
718718
}
719719

720720
fn C_bool(b: bool) -> ValueRef {
721-
if b {
722-
ret C_integral(T_bool(), 1u64, False);
723-
} else { ret C_integral(T_bool(), 0u64, False); }
721+
C_integral(T_bool(), if b { 1u64 } else { 0u64 }, False)
724722
}
725723

726724
fn C_i32(i: i32) -> ValueRef {

src/rustc/middle/trans/type_use.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ fn mark_for_expr(cx: ctx, e: @expr) {
190190
expr_fail(_) | expr_break | expr_cont | expr_unary(_, _) |
191191
expr_lit(_) | expr_assert(_) | expr_check(_, _) |
192192
expr_if_check(_, _, _) | expr_mac(_) | expr_addr_of(_, _) |
193-
expr_ret(_) | expr_loop(_) | expr_bind(_, _) {}
193+
expr_ret(_) | expr_loop(_) | expr_bind(_, _) | expr_loop_body(_) {}
194194
}
195195
}
196196

src/rustc/middle/tstate/pre_post_conditions.rs

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,6 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
360360
find_pre_post_exprs(fcx, es, e.id);
361361
}
362362
expr_tup(elts) { find_pre_post_exprs(fcx, elts, e.id); }
363-
expr_copy(a) {
364-
find_pre_post_expr(fcx, a);
365-
copy_pre_post(fcx.ccx, e.id, a);
366-
}
367363
expr_move(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_move); }
368364
expr_swap(lhs, rhs) { handle_update(fcx, e, lhs, rhs, oper_swap); }
369365
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) {
408404
expr_postcond(fcx.ccx, l));
409405
} else { find_pre_post_exprs(fcx, [l, r], e.id); }
410406
}
411-
expr_unary(_, operand) {
412-
find_pre_post_expr(fcx, operand);
413-
copy_pre_post(fcx.ccx, e.id, operand);
414-
}
415-
expr_addr_of(_, operand) {
416-
find_pre_post_expr(fcx, operand);
417-
copy_pre_post(fcx.ccx, e.id, operand);
418-
}
419-
expr_cast(operand, _) {
420-
find_pre_post_expr(fcx, operand);
421-
copy_pre_post(fcx.ccx, e.id, operand);
407+
expr_addr_of(_, x) | expr_cast(x, _) | expr_unary(_, x) |
408+
expr_loop_body(x) | expr_assert(x) | expr_copy(x) {
409+
find_pre_post_expr(fcx, x);
410+
copy_pre_post(fcx.ccx, e.id, x);
422411
}
423412
expr_while(test, body) {
424413
find_pre_post_expr(fcx, test);
@@ -512,10 +501,6 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
512501
then everything is true! */
513502
prestate, false_postcond(num_local_vars));
514503
}
515-
expr_assert(p) {
516-
find_pre_post_expr(fcx, p);
517-
copy_pre_post(fcx.ccx, e.id, p);
518-
}
519504
expr_check(_, p) {
520505
find_pre_post_expr(fcx, p);
521506
copy_pre_post(fcx.ccx, e.id, p);
@@ -527,12 +512,6 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
527512
expr_if_check(p, conseq, maybe_alt) {
528513
join_then_else(fcx, p, conseq, maybe_alt, e.id, if_check);
529514
}
530-
531-
532-
533-
534-
535-
536515
expr_bind(operator, maybe_args) {
537516
let mut args = [];
538517
let mut cmodes = callee_modes(fcx, operator.id);

src/rustc/middle/tstate/states.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,6 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
422422
init_assign), elts,
423423
return_val);
424424
}
425-
expr_copy(a) { ret find_pre_post_state_sub(fcx, pres, a, e.id, none); }
426425
expr_move(lhs, rhs) {
427426
ret find_pre_post_state_two(fcx, pres, lhs, rhs, e.id, oper_move);
428427
}
@@ -598,17 +597,10 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
598597
}
599598
ret changed | set_poststate_ann(fcx.ccx, e.id, a_post);
600599
}
601-
expr_field(val, _, _) {
602-
ret find_pre_post_state_sub(fcx, pres, val, e.id, none);
603-
}
604-
expr_unary(_, operand) {
605-
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
606-
}
607-
expr_addr_of(_, operand) {
608-
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
609-
}
610-
expr_cast(operand, _) {
611-
ret find_pre_post_state_sub(fcx, pres, operand, e.id, none);
600+
expr_field(x, _, _) | expr_loop_body(x) | expr_unary(_, x) |
601+
expr_addr_of(_, x) | expr_assert(x) | expr_cast(x, _) |
602+
expr_copy(x) {
603+
ret find_pre_post_state_sub(fcx, pres, x, e.id, none);
612604
}
613605
expr_fail(maybe_fail_val) {
614606
// FIXME Should factor out this code,
@@ -622,9 +614,6 @@ fn find_pre_post_state_expr(fcx: fn_ctxt, pres: prestate, e: @expr) -> bool {
622614
option::maybe(maybe_fail_val, false, {|fail_val|
623615
find_pre_post_state_expr(fcx, pres, fail_val)});
624616
}
625-
expr_assert(p) {
626-
ret find_pre_post_state_sub(fcx, pres, p, e.id, none);
627-
}
628617
expr_check(_, p) {
629618
/* predicate p holds after this expression executes */
630619
let c: sp_constr = expr_to_constr(fcx.ccx.tcx, p);

src/rustc/middle/typeck.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2910,6 +2910,24 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
29102910
check_expr_fn_with_unifier(fcx, expr, proto, decl, body,
29112911
unify, expected);
29122912
}
2913+
ast::expr_loop_body(block) {
2914+
let rty = structurally_resolved_type(fcx, expr.span, expected);
2915+
let inner_ty = alt check ty::get(rty).struct {
2916+
ty::ty_fn(fty) {
2917+
demand::simple(fcx, expr.span, fty.output, ty::mk_bool(tcx));
2918+
ty::mk_fn(tcx, {output: ty::mk_nil(tcx) with fty})
2919+
}
2920+
};
2921+
check_expr_with(fcx, block, inner_ty);
2922+
let block_ty = structurally_resolved_type(
2923+
fcx, expr.span, ty::node_id_to_type(tcx, block.id));
2924+
alt check ty::get(block_ty).struct {
2925+
ty::ty_fn(fty) {
2926+
write_ty(tcx, expr.id, ty::mk_fn(tcx, {output: ty::mk_bool(tcx)
2927+
with fty}));
2928+
}
2929+
}
2930+
}
29132931
ast::expr_block(b) {
29142932
// If this is an unchecked block, turn off purity-checking
29152933
bot = check_block(fcx, b);

src/rustc/syntax/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ enum expr_ {
303303
expr_alt(@expr, [arm], alt_mode),
304304
expr_fn(proto, fn_decl, blk, @capture_clause),
305305
expr_fn_block(fn_decl, blk),
306+
expr_loop_body(@expr),
306307
expr_block(blk),
307308

308309
/*

src/rustc/syntax/fold.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
407407
expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs))
408408
}
409409
expr_unary(binop, ohs) { expr_unary(binop, fld.fold_expr(ohs)) }
410+
expr_loop_body(f) { expr_loop_body(fld.fold_expr(f)) }
410411
expr_lit(_) { e }
411412
expr_cast(expr, ty) { expr_cast(fld.fold_expr(expr), ty) }
412413
expr_addr_of(m, ohs) { expr_addr_of(m, fld.fold_expr(ohs)) }

src/rustc/syntax/parse/parser.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,13 +1403,37 @@ fn parse_else_expr(p: parser) -> @ast::expr {
14031403
}
14041404

14051405
fn parse_for_expr(p: parser) -> @ast::expr {
1406-
let lo = p.last_span.lo;
1407-
let decl = parse_local(p, false, false);
1408-
expect_word(p, "in");
1409-
let seq = parse_expr(p);
1410-
let body = parse_block_no_value(p);
1411-
let mut hi = body.span.hi;
1412-
ret mk_expr(p, lo, hi, ast::expr_for(decl, seq, body));
1406+
let lo = p.last_span;
1407+
// FIXME remove this kludge after migration and snapshotting (#1619)
1408+
let new_style = alt p.token {
1409+
token::IDENT(_, false) { alt p.look_ahead(1u) {
1410+
token::DOT | token::LPAREN { true }
1411+
_ { false }
1412+
} }
1413+
token::IDENT(_, true) { true }
1414+
_ { false }
1415+
};
1416+
if new_style {
1417+
let call = parse_expr(p);
1418+
alt call.node {
1419+
ast::expr_call(f, args, true) {
1420+
let b_arg = vec::last(args);
1421+
let last = mk_expr(p, b_arg.span.lo, b_arg.span.hi,
1422+
ast::expr_loop_body(b_arg));
1423+
@{node: ast::expr_call(f, vec::init(args) + [last], true)
1424+
with *call}
1425+
}
1426+
_ {
1427+
p.span_fatal(lo, "`for` must be followed by a block call");
1428+
}
1429+
}
1430+
} else {
1431+
let decl = parse_local(p, false, false);
1432+
expect_word(p, "in");
1433+
let seq = parse_expr(p);
1434+
let body = parse_block_no_value(p);
1435+
mk_expr(p, lo.lo, body.span.hi, ast::expr_for(decl, seq, body))
1436+
}
14131437
}
14141438

14151439
fn parse_while_expr(p: parser) -> @ast::expr {

0 commit comments

Comments
 (0)