1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
import syntax::{ast, ast_util};
import driver::session::session;
import std::map;
import std::map::hashmap;
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;
enum 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::int_hash();
let check_capture_item = fn@(&&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 = fn@(&&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 = fn@(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_any | ast::proto_block {
check_block_captures(cap_clause.copies);
check_block_captures(cap_clause.moves);
}
ast::proto_bare | ast::proto_box | ast::proto_uniq {
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::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_any | ast::proto_block { cap_ref }
ast::proto_bare | ast::proto_box | ast::proto_uniq { 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 mut result = [];
cap_map.values { |cap_var| result += [cap_var]; }
ret result;
}
|