implement capture clauses (move, in particular) and integrate
them into type state and so forth
This commit is contained in:
parent
41ae146057
commit
55a2fd18ec
14 changed files with 305 additions and 53 deletions
132
src/comp/middle/capture.rs
Normal file
132
src/comp/middle/capture.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
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 @@ type generic_info =
|
|||
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 @@ import trans::{
|
|||
// };
|
||||
// };
|
||||
//
|
||||
// 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 @@ import trans::{
|
|||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
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 store_environment(
|
|||
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 store_environment(
|
|||
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 store_environment(
|
|||
|
||||
// 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_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
|||
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_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
|||
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 @@ tag expr_ {
|
|||
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.
|
||||
|
|
|
|||
5
src/test/compile-fail/cap-clause-both-copy-and-move.rs
Normal file
5
src/test/compile-fail/cap-clause-both-copy-and-move.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// error-pattern:error: Variable 'x' captured more than once
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = sendfn[move x; copy x]() -> int { x };
|
||||
}
|
||||
5
src/test/compile-fail/cap-clause-double-copy.rs
Normal file
5
src/test/compile-fail/cap-clause-double-copy.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// error-pattern:error: Variable 'x' captured more than once
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = sendfn[copy x, x]() -> int { x };
|
||||
}
|
||||
5
src/test/compile-fail/cap-clause-double-move.rs
Normal file
5
src/test/compile-fail/cap-clause-double-move.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// error-pattern: error: Variable 'x' captured more than once
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = sendfn[move x, x]() -> int { x };
|
||||
}
|
||||
8
src/test/compile-fail/cap-clause-move-upvar.rs
Normal file
8
src/test/compile-fail/cap-clause-move-upvar.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// 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
|
||||
};
|
||||
}
|
||||
7
src/test/compile-fail/cap-clause-use-after-move.rs
Normal file
7
src/test/compile-fail/cap-clause-use-after-move.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// error-pattern:Unsatisfied precondition constraint
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let _y = sendfn[move x]() { };
|
||||
let _z = x; //< error: Unsatisfied precondition constraint
|
||||
}
|
||||
17
src/test/run-pass/cap-clause-move.rs
Normal file
17
src/test/run-pass/cap-clause-move.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// 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;
|
||||
}
|
||||
5
src/test/run-pass/cap-clause-not-used.rs
Normal file
5
src/test/run-pass/cap-clause-not-used.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// error-pattern: warning: Captured variable 'y' not used in closure
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let _y = sendfn[copy x]() { };
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue