Make resolve recognize upvars

Upvars are now marked with def_upvar throughout, not just when going
through freevars::lookup_def. This makes things less error-prone. One
thing to watch out for is that def_upvar is used in `for each` bodies
too, when they refer to a local outside the body.
This commit is contained in:
Marijn Haverbeke 2011-09-01 14:35:00 +02:00
parent 2d1dec78e7
commit 6ba4eacddf
16 changed files with 223 additions and 257 deletions

View File

@ -155,7 +155,7 @@ fn compile_input(sess: session::session, cfg: ast::crate_cfg, input: &istr,
bind resolve::resolve_crate(sess, ast_map, crate)); bind resolve::resolve_crate(sess, ast_map, crate));
let freevars = let freevars =
time(time_passes, ~"freevar finding", time(time_passes, ~"freevar finding",
bind freevars::annotate_freevars(sess, def_map, crate)); bind freevars::annotate_freevars(def_map, crate));
let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, ast_map, freevars); let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, ast_map, freevars);
time(time_passes, ~"typechecking", time(time_passes, ~"typechecking",
bind typeck::check_crate(ty_cx, crate)); bind typeck::check_crate(ty_cx, crate));
@ -240,7 +240,7 @@ fn pretty_print_input(sess: session::session, cfg: ast::crate_cfg,
let amap = middle::ast_map::map_crate(*crate); let amap = middle::ast_map::map_crate(*crate);
let {def_map: def_map, ext_map: ext_map} = let {def_map: def_map, ext_map: ext_map} =
resolve::resolve_crate(sess, amap, crate); resolve::resolve_crate(sess, amap, crate);
let freevars = freevars::annotate_freevars(sess, def_map, crate); let freevars = freevars::annotate_freevars(def_map, crate);
let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, amap, freevars); let ty_cx = ty::mk_ctxt(sess, def_map, ext_map, amap, freevars);
typeck::check_crate(ty_cx, crate); typeck::check_crate(ty_cx, crate);
ann = {pre: ann_paren_for_expr, post: bind ann_typed_post(ty_cx, _)}; ann = {pre: ann_paren_for_expr, post: bind ann_typed_post(ty_cx, _)};

View File

