Skip to content

Commit 0da1aca

Browse files
committed
Recognize last uses for copied closed-over variables
And clean up and fix some bad things in last_use.rs. Closes #1894
1 parent cccb0fb commit 0da1aca

File tree

8 files changed

+171
-118
lines changed

8 files changed

+171
-118
lines changed

src/comp/metadata/astencode.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,10 @@ import std::serialization::deserializer;
1313
import std::serialization::serializer_helpers;
1414
import std::serialization::deserializer_helpers;
1515
import middle::trans::common::maps;
16-
import middle::ty;
17-
import middle::typeck;
16+
import middle::{ty, typeck, last_use, ast_map};
1817
import middle::typeck::method_origin;
1918
import middle::typeck::dict_res;
2019
import middle::typeck::dict_origin;
21-
import middle::ast_map;
22-
import driver::session;
2320
import driver::session::session;
2421
import middle::freevars::freevar_entry;
2522
import c = common;
@@ -248,7 +245,7 @@ fn decode_id_range(par_doc: ebml::doc) -> id_range {
248245
}
249246
}
250247

251-
fn reserve_id_range(sess: session::session,
248+
fn reserve_id_range(sess: session,
252249
from_id_range: id_range) -> id_range {
253250
// Handle the case of an empty range:
254251
if empty(from_id_range) { ret from_id_range; }
@@ -781,7 +778,7 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
781778
} else if tag == (c::tag_table_copy as uint) {
782779
dcx.maps.copy_map.insert(id, ());
783780
} else if tag == (c::tag_table_last_use as uint) {
784-
dcx.maps.last_uses.insert(id, ());
781+
dcx.maps.last_uses.insert(id, last_use::is_last_use);
785782
} else {
786783
let val_doc = entry_doc[c::tag_table_val];
787784
let val_dsr = serialization::mk_ebml_deserializer(val_doc);

src/comp/middle/last_use.rs

+127-75
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,26 @@ import std::list;
2424
// (by `break` or conditionals), and for handling loops.
2525

2626
// Marks expr_paths that are last uses.
27-
type last_uses = std::map::hashmap<node_id, ()>;
27+
enum is_last_use {
28+
is_last_use,
29+
has_last_use,
30+
closes_over([node_id]),
31+
}
32+
type last_uses = std::map::hashmap<node_id, is_last_use>;
2833

2934
enum seen { unset, seen(node_id), }
3035
enum block_type { func, loop, }
3136

32-
type set = [{def: node_id, exprs: list<node_id>}];
37+
enum use { var_use(node_id), close_over(node_id), }
38+
type set = [{def: node_id, uses: list<use>}];
3339
type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};
3440

35-
type ctx = {last_uses: std::map::hashmap<node_id, bool>,
41+
enum use_id { path(node_id), close(node_id, node_id) }
42+
fn hash_use_id(id: use_id) -> uint {
43+
(alt id { path(i) { i } close(i, j) { (i << 10) + j } }) as uint
44+
}
45+
46+
type ctx = {last_uses: std::map::hashmap<use_id, bool>,
3647
def_map: resolve::def_map,
3748
ref_map: alias::ref_map,
3849
tcx: ty::ctxt,
@@ -43,9 +54,10 @@ type ctx = {last_uses: std::map::hashmap<node_id, bool>,
4354
fn find_last_uses(c: @crate, def_map: resolve::def_map,
4455
ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
4556
let v = visit::mk_vt(@{visit_expr: visit_expr,
57+
visit_stmt: visit_stmt,
4658
visit_fn: visit_fn
4759
with *visit::default_visitor()});
48-
let cx = {last_uses: std::map::new_int_hash(),
60+
let cx = {last_uses: std::map::mk_hashmap(hash_use_id, {|a, b| a == b}),
4961
def_map: def_map,
5062
ref_map: ref_map,
5163
tcx: tcx,
@@ -54,22 +66,26 @@ fn find_last_uses(c: @crate, def_map: resolve::def_map,
5466
visit::visit_crate(*c, cx, v);
5567
let mini_table = std::map::new_int_hash();
5668
cx.last_uses.items {|key, val|
57-
if val {
58-
mini_table.insert(key, ());
59-
let def_node = ast_util::def_id_of_def(def_map.get(key)).node;
60-
mini_table.insert(def_node, ());
69+
if !val { ret; }
70+
alt key {
71+
path(id) {
72+
mini_table.insert(id, is_last_use);
73+
let def_node = ast_util::def_id_of_def(def_map.get(id)).node;
74+
mini_table.insert(def_node, has_last_use);
75+
}
76+
close(fn_id, local_id) {
77+
mini_table.insert(local_id, has_last_use);
78+
let known = alt check mini_table.find(fn_id) {
79+
some(closes_over(ids)) { ids }
80+
none { [] }
81+
};
82+
mini_table.insert(fn_id, closes_over(known + [local_id]));
83+
}
6184
}
6285
}
6386
ret mini_table;
6487
}
6588

66-
fn ex_is_blockish(cx: ctx, id: node_id) -> bool {
67-
alt ty::get(ty::node_id_to_type(cx.tcx, id)).struct {
68-
ty::ty_fn({proto: p, _}) if is_blockish(p) { true }
69-
_ { false }
70-
}
71-
}
72-
7389
fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
7490
alt ex.node {
7591
expr_ret(oexpr) {
@@ -107,15 +123,17 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
107123
cx.current = join_branches([cur, cx.current]);
108124
}
109125
expr_path(_) {
110-
let my_def = ast_util::def_id_of_def(cx.def_map.get(ex.id)).node;
111-
alt cx.ref_map.find(my_def) {
112-
option::some(root_id) { clear_in_current(cx, root_id, false); }
126+
let my_def = cx.def_map.get(ex.id);
127+
let my_def_id = ast_util::def_id_of_def(my_def).node;
128+
alt cx.ref_map.find(my_def_id) {
129+
option::some(root_id) {
130+
clear_in_current(cx, root_id, false);
131+
}
113132
_ {
114-
alt clear_if_path(cx, ex, v, false) {
115-
option::some(my_def) {
116-
cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}];
117-
}
118-
_ {}
133+
option::may(def_is_owned_local(cx, my_def)) {|nid|
134+
clear_in_current(cx, nid, false);
135+
cx.current += [{def: nid,
136+
uses: cons(var_use(ex.id), @nil)}];
119137
}
120138
}
121139
}
@@ -138,7 +156,7 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
138156
// then they are ignored, otherwise they will show up
139157
// as freevars in the body.
140158
vec::iter(cap_clause.moves) {|ci|
141-
clear_def_if_path(cx, cx.def_map.get(ci.id), true);
159+
clear_def_if_local(cx, cx.def_map.get(ci.id), false);
142160
}
143161
visit::visit_expr(ex, cx, v);
144162
}
@@ -148,10 +166,8 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
148166
let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f));
149167
for arg in args {
150168
alt arg.node {
151-
expr_fn(p, _, _, _) if is_blockish(p) {
152-
fns += [arg];
153-
}
154-
expr_fn_block(_, _) if ex_is_blockish(cx, arg.id) {
169+
expr_fn(_, _, _, _) | expr_fn_block(_, _)
170+
if is_blockish(ty::ty_fn_proto(arg_ts[i].ty)) {
155171
fns += [arg];
156172
}
157173
_ {
@@ -169,6 +185,26 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
169185
}
170186
}
171187

188+
fn visit_stmt(s: @stmt, cx: ctx, v: visit::vt<ctx>) {
189+
alt s.node {
190+
stmt_decl(@{node: decl_local(ls), _}, _) {
191+
shadow_in_current(cx, {|id|
192+
for local in ls {
193+
let found = false;
194+
pat_util::pat_bindings(cx.tcx.def_map, local.node.pat,
195+
{|pid, _a, _b|
196+
if pid == id { found = true; }
197+
});
198+
if found { ret true; }
199+
}
200+
false
201+
});
202+
}
203+
_ {}
204+
}
205+
visit::visit_stmt(s, cx, v);
206+
}
207+
172208
fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
173209
sp: span, id: node_id,
174210
cx: ctx, v: visit::vt<ctx>) {
@@ -177,42 +213,48 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
177213
alt proto {
178214
proto_any | proto_block {
179215
visit_block(func, cx, {||
216+
shadow_in_current(cx, {|id|
217+
for arg in decl.inputs { if arg.id == id { ret true; } }
218+
false
219+
});
180220
visit::visit_fn(fk, decl, body, sp, id, cx, v);
181221
});
182222
}
183223
proto_box | proto_uniq | proto_bare {
184224
alt cx.tcx.freevars.find(id) {
185225
some(vars) {
186226
for v in *vars {
187-
clear_in_current(cx, ast_util::def_id_of_def(v.def).node,
188-
false);
227+
option::may(def_is_owned_local(cx, v.def)) {|nid|
228+
clear_in_current(cx, nid, false);
229+
cx.current += [{def: nid,
230+
uses: cons(close_over(id), @nil)}];
231+
}
189232
}
190233
}
191234
_ {}
192235
}
193-
let old = nil;
194-
cx.blocks <-> old;
236+
let old_cur = [], old_blocks = nil;
237+
cx.blocks <-> old_blocks;
238+
cx.current <-> old_cur;
195239
visit::visit_fn(fk, decl, body, sp, id, cx, v);
196-
cx.blocks <-> old;
240+
cx.blocks <-> old_blocks;
197241
leave_fn(cx);
242+
cx.current <-> old_cur;
198243
}
199244
}
200245
}
201246

202247
fn visit_block(tp: block_type, cx: ctx, visit: fn()) {
203248
let local = @{type: tp, mutable second: false, mutable exits: []};
204249
cx.blocks = cons(local, @cx.blocks);
205-
let start_current = cx.current;
206250
visit();
207251
local.second = true;
208252
local.exits = [];
209-
cx.current = start_current;
210253
visit();
211254
let cx_blocks = cx.blocks;
212255
cx.blocks = tail(cx_blocks);
213-
let branches = if tp == func { local.exits + [cx.current] }
214-
else { local.exits };
215-
cx.current = join_branches(branches);
256+
local.exits += [cx.current];
257+
cx.current = join_branches(local.exits);
216258
}
217259

218260
fn add_block_exit(cx: ctx, tp: block_type) -> bool {
@@ -240,82 +282,92 @@ fn join_branches(branches: [set]) -> set {
240282
let found: set = [], i = 0u, l = vec::len(branches);
241283
for set in branches {
242284
i += 1u;
243-
for {def, exprs} in set {
285+
for {def, uses} in set {
244286
if !vec::any(found, {|v| v.def == def}) {
245-
let j = i, nne = exprs;
287+
let j = i, nne = uses;
246288
while j < l {
247-
for {def: d2, exprs} in branches[j] {
289+
for {def: d2, uses} in branches[j] {
248290
if d2 == def {
249-
list::iter(exprs) {|e|
291+
list::iter(uses) {|e|
250292
if !list::has(nne, e) { nne = cons(e, @nne); }
251293
}
252294
}
253295
}
254296
j += 1u;
255297
}
256-
found += [{def: def, exprs: nne}];
298+
found += [{def: def, uses: nne}];
257299
}
258300
}
259301
}
260302
ret found;
261303
}
262304

263305
fn leave_fn(cx: ctx) {
264-
for {def, exprs} in cx.current {
265-
list::iter(exprs) {|ex_id|
266-
if !cx.last_uses.contains_key(ex_id) {
267-
cx.last_uses.insert(ex_id, true);
306+
for {def, uses} in cx.current {
307+
list::iter(uses) {|use|
308+
let key = alt use {
309+
var_use(pth_id) { path(pth_id) }
310+
close_over(fn_id) { close(fn_id, def) }
311+
};
312+
if !cx.last_uses.contains_key(key) {
313+
cx.last_uses.insert(key, true);
268314
}
269315
}
270316
}
271317
}
272318

319+
fn shadow_in_current(cx: ctx, p: fn(node_id) -> bool) {
320+
let out = [];
321+
cx.current <-> out;
322+
for e in out { if !p(e.def) { cx.current += [e]; } }
323+
}
324+
273325
fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
274-
for {def, exprs} in cx.current {
326+
for {def, uses} in cx.current {
275327
if def == my_def {
276-
list::iter(exprs) {|expr|
277-
if !to || !cx.last_uses.contains_key(expr) {
278-
cx.last_uses.insert(expr, to);
328+
list::iter(uses) {|use|
329+
let key = alt use {
330+
var_use(pth_id) { path(pth_id) }
331+
close_over(fn_id) { close(fn_id, def) }
332+
};
333+
if !to || !cx.last_uses.contains_key(key) {
334+
cx.last_uses.insert(key, to);
279335
}
280336
}
281-
cx.current = vec::filter(copy cx.current,
282-
{|x| x.def != my_def});
337+
cx.current = vec::filter(copy cx.current, {|x| x.def != my_def});
283338
break;
284339
}
285340
}
286341
}
287342

288-
fn clear_def_if_path(cx: ctx, d: def, to: bool)
289-
-> option<node_id> {
343+
fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> {
290344
alt d {
291-
def_local(nid) {
292-
clear_in_current(cx, nid, to);
293-
some(nid)
294-
}
295-
def_arg(nid, m) {
345+
def_local(id) { some(id) }
346+
def_arg(id, m) {
296347
alt ty::resolved_mode(cx.tcx, m) {
297-
by_copy | by_move {
298-
clear_in_current(cx, nid, to);
299-
some(nid)
300-
}
301-
by_ref | by_val | by_mutbl_ref {
302-
none
303-
}
348+
by_copy | by_move { some(id) }
349+
by_ref | by_val | by_mutbl_ref { none }
304350
}
305351
}
306-
_ {
307-
none
352+
def_upvar(_, d, fn_id) {
353+
if is_blockish(ty::ty_fn_proto(ty::node_id_to_type(cx.tcx, fn_id))) {
354+
def_is_owned_local(cx, *d)
355+
} else { none }
308356
}
357+
_ { none }
309358
}
310359
}
311360

312-
fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool)
313-
-> option<node_id> {
361+
fn clear_def_if_local(cx: ctx, d: def, to: bool) {
362+
alt def_is_owned_local(cx, d) {
363+
some(nid) { clear_in_current(cx, nid, to); }
364+
_ {}
365+
}
366+
}
367+
368+
fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool) {
314369
alt ex.node {
315-
expr_path(_) {
316-
ret clear_def_if_path(cx, cx.def_map.get(ex.id), to);
317-
}
370+
expr_path(_) { clear_def_if_local(cx, cx.def_map.get(ex.id), to); }
318371
_ { v.visit_expr(ex, cx, v); }
319372
}
320-
ret option::none;
321373
}

src/comp/middle/resolve.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
import syntax::{ast, ast_util, codemap};
32
import syntax::ast::*;
43
import ast::{ident, fn_ident, def, def_id, node_id};
@@ -512,7 +511,6 @@ fn resolve_names(e: @env, c: @ast::crate) {
512511

513512
// Visit helper functions
514513
fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
515-
516514
// Some magic here. Items with the !resolve_unexported attribute
517515
// cause us to consider every name to be exported when resolving their
518516
// contents. This is used to allow the test runner to run unexported
@@ -978,6 +976,7 @@ fn def_is_ty_arg(d: def) -> bool {
978976

979977
fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
980978
-> option<def> {
979+
981980
fn in_scope(e: env, sp: span, name: ident, s: scope, ns: namespace) ->
982981
option<def> {
983982
alt s {

0 commit comments

Comments
 (0)