mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
make it illegal to implicitly capture mutable variables
this is the final part of #1273
This commit is contained in:
parent
d709ed2542
commit
8a9df5aa38
@ -362,7 +362,7 @@ type capture_item = {
|
||||
};
|
||||
|
||||
#[auto_serialize]
|
||||
type capture_clause = [capture_item];
|
||||
type capture_clause = @[capture_item];
|
||||
|
||||
/*
|
||||
// Says whether this is a block the user marked as
|
||||
|
@ -1187,7 +1187,8 @@ fn parse_fn_expr(p: parser, proto: ast::proto) -> @ast::expr {
|
||||
|
||||
let body = parse_block(p);
|
||||
ret mk_expr(p, lo, body.span.hi,
|
||||
ast::expr_fn(proto, decl, body, capture_clause + cc_old));
|
||||
ast::expr_fn(proto, decl, body,
|
||||
@(*capture_clause + cc_old)));
|
||||
}
|
||||
|
||||
fn parse_fn_block_expr(p: parser) -> @ast::expr {
|
||||
@ -1679,7 +1680,7 @@ fn parse_ty_params(p: parser) -> [ast::ty_param] {
|
||||
}
|
||||
|
||||
// FIXME Remove after snapshot
|
||||
fn parse_old_skool_capture_clause(p: parser) -> ast::capture_clause {
|
||||
fn parse_old_skool_capture_clause(p: parser) -> [ast::capture_item] {
|
||||
fn expect_opt_trailing_semi(p: parser) {
|
||||
if !eat(p, token::SEMI) {
|
||||
if p.token != token::RBRACKET {
|
||||
@ -1739,7 +1740,7 @@ fn parse_fn_decl(p: parser, purity: ast::purity,
|
||||
parse_arg_fn, p).node;
|
||||
|
||||
let inputs = either::lefts(args_or_capture_items);
|
||||
let capture_clause = either::rights(args_or_capture_items);
|
||||
let capture_clause = @either::rights(args_or_capture_items);
|
||||
|
||||
// Use the args list to translate each bound variable
|
||||
// mentioned in a constraint to an arg index.
|
||||
@ -1778,7 +1779,7 @@ fn parse_fn_block_decl(p: parser) -> (ast::fn_decl, ast::capture_clause) {
|
||||
purity: ast::impure_fn,
|
||||
cf: ast::return_val,
|
||||
constraints: []},
|
||||
either::rights(inputs_captures));
|
||||
@either::rights(inputs_captures));
|
||||
}
|
||||
|
||||
fn parse_fn_header(p: parser) -> {ident: ast::ident, tps: [ast::ty_param]} {
|
||||
|
@ -1018,7 +1018,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||
// head-box, will be closed by print-block at start
|
||||
ibox(s, 0u);
|
||||
word(s.s, proto_to_str(proto));
|
||||
print_fn_args_and_ret(s, decl, cap_clause);
|
||||
print_fn_args_and_ret(s, decl, *cap_clause);
|
||||
space(s.s);
|
||||
print_block(s, body);
|
||||
}
|
||||
@ -1028,7 +1028,7 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||
// head-box, will be closed by print-block at start
|
||||
ibox(s, 0u);
|
||||
word(s.s, "{");
|
||||
print_fn_block_args(s, decl, cap_clause);
|
||||
print_fn_block_args(s, decl, *cap_clause);
|
||||
print_possibly_embedded_block(s, body, block_block_fn, indent_unit);
|
||||
}
|
||||
ast::expr_loop_body(body) {
|
||||
|
@ -16,8 +16,8 @@ enum fn_kind {
|
||||
fk_item_fn(ident, [ty_param]), //< an item declared with fn()
|
||||
fk_method(ident, [ty_param], @method),
|
||||
fk_res(ident, [ty_param], region_param),
|
||||
fk_anon(proto), //< an anonymous function like fn@(...)
|
||||
fk_fn_block, //< a block {||...}
|
||||
fk_anon(proto, capture_clause), //< an anonymous function like fn@(...)
|
||||
fk_fn_block(capture_clause), //< a block {||...}
|
||||
fk_ctor(ident, [ty_param], node_id /* self id */,
|
||||
def_id /* parent class id */) // class constructor
|
||||
}
|
||||
@ -26,7 +26,7 @@ fn name_of_fn(fk: fn_kind) -> ident {
|
||||
alt fk {
|
||||
fk_item_fn(name, _) | fk_method(name, _, _) | fk_res(name, _, _)
|
||||
| fk_ctor(name, _, _, _) { name }
|
||||
fk_anon(_) | fk_fn_block { "anon" }
|
||||
fk_anon(*) | fk_fn_block(*) { "anon" }
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ fn tps_of_fn(fk: fn_kind) -> [ty_param] {
|
||||
alt fk {
|
||||
fk_item_fn(_, tps) | fk_method(_, tps, _) | fk_res(_, tps, _)
|
||||
| fk_ctor(_, tps, _, _) { tps }
|
||||
fk_anon(_) | fk_fn_block { [] }
|
||||
fk_anon(*) | fk_fn_block(*) { [] }
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,11 +381,13 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||
v.visit_expr(x, e, v);
|
||||
for arms.each {|a| v.visit_arm(a, e, v); }
|
||||
}
|
||||
expr_fn(proto, decl, body, _) {
|
||||
v.visit_fn(fk_anon(proto), decl, body, ex.span, ex.id, e, v);
|
||||
expr_fn(proto, decl, body, cap_clause) {
|
||||
v.visit_fn(fk_anon(proto, cap_clause), decl, body,
|
||||
ex.span, ex.id, e, v);
|
||||
}
|
||||
expr_fn_block(decl, body, _) {
|
||||
v.visit_fn(fk_fn_block, decl, body, ex.span, ex.id, e, v);
|
||||
expr_fn_block(decl, body, cap_clause) {
|
||||
v.visit_fn(fk_fn_block(cap_clause), decl, body,
|
||||
ex.span, ex.id, e, v);
|
||||
}
|
||||
expr_block(b) { v.visit_block(b, e, v); }
|
||||
expr_assign(a, b) { v.visit_expr(b, e, v); v.visit_expr(a, e, v); }
|
||||
|
@ -368,7 +368,7 @@ fn mk_test_wrapper(cx: test_ctxt,
|
||||
let wrapper_expr: ast::expr = {
|
||||
id: cx.sess.next_node_id(),
|
||||
node: ast::expr_fn(ast::proto_bare, wrapper_decl,
|
||||
wrapper_body, []),
|
||||
wrapper_body, @[]),
|
||||
span: span
|
||||
};
|
||||
|
||||
|
@ -223,8 +223,7 @@ fn visit_ids(item: ast::inlined_item, vfn: fn@(ast::node_id)) {
|
||||
vfn(m.self_id);
|
||||
vec::iter(tps) {|tp| vfn(tp.id)}
|
||||
}
|
||||
visit::fk_anon(_) |
|
||||
visit::fk_fn_block {
|
||||
visit::fk_anon(*) | visit::fk_fn_block(*) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,11 @@ type capture_map = map::hashmap<ast::def_id, capture_var>;
|
||||
// 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::int_hash();
|
||||
|
||||
let check_capture_item = fn@(cap_item: ast::capture_item) {
|
||||
for (*cap_clause).each { |cap_item|
|
||||
let cap_def = tcx.def_map.get(cap_item.id);
|
||||
if !vec::any(*freevars, {|fv| fv.def == cap_def}) {
|
||||
tcx.sess.span_warn(
|
||||
@ -52,22 +51,6 @@ fn check_capture_clause(tcx: ty::ctxt,
|
||||
#fmt("variable '%s' captured more than once",
|
||||
cap_item.name));
|
||||
}
|
||||
};
|
||||
|
||||
alt fn_proto {
|
||||
ast::proto_any | ast::proto_block {
|
||||
if vec::is_not_empty(cap_clause) {
|
||||
let cap_item0 = vec::head(cap_clause);
|
||||
tcx.sess.span_err(
|
||||
cap_item0.span,
|
||||
"cannot capture values explicitly with a block closure");
|
||||
}
|
||||
}
|
||||
ast::proto_bare | ast::proto_box | ast::proto_uniq {
|
||||
for cap_clause.each { |cap_item|
|
||||
check_capture_item(cap_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +63,7 @@ fn compute_capture_vars(tcx: ty::ctxt,
|
||||
|
||||
// first add entries for anything explicitly named in the cap clause
|
||||
|
||||
for cap_clause.each { |cap_item|
|
||||
for (*cap_clause).each { |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 cap_item.is_move {
|
||||
|
@ -5,6 +5,7 @@ import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable};
|
||||
import driver::session::session;
|
||||
import std::map::hashmap;
|
||||
import syntax::print::pprust::expr_to_str;
|
||||
import freevars::freevar_entry;
|
||||
|
||||
// Kind analysis pass. There are three kinds:
|
||||
//
|
||||
@ -56,17 +57,61 @@ fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
|
||||
ret ctx.rval_map;
|
||||
}
|
||||
|
||||
type check_fn = fn@(ctx, option<@freevar_entry>, bool, ty::t, sp: span);
|
||||
|
||||
// Yields the appropriate function to check the kind of closed over
|
||||
// variables. `id` is the node_id for some expression that creates the
|
||||
// closure.
|
||||
fn with_appropriate_checker(cx: ctx, id: node_id,
|
||||
b: fn(fn@(ctx, ty::t, sp: span))) {
|
||||
fn with_appropriate_checker(cx: ctx, id: node_id, b: fn(check_fn)) {
|
||||
fn check_for_uniq(cx: ctx, fv: option<@freevar_entry>, is_move: bool,
|
||||
var_t: ty::t, sp: span) {
|
||||
// all captured data must be sendable, regardless of whether it is
|
||||
// moved in or copied in
|
||||
check_send(cx, var_t, sp);
|
||||
|
||||
// check that only immutable variables are implicitly copied in
|
||||
if !is_move {
|
||||
for fv.each { |fv|
|
||||
check_imm_free_var(cx, fv.def, fv.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_box(cx: ctx, fv: option<@freevar_entry>, is_move: bool,
|
||||
var_t: ty::t, sp: span) {
|
||||
// copied in data must be copyable, but moved in data can be anything
|
||||
if !is_move { check_copy(cx, var_t, sp); }
|
||||
|
||||
// check that only immutable variables are implicitly copied in
|
||||
if !is_move {
|
||||
for fv.each { |fv|
|
||||
check_imm_free_var(cx, fv.def, fv.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_block(cx: ctx, fv: option<@freevar_entry>, _is_move: bool,
|
||||
_var_t: ty::t, sp: span) {
|
||||
// only restriction: no capture clauses (we would have to take
|
||||
// ownership of the moved/copied in data).
|
||||
if fv.is_none() {
|
||||
cx.tcx.sess.span_err(
|
||||
sp,
|
||||
"cannot capture values explicitly with a block closure");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_bare(cx: ctx, _fv: option<@freevar_entry>, _is_move: bool,
|
||||
_var_t: ty::t, sp: span) {
|
||||
cx.tcx.sess.span_err(sp, "attempted dynamic environment capture");
|
||||
}
|
||||
|
||||
let fty = ty::node_id_to_type(cx.tcx, id);
|
||||
alt ty::ty_fn_proto(fty) {
|
||||
proto_uniq { b(check_send); }
|
||||
proto_box { b(check_copy); }
|
||||
proto_bare { b(check_none); }
|
||||
proto_any | proto_block { /* no check needed */ }
|
||||
proto_uniq { b(check_for_uniq) }
|
||||
proto_box { b(check_for_box) }
|
||||
proto_bare { b(check_for_bare) }
|
||||
proto_any | proto_block { b(check_for_block) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,59 +120,54 @@ fn with_appropriate_checker(cx: ctx, id: node_id,
|
||||
fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
|
||||
fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {
|
||||
|
||||
// n.b.: This could be the body of either a fn decl or a fn expr. In the
|
||||
// former case, the prototype will be proto_bare and no check occurs. In
|
||||
// the latter case, we do not check the variables that in the capture
|
||||
// clause (as we don't have access to that here) but just those that
|
||||
// appear free. The capture clauses are checked below, in check_expr().
|
||||
//
|
||||
// We could do this check also in check_expr(), but it seems more
|
||||
// "future-proof" to do it this way, as check_fn_body() is supposed to be
|
||||
// the common flow point for all functions that appear in the AST.
|
||||
// Find the check function that enforces the appropriate bounds for this
|
||||
// kind of function:
|
||||
with_appropriate_checker(cx, fn_id) { |chk|
|
||||
|
||||
with_appropriate_checker(cx, fn_id) { |checker|
|
||||
// Begin by checking the variables in the capture clause, if any.
|
||||
// Here we slightly abuse the map function to both check and report
|
||||
// errors and produce a list of the def id's for all capture
|
||||
// variables. This list is used below to avoid checking and reporting
|
||||
// on a given variable twice.
|
||||
let cap_clause = alt fk {
|
||||
visit::fk_anon(_, cc) | visit::fk_fn_block(cc) { cc }
|
||||
visit::fk_item_fn(*) | visit::fk_method(*) |
|
||||
visit::fk_res(*) | visit::fk_ctor(*) { @[] }
|
||||
};
|
||||
let captured_vars = (*cap_clause).map { |cap_item|
|
||||
let cap_def = cx.tcx.def_map.get(cap_item.id);
|
||||
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
|
||||
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
|
||||
chk(cx, none, cap_item.is_move, ty, cap_item.span);
|
||||
cap_def_id
|
||||
};
|
||||
|
||||
// Iterate over any free variables that may not have appeared in the
|
||||
// capture list. Ensure that they too are of the appropriate kind.
|
||||
for vec::each(*freevars::get_freevars(cx.tcx, fn_id)) {|fv|
|
||||
let id = ast_util::def_id_of_def(fv.def).node;
|
||||
if checker == check_copy {
|
||||
|
||||
// skip over free variables that appear in the cap clause
|
||||
if captured_vars.contains(id) { cont; }
|
||||
|
||||
// if this is the last use of the variable, then it will be
|
||||
// a move and not a copy
|
||||
let is_move = {
|
||||
let last_uses = alt check cx.last_uses.find(fn_id) {
|
||||
some(last_use::closes_over(vars)) { vars }
|
||||
none { [] }
|
||||
};
|
||||
if option::is_some(
|
||||
vec::position_elem(last_uses, id)) { cont; }
|
||||
}
|
||||
last_uses.contains(id)
|
||||
};
|
||||
|
||||
let ty = ty::node_id_to_type(cx.tcx, id);
|
||||
checker(cx, ty, fv.span);
|
||||
chk(cx, some(fv), is_move, ty, fv.span);
|
||||
}
|
||||
}
|
||||
|
||||
visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
|
||||
}
|
||||
|
||||
fn check_fn_cap_clause(cx: ctx,
|
||||
id: node_id,
|
||||
cap_clause: capture_clause) {
|
||||
// Check that the variables named in the clause which are not free vars
|
||||
// (if any) are also legal. freevars are checked above in check_fn().
|
||||
// This is kind of a degenerate case, as captured variables will generally
|
||||
// appear free in the body.
|
||||
let freevars = freevars::get_freevars(cx.tcx, id);
|
||||
let freevar_ids = vec::map(*freevars, { |freevar|
|
||||
ast_util::def_id_of_def(freevar.def).node
|
||||
});
|
||||
//log("freevar_ids", freevar_ids);
|
||||
with_appropriate_checker(cx, id) { |checker|
|
||||
for cap_clause.each { |cap_item|
|
||||
let cap_def = cx.tcx.def_map.get(cap_item.id);
|
||||
let cap_def_id = ast_util::def_id_of_def(cap_def).node;
|
||||
if !vec::contains(freevar_ids, cap_def_id) {
|
||||
let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
|
||||
checker(cx, ty, cap_item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
|
||||
alt b.node.expr {
|
||||
some(ex) { maybe_copy(cx, ex); }
|
||||
@ -225,9 +265,6 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) {
|
||||
check_fn_cap_clause(cx, e.id, cap_clause);
|
||||
}
|
||||
_ { }
|
||||
}
|
||||
visit::visit_expr(e, cx, v);
|
||||
@ -307,6 +344,35 @@ fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_imm_free_var(cx: ctx, def: def, sp: span) {
|
||||
let msg = "mutable variables cannot be implicitly captured; \
|
||||
use a capture clause";
|
||||
alt def {
|
||||
def_local(_, is_mutbl) {
|
||||
if is_mutbl {
|
||||
cx.tcx.sess.span_err(sp, msg);
|
||||
}
|
||||
}
|
||||
def_arg(_, mode) {
|
||||
alt ty::resolved_mode(cx.tcx, mode) {
|
||||
by_ref | by_val { /* ok */ }
|
||||
by_mutbl_ref | by_move | by_copy {
|
||||
cx.tcx.sess.span_err(sp, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
def_upvar(_, def1, _) {
|
||||
check_imm_free_var(cx, *def1, sp);
|
||||
}
|
||||
def_binding(*) | def_self(*) { /*ok*/ }
|
||||
_ {
|
||||
cx.tcx.sess.span_bug(
|
||||
sp,
|
||||
#fmt["unknown def for free variable: %?", def]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_copy(cx: ctx, ty: ty::t, sp: span) {
|
||||
if !ty::kind_can_be_copied(ty::type_kind(cx.tcx, ty)) {
|
||||
cx.tcx.sess.span_err(sp, "copying a noncopyable value");
|
||||
@ -319,10 +385,6 @@ fn check_send(cx: ctx, ty: ty::t, sp: span) {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_none(cx: ctx, _ty: ty::t, sp: span) {
|
||||
cx.tcx.sess.span_err(sp, "attempted dynamic environment capture");
|
||||
}
|
||||
|
||||
//
|
||||
// Local Variables:
|
||||
// mode: rust
|
||||
|
@ -159,7 +159,7 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
// n.b.: safe to ignore copies, as if they are unused
|
||||
// then they are ignored, otherwise they will show up
|
||||
// as freevars in the body.
|
||||
for cap_clause.each { |ci|
|
||||
for (*cap_clause).each { |ci|
|
||||
if ci.is_move {
|
||||
clear_def_if_local(cx, cx.def_map.get(ci.id), false);
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ fn visit_expr(ex: @expr, &&cx: @ctx, v: visit::vt<@ctx>) {
|
||||
check_lval(cx, dest, msg_assign);
|
||||
}
|
||||
expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) {
|
||||
for cap_clause.each { |cap_item|
|
||||
for (*cap_clause).each { |cap_item|
|
||||
if cap_item.is_move {
|
||||
let def = cx.tcx.def_map.get(cap_item.id);
|
||||
alt is_illegal_to_modify_def(cx, def, msg_move_out) {
|
||||
|
@ -455,7 +455,7 @@ fn resolve_names(e: @env, c: @ast::crate) {
|
||||
}
|
||||
ast::expr_fn(_, _, _, cap_clause) |
|
||||
ast::expr_fn_block(_, _, cap_clause) {
|
||||
for cap_clause.each { |ci|
|
||||
for (*cap_clause).each { |ci|
|
||||
resolve_capture_item(e, sc, ci);
|
||||
}
|
||||
}
|
||||
@ -615,10 +615,12 @@ fn visit_fn_with_scope(e: @env, fk: visit::fn_kind, decl: ast::fn_decl,
|
||||
for decl.constraints.each {|c| resolve_constr(e, c, sc, v); }
|
||||
let scope = alt fk {
|
||||
visit::fk_item_fn(_, tps) | visit::fk_res(_, tps, _) |
|
||||
visit::fk_method(_, tps, _) | visit::fk_ctor(_, tps, _, _)
|
||||
{ scope_bare_fn(decl, id, tps) }
|
||||
visit::fk_anon(ast::proto_bare) { scope_bare_fn(decl, id, []) }
|
||||
visit::fk_anon(_) | visit::fk_fn_block { scope_fn_expr(decl, id, []) }
|
||||
visit::fk_method(_, tps, _) | visit::fk_ctor(_, tps, _, _) {
|
||||
scope_bare_fn(decl, id, tps) }
|
||||
visit::fk_anon(ast::proto_bare, _) {
|
||||
scope_bare_fn(decl, id, []) }
|
||||
visit::fk_anon(_, _) | visit::fk_fn_block(_) {
|
||||
scope_fn_expr(decl, id, []) }
|
||||
};
|
||||
|
||||
visit::visit_fn(fk, decl, body, sp, id, cons(scope, @sc), v);
|
||||
|
@ -343,12 +343,12 @@ fn find_pre_post_expr(fcx: fn_ctxt, e: @expr) {
|
||||
expr_fn(_, _, _, cap_clause) | expr_fn_block(_, _, cap_clause) {
|
||||
find_pre_post_expr_fn_upvars(fcx, e);
|
||||
|
||||
for cap_clause.each { |cap_item|
|
||||
for (*cap_clause).each { |cap_item|
|
||||
let d = local_node_id_to_local_def_id(fcx, cap_item.id);
|
||||
option::iter(d, { |id| use_var(fcx, id) });
|
||||
}
|
||||
|
||||
for cap_clause.each { |cap_item|
|
||||
for (*cap_clause).each { |cap_item|
|
||||
if cap_item.is_move {
|
||||
log(debug, ("forget_in_postcond: ", cap_item));
|
||||
forget_in_postcond(fcx, e.id, cap_item.id);
|
||||
|
@ -363,7 +363,7 @@ fn find_pre_post_state_cap_clause(fcx: fn_ctxt, e_id: node_id,
|
||||
let ccx = fcx.ccx;
|
||||
let pres_changed = set_prestate_ann(ccx, e_id, pres);
|
||||
let post = tritv_clone(pres);
|
||||
for cap_clause.each { |cap_item|
|
||||
for (*cap_clause).each { |cap_item|
|
||||
if cap_item.is_move {
|
||||
forget_in_poststate(fcx, post, cap_item.id);
|
||||
}
|
||||
|
@ -3565,7 +3565,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
}
|
||||
ast::expr_fn(proto, decl, body, cap_clause) {
|
||||
check_expr_fn(fcx, expr, proto, decl, body, false, expected);
|
||||
capture::check_capture_clause(tcx, expr.id, proto, cap_clause);
|
||||
capture::check_capture_clause(tcx, expr.id, cap_clause);
|
||||
}
|
||||
ast::expr_fn_block(decl, body, cap_clause) {
|
||||
// Take the prototype from the expected type, but default to block:
|
||||
@ -3573,7 +3573,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
alt sty { ty::ty_fn({proto, _}) { some(proto) } _ { none } }
|
||||
}).get_default(ast::proto_box);
|
||||
check_expr_fn(fcx, expr, proto, decl, body, false, expected);
|
||||
capture::check_capture_clause(tcx, expr.id, proto, cap_clause);
|
||||
capture::check_capture_clause(tcx, expr.id, cap_clause);
|
||||
}
|
||||
ast::expr_loop_body(b) {
|
||||
// a loop body is the special argument to a `for` loop. We know that
|
||||
@ -3605,7 +3605,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||
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, proto, cap_clause);
|
||||
capture::check_capture_clause(tcx, b.id, cap_clause);
|
||||
}
|
||||
}
|
||||
let block_ty = structurally_resolved_type(
|
||||
|
@ -1,5 +1,5 @@
|
||||
// error-pattern:variable 'x' captured more than once
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = fn~[move x; copy x]() -> int { x };
|
||||
let y = fn~(move x, copy x) -> int { x };
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// error-pattern:variable 'x' captured more than once
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = fn~[copy x, x]() -> int { x };
|
||||
let y = fn~(copy x, copy x) -> int { x };
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
// error-pattern:variable 'x' captured more than once
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = fn~[move x, x]() -> int { x };
|
||||
let y = fn~(move x, move x) -> int { x };
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
fn to_lambda2(b: fn(uint) -> uint) -> fn@(uint) -> uint {
|
||||
// test case where copy clause specifies a value that is not used
|
||||
// in fn@ body, but value is illegal to copy:
|
||||
ret fn@[copy b](u: uint) -> uint { 22u };
|
||||
ret fn@(u: uint, copy b) -> uint { 22u };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
// error-pattern:unresolved name: z
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = fn~[copy z, x]() {
|
||||
let y = fn~(copy z, copy x) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// error-pattern:unresolved name: z
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let y = fn~[move z, x]() {
|
||||
let y = fn~(move z, move x) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,6 @@
|
||||
|
||||
fn main() {
|
||||
let x = 5;
|
||||
let _y = fn~[move x]() { };
|
||||
let _y = fn~(move x) { };
|
||||
let _z = x; //< error: Unsatisfied precondition constraint
|
||||
}
|
||||
|
14
src/test/compile-fail/cap-clause-with-stack-closure.rs
Normal file
14
src/test/compile-fail/cap-clause-with-stack-closure.rs
Normal file
@ -0,0 +1,14 @@
|
||||
fn foo(_f: fn()) {}
|
||||
fn bar(_f: @int) {}
|
||||
|
||||
fn main() {
|
||||
let x = @3;
|
||||
foo {|| bar(x); }
|
||||
|
||||
let x = @3;
|
||||
foo {|copy x| bar(x); } //! ERROR cannot capture values explicitly with a block closure
|
||||
|
||||
let x = @3;
|
||||
foo {|move x| bar(x); } //! ERROR cannot capture values explicitly with a block closure
|
||||
}
|
||||
|
35
src/test/compile-fail/kindck-implicit-close-over-mut-var.rs
Normal file
35
src/test/compile-fail/kindck-implicit-close-over-mut-var.rs
Normal file
@ -0,0 +1,35 @@
|
||||
fn use(_i: int) {}
|
||||
|
||||
fn foo() {
|
||||
// Here, i is *moved* into the closure: OK
|
||||
let mut i = 0;
|
||||
task::spawn {||
|
||||
use(i);
|
||||
}
|
||||
}
|
||||
|
||||
fn bar() {
|
||||
// Here, i would be implicitly *copied* but it
|
||||
// is mutable: bad
|
||||
let mut i = 0;
|
||||
while i < 10 {
|
||||
task::spawn {||
|
||||
use(i); //! ERROR mutable variables cannot be implicitly captured
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn car() {
|
||||
// Here, i is mutable, but *explicitly* copied:
|
||||
let mut i = 0;
|
||||
while i < 10 {
|
||||
task::spawn {|copy i|
|
||||
use(i);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
8
src/test/compile-fail/kindck-nonsendable-1.rs
Normal file
8
src/test/compile-fail/kindck-nonsendable-1.rs
Normal file
@ -0,0 +1,8 @@
|
||||
fn foo(_x: @uint) {}
|
||||
|
||||
fn main() {
|
||||
let x = @3u;
|
||||
let _ = fn~() { foo(x); }; //! ERROR not a sendable value
|
||||
let _ = fn~(copy x) { foo(x); }; //! ERROR not a sendable value
|
||||
let _ = fn~(move x) { foo(x); }; //! ERROR not a sendable value
|
||||
}
|
17
src/test/compile-fail/kindck-nonsendable-2.rs
Normal file
17
src/test/compile-fail/kindck-nonsendable-2.rs
Normal file
@ -0,0 +1,17 @@
|
||||
fn foo(_x: r) {}
|
||||
|
||||
resource r(_x: ()) {}
|
||||
|
||||
fn main() {
|
||||
let x = r(());
|
||||
let _ = fn~() {
|
||||
// Error even though this is the last use:
|
||||
foo(x); //! ERROR not a sendable value
|
||||
};
|
||||
|
||||
let x = r(());
|
||||
let _ = fn@() {
|
||||
// OK in fn@ because this is the last use:
|
||||
foo(x);
|
||||
};
|
||||
}
|
4
src/test/compile-fail/omitted-arg-in-item-fn.rs
Normal file
4
src/test/compile-fail/omitted-arg-in-item-fn.rs
Normal file
@ -0,0 +1,4 @@
|
||||
// xfail-test
|
||||
|
||||
fn foo(x) { //! ERROR expecting ':' but found ')'
|
||||
}
|
11
src/test/compile-fail/omitted-arg-wrong-types.rs
Normal file
11
src/test/compile-fail/omitted-arg-wrong-types.rs
Normal file
@ -0,0 +1,11 @@
|
||||
// xfail-test
|
||||
|
||||
fn let_in<T>(x: T, f: fn(T)) {}
|
||||
|
||||
fn main() {
|
||||
let_in(3u, fn&(i) { assert i == 3; });
|
||||
//!^ ERROR expected `uint` but found `int`
|
||||
|
||||
let_in(3, fn&(i) { assert i == 3u; });
|
||||
//!^ ERROR expected `int` but found `uint`
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
// error-pattern: not a sendable value
|
||||
|
||||
fn main() {
|
||||
let x = @3u;
|
||||
let _f = fn~(y: uint) -> uint { ret *x+y; };
|
||||
}
|
@ -10,7 +10,7 @@ fn main() {
|
||||
let mut i = 10;
|
||||
while (i > 0) {
|
||||
log(debug, i);
|
||||
task::spawn {|| child(i, ch); };
|
||||
task::spawn {|copy i| child(i, ch); };
|
||||
i = i - 1;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ fn test00() {
|
||||
while i < number_of_tasks {
|
||||
let builder = task::builder();
|
||||
results += [task::future_result(builder)];
|
||||
task::run(builder) {||
|
||||
task::run(builder) {|copy i|
|
||||
test00_start(ch, i, number_of_messages)
|
||||
}
|
||||
i = i + 1;
|
||||
|
@ -43,7 +43,9 @@ fn test00() {
|
||||
i = i + 1;
|
||||
let builder = task::builder();
|
||||
results += [task::future_result(builder)];
|
||||
task::run(builder) {|| test00_start(ch, i, number_of_messages);}
|
||||
task::run(builder) {|copy i|
|
||||
test00_start(ch, i, number_of_messages);
|
||||
}
|
||||
}
|
||||
let mut sum: int = 0;
|
||||
for results.each {|r|
|
||||
@ -128,7 +130,9 @@ fn test06() {
|
||||
i = i + 1;
|
||||
let builder = task::builder();
|
||||
results += [task::future_result(builder)];
|
||||
task::run(builder) {|| test06_start(i);};
|
||||
task::run(builder) {|copy i|
|
||||
test06_start(i);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ import task;
|
||||
|
||||
fn main() {
|
||||
let mut i = 10;
|
||||
while i > 0 { task::spawn {|| child(i); }; i = i - 1; }
|
||||
while i > 0 { task::spawn {|copy i| child(i); }; i = i - 1; }
|
||||
#debug("main thread exiting");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user