@ -51,7 +51,7 @@ fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) {
let cx = @{tcx: tcx, let cx = @{tcx: tcx,
local_map: std::map::new_int_hash(), local_map: std::map::new_int_hash(),
mutable next_local: 0u}; mutable next_local: 0u};
let v = @{visit_fn: bind visit_fn(cx, _, _, _, _, _, _, _), let v = @{visit_fn: visit_fn,
visit_expr: bind visit_expr(cx, _, _, _), visit_expr: bind visit_expr(cx, _, _, _),
visit_decl: bind visit_decl(cx, _, _, _) visit_decl: bind visit_decl(cx, _, _, _)
with *visit::default_visitor::<scope>()}; with *visit::default_visitor::<scope>()};
@ -59,39 +59,15 @@ fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) {
tcx.sess.abort_if_errors(); tcx.sess.abort_if_errors();
} }
fn visit_fn(cx: &@ctx, f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span, fn visit_fn(f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span,
_name: &fn_ident, id: ast::node_id, sc: &scope, v: &vt<scope>) { _name: &fn_ident, _id: ast::node_id, sc: &scope, v: &vt<scope>) {
visit::visit_fn_decl(f.decl, sc, v); visit::visit_fn_decl(f.decl, sc, v);
let scope = let scope = alt f.proto {
alt f.proto { // Blocks need to obey any restrictions from the enclosing scope.
ast::proto_block. | ast::proto_closure. { sc }
// Blocks need to obey any restrictions from the enclosing scope. // Non capturing functions start out fresh.
ast::proto_block. { _ { @[] }
sc };
}
// Closures need to prohibit writing to any of the upvars.
// This doesn't seem like a particularly clean way to do this.
ast::proto_closure. {
let dnums = [];
for each nid in freevars::get_freevar_defs(cx.tcx, id).keys() {
dnums += [nid];
};
// I'm not sure if there is anything sensical to put here
@[@{root_var: none,
local_id: cx.next_local,
bindings: dnums,
unsafe_ty: none,
depends_on: [],
mutable ok: valid}]
}
// Non capturing functions start out fresh.
_ {
@[]
}
};
v.visit_block(f.body, scope, v); v.visit_block(f.body, scope, v);
} }
@ -489,7 +465,8 @@ fn ty_can_unsafely_include(cx: &ctx, needle: ty::t, haystack: ty::t,
fn def_is_local(d: &ast::def, objfields_count: bool) -> bool { fn def_is_local(d: &ast::def, objfields_count: bool) -> bool {
ret alt d { ret alt d {
ast::def_local(_) | ast::def_arg(_, _) | ast::def_binding(_) { true } ast::def_local(_) | ast::def_arg(_, _) | ast::def_binding(_) |
ast::def_upvar(_, _, _) { true }
ast::def_obj_field(_, _) { objfields_count } ast::def_obj_field(_, _) { objfields_count }
_ { false } _ { false }
}; };

View File

@ -15,23 +15,13 @@ import middle::resolve;
import syntax::codemap::span; import syntax::codemap::span;
export annotate_freevars; export annotate_freevars;
export freevar_set;
export freevar_map; export freevar_map;
export get_freevar_info;
export get_freevars; export get_freevars;
export get_freevar_defs;
export has_freevars; export has_freevars;
export is_freevar_of;
export def_lookup;
// Throughout the compiler, variables are generally dealt with using the // A vector of defs representing the free variables referred to in a function.
// node_ids of the reference sites and not the def_id of the definition // (The def_upvar will already have been stripped).
// site. Thus we store a set are the definitions along with a vec of one type freevar_info = @[ast::def];
// "canonical" referencing node_id per free variable. The set is useful for
// testing membership, the list of referencing sites is what you want for most
// other things.
type freevar_set = hashset<ast::node_id>;
type freevar_info = {defs: freevar_set, refs: @[ast::node_id]};
type freevar_map = hashmap<ast::node_id, freevar_info>; type freevar_map = hashmap<ast::node_id, freevar_info>;
// Searches through part of the AST for all references to locals or // Searches through part of the AST for all references to locals or
@ -39,67 +29,54 @@ type freevar_map = hashmap<ast::node_id, freevar_info>;
// Since we want to be able to collect upvars in some arbitrary piece // Since we want to be able to collect upvars in some arbitrary piece
// of the AST, we take a walker function that we invoke with a visitor // of the AST, we take a walker function that we invoke with a visitor
// in order to start the search. // in order to start the search.
fn collect_freevars(def_map: &resolve::def_map, sess: &session::session, fn collect_freevars(def_map: &resolve::def_map,
walker: &fn(&visit::vt<()>), walker: &fn(&visit::vt<int>)) -> freevar_info {
initial_decls: [ast::node_id]) -> freevar_info { let seen = new_int_hash();
let decls = new_int_hash();
for decl: ast::node_id in initial_decls { set_add(decls, decl); }
let refs = @mutable []; let refs = @mutable [];
let walk_fn = fn ignore_item(_i: &@ast::item, _depth: &int, _v: &visit::vt<int>) {}
lambda (f: &ast::_fn, _tps: &[ast::ty_param], _sp: &span,
_i: &ast::fn_ident, _nid: ast::node_id) {
for a: ast::arg in f.decl.inputs { set_add(decls, a.id); }
};
let walk_expr =
lambda (expr: &@ast::expr) {
alt expr.node {
ast::expr_path(path) {
if !def_map.contains_key(expr.id) {
sess.span_fatal(expr.span,
~"internal error in collect_freevars");
}
alt def_map.get(expr.id) {
ast::def_arg(did, _) { *refs += [expr.id]; }
ast::def_local(did) { *refs += [expr.id]; }
ast::def_binding(did) { *refs += [expr.id]; }
_ {/* no-op */ }
}
}
_ { }
}
};
let walk_local =
lambda (local: &@ast::local) {
for each b: @ast::pat in ast_util::pat_bindings(local.node.pat) {
set_add(decls, b.id);
}
};
let walk_pat =
lambda (p: &@ast::pat) {
alt p.node { ast::pat_bind(_) { set_add(decls, p.id); } _ { } }
};
walker(visit::mk_simple_visitor(@{visit_local: walk_local, let walk_expr = lambda(expr: &@ast::expr, depth: &int,
visit_pat: walk_pat, v: &visit::vt<int>) {
visit_expr: walk_expr, alt expr.node {
visit_fn: walk_fn ast::expr_fn(f) {
with if f.proto == ast::proto_block ||
*visit::default_simple_visitor()})); f.proto == ast::proto_closure {
// Calculate (refs - decls). This is the set of captured upvars. visit::visit_expr(expr, depth + 1, v);
// We build a vec of the node ids of the uses and a set of the }
// node ids of the definitions. }
let canonical_refs = []; ast::expr_for_each(dcl, x, b) {
let defs = new_int_hash(); v.visit_local(dcl, depth, v);
for ref_id_: ast::node_id in *refs { v.visit_expr(x, depth, v);
let ref_id = ref_id_; v.visit_block(b, depth + 1, v);
let def_id = ast_util::def_id_of_def(def_map.get(ref_id)).node; }
if !decls.contains_key(def_id) && !defs.contains_key(def_id) { ast::expr_path(path) {
canonical_refs += [ref_id]; let def = def_map.get(expr.id), i = 0;
set_add(defs, def_id); while i < depth {
alt {def} {
ast::def_upvar(_, inner, _) {
def = *inner;
}
_ { break; }
}
i += 1;
}
if i == depth { // Made it to end of loop
let dnum = ast_util::def_id_of_def(def).node;
if !seen.contains_key(dnum) {
*refs += [def];
seen.insert(dnum, ());
}
}
}
_ { visit::visit_expr(expr, depth, v); }
} }
} };
ret {defs: defs, refs: @canonical_refs};
walker(visit::mk_vt(@{visit_item: ignore_item,
visit_expr: walk_expr
with *visit::default_visitor()}));
ret @*refs;
} }
// Build a map from every function and for-each body to a set of the // Build a map from every function and for-each body to a set of the
@ -107,35 +84,30 @@ fn collect_freevars(def_map: &resolve::def_map, sess: &session::session,
// efficient as it fully recomputes the free variables at every // efficient as it fully recomputes the free variables at every
// node of interest rather than building up the free variables in // node of interest rather than building up the free variables in
// one pass. This could be improved upon if it turns out to matter. // one pass. This could be improved upon if it turns out to matter.
fn annotate_freevars(sess: &session::session, def_map: &resolve::def_map, fn annotate_freevars(def_map: &resolve::def_map,
crate: &@ast::crate) -> freevar_map { crate: &@ast::crate) -> freevar_map {
let freevars = new_int_hash(); let freevars = new_int_hash();
let walk_fn = let walk_fn = lambda (f: &ast::_fn, tps: &[ast::ty_param], sp: &span,
lambda (f: &ast::_fn, tps: &[ast::ty_param], sp: &span, i: &ast::fn_ident, nid: ast::node_id) {
i: &ast::fn_ident, nid: ast::node_id) { let start_walk = lambda (v: &visit::vt<int>) {
let start_walk = v.visit_fn(f, tps, sp, i, nid, 1, v);
lambda (v: &visit::vt<()>) {
v.visit_fn(f, tps, sp, i, nid, (), v);
};
let vars = collect_freevars(def_map, sess, start_walk, []);
freevars.insert(nid, vars);
};
let walk_expr =
lambda (expr: &@ast::expr) {
alt expr.node {
ast::expr_for_each(local, _, body) {
let start_walk =
lambda (v: &visit::vt<()>) {
v.visit_block(body, (), v);
};
let bound = ast_util::pat_binding_ids(local.node.pat);
let vars = collect_freevars(def_map, sess, start_walk, bound);
freevars.insert(body.node.id, vars);
}
_ { }
}
}; };
let vars = collect_freevars(def_map, start_walk);
freevars.insert(nid, vars);
};
let walk_expr = lambda (expr: &@ast::expr) {
alt expr.node {
ast::expr_for_each(local, _, body) {
let start_walk = lambda (v: &visit::vt<int>) {
v.visit_block(body, 1, v);
};
let vars = collect_freevars(def_map, start_walk);
freevars.insert(body.node.id, vars);
}
_ { }
}
};
let visitor = let visitor =
visit::mk_simple_visitor(@{visit_fn: walk_fn, visit_expr: walk_expr visit::mk_simple_visitor(@{visit_fn: walk_fn, visit_expr: walk_expr
@ -145,7 +117,7 @@ fn annotate_freevars(sess: &session::session, def_map: &resolve::def_map,
ret freevars; ret freevars;
} }
fn get_freevar_info(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_info { fn get_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_info {
alt tcx.freevars.find(fid) { alt tcx.freevars.find(fid) {
none. { none. {
fail "get_freevars: " + istr::to_estr(int::str(fid)) fail "get_freevars: " + istr::to_estr(int::str(fid))
@ -154,31 +126,9 @@ fn get_freevar_info(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_info {
some(d) { ret d; } some(d) { ret d; }
} }
} }
fn get_freevar_defs(tcx: &ty::ctxt, fid: ast::node_id) -> freevar_set {
ret get_freevar_info(tcx, fid).defs;
}
fn get_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> @[ast::node_id] {
ret get_freevar_info(tcx, fid).refs;
}
fn has_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> bool { fn has_freevars(tcx: &ty::ctxt, fid: ast::node_id) -> bool {
ret get_freevar_defs(tcx, fid).size() != 0u; ret std::vec::len(*get_freevars(tcx, fid)) != 0u;
} }
fn is_freevar_of(tcx: &ty::ctxt, def: ast::node_id, f: ast::node_id) -> bool {
ret get_freevar_defs(tcx, f).contains_key(def);
}
fn def_lookup(tcx: &ty::ctxt, f: ast::node_id, id: ast::node_id) ->
option::t<ast::def> {
alt tcx.def_map.find(id) {
none. { ret none; }
some(d) {
let did = ast_util::def_id_of_def(d);
if f != -1 && is_freevar_of(tcx, did.node, f) {
ret some(ast::def_upvar(did, @d));
} else { ret some(d); }
}
}
}
// Local Variables: // Local Variables:
// mode: rust // mode: rust

View File

@ -238,14 +238,18 @@ fn check_call(cx: &@ctx, f: &@expr, args: &[@expr]) {
} }
fn is_immutable_def(def: &def) -> option::t<istr> { fn is_immutable_def(def: &def) -> option::t<istr> {
ret alt def { alt def {
def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) | def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) |
def_use(_) { some(~"static item") } def_use(_) { some(~"static item") }
def_obj_field(_, imm.) { some(~"immutable object field") } def_obj_field(_, imm.) { some(~"immutable object field") }
def_arg(_, alias(false)) { some(~"immutable alias") } def_arg(_, alias(false)) { some(~"immutable alias") }
def_upvar(_, inner, mut) {
if !mut { some(~"upvar") }
else { is_immutable_def(*inner) }
}
def_binding(_) { some(~"binding") } def_binding(_) { some(~"binding") }
_ { none } _ { none }
}; }
} }
// Local Variables: // Local Variables:

View File

@ -55,7 +55,7 @@ tag scope {
scope_item(@ast::item); scope_item(@ast::item);
scope_fn(ast::fn_decl, ast::proto, [ast::ty_param]); scope_fn(ast::fn_decl, ast::proto, [ast::ty_param]);
scope_native_item(@ast::native_item); scope_native_item(@ast::native_item);
scope_loop(@ast::local); // there's only 1 decl per loop. scope_loop(@ast::local, bool); // there's only 1 decl per loop.
scope_block(ast::blk, @mutable uint, @mutable uint); scope_block(ast::blk, @mutable uint, @mutable uint);
scope_arm(ast::arm); scope_arm(ast::arm);
@ -404,14 +404,12 @@ fn visit_arm_with_scope(a: &ast::arm, sc: &scopes, v: &vt<scopes>) {
fn visit_expr_with_scope(x: &@ast::expr, sc: &scopes, v: &vt<scopes>) { fn visit_expr_with_scope(x: &@ast::expr, sc: &scopes, v: &vt<scopes>) {
alt x.node { alt x.node {
ast::expr_for(decl, coll, blk) | ast::expr_for_each(decl, coll, blk) { ast::expr_for(decl, coll, blk) | ast::expr_for_each(decl, coll, blk) {
let new_sc = cons::<scope>(scope_loop(decl), @sc); let f_e = alt x.node { expr_for_each(_, _, _) { true } _ { false } };
let new_sc = cons(scope_loop(decl, f_e), @sc);
v.visit_expr(coll, sc, v); v.visit_expr(coll, sc, v);
v.visit_local(decl, new_sc, v); v.visit_local(decl, new_sc, v);
v.visit_block(blk, new_sc, v); v.visit_block(blk, new_sc, v);
} }
ast::expr_fn(f) {
visit::visit_expr(x, cons(scope_fn(f.decl, f.proto, []), @sc), v);
}
_ { visit::visit_expr(x, sc, v); } _ { visit::visit_expr(x, sc, v); }
} }
} }
@ -622,9 +620,18 @@ fn scope_is_fn(sc: &scope) -> bool {
}; };
} }
fn scope_closes(sc: &scope) -> option::t<bool> {
alt sc {
scope_fn(_, ast::proto_block., _) | scope_loop(_, true) { some(true) }
scope_fn(_, ast::proto_closure., _) { some(false) }
_ { none }
}
}
fn def_is_local(d: &def) -> bool { fn def_is_local(d: &def) -> bool {
ret alt d { ret alt d {
ast::def_arg(_, _) | ast::def_local(_) | ast::def_binding(_) { true } ast::def_arg(_, _) | ast::def_local(_) | ast::def_binding(_) |
ast::def_upvar(_, _, _) { true }
_ { false } _ { false }
}; };
} }
@ -675,7 +682,7 @@ fn lookup_in_scope(e: &env, sc: scopes, sp: &span, name: &ident,
scope_fn(decl, _, ty_params) { scope_fn(decl, _, ty_params) {
ret lookup_in_fn(name, decl, ty_params, ns); ret lookup_in_fn(name, decl, ty_params, ns);
} }
scope_loop(local) { scope_loop(local, _) {
if ns == ns_value { if ns == ns_value {
alt lookup_in_pat(name, local.node.pat) { alt lookup_in_pat(name, local.node.pat) {
some(did) { ret some(ast::def_binding(did)); } some(did) { ret some(ast::def_binding(did)); }
@ -698,8 +705,8 @@ fn lookup_in_scope(e: &env, sc: scopes, sp: &span, name: &ident,
ret none::<def>; ret none::<def>;
} }
let left_fn = false; let left_fn = false;
let closing = [];
// Used to determine whether obj fields are in scope // Used to determine whether obj fields are in scope
let left_fn_level2 = false; let left_fn_level2 = false;
while true { while true {
alt { sc } { alt { sc } {
@ -708,27 +715,38 @@ fn lookup_in_scope(e: &env, sc: scopes, sp: &span, name: &ident,
let fnd = in_scope(e, sp, name, hd, ns); let fnd = in_scope(e, sp, name, hd, ns);
if !is_none(fnd) { if !is_none(fnd) {
let df = option::get(fnd); let df = option::get(fnd);
if left_fn && def_is_local(df) || let local = def_is_local(df);
if left_fn && local ||
left_fn_level2 && def_is_obj_field(df) || left_fn_level2 && def_is_obj_field(df) ||
scope_is_fn(hd) && left_fn && def_is_ty_arg(df) { scope_is_fn(hd) && left_fn && def_is_ty_arg(df) {
let msg = let msg = alt ns {
alt ns { ns_type. {
ns_type. { ~"Attempt to use a type argument out of scope"
~"Attempt to use a type \ }
argument out of scope" _ {
} ~"attempted dynamic environment-capture"
_ { }
~"attempted dynamic \ };
environment-capture"
}
};
e.sess.span_fatal(sp, msg); e.sess.span_fatal(sp, msg);
} else if local {
let i = vec::len(closing);
while i > 0u {
i -= 1u;
df = ast::def_upvar(ast_util::def_id_of_def(df),
@df, closing[i]);
fnd = some(df);
}
} }
ret fnd; ret fnd;
} }
if left_fn { left_fn_level2 = true; } if left_fn {
if (ns == ns_value || ns == ns_type) && !left_fn { left_fn_level2 = true;
} else if ns == ns_value || ns == ns_type {
left_fn = scope_is_fn(hd); left_fn = scope_is_fn(hd);
alt scope_closes(hd) {
some(mut) { closing += [mut]; }
_ {}
}
} }
sc = *tl; sc = *tl;
} }
@ -1177,6 +1195,7 @@ fn ns_for_def(d: def) -> namespace {
ast::def_const(_) { ns_value } ast::def_const(_) { ns_value }
ast::def_arg(_, _) { ns_value } ast::def_arg(_, _) { ns_value }
ast::def_local(_) { ns_value } ast::def_local(_) { ns_value }
ast::def_upvar(_, _, _) { ns_value }
ast::def_variant(_, _) { ns_value } ast::def_variant(_, _) { ns_value }
ast::def_ty(_) { ns_type } ast::def_ty(_) { ns_type }
ast::def_binding(_) { ns_type } ast::def_binding(_) { ns_type }

View File

@ -2824,8 +2824,8 @@ fn build_environment(bcx: @block_ctxt, lltydescs: [ValueRef],
// Given a context and a list of upvars, build a closure. This just // Given a context and a list of upvars, build a closure. This just
// collects the upvars and packages them up for build_environment. // collects the upvars and packages them up for build_environment.
fn build_closure(cx: &@block_ctxt, upvars: &@[ast::node_id], copying: bool) -> fn build_closure(cx: &@block_ctxt, upvars: &@[ast::def], copying: bool)
{ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} { -> {ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
let closure_vals: [lval_result] = []; let closure_vals: [lval_result] = [];
let closure_tys: [ty::t] = []; let closure_tys: [ty::t] = [];
// If we need to, package up the iterator body to call // If we need to, package up the iterator body to call
@ -2834,8 +2834,9 @@ fn build_closure(cx: &@block_ctxt, upvars: &@[ast::node_id], copying: bool) ->
closure_tys += [option::get(cx.fcx.iterbodyty)]; closure_tys += [option::get(cx.fcx.iterbodyty)];
} }
// Package up the upvars // Package up the upvars
for nid: ast::node_id in *upvars { for def in *upvars {
closure_vals += [trans_var(cx, cx.sp, nid)]; closure_vals += [trans_local_var(cx, def)];
let nid = ast_util::def_id_of_def(def).node;
let ty = ty::node_id_to_monotype(bcx_tcx(cx), nid); let ty = ty::node_id_to_monotype(bcx_tcx(cx), nid);
if !copying { ty = ty::mk_mut_ptr(bcx_tcx(cx), ty); } if !copying { ty = ty::mk_mut_ptr(bcx_tcx(cx), ty); }
closure_tys += [ty]; closure_tys += [ty];
@ -2882,7 +2883,7 @@ fn find_environment_tydescs(bcx: &@block_ctxt, envty: ty::t,
// and a list of upvars, generate code to load and populate the environment // and a list of upvars, generate code to load and populate the environment
// with the upvars and type descriptors. // with the upvars and type descriptors.
fn load_environment(enclosing_cx: &@block_ctxt, fcx: &@fn_ctxt, envty: ty::t, fn load_environment(enclosing_cx: &@block_ctxt, fcx: &@fn_ctxt, envty: ty::t,
upvars: &@[ast::node_id], copying: bool) { upvars: &@[ast::def], copying: bool) {
let bcx = new_raw_block_ctxt(fcx, fcx.llcopyargs); let bcx = new_raw_block_ctxt(fcx, fcx.llcopyargs);
let ty = ty::mk_imm_box(bcx_tcx(bcx), envty); let ty = ty::mk_imm_box(bcx_tcx(bcx), envty);
@ -2913,14 +2914,13 @@ fn load_environment(enclosing_cx: &@block_ctxt, fcx: &@fn_ctxt, envty: ty::t,
i += 1u; i += 1u;
} }
// Load the acutal upvars. // Load the actual upvars.
for upvar_id: ast::node_id in *upvars { for upvar_def in *upvars {
let upvarptr = GEP_tup_like(bcx, ty, llclosure, path + [i as int]); let upvarptr = GEP_tup_like(bcx, ty, llclosure, path + [i as int]);
bcx = upvarptr.bcx; bcx = upvarptr.bcx;
let llupvarptr = upvarptr.val; let llupvarptr = upvarptr.val;
if !copying { llupvarptr = Load(bcx, llupvarptr); } if !copying { llupvarptr = Load(bcx, llupvarptr); }
let def_id = ast_util::def_id_of_def(bcx_tcx(bcx). let def_id = ast_util::def_id_of_def(upvar_def);
def_map.get(upvar_id));
fcx.llupvars.insert(def_id.node, llupvarptr); fcx.llupvars.insert(def_id.node, llupvarptr);
i += 1u; i += 1u;
} }
@ -3134,34 +3134,44 @@ fn lookup_discriminant(lcx: &@local_ctxt, vid: &ast::def_id) -> ValueRef {
} }
} }
fn trans_var(cx: &@block_ctxt, sp: &span, id: ast::node_id) -> lval_result { fn trans_local_var(cx: &@block_ctxt, def: &ast::def) -> lval_result {
let ccx = bcx_ccx(cx); alt def {
alt freevars::def_lookup(bcx_tcx(cx), cx.fcx.id, id) { ast::def_upvar(did, _, _) {
some(ast::def_upvar(did, _)) {
assert (cx.fcx.llupvars.contains_key(did.node)); assert (cx.fcx.llupvars.contains_key(did.node));
ret lval_mem(cx, cx.fcx.llupvars.get(did.node)); ret lval_mem(cx, cx.fcx.llupvars.get(did.node));
} }
some(ast::def_arg(did, _)) { ast::def_arg(did, _) {
assert (cx.fcx.llargs.contains_key(did.node)); assert (cx.fcx.llargs.contains_key(did.node));
ret lval_mem(cx, cx.fcx.llargs.get(did.node)); ret lval_mem(cx, cx.fcx.llargs.get(did.node));
} }
some(ast::def_local(did)) { ast::def_local(did) {
assert (cx.fcx.lllocals.contains_key(did.node)); assert (cx.fcx.lllocals.contains_key(did.node));
ret lval_mem(cx, cx.fcx.lllocals.get(did.node)); ret lval_mem(cx, cx.fcx.lllocals.get(did.node));
} }
some(ast::def_binding(did)) { ast::def_binding(did) {
assert (cx.fcx.lllocals.contains_key(did.node)); assert (cx.fcx.lllocals.contains_key(did.node));
ret lval_mem(cx, cx.fcx.lllocals.get(did.node)); ret lval_mem(cx, cx.fcx.lllocals.get(did.node));
} }
some(ast::def_obj_field(did, _)) { ast::def_obj_field(did, _) {
assert (cx.fcx.llobjfields.contains_key(did.node)); assert (cx.fcx.llobjfields.contains_key(did.node));
ret lval_mem(cx, cx.fcx.llobjfields.get(did.node)); ret lval_mem(cx, cx.fcx.llobjfields.get(did.node));
} }
some(ast::def_fn(did, _)) { _ {
bcx_ccx(cx).sess.span_unimpl
(cx.sp, ~"unsupported def type in trans_local_def");
}
}
}
fn trans_var(cx: &@block_ctxt, sp: &span, def: &ast::def,
id: ast::node_id) -> lval_result {
let ccx = bcx_ccx(cx);
alt def {
ast::def_fn(did, _) {
let tyt = ty::lookup_item_type(ccx.tcx, did); let tyt = ty::lookup_item_type(ccx.tcx, did);
ret lval_generic_fn(cx, tyt, did, id); ret lval_generic_fn(cx, tyt, did, id);
} }
some(ast::def_variant(tid, vid)) { ast::def_variant(tid, vid) {
let v_tyt = ty::lookup_item_type(ccx.tcx, vid); let v_tyt = ty::lookup_item_type(ccx.tcx, vid);
alt ty::struct(ccx.tcx, v_tyt.ty) { alt ty::struct(ccx.tcx, v_tyt.ty) {
ty::ty_fn(_, _, _, _, _) { ty::ty_fn(_, _, _, _, _) {
@ -3188,7 +3198,7 @@ fn trans_var(cx: &@block_ctxt, sp: &span, id: ast::node_id) -> lval_result {
} }
} }
} }
some(ast::def_const(did)) { ast::def_const(did) {
if did.crate == ast::local_crate { if did.crate == ast::local_crate {
assert (ccx.consts.contains_key(did.node)); assert (ccx.consts.contains_key(did.node));
ret lval_mem(cx, ccx.consts.get(did.node)); ret lval_mem(cx, ccx.consts.get(did.node));
@ -3203,17 +3213,17 @@ fn trans_var(cx: &@block_ctxt, sp: &span, id: ast::node_id) -> lval_result {
tp)); tp));
} }
} }
some(ast::def_native_fn(did)) { ast::def_native_fn(did) {
let tyt = ty::lookup_item_type(ccx.tcx, did); let tyt = ty::lookup_item_type(ccx.tcx, did);
ret lval_generic_fn(cx, tyt, did, id); ret lval_generic_fn(cx, tyt, did, id);
} }
_ { ccx.sess.span_unimpl(cx.sp, ~"def variant in trans"); } _ { ret trans_local_var(cx, def); }
} }
} }
fn trans_path(cx: &@block_ctxt, p: &ast::path, id: ast::node_id) -> fn trans_path(cx: &@block_ctxt, p: &ast::path, id: ast::node_id) ->
lval_result { lval_result {
ret trans_var(cx, p.span, id); ret trans_var(cx, p.span, bcx_tcx(cx).def_map.get(id), id);
} }
fn trans_field(cx: &@block_ctxt, sp: &span, v: ValueRef, t0: ty::t, fn trans_field(cx: &@block_ctxt, sp: &span, v: ValueRef, t0: ty::t,

View File

@ -531,16 +531,6 @@ fn constraints_expr(cx: &ty::ctxt, e: @expr) -> [@ty::constr] {
} }
} }
fn node_id_to_def_upvar_strict(cx: &fn_ctxt, id: node_id) -> def {
alt freevars::def_lookup(cx.ccx.tcx, cx.id, id) {
none. {
log_err "node_id_to_def: node_id "
+ istr::to_estr(int::str(id)) + " has no def";
fail;
}
some(d) { ret d; }
}
}
fn node_id_to_def_strict(cx: &ty::ctxt, id: node_id) -> def { fn node_id_to_def_strict(cx: &ty::ctxt, id: node_id) -> def {
alt cx.def_map.find(id) { alt cx.def_map.find(id) {
none. { none. {
@ -555,9 +545,6 @@ fn node_id_to_def_strict(cx: &ty::ctxt, id: node_id) -> def {
fn node_id_to_def(ccx: &crate_ctxt, id: node_id) -> option::t<def> { fn node_id_to_def(ccx: &crate_ctxt, id: node_id) -> option::t<def> {
ret ccx.tcx.def_map.find(id); ret ccx.tcx.def_map.find(id);
} }
fn node_id_to_def_upvar(cx: &fn_ctxt, id: node_id) -> option::t<def> {
ret freevars::def_lookup(cx.ccx.tcx, cx.id, id);
}
fn norm_a_constraint(id: def_id, c: &constraint) -> [norm_constraint] { fn norm_a_constraint(id: def_id, c: &constraint) -> [norm_constraint] {
alt c { alt c {
@ -620,21 +607,11 @@ fn expr_to_constr_arg(tcx: ty::ctxt, e: &@expr) -> @constr_arg_use {
alt e.node { alt e.node {
expr_path(p) { expr_path(p) {
alt tcx.def_map.find(e.id) { alt tcx.def_map.find(e.id) {
some(def_local(l_id)) { some(def_local(id)) | some(def_arg(id, _)) | some(def_binding(id)) |
some(def_upvar(id, _, _)) {
ret @respan(p.span, ret @respan(p.span,
carg_ident({ident: p.node.idents[0], carg_ident({ident: p.node.idents[0], node: id.node}));
node: l_id.node}));
} }
some(def_arg(a_id, _)) {
ret @respan(p.span,
carg_ident({ident: p.node.idents[0],
node: a_id.node}));
}
some (def_binding(b_id)) {
ret @respan(p.span,
carg_ident({ident: p.node.idents[0],
node: b_id.node}));
}
some(_) { some(_) {
tcx.sess.bug(~"exprs_to_constr_args: non-local variable " + tcx.sess.bug(~"exprs_to_constr_args: non-local variable " +
~"as pred arg"); ~"as pred arg");
@ -848,8 +825,9 @@ tag if_ty { if_check; plain_if; }
fn local_node_id_to_def_id_strict(fcx: &fn_ctxt, sp: &span, i: &node_id) -> fn local_node_id_to_def_id_strict(fcx: &fn_ctxt, sp: &span, i: &node_id) ->
def_id { def_id {
alt local_node_id_to_def(fcx, i) { alt local_node_id_to_def(fcx, i) {
some(def_local(d_id)) { ret d_id; } some(def_local(id)) | some(def_arg(id, _)) | some(def_upvar(id, _, _)) {
some(def_arg(a_id, _)) { ret a_id; } ret id;
}
some(_) { some(_) {
fcx.ccx.tcx.sess.span_fatal(sp, fcx.ccx.tcx.sess.span_fatal(sp,
~"local_node_id_to_def_id: id \ ~"local_node_id_to_def_id: id \
@ -870,17 +848,16 @@ fn local_node_id_to_def(fcx: &fn_ctxt, i: &node_id) -> option::t<def> {
fn local_node_id_to_def_id(fcx: &fn_ctxt, i: &node_id) -> option::t<def_id> { fn local_node_id_to_def_id(fcx: &fn_ctxt, i: &node_id) -> option::t<def_id> {
alt local_node_id_to_def(fcx, i) { alt local_node_id_to_def(fcx, i) {
some(def_local(d_id)) { some(d_id) } some(def_local(id)) | some(def_arg(id, _)) | some(def_binding(id)) |
some(def_arg(a_id, _)) { some(a_id) } some(def_upvar(id, _, _)) { some(id) }
_ { none } _ { none }
} }
} }
fn local_node_id_to_local_def_id(fcx: &fn_ctxt, i: &node_id) -> fn local_node_id_to_local_def_id(fcx: &fn_ctxt, i: &node_id) ->
option::t<node_id> { option::t<node_id> {
alt local_node_id_to_def(fcx, i) { alt local_node_id_to_def_id(fcx, i) {
some(def_local(d_id)) { some(d_id.node) } some(did) { some(did.node) }
some(def_arg(a_id, _)) { some(a_id.node) }
_ { none } _ { none }
} }
} }

View File

@ -210,7 +210,7 @@ fn join_then_else(fcx: &fn_ctxt, antec: &@expr, conseq: &blk,
fn gen_if_local(fcx: &fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id, fn gen_if_local(fcx: &fn_ctxt, lhs: @expr, rhs: @expr, larger_id: node_id,
new_var: node_id, pth: &path) { new_var: node_id, pth: &path) {
alt node_id_to_def_upvar(fcx, new_var) { alt node_id_to_def(fcx.ccx, new_var) {
some(d) { some(d) {
alt d { alt d {
def_local(d_id) { def_local(d_id) {
@ -249,7 +249,7 @@ fn handle_update(fcx: &fn_ctxt, parent: &@expr, lhs: &@expr, rhs: &@expr,
} }
_ { _ {
// pure and assign_op require the lhs to be init'd // pure and assign_op require the lhs to be init'd
let df = node_id_to_def_upvar_strict(fcx, lhs.id); let df = node_id_to_def_strict(fcx.ccx.tcx, lhs.id);
alt df { alt df {
def_local(d_id) { def_local(d_id) {
let i = let i =
@ -291,13 +291,16 @@ fn handle_update(fcx: &fn_ctxt, parent: &@expr, lhs: &@expr, rhs: &@expr,
} }
} }
/* FIXME: Can't deinitialize an upvar -- tests for that? */
fn handle_var(fcx: &fn_ctxt, rslt: &pre_and_post, id: node_id, name: ident) { fn handle_var(fcx: &fn_ctxt, rslt: &pre_and_post, id: node_id, name: ident) {
let df = node_id_to_def_upvar_strict(fcx, id); handle_var_def(fcx, rslt, node_id_to_def_strict(fcx.ccx.tcx, id), name);
alt df { }
fn handle_var_def(fcx: &fn_ctxt, rslt: &pre_and_post, def: &def,
name: ident) {
alt def {
def_local(d_id) | def_arg(d_id, _) { def_local(d_id) | def_arg(d_id, _) {
let i = bit_num(fcx, ninit(d_id.node, name));
use_var(fcx, d_id.node); use_var(fcx, d_id.node);
let i = bit_num(fcx, ninit(d_id.node, name));
require_and_preserve(i, rslt); require_and_preserve(i, rslt);
} }
_ {/* nothing to check */ } _ {/* nothing to check */ }
@ -369,8 +372,9 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
expr_fn(f) { expr_fn(f) {
let rslt = expr_pp(fcx.ccx, e); let rslt = expr_pp(fcx.ccx, e);
clear_pp(rslt); clear_pp(rslt);
let upvars = freevars::get_freevars(fcx.ccx.tcx, e.id); for def in *freevars::get_freevars(fcx.ccx.tcx, e.id) {
for id: node_id in *upvars { handle_var(fcx, rslt, id, ~"upvar"); } handle_var_def(fcx, rslt, def, ~"upvar");
}
} }
expr_block(b) { expr_block(b) {
find_pre_post_block(fcx, b); find_pre_post_block(fcx, b);
@ -474,6 +478,11 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
} }
expr_for_each(d, index, body) { expr_for_each(d, index, body) {
find_pre_post_loop(fcx, d, index, body, e.id); find_pre_post_loop(fcx, d, index, body, e.id);
let rslt = expr_pp(fcx.ccx, e);
clear_pp(rslt);
for def in *freevars::get_freevars(fcx.ccx.tcx, body.node.id) {
handle_var_def(fcx, rslt, def, ~"upvar");
}
} }
expr_index(val, sub) { find_pre_post_exprs(fcx, [val, sub], e.id); } expr_index(val, sub) { find_pre_post_exprs(fcx, [val, sub], e.id); }
expr_alt(ex, alts) { expr_alt(ex, alts) {

View File

@ -228,7 +228,7 @@ fn find_pre_post_state_loop(fcx: &fn_ctxt, pres: prestate, l: &@local,
fn gen_if_local(fcx: &fn_ctxt, p: &poststate, e: &@expr) -> bool { fn gen_if_local(fcx: &fn_ctxt, p: &poststate, e: &@expr) -> bool {
alt e.node { alt e.node {
expr_path(pth) { expr_path(pth) {
alt freevars::def_lookup(fcx.ccx.tcx, fcx.id, e.id) { alt fcx.ccx.tcx.def_map.find(e.id) {
some(def_local(loc)) { some(def_local(loc)) {
ret set_in_poststate_ident(fcx, loc.node, ret set_in_poststate_ident(fcx, loc.node,
path_to_ident(fcx.ccx.tcx, pth), p); path_to_ident(fcx.ccx.tcx, pth), p);

View File

@ -1790,11 +1790,7 @@ mod unify {
tag union_result { unres_ok; unres_err(type_err); } tag union_result { unres_ok; unres_err(type_err); }
tag fixup_result { tag fixup_result {
fix_ok(t); // fixup succeeded fix_ok(t); // fixup succeeded
fix_err(int); // fixup failed because a type variable was unresolved fix_err(int); // fixup failed because a type variable was unresolved
} }
type var_bindings = type var_bindings =
{sets: ufind::ufind, types: smallintmap::smallintmap<t>}; {sets: ufind::ufind, types: smallintmap::smallintmap<t>};
@ -2605,6 +2601,7 @@ fn def_has_ty_params(def: &ast::def) -> bool {
ast::def_const(_) { ret false; } ast::def_const(_) { ret false; }
ast::def_arg(_, _) { ret false; } ast::def_arg(_, _) { ret false; }
ast::def_local(_) { ret false; } ast::def_local(_) { ret false; }
ast::def_upvar(_, _, _) { ret false; }
ast::def_variant(_, _) { ret true; } ast::def_variant(_, _) { ret true; }
ast::def_ty(_) { ret false; } ast::def_ty(_) { ret false; }
ast::def_ty_arg(_, _) { ret false; } ast::def_ty_arg(_, _) { ret false; }

View File

@ -148,6 +148,9 @@ fn ty_param_kinds_and_ty_for_def(fcx: &@fn_ctxt, sp: &span, defn: &ast::def)
ast::def_ty(_) { ast::def_ty(_) {
fcx.ccx.tcx.sess.span_fatal(sp, ~"expected value but found type"); fcx.ccx.tcx.sess.span_fatal(sp, ~"expected value but found type");
} }
ast::def_upvar(_, inner, _) {
ret ty_param_kinds_and_ty_for_def(fcx, sp, *inner);
}
_ { _ {
// FIXME: handle other names. // FIXME: handle other names.
fcx.ccx.tcx.sess.unimpl(~"definition variant"); fcx.ccx.tcx.sess.unimpl(~"definition variant");
@ -1128,6 +1131,14 @@ mod writeback {
fn visit_expr(e: &@ast::expr, wbcx: &wb_ctxt, v: &wb_vt) { fn visit_expr(e: &@ast::expr, wbcx: &wb_ctxt, v: &wb_vt) {
if !wbcx.success { ret; } if !wbcx.success { ret; }
resolve_type_vars_for_node(wbcx, e.span, e.id); resolve_type_vars_for_node(wbcx, e.span, e.id);
alt e.node {
ast::expr_fn(f) {
for input in f.decl.inputs {
resolve_type_vars_for_node(wbcx, e.span, input.id);
}
}
_ {}
}
visit::visit_expr(e, wbcx, v); visit::visit_expr(e, wbcx, v);
} }
fn visit_block(b: &ast::blk, wbcx: &wb_ctxt, v: &wb_vt) { fn visit_block(b: &ast::blk, wbcx: &wb_ctxt, v: &wb_vt) {
@ -2683,6 +2694,7 @@ fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id,
next_var_id: gather_result.next_var_id, next_var_id: gather_result.next_var_id,
mutable fixups: fixups, mutable fixups: fixups,
ccx: ccx}; ccx: ccx};
check_constraints(fcx, decl.constraints, decl.inputs); check_constraints(fcx, decl.constraints, decl.inputs);
check_block(fcx, body); check_block(fcx, body);
@ -2700,6 +2712,13 @@ fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id,
none. {} none. {}
} }
let args = ty::ty_fn_args(ccx.tcx, ty::node_id_to_type(ccx.tcx, id));
let i = 0u;
for arg: ty::arg in args {
write::ty_only_fixup(fcx, f.decl.inputs[i].id, arg.ty);
i += 1u;
}
// If we don't have any enclosing function scope, it is time to // If we don't have any enclosing function scope, it is time to
// force any remaining type vars to be resolved. // force any remaining type vars to be resolved.
// If we have an enclosing function scope, our type variables will be // If we have an enclosing function scope, our type variables will be

View File

@ -44,11 +44,7 @@ tag def {
def_use(def_id); def_use(def_id);
def_native_ty(def_id); def_native_ty(def_id);
def_native_fn(def_id); def_native_fn(def_id);
def_upvar(def_id, @def, bool /* writable */);
/* A "fake" def for upvars. This never appears in the def_map, but
* freevars::def_lookup will return it for a def that is an upvar.
* It contains the actual def. */
def_upvar(def_id, @def);
} }
// The set of meta_items that define the compilation environment of the crate, // The set of meta_items that define the compilation environment of the crate,

View File

@ -42,7 +42,7 @@ fn def_id_of_def(d: def) -> def_id {
def_use(id) { ret id; } def_use(id) { ret id; }
def_native_ty(id) { ret id; } def_native_ty(id) { ret id; }
def_native_fn(id) { ret id; } def_native_fn(id) { ret id; }
def_upvar(id, _) { ret id; } def_upvar(id, _, _) { ret id; }
} }
} }
@ -216,3 +216,12 @@ fn ternary_to_if(e: &@expr) -> @expr {
_ { fail; } _ { fail; }
} }
} }
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:

View File

@ -173,7 +173,6 @@ fn bad_expr_word_table() -> hashmap<istr, ()> {
words.insert(~"prove", ()); words.insert(~"prove", ());
words.insert(~"native", ()); words.insert(~"native", ());
words.insert(~"fn", ()); words.insert(~"fn", ());
words.insert(~"block", ());
words.insert(~"lambda", ()); words.insert(~"lambda", ());
words.insert(~"pure", ()); words.insert(~"pure", ());
words.insert(~"iter", ()); words.insert(~"iter", ());

View File

@ -1,4 +1,4 @@
// error-pattern:assigning to immutable alias // error-pattern:assigning to upvar
// Make sure that nesting a block within a lambda doesn't let us // Make sure that nesting a block within a lambda doesn't let us
// mutate upvars from a lambda. // mutate upvars from a lambda.
fn main() { fn main() {

View File

@ -1,4 +1,4 @@
// error-pattern:assigning to immutable alias // error-pattern:assigning to upvar
// Make sure we can't write to upvars from lambdas // Make sure we can't write to upvars from lambdas
fn main() { fn main() {
let i = 0; let i = 0;