diff --git a/src/comp/driver/rustc.rs b/src/comp/driver/rustc.rs index 05fe635b242..518a6251702 100644 --- a/src/comp/driver/rustc.rs +++ b/src/comp/driver/rustc.rs @@ -5,7 +5,8 @@ import metadata::{creader, cstore}; import syntax::parse::{parser}; import syntax::{ast, codemap}; import front::attr; -import middle::{trans, resolve, freevars, kind, ty, typeck, fn_usage}; +import middle::{trans, resolve, freevars, kind, ty, typeck, fn_usage, + last_use}; import syntax::print::{pp, pprust}; import util::{ppaux, filesearch}; import back::link; @@ -138,6 +139,8 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str, bind freevars::annotate_freevars(def_map, crate)); let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, ast_map, freevars); time(time_passes, "typechecking", bind typeck::check_crate(ty_cx, crate)); + let last_uses = time(time_passes, "last use finding", + bind last_use::find_last_uses(crate, def_map, ty_cx)); time(time_passes, "function usage", bind fn_usage::check_crate_fn_usage(ty_cx, crate)); time(time_passes, "alt checking", @@ -150,7 +153,8 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: str, let copy_map = time(time_passes, "alias checking", bind middle::alias::check_crate(ty_cx, crate)); - time(time_passes, "kind checking", bind kind::check_crate(ty_cx, crate)); + time(time_passes, "kind checking", + bind kind::check_crate(ty_cx, last_uses, crate)); time(time_passes, "const checking", bind middle::check_const::check_crate(ty_cx, crate)); if sess.get_opts().no_trans { ret; } diff --git a/src/comp/middle/kind.rs b/src/comp/middle/kind.rs index 6437e00f99b..3618a17ff2c 100644 --- a/src/comp/middle/kind.rs +++ b/src/comp/middle/kind.rs @@ -15,11 +15,14 @@ type rval_map = std::map::hashmap; type ctx = {tcx: ty::ctxt, rval_map: rval_map, + last_uses: last_use::last_uses, mutable ret_by_ref: bool}; -fn check_crate(tcx: ty::ctxt, crate: @crate) -> rval_map { +fn check_crate(tcx: ty::ctxt, last_uses: last_use::last_uses, + crate: @crate) -> rval_map { let ctx = {tcx: tcx, rval_map: std::map::new_int_hash(), + last_uses: last_uses, mutable ret_by_ref: false}; let visit = visit::mk_vt(@{ visit_expr: check_expr, @@ -118,7 +121,7 @@ fn maybe_copy(cx: ctx, ex: @expr) { } fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) { - if ty::expr_is_lval(cx.tcx, ex) { + if ty::expr_is_lval(cx.tcx, ex) && !cx.last_uses.contains_key(ex.id) { let ty = ty::expr_ty(cx.tcx, ex); check_copy(cx, ty, ex.span); // FIXME turn this on again once vector types are no longer unique. diff --git a/src/comp/middle/last_use.rs b/src/comp/middle/last_use.rs new file mode 100644 index 00000000000..5d8aa2493d2 --- /dev/null +++ b/src/comp/middle/last_use.rs @@ -0,0 +1,219 @@ +import syntax::{visit, ast_util}; +import syntax::ast::*; +import std::list::{list, nil, cons, tail}; +import std::{vec, list, option}; + +// Marks expr_paths that are last uses. +type last_uses = std::map::hashmap; + +tag seen { unset; seen(node_id); } +tag block_type { func; loop; } + +type set = [{def: node_id, exprs: list}]; +type bl = @{type: block_type, mutable second: bool, mutable exits: [set]}; + +type ctx = {last_uses: std::map::hashmap, + def_map: resolve::def_map, + tcx: ty::ctxt, + // The current set of local last uses + mutable current: set, + mutable blocks: list}; + +fn find_last_uses(c: @crate, def_map: resolve::def_map, tcx: ty::ctxt) + -> last_uses { + let v = visit::mk_vt(@{visit_expr: visit_expr, + visit_fn: visit_fn + with *visit::default_visitor()}); + let cx = {last_uses: std::map::new_int_hash(), + def_map: def_map, + tcx: tcx, + mutable current: [], + mutable blocks: nil}; + visit::visit_crate(*c, cx, v); + let mini_table = std::map::new_int_hash(); + cx.last_uses.items {|key, val| if val { mini_table.insert(key, ()); }} + ret mini_table; +} + +fn visit_expr(ex: @expr, cx: ctx, v: visit::vt) { + alt ex.node { + expr_ret(oexpr) { + visit::visit_expr_opt(oexpr, cx, v); + if !add_block_exit(cx, func) { leave_fn(cx); } + } + expr_fail(oexpr) { + visit::visit_expr_opt(oexpr, cx, v); + leave_fn(cx); + } + expr_break. { add_block_exit(cx, loop); } + expr_while(_, _) | expr_do_while(_, _) { + visit_block(loop, cx) {|| visit::visit_expr(ex, cx, v);} + } + expr_for(_, coll, blk) { + v.visit_expr(coll, cx, v); + visit_block(loop, cx) {|| visit::visit_block(blk, cx, v);} + } + expr_ternary(_, _, _) { + v.visit_expr(ast_util::ternary_to_if(ex), cx, v); + } + expr_alt(input, arms) { + v.visit_expr(input, cx, v); + let before = cx.current, sets = []; + for arm in arms { + cx.current = before; + v.visit_arm(arm, cx, v); + sets += [cx.current]; + } + cx.current = join_branches(sets); + } + expr_if(cond, then, els) { + v.visit_expr(cond, cx, v); + let cur = cx.current; + visit::visit_block(then, cx, v); + cx.current <-> cur; + visit::visit_expr_opt(els, cx, v); + cx.current = join_branches([cur, cx.current]); + } + expr_path(_) { + alt clear_if_path(cx, ex, v, false) { + option::some(my_def) { + cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}]; + } + _ {} + } + } + expr_swap(lhs, rhs) { + clear_if_path(cx, lhs, v, false); + clear_if_path(cx, rhs, v, false); + } + expr_move(dest, src) | expr_assign(dest, src) { + v.visit_expr(src, cx, v); + clear_if_path(cx, dest, v, true); + } + expr_assign_op(_, dest, src) { + v.visit_expr(src, cx, v); + v.visit_expr(dest, cx, v); + clear_if_path(cx, dest, v, true); + } + expr_call(f, args, _) { + v.visit_expr(f, cx, v); + let i = 0u; + for arg_t in ty::ty_fn_args(cx.tcx, ty::expr_ty(cx.tcx, f)) { + alt arg_t.mode { + by_mut_ref. { clear_if_path(cx, args[i], v, false); } + _ { v.visit_expr(args[i], cx, v); } + } + i += 1u; + } + } + _ { visit::visit_expr(ex, cx, v); } + } +} + +fn visit_fn(f: _fn, tps: [ty_param], sp: syntax::codemap::span, + ident: fn_ident, id: node_id, cx: ctx, v: visit::vt) { + if f.proto == proto_block { + visit_block(func, cx, {|| + visit::visit_fn(f, tps, sp, ident, id, cx, v); + }); + } else { + let old = nil; + cx.blocks <-> old; + visit::visit_fn(f, tps, sp, ident, id, cx, v); + cx.blocks <-> old; + leave_fn(cx); + } +} + +fn visit_block(tp: block_type, cx: ctx, visit: block()) { + let local = @{type: tp, mutable second: false, mutable exits: []}; + cx.blocks = cons(local, @cx.blocks); + visit(); + local.second = true; + visit(); + cx.blocks = tail(cx.blocks); + cx.current = join_branches(local.exits); +} + +fn add_block_exit(cx: ctx, tp: block_type) -> bool { + let cur = cx.blocks; + while cur != nil { + alt cur { + cons(b, tail) { + if (b.type == tp) { + if !b.second { b.exits += [cx.current]; } + ret true; + } + cur = *tail; + } + } + } + ret false; +} + +fn join_branches(branches: [set]) -> set { + let found: set = [], i = 0u, l = vec::len(branches); + for set in branches { + i += 1u; + for {def, exprs} in set { + if !vec::any({|v| v.def == def}, found) { + let j = i, ne = exprs; + while j < l { + for {def: d2, exprs} in branches[j] { + if d2 == def { + list::iter(exprs) {|e| + if !list::has(ne, e) { ne = cons(e, @ne); } + } + } + } + j += 1u; + } + found += [{def: def, exprs: ne}]; + } + } + } + ret found; +} + +fn leave_fn(cx: ctx) { + for {def, exprs} in cx.current { + list::iter(exprs) {|ex_id| + if !cx.last_uses.contains_key(ex_id) { + cx.last_uses.insert(ex_id, true); + } + } + } +} + +fn clear_in_current(cx: ctx, my_def: node_id, to: bool) { + for {def, exprs} in cx.current { + if def == my_def { + list::iter(exprs) {|expr| + if !to || !cx.last_uses.contains_key(expr) { + cx.last_uses.insert(expr, to); + } + } + cx.current = vec::filter({|x| x.def != my_def}, + copy cx.current); + break; + } + } +} + +fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt, to: bool) + -> option::t { + alt ex.node { + expr_path(_) { + alt cx.def_map.get(ex.id) { + def_local(def_id, let_copy.) | def_arg(def_id, by_copy.) | + def_arg(def_id, by_move.) { + clear_in_current(cx, def_id.node, to); + ret option::some(def_id.node); + } + _ {} + } + } + _ { v.visit_expr(ex, cx, v); } + } + ret option::none; +} diff --git a/src/comp/rustc.rc b/src/comp/rustc.rc index 9215cbcb9c1..af6369b5bce 100644 --- a/src/comp/rustc.rc +++ b/src/comp/rustc.rc @@ -30,6 +30,7 @@ mod middle { mod check_const; mod mut; mod alias; + mod last_use; mod kind; mod freevars; mod shape; diff --git a/src/lib/list.rs b/src/lib/list.rs index 60e8e4821fe..300a5c72a67 100644 --- a/src/lib/list.rs +++ b/src/lib/list.rs @@ -134,6 +134,23 @@ fn append(l: list, m: list) -> list { } } +/* +Function: iter + +Iterate over a list +*/ +fn iter(l: list, f: block(T)) { + let cur = l; + while cur != nil { + alt cur { + cons(hd, tl) { + f(hd); + cur = *tl; + } + } + } +} + // Local Variables: // mode: rust; // fill-column: 78; diff --git a/src/lib/vec.rs b/src/lib/vec.rs index 35e416c121d..2ae7f2247b1 100644 --- a/src/lib/vec.rs +++ b/src/lib/vec.rs @@ -703,8 +703,8 @@ Iterates over vector `v` and, for each element, calls function `f` with the element's value and index. */ fn iter2(v: [const T], f: block(uint, T)) { - let i = 0u; - for x in v { f(i, x); i += 1u; } + let i = 0u, l = len(v); + while i < l { f(i, v[i]); i += 1u; } } /*