提交 55a2fd18 编写于 作者: N Niko Matsakis

implement capture clauses (move, in particular) and integrate

them into type state and so forth
上级 41ae1460
import syntax::{ast, ast_util};
import std::map;
export capture_mode;
export capture_var;
export capture_map;
export check_capture_clause;
export compute_capture_vars;
export cap_copy;
export cap_move;
export cap_drop;
export cap_ref;
tag capture_mode {
cap_copy; //< Copy the value into the closure.
cap_move; //< Move the value into the closure.
cap_drop; //< Drop value after creating closure.
cap_ref; //< Reference directly from parent stack frame (block fn).
}
type capture_var = {
def: ast::def, //< The variable being accessed free.
mode: capture_mode //< How is the variable being accessed.
};
type capture_map = map::hashmap<ast::def_id, capture_var>;
// checks the capture clause for a fn_expr() and issues warnings or
// errors for any irregularities which we identify.
fn check_capture_clause(tcx: ty::ctxt,
fn_expr_id: ast::node_id,
fn_proto: ast::proto,
cap_clause: ast::capture_clause) {
let freevars = freevars::get_freevars(tcx, fn_expr_id);
let seen_defs = map::new_int_hash();
let check_capture_item = lambda(&&cap_item: @ast::capture_item) {
let cap_def = tcx.def_map.get(cap_item.id);
if !vec::any(*freevars, {|fv| fv.def == cap_def}) {
tcx.sess.span_warn(
cap_item.span,
#fmt("Captured variable '%s' not used in closure",
cap_item.name));
}
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
if !seen_defs.insert(cap_def_id, ()) {
tcx.sess.span_err(
cap_item.span,
#fmt("Variable '%s' captured more than once",
cap_item.name));
}
};
let check_not_upvar = lambda(&&cap_item: @ast::capture_item) {
alt tcx.def_map.get(cap_item.id) {
ast::def_upvar(_, _, _) {
tcx.sess.span_err(
cap_item.span,
#fmt("Upvars (like '%s') cannot be moved into a closure",
cap_item.name));
}
_ {}
}
};
let check_block_captures = lambda(v: [@ast::capture_item]) {
if check vec::is_not_empty(v) {
let cap_item0 = vec::head(v);
tcx.sess.span_err(
cap_item0.span,
"Cannot capture values explicitly with a block closure");
}
};
alt fn_proto {
ast::proto_block. {
check_block_captures(cap_clause.copies);
check_block_captures(cap_clause.moves);
}
ast::proto_bare. | ast::proto_shared(_) | ast::proto_send. {
vec::iter(cap_clause.copies, check_capture_item);
vec::iter(cap_clause.moves, check_capture_item);
vec::iter(cap_clause.moves, check_not_upvar);
}
}
}
fn compute_capture_vars(tcx: ty::ctxt,
fn_expr_id: ast::node_id,
fn_proto: ast::proto,
cap_clause: ast::capture_clause) -> [capture_var] {
let freevars = freevars::get_freevars(tcx, fn_expr_id);
let cap_map = map::new_int_hash();
vec::iter(cap_clause.copies) { |cap_item|
let cap_def = tcx.def_map.get(cap_item.id);
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
if vec::any(*freevars, {|fv| fv.def == cap_def}) {
cap_map.insert(cap_def_id, { def:cap_def, mode:cap_copy });
}
}
vec::iter(cap_clause.moves) { |cap_item|
let cap_def = tcx.def_map.get(cap_item.id);
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
if vec::any(*freevars, {|fv| fv.def == cap_def}) {
cap_map.insert(cap_def_id, { def:cap_def, mode:cap_move });
} else {
cap_map.insert(cap_def_id, { def:cap_def, mode:cap_drop });
}
}
let implicit_mode = alt fn_proto {
ast::proto_block. { cap_ref }
ast::proto_bare. | ast::proto_shared(_) | ast::proto_send. { cap_copy }
};
vec::iter(*freevars) { |fvar|
let fvar_def_id = ast_util::def_id_of_def(fvar.def).node;
alt cap_map.find(fvar_def_id) {
option::some(_) { /* was explicitly named, do nothing */ }
option::none. {
cap_map.insert(fvar_def_id, {def:fvar.def, mode:implicit_mode});
}
}
}
let result = [];
cap_map.values { |cap_var| result += [cap_var]; }
ret result;
}
......@@ -2566,7 +2566,11 @@ fn trans_do_while(cx: @block_ctxt, body: ast::blk, cond: @ast::expr) ->
static_tis: [option::t<@tydesc_info>],
tydescs: [ValueRef]};
tag lval_kind { temporary; owned; owned_imm; }
tag lval_kind {
temporary; //< Temporary value passed by value if of immediate type
owned; //< Non-temporary value passed by pointer
owned_imm; //< Non-temporary value passed by value
}
type local_var_result = {val: ValueRef, kind: lval_kind};
type lval_result = {bcx: @block_ctxt, val: ValueRef, kind: lval_kind};
tag callee_env { obj_env(ValueRef); null_env; is_closure; }
......@@ -3550,12 +3554,13 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
assert op != ast::deref; // lvals are handled above
ret trans_unary(bcx, op, x, e.id, dest);
}
// NDM captures
ast::expr_fn(f, cap_clause) {
ret trans_closure::trans_expr_fn(bcx, f, e.span, e.id, dest);
ret trans_closure::trans_expr_fn(
bcx, f, e.span, e.id, *cap_clause, dest);
}
ast::expr_bind(f, args) {
ret trans_closure::trans_bind(bcx, f, args, e.id, dest);
ret trans_closure::trans_bind(
bcx, f, args, e.id, dest);
}
ast::expr_copy(a) {
if !expr_is_lval(bcx, a) {
......
......@@ -43,8 +43,10 @@
// };
// };
//
// NB: this is defined in the code in T_closure_ptr and
// closure_ty_to_tuple_ty (below).
// NB: this struct is defined in the code in trans_common::T_closure()
// and mk_closure_ty() below. The former defines the LLVM version and
// the latter the Rust equivalent. It occurs to me that these could
// perhaps be unified, but currently they are not.
//
// Note that the closure carries a type descriptor that describes
// itself. Trippy. This is needed because the precise types of the
......@@ -64,8 +66,17 @@
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tag environment_value {
// Evaluate expr and store result in env (used for bind).
env_expr(@ast::expr);
env_direct(ValueRef, ty::t, bool);
// Copy the value from this llvm ValueRef into the environment.
env_copy(ValueRef, ty::t, lval_kind);
// Move the value from this llvm ValueRef into the environment.
env_move(ValueRef, ty::t, lval_kind);
// Access by reference (used for blocks).
env_ref(ValueRef, ty::t, lval_kind);
}
// Given a closure ty, emits a corresponding tuple ty
......@@ -143,8 +154,10 @@ fn maybe_clone_tydesc(bcx: @block_ctxt,
let bound_tys = [];
for bv in bound_values {
bound_tys += [alt bv {
env_direct(_, t, _) { t }
env_expr(e) { ty::expr_ty(tcx, e) }
env_copy(_, t, _) { t }
env_move(_, t, _) { t }
env_ref(_, t, _) { t }
env_expr(e) { ty::expr_ty(tcx, e) }
}];
}
let bound_data_ty = ty::mk_tup(tcx, bound_tys);
......@@ -239,17 +252,29 @@ fn maybe_clone_tydesc(bcx: @block_ctxt,
add_clean_temp_mem(bcx, bound.val, bound_tys[i]);
temp_cleanups += [bound.val];
}
env_direct(val, ty, is_mem) {
alt ck {
ty::closure_shared. | ty::closure_send. {
let val1 = is_mem ? load_if_immediate(bcx, val, ty) : val;
bcx = trans::copy_val(bcx, INIT, bound.val, val1, ty);
}
ty::closure_block. {
let addr = is_mem ? val : do_spill_noroot(bcx, val);
Store(bcx, addr, bound.val);
}
}
env_copy(val, ty, owned.) {
let val1 = load_if_immediate(bcx, val, ty);
bcx = trans::copy_val(bcx, INIT, bound.val, val1, ty);
}
env_copy(val, ty, owned_imm.) {
bcx = trans::copy_val(bcx, INIT, bound.val, val, ty);
}
env_copy(_, _, temporary.) {
fail "Cannot capture temporary upvar";
}
env_move(val, ty, kind) {
let src = {bcx:bcx, val:val, kind:kind};
bcx = move_val(bcx, INIT, bound.val, src, ty);
}
env_ref(val, ty, owned.) {
Store(bcx, val, bound.val);
}
env_ref(val, ty, owned_imm.) {
let addr = do_spill_noroot(bcx, val);
Store(bcx, addr, bound.val);
}
env_ref(_, _, temporary.) {
fail "Cannot capture temporary upvar";
}
}
}
......@@ -260,25 +285,38 @@ fn maybe_clone_tydesc(bcx: @block_ctxt,
// Given a context and a list of upvars, build a closure. This just
// collects the upvars and packages them up for store_environment.
fn build_closure(cx: @block_ctxt,
upvars: freevar_info,
fn build_closure(bcx0: @block_ctxt,
cap_vars: [capture::capture_var],
ck: ty::closure_kind)
-> closure_result {
// If we need to, package up the iterator body to call
let env_vals = [];
let tcx = bcx_tcx(cx);
// Package up the upvars
vec::iter(*upvars) { |upvar|
let lv = trans_local_var(cx, upvar.def);
let nid = ast_util::def_id_of_def(upvar.def).node;
let bcx = bcx0;
let tcx = bcx_tcx(bcx);
// Package up the captured upvars
vec::iter(cap_vars) { |cap_var|
let lv = trans_local_var(bcx, cap_var.def);
let nid = ast_util::def_id_of_def(cap_var.def).node;
let ty = ty::node_id_to_monotype(tcx, nid);
alt ck {
ty::closure_block. { ty = ty::mk_mut_ptr(tcx, ty); }
ty::closure_send. | ty::closure_shared. {}
alt cap_var.mode {
capture::cap_ref. {
assert ck == ty::closure_block;
ty = ty::mk_mut_ptr(tcx, ty);
env_vals += [env_ref(lv.val, ty, lv.kind)];
}
capture::cap_copy. {
env_vals += [env_copy(lv.val, ty, lv.kind)];
}
capture::cap_move. {
env_vals += [env_move(lv.val, ty, lv.kind)];
}
capture::cap_drop. {
bcx = drop_ty(bcx, lv.val, ty);
}
}
env_vals += [env_direct(lv.val, ty, lv.kind == owned)];
}
ret store_environment(cx, copy cx.fcx.lltydescs, env_vals, ck);
ret store_environment(bcx, copy bcx.fcx.lltydescs, env_vals, ck);
}
// Given an enclosing block context, a new function context, a closure type,
......@@ -287,7 +325,7 @@ fn build_closure(cx: @block_ctxt,
fn load_environment(enclosing_cx: @block_ctxt,
fcx: @fn_ctxt,
boxed_closure_ty: ty::t,
upvars: freevar_info,
cap_vars: [capture::capture_var],
ck: ty::closure_kind) {
let bcx = new_raw_block_ctxt(fcx, fcx.llloadenv);
......@@ -311,23 +349,34 @@ fn load_environment(enclosing_cx: @block_ctxt,
// Populate the upvars from the environment.
let path = [0, abi::box_rc_field_body, abi::closure_elt_bindings];
vec::iteri(*upvars) { |i, upvar|
check type_is_tup_like(bcx, boxed_closure_ty);
let upvarptr =
GEP_tup_like(bcx, boxed_closure_ty, llclosure, path + [i as int]);
bcx = upvarptr.bcx;
let llupvarptr = upvarptr.val;
alt ck {
ty::closure_block. { llupvarptr = Load(bcx, llupvarptr); }
ty::closure_send. | ty::closure_shared. { }
let i = 0u;
vec::iter(cap_vars) { |cap_var|
alt cap_var.mode {
capture::cap_drop. { /* ignore */ }
_ {
check type_is_tup_like(bcx, boxed_closure_ty);
let upvarptr = GEP_tup_like(
bcx, boxed_closure_ty, llclosure, path + [i as int]);
bcx = upvarptr.bcx;
let llupvarptr = upvarptr.val;
alt ck {
ty::closure_block. { llupvarptr = Load(bcx, llupvarptr); }
ty::closure_send. | ty::closure_shared. { }
}
let def_id = ast_util::def_id_of_def(cap_var.def);
fcx.llupvars.insert(def_id.node, llupvarptr);
i += 1u;
}
}
let def_id = ast_util::def_id_of_def(upvar.def);
fcx.llupvars.insert(def_id.node, llupvarptr);
}
}
fn trans_expr_fn(bcx: @block_ctxt, f: ast::_fn, sp: span,
id: ast::node_id, dest: dest) -> @block_ctxt {
fn trans_expr_fn(bcx: @block_ctxt,
f: ast::_fn,
sp: span,
id: ast::node_id,
cap_clause: ast::capture_clause,
dest: dest) -> @block_ctxt {
if dest == ignore { ret bcx; }
let ccx = bcx_ccx(bcx), bcx = bcx;
let fty = node_id_type(ccx, id);
......@@ -339,10 +388,11 @@ fn trans_expr_fn(bcx: @block_ctxt, f: ast::_fn, sp: span,
register_fn(ccx, sp, sub_cx.path, "anon fn", [], id);
let trans_closure_env = lambda(ck: ty::closure_kind) -> ValueRef {
let upvars = get_freevars(ccx.tcx, id);
let {llbox, box_ty, bcx} = build_closure(bcx, upvars, ck);
let cap_vars = capture::compute_capture_vars(
ccx.tcx, id, f.proto, cap_clause);
let {llbox, box_ty, bcx} = build_closure(bcx, cap_vars, ck);
trans_closure(sub_cx, sp, f, llfn, no_self, [], id, {|fcx|
load_environment(bcx, fcx, box_ty, upvars, ck);
load_environment(bcx, fcx, box_ty, cap_vars, ck);
});
llbox
};
......@@ -420,7 +470,7 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
let sp = cx.sp;
let llclosurety = T_ptr(type_of(ccx, sp, outgoing_fty));
let src_loc = PointerCast(bcx, cl, llclosurety);
([env_direct(src_loc, pair_ty, true)], none)
([env_copy(src_loc, pair_ty, owned)], none)
}
none. { ([], some(f_res.val)) }
};
......
......@@ -347,6 +347,13 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
handle_var_def(fcx, rslt, def.def, "upvar");
}
let use_cap_item = lambda(&&cap_item: @capture_item) {
let d = local_node_id_to_local_def_id(fcx, cap_item.id);
option::may(d, { |id| use_var(fcx, id) });
};
vec::iter(cap_clause.copies, use_cap_item);
vec::iter(cap_clause.moves, use_cap_item);
vec::iter(cap_clause.moves) { |cap_item|
log ("forget_in_postcond: ", cap_item);
forget_in_postcond(fcx, e.id, cap_item.id);
......
......@@ -1939,9 +1939,8 @@ fn check_binop_type_compat(fcx: @fn_ctxt, span: span, ty: ty::t,
if !arm_non_bot { result_ty = ty::mk_bot(tcx); }
write::ty_only_fixup(fcx, id, result_ty);
}
ast::expr_fn(f, captures) { // NDM captures
let cx = @{tcx: tcx};
let fty = ty_of_fn_decl(cx.tcx, m_check_tyvar(fcx), f.decl,
ast::expr_fn(f, captures) {
let fty = ty_of_fn_decl(tcx, m_check_tyvar(fcx), f.decl,
f.proto, [], none).ty;
write::ty_only_fixup(fcx, id, fty);
......@@ -1956,6 +1955,8 @@ fn check_binop_type_compat(fcx: @fn_ctxt, span: span, ty: ty::t,
if f.proto == ast::proto_block {
write::ty_only_fixup(fcx, id, expected);
}
capture::check_capture_clause(tcx, expr.id, f.proto, *captures);
}
ast::expr_block(b) {
// If this is an unchecked block, turn off purity-checking
......
......@@ -38,6 +38,7 @@ mod middle {
mod shape;
mod gc;
mod debuginfo;
mod capture;
mod tstate {
mod ck;
......
......@@ -261,6 +261,10 @@ fn proto_kind(p: proto) -> kind {
expr_mac(mac);
}
// AST nodes that represent a capture clause, which is used to declare
// variables that are copied or moved explicitly into the closure. In some
// cases, local variables can also be copied implicitly into the closure if
// they are used in the closure body.
type capture_item = {
id: int,
name: ident, // Currently, can only capture a local var.
......
// error-pattern:error: Variable 'x' captured more than once
fn main() {
let x = 5;
let y = sendfn[move x; copy x]() -> int { x };
}
\ No newline at end of file
// error-pattern:error: Variable 'x' captured more than once
fn main() {
let x = 5;
let y = sendfn[copy x, x]() -> int { x };
}
// error-pattern: error: Variable 'x' captured more than once
fn main() {
let x = 5;
let y = sendfn[move x, x]() -> int { x };
}
// error-pattern: error: Upvars (like 'x') cannot be moved into a closure
fn main() {
let x = 5;
let _y = sendfn[move x]() -> int {
let _z = sendfn[move x]() -> int { x };
22
};
}
// error-pattern:Unsatisfied precondition constraint
fn main() {
let x = 5;
let _y = sendfn[move x]() { };
let _z = x; //< error: Unsatisfied precondition constraint
}
// error-pattern: warning: Captured variable 'y' not used in closure
fn main() {
let x = ~1;
let y = ptr::addr_of(*x) as uint;
let lam_copy = lambda[copy x]() -> uint { ptr::addr_of(*x) as uint };
let lam_move = lambda[move x]() -> uint { ptr::addr_of(*x) as uint };
assert lam_copy() != y;
assert lam_move() == y;
let x = ~2;
let y = ptr::addr_of(*x) as uint;
let snd_copy = sendfn[copy x]() -> uint { ptr::addr_of(*x) as uint };
let snd_move = sendfn[move x]() -> uint { ptr::addr_of(*x) as uint };
assert snd_copy() != y;
assert snd_move() == y;
}
// error-pattern: warning: Captured variable 'y' not used in closure
fn main() {
let x = 5;
let _y = sendfn[copy x]() { };
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册