diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 5f2a5b2accb7..a356e716a9a9 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -318,6 +318,8 @@ enum expr_ { // easily type this (a function returning nil on the inside but bool on // the outside). expr_loop_body(@expr), + // Like expr_loop_body but for 'do' blocks + expr_do_body(@expr), expr_block(blk), /* diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 0d5966db4f57..7ddbfb52e945 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -415,6 +415,7 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ { } expr_unary(binop, ohs) { expr_unary(binop, fld.fold_expr(ohs)) } expr_loop_body(f) { expr_loop_body(fld.fold_expr(f)) } + expr_do_body(f) { expr_do_body(fld.fold_expr(f)) } expr_lit(_) { copy 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)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 08eaa7792917..45badde00169 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -772,6 +772,8 @@ class parser { ret pexpr(self.parse_if_expr()); } else if self.eat_keyword("for") { ret pexpr(self.parse_for_expr()); + } else if self.eat_keyword("do") { + ret pexpr(self.parse_do_expr()); } else if self.eat_keyword("while") { ret pexpr(self.parse_while_expr()); } else if self.eat_keyword("loop") { @@ -1312,6 +1314,23 @@ class parser { } } + fn parse_do_expr() -> @expr { + let lo = self.last_span; + let call = self.parse_expr_res(RESTRICT_STMT_EXPR); + alt call.node { + expr_call(f, args, true) { + let b_arg = vec::last(args); + let last = self.mk_expr(b_arg.span.lo, b_arg.span.hi, + expr_do_body(b_arg)); + @{node: expr_call(f, vec::init(args) + [last], true) + with *call} + } + _ { + self.span_fatal(lo, "`do` must be followed by a block call"); + } + } + } + fn parse_while_expr() -> @expr { let lo = self.last_span.lo; let cond = self.parse_expr(); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 7fb6165f7f12..ec14778dc02d 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -287,7 +287,7 @@ fn restricted_keyword_table() -> hashmap { "assert", "be", "break", "check", "claim", "class", "const", "cont", "copy", "crust", - "drop", + "do", "drop", "else", "enum", "export", "fail", "false", "fn", "for", "if", "iface", "impl", "import", diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 43b498eae62f..e0e30dc72fcc 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1048,6 +1048,9 @@ fn print_expr(s: ps, &&expr: @ast::expr) { ast::expr_loop_body(body) { print_expr(s, body); } + ast::expr_do_body(body) { + print_expr(s, body); + } ast::expr_block(blk) { // containing cbox, will be closed by print-block at } cbox(s, indent_unit); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index a7db0fe08513..4a68850674d7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -382,7 +382,8 @@ fn visit_expr(ex: @expr, e: E, v: vt) { for args.each {|eo| visit_expr_opt(eo, e, v); } } expr_binary(_, a, b) { v.visit_expr(a, e, v); v.visit_expr(b, e, v); } - expr_addr_of(_, x) | expr_unary(_, x) | expr_loop_body(x) | + expr_addr_of(_, x) | expr_unary(_, x) | + expr_loop_body(x) | expr_do_body(x) | expr_check(_, x) | expr_assert(x) { v.visit_expr(x, e, v); } diff --git a/src/rustc/middle/borrowck/categorization.rs b/src/rustc/middle/borrowck/categorization.rs index 4a6f8c15147c..b8ec0f9c2f1f 100644 --- a/src/rustc/middle/borrowck/categorization.rs +++ b/src/rustc/middle/borrowck/categorization.rs @@ -174,7 +174,7 @@ impl public_methods for borrowck_ctxt { ast::expr_swap(*) | ast::expr_move(*) | ast::expr_assign(*) | ast::expr_assign_op(*) | ast::expr_fn(*) | ast::expr_fn_block(*) | ast::expr_assert(*) | ast::expr_check(*) | ast::expr_ret(*) | - ast::expr_loop_body(*) | ast::expr_unary(*) | + ast::expr_loop_body(*) | ast::expr_do_body(*) | ast::expr_unary(*) | ast::expr_copy(*) | ast::expr_cast(*) | ast::expr_fail(*) | ast::expr_vstore(*) | ast::expr_vec(*) | ast::expr_tup(*) | ast::expr_if_check(*) | ast::expr_if(*) | ast::expr_log(*) | diff --git a/src/rustc/middle/liveness.rs b/src/rustc/middle/liveness.rs index 51d2285d6fea..981ec4d63e35 100644 --- a/src/rustc/middle/liveness.rs +++ b/src/rustc/middle/liveness.rs @@ -412,7 +412,8 @@ fn visit_expr(expr: @expr, &&self: @ir_maps, vt: vt<@ir_maps>) { expr_vec(*) | expr_rec(*) | expr_call(*) | expr_tup(*) | expr_bind(*) | expr_new(*) | expr_log(*) | expr_binary(*) | expr_assert(*) | expr_check(*) | expr_addr_of(*) | expr_copy(*) | - expr_loop_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | + expr_loop_body(*) | expr_do_body(*) | expr_cast(*) | + expr_unary(*) | expr_fail(*) | expr_break | expr_cont | expr_lit(_) | expr_ret(*) | expr_block(*) | expr_move(*) | expr_assign(*) | expr_swap(*) | expr_assign_op(*) | expr_mac(*) { @@ -1054,6 +1055,7 @@ class liveness { expr_addr_of(_, e) | expr_copy(e) | expr_loop_body(e) | + expr_do_body(e) | expr_cast(e, _) | expr_unary(_, e) { self.propagate_through_expr(e, succ) @@ -1406,7 +1408,8 @@ fn check_expr(expr: @expr, &&self: @liveness, vt: vt<@liveness>) { expr_vec(*) | expr_rec(*) | expr_tup(*) | expr_bind(*) | expr_new(*) | expr_log(*) | expr_binary(*) | expr_assert(*) | expr_check(*) | expr_copy(*) | - expr_loop_body(*) | expr_cast(*) | expr_unary(*) | expr_fail(*) | + expr_loop_body(*) | expr_do_body(*) | + expr_cast(*) | expr_unary(*) | expr_fail(*) | expr_ret(*) | expr_break | expr_cont | expr_lit(_) | expr_block(*) | expr_swap(*) | expr_mac(*) | expr_addr_of(*) { visit::visit_expr(expr, self, vt); diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs index 69280657a0d0..d6520b45d4a0 100644 --- a/src/rustc/middle/trans/base.rs +++ b/src/rustc/middle/trans/base.rs @@ -3622,6 +3622,9 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block { ast::expr_loop_body(blk) { ret trans_loop_body(bcx, e, none, dest); } + ast::expr_do_body(blk) { + ret trans_expr(bcx, blk, dest); + } ast::expr_bind(f, args) { ret closure::trans_bind( bcx, f, args, e.id, dest); diff --git a/src/rustc/middle/trans/type_use.rs b/src/rustc/middle/trans/type_use.rs index 03cd889e4804..e42028fbdf6c 100644 --- a/src/rustc/middle/trans/type_use.rs +++ b/src/rustc/middle/trans/type_use.rs @@ -223,7 +223,8 @@ fn mark_for_expr(cx: ctx, e: @expr) { expr_while(_, _) | 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_loop_body(_) {} + expr_ret(_) | expr_loop(_) | expr_bind(_, _) | + expr_loop_body(_) | expr_do_body(_) {} } } diff --git a/src/rustc/middle/tstate/pre_post_conditions.rs b/src/rustc/middle/tstate/pre_post_conditions.rs index a51a4f72a0e8..110989a6a793 100644 --- a/src/rustc/middle/tstate/pre_post_conditions.rs +++ b/src/rustc/middle/tstate/pre_post_conditions.rs @@ -364,7 +364,7 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) { } else { find_pre_post_exprs(fcx, [l, r], e.id); } } expr_addr_of(_, x) | expr_cast(x, _) | expr_unary(_, x) | - expr_loop_body(x) | expr_assert(x) | expr_copy(x) { + expr_loop_body(x) | expr_do_body(x) | expr_assert(x) | expr_copy(x) { find_pre_post_expr(fcx, x); copy_pre_post(fcx.ccx, e.id, x); } diff --git a/src/rustc/middle/tstate/states.rs b/src/rustc/middle/tstate/states.rs index 2b39288152b1..dfc511122a65 100644 --- a/src/rustc/middle/tstate/states.rs +++ b/src/rustc/middle/tstate/states.rs @@ -486,7 +486,8 @@ 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(x, _, _) | expr_loop_body(x) | expr_unary(_, x) | + expr_field(x, _, _) | expr_loop_body(x) | expr_do_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); diff --git a/src/rustc/middle/typeck/check.rs b/src/rustc/middle/typeck/check.rs index f547fb5d50cc..0ca184e59165 100644 --- a/src/rustc/middle/typeck/check.rs +++ b/src/rustc/middle/typeck/check.rs @@ -1364,6 +1364,32 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, } } } + ast::expr_do_body(b) { + let expected_sty = unpack_expected(fcx, expected, {|x|some(x)}).get(); + let (inner_ty, proto) = alt expected_sty { + ty::ty_fn(fty) { + (ty::mk_fn(tcx, fty), fty.proto) + } + _ { + tcx.sess.span_fatal(expr.span, "a do function's last argument \ + should be of function type"); + } + }; + alt check b.node { + ast::expr_fn_block(decl, body, cap_clause) { + check_expr_fn(fcx, b, proto, decl, body, true, some(inner_ty)); + demand::suptype(fcx, b.span, inner_ty, fcx.expr_ty(b)); + capture::check_capture_clause(tcx, b.id, cap_clause); + } + } + let block_ty = structurally_resolved_type( + fcx, expr.span, fcx.node_ty(b.id)); + alt check ty::get(block_ty).struct { + ty::ty_fn(fty) { + fcx.write_ty(expr.id, ty::mk_fn(tcx, fty)); + } + } + } ast::expr_block(b) { // If this is an unchecked block, turn off purity-checking bot = check_block(fcx, b); diff --git a/src/test/compile-fail/do1.rs b/src/test/compile-fail/do1.rs new file mode 100644 index 000000000000..76a10c6d2f16 --- /dev/null +++ b/src/test/compile-fail/do1.rs @@ -0,0 +1,3 @@ +fn main() { + let x = do y; //! ERROR: `do` must be followed by a block call +} diff --git a/src/test/compile-fail/do2.rs b/src/test/compile-fail/do2.rs new file mode 100644 index 000000000000..8950e69e0091 --- /dev/null +++ b/src/test/compile-fail/do2.rs @@ -0,0 +1,5 @@ +fn f(f: fn@(int) -> bool) -> bool { f(10) } + +fn main() { + assert do f() { |i| i == 10 } == 10; //! ERROR: expected `bool` but found `int` +} diff --git a/src/test/run-pass/do1.rs b/src/test/run-pass/do1.rs new file mode 100644 index 000000000000..af173d62d6a3 --- /dev/null +++ b/src/test/run-pass/do1.rs @@ -0,0 +1,5 @@ +fn f(f: fn@(int)) { f(10) } + +fn main() { + do f() { |i| assert i == 10 } +} diff --git a/src/test/run-pass/do2.rs b/src/test/run-pass/do2.rs new file mode 100644 index 000000000000..c8028f806dac --- /dev/null +++ b/src/test/run-pass/do2.rs @@ -0,0 +1,5 @@ +fn f(f: fn@(int) -> int) -> int { f(10) } + +fn main() { + assert do f() { |i| i } == 10; +} diff --git a/src/test/run-pass/do3.rs b/src/test/run-pass/do3.rs new file mode 100644 index 000000000000..c4796eb20709 --- /dev/null +++ b/src/test/run-pass/do3.rs @@ -0,0 +1,5 @@ +fn f(f: fn@(int) -> int) -> int { f(10) } + +fn main() { + assert do f { |i| i } == 10; +}