diff --git a/src/comp/metadata/tydecode.rs b/src/comp/metadata/tydecode.rs index 4992dc8871a8f..003d423747a0f 100644 --- a/src/comp/metadata/tydecode.rs +++ b/src/comp/metadata/tydecode.rs @@ -257,6 +257,11 @@ fn parse_ty(@pstate st, str_def sd) -> ty::t { ret ty::mk_fn(st.tcx, ast::proto_iter, func._0, func._1, func._2, func._3); } + case ('B') { + auto func = parse_ty_fn(st, sd); + ret ty::mk_fn(st.tcx, ast::proto_block, func._0, func._1, func._2, + func._3); + } case ('N') { auto abi; alt (next(st) as char) { diff --git a/src/comp/metadata/tyencode.rs b/src/comp/metadata/tyencode.rs index 873ec5c549f57..d487946ba2434 100644 --- a/src/comp/metadata/tyencode.rs +++ b/src/comp/metadata/tyencode.rs @@ -205,6 +205,7 @@ fn enc_proto(&ioivec::writer w, proto proto) { alt (proto) { case (proto_iter) { w.write_char('W'); } case (proto_fn) { w.write_char('F'); } + case (proto_block) { w.write_char('B'); } } } diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index d50d5a54bdb72..09ec32379133d 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -49,7 +49,7 @@ export def_map; tag scope { scope_crate; scope_item(@ast::item); - scope_fn(ast::fn_decl, ast::ty_param[]); + scope_fn(ast::fn_decl, ast::proto, ast::ty_param[]); scope_native_item(@ast::native_item); scope_loop(@ast::local); // there's only 1 decl per loop. scope_block(ast::blk); @@ -358,7 +358,7 @@ fn visit_fn_with_scope(&@env e, &ast::_fn f, &ast::ty_param[] tp, &span sp, resolve_constr(e, id, c, sc, v); } visit::visit_fn(f, tp, sp, name, id, - cons(scope_fn(f.decl, tp), @sc), v); + cons(scope_fn(f.decl, f.proto, tp), @sc), v); } fn visit_block_with_scope(&ast::blk b, &scopes sc, &vt[scopes] v) { @@ -375,7 +375,7 @@ fn visit_expr_with_scope(&@ast::expr x, &scopes sc, &vt[scopes] v) { ast::expr_for(?d, _, _) | ast::expr_for_each(?d, _, _) { cons[scope](scope_loop(d), @sc) } - ast::expr_fn(?f) { cons(scope_fn(f.decl, ~[]), @sc) } + ast::expr_fn(?f) { cons(scope_fn(f.decl, f.proto, ~[]), @sc) } _ { sc } }; visit::visit_expr(x, new_sc, v); @@ -530,7 +530,7 @@ fn unresolved_err(&env e, &scopes sc, &span sp, &ident name, &str kind) { alt sc { cons(?cur, ?rest) { alt cur { - scope_crate | scope_fn(_, _) | + scope_crate | scope_fn(_, _, _) | scope_item(@{node: ast::item_mod(_), _}) { ret cur; } @@ -596,9 +596,11 @@ fn lookup_in_scope_strict(&env e, scopes sc, &span sp, &ident name, fn scope_is_fn(&scope sc) -> bool { ret alt (sc) { - scope_fn(_, _) | scope_native_item(_) { true } - _ { false } - }; + scope_fn(_, ast::proto_iter, _) | + scope_fn(_, ast::proto_fn, _) | + scope_native_item(_) { true } + _ { false } + }; } fn def_is_local(&def d) -> bool { @@ -657,7 +659,7 @@ fn lookup_in_scope(&env e, scopes sc, &span sp, &ident name, namespace ns) -> } } } - case (scope_fn(?decl, ?ty_params)) { + case (scope_fn(?decl, _, ?ty_params)) { ret lookup_in_fn(name, decl, ty_params, ns); } case (scope_loop(?local)) { diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index ef62be865d3da..351b3b504400b 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -5358,8 +5358,14 @@ fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output) -> auto sub_cx = extend_path(cx.fcx.lcx, ccx.names.next("anon")); auto s = mangle_internal_name_by_path(ccx, sub_cx.path); auto llfn = decl_internal_fastcall_fn(ccx.llmod, s, llfnty); - trans_fn(sub_cx, e.span, f, llfn, none, ~[], e.id); - ret rslt(cx, create_fn_pair(ccx, s, llfnty, llfn, false)); + + auto fn_res = trans_closure(some(cx), some(llfnty), sub_cx, + e.span, f, llfn, none, ~[], e.id); + auto fn_pair = alt (fn_res) { + some(?fn_pair) { fn_pair } + none { create_fn_pair(ccx, s, llfnty, llfn, false) } + }; + ret rslt(cx, fn_pair); } case (ast::expr_block(?blk)) { auto sub_cx = new_scope_block_ctxt(cx, "block-expr body"); @@ -6651,10 +6657,16 @@ fn finish_fn(&@fn_ctxt fcx, BasicBlockRef lltop) { new_builder(fcx.lldynamicallocas).Br(lltop); } - -fn trans_fn_inner(@local_ctxt cx, &span sp, &ast::_fn f, ValueRef llfndecl, - option::t[ty::t] ty_self, &ast::ty_param[] ty_params, - ast::node_id id) { +// trans_closure: Builds an LLVM function out of a source function. +// If the function closes over its environment a closure will be +// returned. +fn trans_closure(&option::t[@block_ctxt] bcx_maybe, + &option::t[TypeRef] llfnty, + @local_ctxt cx, + &span sp, &ast::_fn f, ValueRef llfndecl, + option::t[ty::t] ty_self, + &ast::ty_param[] ty_params, ast::node_id id) + -> option::t[ValueRef] { set_uwtable(llfndecl); // Set up arguments to the function. @@ -6664,12 +6676,34 @@ fn trans_fn_inner(@local_ctxt cx, &span sp, &ast::_fn f, ValueRef llfndecl, f.decl.inputs, ty_params); copy_any_self_to_alloca(fcx); alt ({ fcx.llself }) { - case (some(?llself)) { populate_fn_ctxt_from_llself(fcx, llself); } - case (_) { } + some(?llself) { populate_fn_ctxt_from_llself(fcx, llself); } + _ { } } auto arg_tys = arg_tys_of_fn(fcx.lcx.ccx, id); copy_args_to_allocas(fcx, f.decl.inputs, arg_tys); + // Figure out if we need to build a closure and act accordingly + auto closure = none; + alt(f.proto) { + ast::proto_block { + auto bcx = option::get(bcx_maybe); + auto upvars = get_freevars(cx.ccx.tcx, id); + + auto llenv = build_environment(bcx, upvars); + + // Generate code to load the environment out of the + // environment pointer. + load_environment(bcx, fcx, llenv.ptrty, upvars); + // Build the closure. + closure = some(create_real_fn_pair(bcx, option::get(llfnty), + llfndecl, llenv.ptr)); + } + ast::proto_closure { + fail "copy capture not implemented yet"; + } + _ {} + } + // Create the first basic block in the function and keep a handle on it to // pass to finish_fn later. auto bcx = new_top_block_ctxt(fcx); @@ -6702,6 +6736,14 @@ fn trans_fn_inner(@local_ctxt cx, &span sp, &ast::_fn f, ValueRef llfndecl, // Insert the mandatory first few basic blocks before lltop. finish_fn(fcx, lltop); + + ret closure; +} + +fn trans_fn_inner(@local_ctxt cx, &span sp, &ast::_fn f, ValueRef llfndecl, + option::t[ty::t] ty_self, &ast::ty_param[] ty_params, + ast::node_id id) { + trans_closure(none, none, cx, sp, f, llfndecl, ty_self, ty_params, id); } diff --git a/src/comp/middle/tstate/auxiliary.rs b/src/comp/middle/tstate/auxiliary.rs index 7e01ed3ca787b..0f9754d1f8d5f 100644 --- a/src/comp/middle/tstate/auxiliary.rs +++ b/src/comp/middle/tstate/auxiliary.rs @@ -486,6 +486,15 @@ fn constraints_expr(&ty::ctxt cx, @expr e) -> (@ty::constr)[] { } } +fn node_id_to_def_upvar_strict(&fn_ctxt cx, node_id id) -> def { + alt (freevars::def_lookup(cx.ccx.tcx, cx.id, id)) { + case (none) { + log_err "node_id_to_def: node_id " + int::str(id) + " has no def"; + fail; + } + case (some(?d)) { ret d; } + } +} fn node_id_to_def_strict(&ty::ctxt cx, node_id id) -> def { alt (cx.def_map.find(id)) { case (none) { diff --git a/src/comp/middle/tstate/pre_post_conditions.rs b/src/comp/middle/tstate/pre_post_conditions.rs index faeceddf3c253..ece399d9b740d 100644 --- a/src/comp/middle/tstate/pre_post_conditions.rs +++ b/src/comp/middle/tstate/pre_post_conditions.rs @@ -297,6 +297,18 @@ fn handle_update(&fn_ctxt fcx, &@expr parent, } } +fn handle_var(&fn_ctxt fcx, &pre_and_post rslt, node_id id, ident name) { + auto df = node_id_to_def_upvar_strict(fcx, id); + alt (df) { + case (def_local(?d_id)) { + auto i = bit_num(fcx, ninit(d_id._1, name)); + use_var(fcx, d_id._1); + require_and_preserve(i, rslt); + } + case (_) {/* nothing to check */ } + } +} + /* Fills in annotations as a side effect. Does not rebuild the expr */ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) { auto enclosing = fcx.enclosing; @@ -338,17 +350,7 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) { case (expr_path(?p)) { auto rslt = expr_pp(fcx.ccx, e); clear_pp(rslt); - auto df = node_id_to_def_strict(fcx.ccx.tcx, e.id); - alt (df) { - case (def_local(?d_id)) { - auto i = - bit_num(fcx, - ninit(d_id._1, path_to_ident(fcx.ccx.tcx, p))); - use_var(fcx, d_id._1); - require_and_preserve(i, rslt); - } - case (_) {/* nothing to check */ } - } + handle_var(fcx, rslt, e.id, path_to_ident(fcx.ccx.tcx, p)); } case (expr_self_method(?v)) { clear_pp(expr_pp(fcx.ccx, e)); } case (expr_log(_, ?arg)) { @@ -368,7 +370,14 @@ fn find_pre_post_expr(&fn_ctxt fcx, @expr e) { case (none) { clear_pp(expr_pp(fcx.ccx, e)); } } } - case (expr_fn(?f)) { clear_pp(expr_pp(fcx.ccx, e)); } + case (expr_fn(?f)) { + auto rslt = expr_pp(fcx.ccx, e); + clear_pp(rslt); + auto upvars = freevars::get_freevar_uses(fcx.ccx.tcx, e.id); + for (node_id id in *upvars) { + handle_var(fcx, rslt, id, "upvar"); + } + } case (expr_block(?b)) { find_pre_post_block(fcx, b); auto p = block_pp(fcx.ccx, b); diff --git a/src/comp/middle/tstate/states.rs b/src/comp/middle/tstate/states.rs index 5161574eed22d..66425ced125d5 100644 --- a/src/comp/middle/tstate/states.rs +++ b/src/comp/middle/tstate/states.rs @@ -206,7 +206,7 @@ fn find_pre_post_state_loop(&fn_ctxt fcx, prestate pres, &@local l, fn gen_if_local(&fn_ctxt fcx, &poststate p, &@expr e) -> bool { alt (e.node) { case (expr_path(?pth)) { - alt (node_id_to_def(fcx.ccx, e.id)) { + alt (freevars::def_lookup(fcx.ccx.tcx, fcx.id, e.id)) { case (some(def_local(?loc))) { ret set_in_poststate_ident(fcx, loc._1, path_to_ident(fcx.ccx.tcx, pth), p); diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs index d689c62d6df42..e1c420d54a2ac 100644 --- a/src/comp/middle/ty.rs +++ b/src/comp/middle/ty.rs @@ -910,6 +910,7 @@ fn type_is_structural(&ctxt cx, &t ty) -> bool { fn type_is_copyable(&ctxt cx, &t ty) -> bool { ret alt (struct(cx, ty)) { case (ty_res(_, _, _)) { false } + case (ty_fn(proto_block, _, _, _, _)) { false } case (_) { true } }; } diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index b56afe3d70505..0aa8f786a5773 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -65,10 +65,13 @@ type crate_ctxt = rec(mutable obj_info[] obj_infos, ty::ctxt tcx); type fn_ctxt = rec(ty::t ret_ty, ast::purity purity, + // var_bindings, locals, local_names, and next_var_id are shared + // with any nested functions that capture the environment + // (and with any functions whose environment is being captured). @ty::unify::var_bindings var_bindings, hashmap[ast::node_id, int] locals, hashmap[ast::node_id, ast::ident] local_names, - mutable int next_var_id, + @mutable int next_var_id, mutable ast::node_id[] fixups, @crate_ctxt ccx); @@ -234,6 +237,18 @@ fn structure_of(&@fn_ctxt fcx, &span sp, ty::t typ) -> ty::sty { ret ty::struct(fcx.ccx.tcx, structurally_resolved_type(fcx, sp, typ)); } +// Returns the one-level-deep structure of the given type or none if it +// is not known yet. +fn structure_of_maybe(&@fn_ctxt fcx, &span sp, ty::t typ) + -> option::t[ty::sty] { + auto r = + ty::unify::resolve_type_structure(fcx.ccx.tcx, fcx.var_bindings, typ); + ret alt (r) { + case (fix_ok(?typ_s)) { some(ty::struct(fcx.ccx.tcx, typ_s)) } + case (fix_err(_)) { none } + } +} + fn type_is_integral(&@fn_ctxt fcx, &span sp, ty::t typ) -> bool { auto typ_s = structurally_resolved_type(fcx, sp, typ); ret ty::type_is_integral(fcx.ccx.tcx, typ_s); @@ -471,6 +486,15 @@ mod write { } } +// Determine the proto for a fn type given the proto for its associated +// code. This is needed because fn and lambda have fn type while iter +// has iter type and block has block type. This may end up changing. +fn proto_to_ty_proto(&ast::proto proto) -> ast::proto { + ret alt (proto) { + ast::proto_iter | ast::proto_block { proto } + _ { ast::proto_fn } + }; +} // Item collection - a pair of bootstrap passes: // @@ -512,8 +536,8 @@ mod collect { out_constrs += ~[ty::ast_constr_to_constr(cx.tcx, constr)]; } auto t_fn = - ty::mk_fn(cx.tcx, proto, input_tys, output_ty, decl.cf, - out_constrs); + ty::mk_fn(cx.tcx, proto_to_ty_proto(proto), input_tys, + output_ty, decl.cf, out_constrs); auto ty_param_count = ivec::len[ast::ty_param](ty_params); auto tpt = tup(ty_param_count, t_fn); alt (def_id) { @@ -591,9 +615,9 @@ mod collect { for (@ast::constr constr in m.node.meth.decl.constraints) { out_constrs += ~[ty::ast_constr_to_constr(cx.tcx, constr)]; } - ret rec(proto=m.node.meth.proto, ident=m.node.ident, - inputs=inputs, output=output, cf=m.node.meth.decl.cf, - constrs=out_constrs); + ret rec(proto=proto_to_ty_proto(m.node.meth.proto), + ident=m.node.ident, inputs=inputs, output=output, + cf=m.node.meth.decl.cf, constrs=out_constrs); } fn ty_of_obj(@ctxt cx, &ast::ident id, &ast::_obj ob, &ast::ty_param[] ty_params) -> ty::ty_param_count_and_ty { @@ -872,7 +896,7 @@ mod unify { } } -tag autoderef_kind { AUTODEREF_OK; NO_AUTODEREF; } +tag autoderef_kind { AUTODEREF_OK; NO_AUTODEREF; AUTODEREF_BLOCK_COERCE; } // FIXME This is almost a duplicate of ty::type_autoderef, with structure_of // instead of ty::struct. @@ -917,6 +941,29 @@ fn count_boxes(&@fn_ctxt fcx, &span sp, &ty::t t) -> uint { fail; } +fn do_fn_block_coerce(&@fn_ctxt fcx, &span sp, + &ty::t actual, &ty::t expected) -> ty::t { + // fns can be silently coerced to blocks when being used as + // function call or bind arguments, but not the reverse. + // If our actual type is a fn and our expected type is a block, + // build up a new expected type that is identical to the old one + // except for its proto. If we don't know the expected or actual + // types, that's fine, but we can't do the coercion. + ret alt (structure_of_maybe(fcx, sp, actual)) { + some(ty::ty_fn(ast::proto_fn, ?args, ?ret_ty, ?cf, ?constrs)) { + alt (structure_of_maybe(fcx, sp, expected)) { + some(ty::ty_fn(ast::proto_block, _, _, _, _)) { + ty::mk_fn(fcx.ccx.tcx, + ast::proto_block, args, ret_ty, cf, constrs) + } + _ { actual } + } + } + _ { actual } + } +} + + fn resolve_type_vars_if_possible(&@fn_ctxt fcx, ty::t typ) -> ty::t { alt (ty::unify::fixup_vars(fcx.ccx.tcx, fcx.var_bindings, typ)) { case (fix_ok(?new_type)) { ret new_type; } @@ -951,6 +998,8 @@ mod demand { expected_1 = do_autoderef(fcx, sp, expected_1); actual_1 = do_autoderef(fcx, sp, actual_1); implicit_boxes = count_boxes(fcx, sp, actual); + } else if (adk == AUTODEREF_BLOCK_COERCE) { + actual_1 = do_fn_block_coerce(fcx, sp, actual, expected); } let ty::t[mutable] ty_param_substs = ~[mutable]; let int[] ty_param_subst_var_ids = ~[]; @@ -1175,11 +1224,12 @@ type gather_result = rec(@ty::unify::var_bindings var_bindings, hashmap[ast::node_id, int] locals, hashmap[ast::node_id, ast::ident] local_names, - int next_var_id); + @mutable int next_var_id); // Used only as a helper for check_fn. fn gather_locals(&@crate_ctxt ccx, &ast::_fn f, - &ast::node_id id) -> gather_result { + &ast::node_id id, &option::t[@fn_ctxt] old_fcx) + -> gather_result { fn next_var_id(@mutable int nvi) -> int { auto rv = *nvi; *nvi += 1; @@ -1201,10 +1251,22 @@ fn gather_locals(&@crate_ctxt ccx, &ast::_fn f, } } } - auto vb = ty::unify::mk_var_bindings(); - auto locals = new_int_hash[int](); - auto local_names = new_int_hash[ast::ident](); - auto nvi = @mutable 0; + + auto vb; auto locals; auto local_names; auto nvi; + alt (old_fcx) { + none { + vb = ty::unify::mk_var_bindings(); + locals = new_int_hash[int](); + local_names = new_int_hash[ast::ident](); + nvi = @mutable 0; + } + some(?fcx) { + vb = fcx.var_bindings; + locals = fcx.locals; + local_names = fcx.local_names; + nvi = fcx.next_var_id; + } + } // Add object fields, if any. auto obj_fields = ~[]; @@ -1288,7 +1350,7 @@ fn gather_locals(&@crate_ctxt ccx, &ast::_fn f, ret rec(var_bindings=vb, locals=locals, local_names=local_names, - next_var_id=*nvi); + next_var_id=nvi); } @@ -1571,8 +1633,9 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) { alt (a_opt) { case (some(?a)) { check_expr(fcx, a); - demand::simple(fcx, a.span, arg_tys.(i).ty, - expr_ty(fcx.ccx.tcx, a)); + demand::full(fcx, a.span, arg_tys.(i).ty, + expr_ty(fcx.ccx.tcx, a), ~[], + AUTODEREF_BLOCK_COERCE); } case (none) { check_ty_vars = true; @@ -2029,7 +2092,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) { collect::ty_of_fn_decl(cx, convert, ty_of_arg, f.decl, f.proto, ~[], none)._1; write::ty_only_fixup(fcx, id, fty); - check_fn(fcx.ccx, f, id); + check_fn(fcx.ccx, f, id, some(fcx)); } case (ast::expr_block(?b)) { check_block(fcx, b); @@ -2523,8 +2586,8 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) { } fn next_ty_var_id(@fn_ctxt fcx) -> int { - auto id = fcx.next_var_id; - fcx.next_var_id += 1; + auto id = *fcx.next_var_id; + *fcx.next_var_id = fcx.next_var_id + 1; ret id; } @@ -2622,16 +2685,17 @@ fn check_const(&@crate_ctxt ccx, &span sp, &@ast::expr e, &ast::node_id id) { var_bindings=ty::unify::mk_var_bindings(), locals=new_int_hash[int](), local_names=new_int_hash[ast::ident](), - mutable next_var_id=0, + next_var_id=@mutable 0, mutable fixups=fixups, ccx=ccx); check_expr(fcx, e); } -fn check_fn(&@crate_ctxt ccx, &ast::_fn f, &ast::node_id id) { +fn check_fn(&@crate_ctxt ccx, &ast::_fn f, &ast::node_id id, + &option::t[@fn_ctxt] old_fcx) { auto decl = f.decl; auto body = f.body; - auto gather_result = gather_locals(ccx, f, id); + auto gather_result = gather_locals(ccx, f, id, old_fcx); let ast::node_id[] fixups = ~[]; let @fn_ctxt fcx = @rec(ret_ty=ast_ty_to_ty_crate(ccx, decl.output), @@ -2639,7 +2703,7 @@ fn check_fn(&@crate_ctxt ccx, &ast::_fn f, &ast::node_id id) { var_bindings=gather_result.var_bindings, locals=gather_result.locals, local_names=gather_result.local_names, - mutable next_var_id=gather_result.next_var_id, + next_var_id=gather_result.next_var_id, mutable fixups=fixups, ccx=ccx); @@ -2672,7 +2736,7 @@ fn check_fn(&@crate_ctxt ccx, &ast::_fn f, &ast::node_id id) { } fn check_method(&@crate_ctxt ccx, &@ast::method method) { - check_fn(ccx, method.node.meth, method.node.id); + check_fn(ccx, method.node.meth, method.node.id, none); } fn check_item(@crate_ctxt ccx, &@ast::item it) { @@ -2681,10 +2745,10 @@ fn check_item(@crate_ctxt ccx, &@ast::item it) { check_const(ccx, it.span, e, it.id); } case (ast::item_fn(?f, _)) { - check_fn(ccx, f, it.id); + check_fn(ccx, f, it.id, none); } case (ast::item_res(?f, ?dtor_id, _, _)) { - check_fn(ccx, f, dtor_id); + check_fn(ccx, f, dtor_id, none); } case (ast::item_obj(?ob, _, _)) { // We're entering an object, so gather up the info we need. diff --git a/src/comp/syntax/ast.rs b/src/comp/syntax/ast.rs index 3fa40c001c3fa..40f01bb8652c7 100644 --- a/src/comp/syntax/ast.rs +++ b/src/comp/syntax/ast.rs @@ -164,7 +164,7 @@ tag layer { layer_value; layer_state; layer_gc; } tag _auth { auth_unsafe; } -tag proto { proto_iter; proto_fn; } +tag proto { proto_iter; proto_fn; proto_block; proto_closure; } tag binop { add; diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index 6b616c909ab4c..71045be6d9ef5 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -172,6 +172,8 @@ fn bad_expr_word_table() -> hashmap[str, ()] { words.insert("native", ()); words.insert("auto", ()); words.insert("fn", ()); + words.insert("block", ()); + words.insert("lambda", ()); words.insert("pred", ()); words.insert("iter", ()); words.insert("block", ()); @@ -301,6 +303,8 @@ fn parse_proto(&parser p) -> ast::proto { ret ast::proto_iter; } else if (eat_word(p, "fn")) { ret ast::proto_fn; + } else if (eat_word(p, "block")) { + ret ast::proto_block; } else if (eat_word(p, "pred")) { ret ast::proto_fn; } else { unexpected(p, p.peek()); } @@ -589,6 +593,10 @@ fn parse_ty(&parser p) -> @ast::ty { auto flo = p.get_last_lo_pos(); t = parse_ty_fn(ast::proto_fn, p, flo); alt (t) { case (ast::ty_fn(_, _, ?out, _, _)) { hi = out.span.hi; } } + } else if (eat_word(p, "block")) { + auto flo = p.get_last_lo_pos(); + t = parse_ty_fn(ast::proto_block, p, flo); + alt (t) { case (ast::ty_fn(_, _, ?out, _, _)) { hi = out.span.hi; } } } else if (eat_word(p, "iter")) { auto flo = p.get_last_lo_pos(); t = parse_ty_fn(ast::proto_iter, p, flo); @@ -837,7 +845,11 @@ fn parse_bottom_expr(&parser p) -> @ast::expr { } else if (eat_word(p, "spawn")) { ret parse_spawn_expr(p); } else if (eat_word(p, "fn")) { - ret parse_fn_expr(p); + ret parse_fn_expr(p, ast::proto_fn); + } else if (eat_word(p, "block")) { + ret parse_fn_expr(p, ast::proto_block); + } else if (eat_word(p, "lambda")) { + ret parse_fn_expr(p, ast::proto_closure); } else if (eat_word(p, "tup")) { fn parse_elt(&parser p) -> ast::elt { auto m = parse_mutability(p); @@ -1335,11 +1347,11 @@ fn parse_if_expr(&parser p) -> @ast::expr { } } -fn parse_fn_expr(&parser p) -> @ast::expr { +fn parse_fn_expr(&parser p, ast::proto proto) -> @ast::expr { auto lo = p.get_last_lo_pos(); auto decl = parse_fn_decl(p, ast::impure_fn); auto body = parse_block(p); - auto _fn = rec(decl=decl, proto=ast::proto_fn, body=body); + auto _fn = rec(decl=decl, proto=proto, body=body); ret mk_expr(p, lo, body.span.hi, ast::expr_fn(_fn)); } @@ -1669,7 +1681,7 @@ fn parse_source_stmt(&parser p) -> @ast::stmt { } case (fn_no_item) { // parse_item will have already skipped "fn" - auto e = parse_fn_expr(p); + auto e = parse_fn_expr(p, ast::proto_fn); e = parse_dot_or_call_expr_with(p, e); ret @spanned(lo, e.span.hi, ast::stmt_expr(e, p.get_id())); } diff --git a/src/comp/syntax/print/pprust.rs b/src/comp/syntax/print/pprust.rs index a99da8f5d1ee4..9949954ee9b53 100644 --- a/src/comp/syntax/print/pprust.rs +++ b/src/comp/syntax/print/pprust.rs @@ -1573,6 +1573,8 @@ fn proto_to_str(&ast::proto p) -> str { ret alt (p) { ast::proto_fn { "fn" } ast::proto_iter { "iter" } + ast::proto_block { "block" } + ast::proto_closure { "lambda" } }; } diff --git a/src/test/compile-fail/block-coerce-no.rs b/src/test/compile-fail/block-coerce-no.rs new file mode 100644 index 0000000000000..18331262263f8 --- /dev/null +++ b/src/test/compile-fail/block-coerce-no.rs @@ -0,0 +1,20 @@ +// error-pattern: mismatched types +// xfail-stage0 + +// Make sure that fn-to-block coercion isn't incorrectly lifted over +// other tycons. + +fn coerce(&block() b) -> fn() { + fn lol(&fn(&block()) -> fn() f, &block() g) -> fn() { + ret f(g); + } + fn fn_id (&fn() f) -> fn() { ret f } + ret lol(fn_id, b); +} + + +fn main() { + auto i = 8; + auto f = coerce(block() { log_err i; } ); + f(); +} diff --git a/src/test/compile-fail/block-copy.rs b/src/test/compile-fail/block-copy.rs new file mode 100644 index 0000000000000..d8df0a92db845 --- /dev/null +++ b/src/test/compile-fail/block-copy.rs @@ -0,0 +1,9 @@ +// error-pattern: non-copyable +// xfail-stage0 + +fn lol(&block() f) -> block() { ret f; } +fn main() { + auto i = 8; + auto f = lol(block() { log_err i; } ); + f(); +} diff --git a/src/test/compile-fail/block-uninit.rs b/src/test/compile-fail/block-uninit.rs new file mode 100644 index 0000000000000..ff86906962f97 --- /dev/null +++ b/src/test/compile-fail/block-uninit.rs @@ -0,0 +1,8 @@ +// error-pattern: Unsatisfied precondition constraint +// xfail-stage0 + +fn force(&block() f) { f(); } +fn main() { + let int x; + force(block() { log_err x; }); +} diff --git a/src/test/run-pass/block-control.rs b/src/test/run-pass/block-control.rs new file mode 100644 index 0000000000000..2bb457b659db7 --- /dev/null +++ b/src/test/run-pass/block-control.rs @@ -0,0 +1,81 @@ +// xfail-stage0 + +// I think that this is actually pretty workable as a scheme for using +// blocks to affect control flow. It is a little syntactically heavyweight +// right now, but a lot of that can be fixed. A more lightweight notation +// for blocks will go a long way. The other big problem is the excess of +// closing parens and braces, but that can mostly be fixed with some tweaks +// to the macros that are used to invoke iterators so that we could write +// something like: +// #iter (for_each(v), fn (&T y) -> flow[bool] { ... }); + +tag flow[T] { + f_normal; + f_break; + f_continue; + f_return(T); +} + +// Right now we the parser doesn't allow macros to be defined at top level. +// However, they don't obey scope and so we can just stick them in a +// function and it will work out right... +fn bullshit_hack() { +#macro([#call_body(body), + alt (body) { + f_normal { } + f_break { break; } + f_continue { cont; } + f_return(?x) { ret f_return(x); } + }]); +#macro([#iter(e), + alt (e) { + f_normal { } + f_break | f_continue { fail; } + f_return(?x) { ret x; } + }]); +#macro([#ret(e), {ret f_return(e);}]); +#macro([#cont(e), {ret f_continue;}]); +#macro([#break(e), {ret f_break;}]); +#macro([#body(body), {{body} ret f_normal;}]); +} + +fn for_each[T,S](&T[] v, &block (&T) -> flow[S] f) -> flow[S] { + for (T x in v) { + #call_body (f(x)); + } + ret f_normal; +} + +fn contains[T](&T[] v, &T x) -> bool { + #iter (for_each(v, block (&T y) -> flow[bool] { #body({ + if (x == y) { #ret (true); } + })})); + ret false; +} + +fn map[T,S](&T[] v, &block (&T) -> S f) -> S[] { + auto w = ~[]; + // We don't invoke the #iter macro because we can't force a return. + for_each(v, block (&T x) -> flow[()] { #body({ + w += ~[f(x)]; + })}); + ret w; +} + +fn log_int_vec(&int[] v) { + for_each(v, block (&int i) -> flow[()] { #body({ + log_err i; + })}); +} + +fn main() { + auto v = ~[1,2,3,4,5,6,7]; + assert(contains(v, 7) == true); + assert(contains(v, 2) == true); + assert(contains(v, 0) == false); + fn f(&int i) -> int { ret i*i; }; + auto w = map(v, f); + assert(contains(w, 36) == true); + assert(contains(w, 5) == false); + log_int_vec(w); +} diff --git a/src/test/run-pass/block-fn-coerce.rs b/src/test/run-pass/block-fn-coerce.rs new file mode 100644 index 0000000000000..f728fbbb71fd8 --- /dev/null +++ b/src/test/run-pass/block-fn-coerce.rs @@ -0,0 +1,9 @@ +// xfail-stage0 + +fn force(&block() -> int f) -> int { ret f(); } +fn main() { + auto f = fn() -> int { ret 7 }; + assert(force(f) == 7); + auto g = bind force(f); + assert(g() == 7); +} diff --git a/src/test/run-pass/block-iter-1.rs b/src/test/run-pass/block-iter-1.rs new file mode 100644 index 0000000000000..6d887b2f927af --- /dev/null +++ b/src/test/run-pass/block-iter-1.rs @@ -0,0 +1,22 @@ +// xfail-stage0 + +fn iter_vec[T](&vec[T] v, &block (&T) f) { + for (T x in v) { + f(x); + } +} + +fn main() { + auto v = [1,2,3,4,5,6,7]; + auto odds = 0; + iter_vec(v, + block (&int i) { + log_err i; + if (i % 2 == 1) { + odds += 1; + } + log_err odds; + }); + log_err odds; + assert(odds == 4); +} diff --git a/src/test/run-pass/block-iter-2.rs b/src/test/run-pass/block-iter-2.rs new file mode 100644 index 0000000000000..1bc1997d8365a --- /dev/null +++ b/src/test/run-pass/block-iter-2.rs @@ -0,0 +1,22 @@ +// xfail-stage0 + +fn iter_vec[T](&vec[T] v, &block (&T) f) { + for (T x in v) { + f(x); + } +} + +fn main() { + auto v = [1,2,3,4,5]; + auto sum = 0; + iter_vec(v, block (&int i) + { + iter_vec(v, block (&int j) + { + log_err i*j; + sum += i*j; + }); + }); + log_err sum; + assert(sum == 225); +}