Recognize last uses for copied closed-over variables
And clean up and fix some bad things in last_use.rs. Closes #1894
This commit is contained in:
parent
cccb0fbf72
commit
0da1aca498
@ -13,13 +13,10 @@ import std::serialization::deserializer;
|
||||
import std::serialization::serializer_helpers;
|
||||
import std::serialization::deserializer_helpers;
|
||||
import middle::trans::common::maps;
|
||||
import middle::ty;
|
||||
import middle::typeck;
|
||||
import middle::{ty, typeck, last_use, ast_map};
|
||||
import middle::typeck::method_origin;
|
||||
import middle::typeck::dict_res;
|
||||
import middle::typeck::dict_origin;
|
||||
import middle::ast_map;
|
||||
import driver::session;
|
||||
import driver::session::session;
|
||||
import middle::freevars::freevar_entry;
|
||||
import c = common;
|
||||
@ -248,7 +245,7 @@ fn decode_id_range(par_doc: ebml::doc) -> id_range {
|
||||
}
|
||||
}
|
||||
|
||||
fn reserve_id_range(sess: session::session,
|
||||
fn reserve_id_range(sess: session,
|
||||
from_id_range: id_range) -> id_range {
|
||||
// Handle the case of an empty range:
|
||||
if empty(from_id_range) { ret from_id_range; }
|
||||
@ -781,7 +778,7 @@ fn decode_side_tables(xcx: extended_decode_ctxt,
|
||||
} else if tag == (c::tag_table_copy as uint) {
|
||||
dcx.maps.copy_map.insert(id, ());
|
||||
} else if tag == (c::tag_table_last_use as uint) {
|
||||
dcx.maps.last_uses.insert(id, ());
|
||||
dcx.maps.last_uses.insert(id, last_use::is_last_use);
|
||||
} else {
|
||||
let val_doc = entry_doc[c::tag_table_val];
|
||||
let val_dsr = serialization::mk_ebml_deserializer(val_doc);
|
||||
|
@ -24,15 +24,26 @@ import std::list;
|
||||
// (by `break` or conditionals), and for handling loops.
|
||||
|
||||
// Marks expr_paths that are last uses.
|
||||
type last_uses = std::map::hashmap<node_id, ()>;
|
||||
enum is_last_use {
|
||||
is_last_use,
|
||||
has_last_use,
|
||||
closes_over([node_id]),
|
||||
}
|
||||
type last_uses = std::map::hashmap<node_id, is_last_use>;
|
||||
|
||||
enum seen { unset, seen(node_id), }
|
||||
enum block_type { func, loop, }
|
||||
|
||||
type set = [{def: node_id, exprs: list<node_id>}];
|
||||
enum use { var_use(node_id), close_over(node_id), }
|
||||
type set = [{def: node_id, uses: list<use>}];
|
||||
type bl = @{type: block_type, mutable second: bool, mutable exits: [set]};
|
||||
|
||||
type ctx = {last_uses: std::map::hashmap<node_id, bool>,
|
||||
enum use_id { path(node_id), close(node_id, node_id) }
|
||||
fn hash_use_id(id: use_id) -> uint {
|
||||
(alt id { path(i) { i } close(i, j) { (i << 10) + j } }) as uint
|
||||
}
|
||||
|
||||
type ctx = {last_uses: std::map::hashmap<use_id, bool>,
|
||||
def_map: resolve::def_map,
|
||||
ref_map: alias::ref_map,
|
||||
tcx: ty::ctxt,
|
||||
@ -43,9 +54,10 @@ type ctx = {last_uses: std::map::hashmap<node_id, bool>,
|
||||
fn find_last_uses(c: @crate, def_map: resolve::def_map,
|
||||
ref_map: alias::ref_map, tcx: ty::ctxt) -> last_uses {
|
||||
let v = visit::mk_vt(@{visit_expr: visit_expr,
|
||||
visit_stmt: visit_stmt,
|
||||
visit_fn: visit_fn
|
||||
with *visit::default_visitor()});
|
||||
let cx = {last_uses: std::map::new_int_hash(),
|
||||
let cx = {last_uses: std::map::mk_hashmap(hash_use_id, {|a, b| a == b}),
|
||||
def_map: def_map,
|
||||
ref_map: ref_map,
|
||||
tcx: tcx,
|
||||
@ -54,22 +66,26 @@ fn find_last_uses(c: @crate, def_map: resolve::def_map,
|
||||
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, ());
|
||||
let def_node = ast_util::def_id_of_def(def_map.get(key)).node;
|
||||
mini_table.insert(def_node, ());
|
||||
if !val { ret; }
|
||||
alt key {
|
||||
path(id) {
|
||||
mini_table.insert(id, is_last_use);
|
||||
let def_node = ast_util::def_id_of_def(def_map.get(id)).node;
|
||||
mini_table.insert(def_node, has_last_use);
|
||||
}
|
||||
close(fn_id, local_id) {
|
||||
mini_table.insert(local_id, has_last_use);
|
||||
let known = alt check mini_table.find(fn_id) {
|
||||
some(closes_over(ids)) { ids }
|
||||
none { [] }
|
||||
};
|
||||
mini_table.insert(fn_id, closes_over(known + [local_id]));
|
||||
}
|
||||
}
|
||||
}
|
||||
ret mini_table;
|
||||
}
|
||||
|
||||
fn ex_is_blockish(cx: ctx, id: node_id) -> bool {
|
||||
alt ty::get(ty::node_id_to_type(cx.tcx, id)).struct {
|
||||
ty::ty_fn({proto: p, _}) if is_blockish(p) { true }
|
||||
_ { false }
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
alt ex.node {
|
||||
expr_ret(oexpr) {
|
||||
@ -107,15 +123,17 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
cx.current = join_branches([cur, cx.current]);
|
||||
}
|
||||
expr_path(_) {
|
||||
let my_def = ast_util::def_id_of_def(cx.def_map.get(ex.id)).node;
|
||||
alt cx.ref_map.find(my_def) {
|
||||
option::some(root_id) { clear_in_current(cx, root_id, false); }
|
||||
let my_def = cx.def_map.get(ex.id);
|
||||
let my_def_id = ast_util::def_id_of_def(my_def).node;
|
||||
alt cx.ref_map.find(my_def_id) {
|
||||
option::some(root_id) {
|
||||
clear_in_current(cx, root_id, false);
|
||||
}
|
||||
_ {
|
||||
alt clear_if_path(cx, ex, v, false) {
|
||||
option::some(my_def) {
|
||||
cx.current += [{def: my_def, exprs: cons(ex.id, @nil)}];
|
||||
}
|
||||
_ {}
|
||||
option::may(def_is_owned_local(cx, my_def)) {|nid|
|
||||
clear_in_current(cx, nid, false);
|
||||
cx.current += [{def: nid,
|
||||
uses: cons(var_use(ex.id), @nil)}];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -138,7 +156,7 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
// then they are ignored, otherwise they will show up
|
||||
// as freevars in the body.
|
||||
vec::iter(cap_clause.moves) {|ci|
|
||||
clear_def_if_path(cx, cx.def_map.get(ci.id), true);
|
||||
clear_def_if_local(cx, cx.def_map.get(ci.id), false);
|
||||
}
|
||||
visit::visit_expr(ex, cx, v);
|
||||
}
|
||||
@ -148,10 +166,8 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
let arg_ts = ty::ty_fn_args(ty::expr_ty(cx.tcx, f));
|
||||
for arg in args {
|
||||
alt arg.node {
|
||||
expr_fn(p, _, _, _) if is_blockish(p) {
|
||||
fns += [arg];
|
||||
}
|
||||
expr_fn_block(_, _) if ex_is_blockish(cx, arg.id) {
|
||||
expr_fn(_, _, _, _) | expr_fn_block(_, _)
|
||||
if is_blockish(ty::ty_fn_proto(arg_ts[i].ty)) {
|
||||
fns += [arg];
|
||||
}
|
||||
_ {
|
||||
@ -169,6 +185,26 @@ fn visit_expr(ex: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_stmt(s: @stmt, cx: ctx, v: visit::vt<ctx>) {
|
||||
alt s.node {
|
||||
stmt_decl(@{node: decl_local(ls), _}, _) {
|
||||
shadow_in_current(cx, {|id|
|
||||
for local in ls {
|
||||
let found = false;
|
||||
pat_util::pat_bindings(cx.tcx.def_map, local.node.pat,
|
||||
{|pid, _a, _b|
|
||||
if pid == id { found = true; }
|
||||
});
|
||||
if found { ret true; }
|
||||
}
|
||||
false
|
||||
});
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
visit::visit_stmt(s, cx, v);
|
||||
}
|
||||
|
||||
fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
||||
sp: span, id: node_id,
|
||||
cx: ctx, v: visit::vt<ctx>) {
|
||||
@ -177,6 +213,10 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
||||
alt proto {
|
||||
proto_any | proto_block {
|
||||
visit_block(func, cx, {||
|
||||
shadow_in_current(cx, {|id|
|
||||
for arg in decl.inputs { if arg.id == id { ret true; } }
|
||||
false
|
||||
});
|
||||
visit::visit_fn(fk, decl, body, sp, id, cx, v);
|
||||
});
|
||||
}
|
||||
@ -184,17 +224,22 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
||||
alt cx.tcx.freevars.find(id) {
|
||||
some(vars) {
|
||||
for v in *vars {
|
||||
clear_in_current(cx, ast_util::def_id_of_def(v.def).node,
|
||||
false);
|
||||
option::may(def_is_owned_local(cx, v.def)) {|nid|
|
||||
clear_in_current(cx, nid, false);
|
||||
cx.current += [{def: nid,
|
||||
uses: cons(close_over(id), @nil)}];
|
||||
}
|
||||
}
|
||||
}
|
||||
_ {}
|
||||
}
|
||||
let old = nil;
|
||||
cx.blocks <-> old;
|
||||
let old_cur = [], old_blocks = nil;
|
||||
cx.blocks <-> old_blocks;
|
||||
cx.current <-> old_cur;
|
||||
visit::visit_fn(fk, decl, body, sp, id, cx, v);
|
||||
cx.blocks <-> old;
|
||||
cx.blocks <-> old_blocks;
|
||||
leave_fn(cx);
|
||||
cx.current <-> old_cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -202,17 +247,14 @@ fn visit_fn(fk: visit::fn_kind, decl: fn_decl, body: blk,
|
||||
fn visit_block(tp: block_type, cx: ctx, visit: fn()) {
|
||||
let local = @{type: tp, mutable second: false, mutable exits: []};
|
||||
cx.blocks = cons(local, @cx.blocks);
|
||||
let start_current = cx.current;
|
||||
visit();
|
||||
local.second = true;
|
||||
local.exits = [];
|
||||
cx.current = start_current;
|
||||
visit();
|
||||
let cx_blocks = cx.blocks;
|
||||
cx.blocks = tail(cx_blocks);
|
||||
let branches = if tp == func { local.exits + [cx.current] }
|
||||
else { local.exits };
|
||||
cx.current = join_branches(branches);
|
||||
local.exits += [cx.current];
|
||||
cx.current = join_branches(local.exits);
|
||||
}
|
||||
|
||||
fn add_block_exit(cx: ctx, tp: block_type) -> bool {
|
||||
@ -240,20 +282,20 @@ 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 {
|
||||
for {def, uses} in set {
|
||||
if !vec::any(found, {|v| v.def == def}) {
|
||||
let j = i, nne = exprs;
|
||||
let j = i, nne = uses;
|
||||
while j < l {
|
||||
for {def: d2, exprs} in branches[j] {
|
||||
for {def: d2, uses} in branches[j] {
|
||||
if d2 == def {
|
||||
list::iter(exprs) {|e|
|
||||
list::iter(uses) {|e|
|
||||
if !list::has(nne, e) { nne = cons(e, @nne); }
|
||||
}
|
||||
}
|
||||
}
|
||||
j += 1u;
|
||||
}
|
||||
found += [{def: def, exprs: nne}];
|
||||
found += [{def: def, uses: nne}];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,61 +303,71 @@ fn join_branches(branches: [set]) -> set {
|
||||
}
|
||||
|
||||
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);
|
||||
for {def, uses} in cx.current {
|
||||
list::iter(uses) {|use|
|
||||
let key = alt use {
|
||||
var_use(pth_id) { path(pth_id) }
|
||||
close_over(fn_id) { close(fn_id, def) }
|
||||
};
|
||||
if !cx.last_uses.contains_key(key) {
|
||||
cx.last_uses.insert(key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn shadow_in_current(cx: ctx, p: fn(node_id) -> bool) {
|
||||
let out = [];
|
||||
cx.current <-> out;
|
||||
for e in out { if !p(e.def) { cx.current += [e]; } }
|
||||
}
|
||||
|
||||
fn clear_in_current(cx: ctx, my_def: node_id, to: bool) {
|
||||
for {def, exprs} in cx.current {
|
||||
for {def, uses} 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);
|
||||
list::iter(uses) {|use|
|
||||
let key = alt use {
|
||||
var_use(pth_id) { path(pth_id) }
|
||||
close_over(fn_id) { close(fn_id, def) }
|
||||
};
|
||||
if !to || !cx.last_uses.contains_key(key) {
|
||||
cx.last_uses.insert(key, to);
|
||||
}
|
||||
}
|
||||
cx.current = vec::filter(copy cx.current,
|
||||
{|x| x.def != my_def});
|
||||
cx.current = vec::filter(copy cx.current, {|x| x.def != my_def});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_def_if_path(cx: ctx, d: def, to: bool)
|
||||
-> option<node_id> {
|
||||
fn def_is_owned_local(cx: ctx, d: def) -> option<node_id> {
|
||||
alt d {
|
||||
def_local(nid) {
|
||||
clear_in_current(cx, nid, to);
|
||||
some(nid)
|
||||
}
|
||||
def_arg(nid, m) {
|
||||
def_local(id) { some(id) }
|
||||
def_arg(id, m) {
|
||||
alt ty::resolved_mode(cx.tcx, m) {
|
||||
by_copy | by_move {
|
||||
clear_in_current(cx, nid, to);
|
||||
some(nid)
|
||||
}
|
||||
by_ref | by_val | by_mutbl_ref {
|
||||
none
|
||||
}
|
||||
by_copy | by_move { some(id) }
|
||||
by_ref | by_val | by_mutbl_ref { none }
|
||||
}
|
||||
}
|
||||
_ {
|
||||
none
|
||||
def_upvar(_, d, fn_id) {
|
||||
if is_blockish(ty::ty_fn_proto(ty::node_id_to_type(cx.tcx, fn_id))) {
|
||||
def_is_owned_local(cx, *d)
|
||||
} else { none }
|
||||
}
|
||||
_ { none }
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool)
|
||||
-> option<node_id> {
|
||||
fn clear_def_if_local(cx: ctx, d: def, to: bool) {
|
||||
alt def_is_owned_local(cx, d) {
|
||||
some(nid) { clear_in_current(cx, nid, to); }
|
||||
_ {}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_if_path(cx: ctx, ex: @expr, v: visit::vt<ctx>, to: bool) {
|
||||
alt ex.node {
|
||||
expr_path(_) {
|
||||
ret clear_def_if_path(cx, cx.def_map.get(ex.id), to);
|
||||
}
|
||||
expr_path(_) { clear_def_if_local(cx, cx.def_map.get(ex.id), to); }
|
||||
_ { v.visit_expr(ex, cx, v); }
|
||||
}
|
||||
ret option::none;
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
import syntax::{ast, ast_util, codemap};
|
||||
import syntax::ast::*;
|
||||
import ast::{ident, fn_ident, def, def_id, node_id};
|
||||
@ -512,7 +511,6 @@ fn resolve_names(e: @env, c: @ast::crate) {
|
||||
|
||||
// Visit helper functions
|
||||
fn visit_item_with_scope(e: @env, i: @ast::item, sc: scopes, v: vt<scopes>) {
|
||||
|
||||
// Some magic here. Items with the !resolve_unexported attribute
|
||||
// cause us to consider every name to be exported when resolving their
|
||||
// contents. This is used to allow the test runner to run unexported
|
||||
@ -978,6 +976,7 @@ fn def_is_ty_arg(d: def) -> bool {
|
||||
|
||||
fn lookup_in_scope(e: env, sc: scopes, sp: span, name: ident, ns: namespace)
|
||||
-> option<def> {
|
||||
|
||||
fn in_scope(e: env, sp: span, name: ident, s: scope, ns: namespace) ->
|
||||
option<def> {
|
||||
alt s {
|
||||
|
@ -20,7 +20,6 @@ import std::map::{new_int_hash, new_str_hash};
|
||||
import driver::session;
|
||||
import session::session;
|
||||
import front::attr;
|
||||
import middle::freevars::*;
|
||||
import middle::inline::inline_map;
|
||||
import back::{link, abi, upcall};
|
||||
import syntax::{ast, ast_util, codemap};
|
||||
|
@ -8,7 +8,6 @@ import build::*;
|
||||
import base::*;
|
||||
import type_of::*;
|
||||
import type_of::type_of; // Issue #1873
|
||||
import middle::freevars::{get_freevars, freevar_info};
|
||||
import back::abi;
|
||||
import syntax::codemap::span;
|
||||
import syntax::print::pprust::expr_to_str;
|
||||
@ -353,12 +352,11 @@ fn store_environment(
|
||||
// collects the upvars and packages them up for store_environment.
|
||||
fn build_closure(bcx0: block,
|
||||
cap_vars: [capture::capture_var],
|
||||
ck: ty::closure_kind)
|
||||
-> closure_result {
|
||||
ck: ty::closure_kind,
|
||||
id: ast::node_id) -> closure_result {
|
||||
// If we need to, package up the iterator body to call
|
||||
let env_vals = [];
|
||||
let bcx = bcx0;
|
||||
let tcx = bcx.tcx();
|
||||
let bcx = bcx0, ccx = bcx.ccx(), tcx = ccx.tcx;
|
||||
|
||||
// Package up the captured upvars
|
||||
vec::iter(cap_vars) { |cap_var|
|
||||
@ -373,7 +371,12 @@ fn build_closure(bcx0: block,
|
||||
env_vals += [env_ref(lv.val, ty, lv.kind)];
|
||||
}
|
||||
capture::cap_copy {
|
||||
env_vals += [env_copy(lv.val, ty, lv.kind)];
|
||||
let mv = alt check ccx.maps.last_uses.find(id) {
|
||||
none { false }
|
||||
some(last_use::closes_over(vars)) { vec::contains(vars, nid) }
|
||||
};
|
||||
if mv { env_vals += [env_move(lv.val, ty, lv.kind)]; }
|
||||
else { env_vals += [env_copy(lv.val, ty, lv.kind)]; }
|
||||
}
|
||||
capture::cap_move {
|
||||
env_vals += [env_move(lv.val, ty, lv.kind)];
|
||||
@ -465,7 +468,7 @@ fn trans_expr_fn(bcx: block,
|
||||
let trans_closure_env = fn@(ck: ty::closure_kind) -> ValueRef {
|
||||
let cap_vars = capture::compute_capture_vars(
|
||||
ccx.tcx, id, proto, cap_clause);
|
||||
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck);
|
||||
let {llbox, cdata_ty, bcx} = build_closure(bcx, cap_vars, ck, id);
|
||||
trans_closure(ccx, sub_path, decl, body, llfn, no_self, [],
|
||||
bcx.fcx.param_substs, id, {|fcx|
|
||||
load_environment(bcx, fcx, cdata_ty, cap_vars, ck);
|
||||
|
@ -1303,33 +1303,33 @@ fn gather_locals(ccx: @crate_ctxt,
|
||||
body: ast::blk,
|
||||
id: ast::node_id,
|
||||
old_fcx: option<@fn_ctxt>) -> gather_result {
|
||||
let {vb: vb, locals: locals, nvi: nvi} =
|
||||
alt old_fcx {
|
||||
none {
|
||||
{vb: ty::unify::mk_var_bindings(),
|
||||
locals: new_int_hash::<int>(),
|
||||
nvi: @mutable 0}
|
||||
}
|
||||
some(fcx) {
|
||||
{vb: fcx.var_bindings,
|
||||
locals: fcx.locals,
|
||||
nvi: fcx.next_var_id}
|
||||
}
|
||||
};
|
||||
let {vb: vb, locals: locals, nvi: nvi} = alt old_fcx {
|
||||
none {
|
||||
{vb: ty::unify::mk_var_bindings(),
|
||||
locals: new_int_hash::<int>(),
|
||||
nvi: @mutable 0}
|
||||
}
|
||||
some(fcx) {
|
||||
{vb: fcx.var_bindings,
|
||||
locals: fcx.locals,
|
||||
nvi: fcx.next_var_id}
|
||||
}
|
||||
};
|
||||
let tcx = ccx.tcx;
|
||||
|
||||
let next_var_id = fn@() -> int { let rv = *nvi; *nvi += 1; ret rv; };
|
||||
|
||||
let assign = fn@(nid: ast::node_id, ty_opt: option<ty::t>) {
|
||||
let var_id = next_var_id();
|
||||
locals.insert(nid, var_id);
|
||||
alt ty_opt {
|
||||
none {/* nothing to do */ }
|
||||
some(typ) {
|
||||
ty::unify::unify(ty::mk_var(tcx, var_id), typ,
|
||||
ty::unify::in_bindings(vb), tcx);
|
||||
}
|
||||
}
|
||||
};
|
||||
let var_id = next_var_id();
|
||||
locals.insert(nid, var_id);
|
||||
alt ty_opt {
|
||||
none {/* nothing to do */ }
|
||||
some(typ) {
|
||||
ty::unify::unify(ty::mk_var(tcx, var_id), typ,
|
||||
ty::unify::in_bindings(vb), tcx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Add formal parameters.
|
||||
let args = ty::ty_fn_args(ty::node_id_to_type(ccx.tcx, id));
|
||||
@ -1341,10 +1341,10 @@ fn gather_locals(ccx: @crate_ctxt,
|
||||
|
||||
// Add explicitly-declared locals.
|
||||
let visit_local = fn@(local: @ast::local, &&e: (), v: visit::vt<()>) {
|
||||
let local_ty = ast_ty_to_ty_crate_infer(ccx, local.node.ty);
|
||||
assign(local.node.id, local_ty);
|
||||
visit::visit_local(local, e, v);
|
||||
};
|
||||
let local_ty = ast_ty_to_ty_crate_infer(ccx, local.node.ty);
|
||||
assign(local.node.id, local_ty);
|
||||
visit::visit_local(local, e, v);
|
||||
};
|
||||
|
||||
// Add pattern bindings.
|
||||
let visit_pat = fn@(p: @ast::pat, &&e: (), v: visit::vt<()>) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Check usage and precedence of block arguments in expressions:
|
||||
// xfail-test
|
||||
fn main() {
|
||||
let v = [-1f, 0f, 1f, 2f, 3f];
|
||||
|
||||
|
@ -15,6 +15,8 @@ fn main() {
|
||||
let y_in_child = ptr::addr_of(*y) as uint;
|
||||
comm::send(ch, y_in_child);
|
||||
});
|
||||
// Ensure last-use analysis doesn't move y to child.
|
||||
let _q = y;
|
||||
|
||||
let x_in_child = comm::recv(p);
|
||||
assert x_in_parent == x_in_child;
|
||||
|
Loading…
x
Reference in New Issue
Block a user