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));
let freevars =
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);
time(time_passes, ~"typechecking",
bind typeck::check_crate(ty_cx, crate));
@ -240,7 +240,7 @@ fn ann_identified_post(node: &pprust::ann_node) {
let amap = middle::ast_map::map_crate(*crate);
let {def_map: def_map, ext_map: ext_map} =
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);
typeck::check_crate(ty_cx, crate);
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,
local_map: std::map::new_int_hash(),
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_decl: bind visit_decl(cx, _, _, _)
with *visit::default_visitor::<scope>()};
@ -59,39 +59,15 @@ fn check_crate(tcx: ty::ctxt, crate: &@ast::crate) {
tcx.sess.abort_if_errors();
}
fn visit_fn(cx: &@ctx, f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span,
_name: &fn_ident, id: ast::node_id, sc: &scope, v: &vt<scope>) {
fn visit_fn(f: &ast::_fn, _tp: &[ast::ty_param], _sp: &span,
_name: &fn_ident, _id: ast::node_id, sc: &scope, v: &vt<scope>) {
visit::visit_fn_decl(f.decl, sc, v);
let scope =
alt f.proto {
// Blocks need to obey any restrictions from the enclosing scope.
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.
_ {
@[]
}
};
let scope = alt f.proto {
// Blocks need to obey any restrictions from the enclosing scope.
ast::proto_block. | ast::proto_closure. { sc }
// Non capturing functions start out fresh.
_ { @[] }
};
v.visit_block(f.body, scope, v);
}
@ -489,7 +465,8 @@ fn helper(tcx: &ty::ctxt, needle: ty::t, haystack: ty::t, mut: bool) ->
fn def_is_local(d: &ast::def, objfields_count: bool) -> bool {
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 }
_ { false }
};

View File

@ -15,23 +15,13 @@
import syntax::codemap::span;
export annotate_freevars;
export freevar_set;
export freevar_map;
export get_freevar_info;
export get_freevars;
export get_freevar_defs;
export has_freevars;
export is_freevar_of;
export def_lookup;
// Throughout the compiler, variables are generally dealt with using the
// node_ids of the reference sites and not the def_id of the definition
// site. Thus we store a set are the definitions along with a vec of one
// "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]};
// A vector of defs representing the free variables referred to in a function.
// (The def_upvar will already have been stripped).
type freevar_info = @[ast::def];
type freevar_map = hashmap<ast::node_id, freevar_info>;
// Searches through part of the AST for all references to locals or
@ -39,67 +29,54 @@
// 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
// in order to start the search.
fn collect_freevars(def_map: &resolve::def_map, sess: &session::session,
walker: &fn(&visit::vt<()>),
initial_decls: [ast::node_id]) -> freevar_info {
let decls = new_int_hash();
for decl: ast::node_id in initial_decls { set_add(decls, decl); }
fn collect_freevars(def_map: &resolve::def_map,
walker: &fn(&visit::vt<int>)) -> freevar_info {
let seen = new_int_hash();
let refs = @mutable [];
let walk_fn =
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); } _ { } }
};
fn ignore_item(_i: &@ast::item, _depth: &int, _v: &visit::vt<int>) {}
walker(visit::mk_simple_visitor(@{visit_local: walk_local,
visit_pat: walk_pat,
visit_expr: walk_expr,
visit_fn: walk_fn
with
*visit::default_simple_visitor()}));
// Calculate (refs - decls). This is the set of captured upvars.
// We build a vec of the node ids of the uses and a set of the
// node ids of the definitions.
let canonical_refs = [];
let defs = new_int_hash();
for ref_id_: ast::node_id in *refs {
let ref_id = ref_id_;
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) {
canonical_refs += [ref_id];
set_add(defs, def_id);
let walk_expr = lambda(expr: &@ast::expr, depth: &int,
v: &visit::vt<int>) {
alt expr.node {
ast::expr_fn(f) {
if f.proto == ast::proto_block ||
f.proto == ast::proto_closure {
visit::visit_expr(expr, depth + 1, v);
}
}
ast::expr_for_each(dcl, x, b) {
v.visit_local(dcl, depth, v);
v.visit_expr(x, depth, v);
v.visit_block(b, depth + 1, v);
}
ast::expr_path(path) {
let def = def_map.get(expr.id), i = 0;
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
@ -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
// node of interest rather than building up the free variables in
// 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 {
let freevars = new_int_hash();
let walk_fn =
lambda (f: &ast::_fn, tps: &[ast::ty_param], sp: &span,
i: &ast::fn_ident, nid: ast::node_id) {
let start_walk =
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 walk_fn = lambda (f: &ast::_fn, tps: &[ast::ty_param], sp: &span,
i: &ast::fn_ident, nid: ast::node_id) {
let start_walk = lambda (v: &visit::vt<int>) {
v.visit_fn(f, tps, sp, i, nid, 1, v);
};
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 =
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;
}
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) {
none. {
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; }
}
}
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 {
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:
// 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> {
ret alt def {
alt def {
def_fn(_, _) | def_mod(_) | def_native_mod(_) | def_const(_) |
def_use(_) { some(~"static item") }
def_obj_field(_, imm.) { some(~"immutable object field") }
def_arg(_, alias(false)) { some(~"immutable alias") }
def_upvar(_, inner, mut) {
if !mut { some(~"upvar") }
else { is_immutable_def(*inner) }
}
def_binding(_) { some(~"binding") }
_ { none }
};
}
}
// Local Variables:

View File

@ -55,7 +55,7 @@
scope_item(@ast::item);
scope_fn(ast::fn_decl, ast::proto, [ast::ty_param]);
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_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>) {
alt x.node {
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_local(decl, 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); }
}
}
@ -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 {
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 }
};
}
@ -675,7 +682,7 @@ fn in_scope(e: &env, sp: &span, name: &ident, s: &scope, ns: namespace) ->
scope_fn(decl, _, ty_params) {
ret lookup_in_fn(name, decl, ty_params, ns);
}
scope_loop(local) {
scope_loop(local, _) {
if ns == ns_value {
alt lookup_in_pat(name, local.node.pat) {
some(did) { ret some(ast::def_binding(did)); }
@ -698,8 +705,8 @@ fn in_scope(e: &env, sp: &span, name: &ident, s: &scope, ns: namespace) ->
ret none::<def>;
}
let left_fn = false;
let closing = [];
// Used to determine whether obj fields are in scope
let left_fn_level2 = false;
while true {
alt { sc } {
@ -708,27 +715,38 @@ fn in_scope(e: &env, sp: &span, name: &ident, s: &scope, ns: namespace) ->
let fnd = in_scope(e, sp, name, hd, ns);
if !is_none(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) ||
scope_is_fn(hd) && left_fn && def_is_ty_arg(df) {
let msg =
alt ns {
ns_type. {
~"Attempt to use a type \
argument out of scope"
}
_ {
~"attempted dynamic \
environment-capture"
}
};
let msg = alt ns {
ns_type. {
~"Attempt to use a type argument out of scope"
}
_ {
~"attempted dynamic environment-capture"
}
};
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;
}
if left_fn { left_fn_level2 = true; }
if (ns == ns_value || ns == ns_type) && !left_fn {
if left_fn {
left_fn_level2 = true;
} else if ns == ns_value || ns == ns_type {
left_fn = scope_is_fn(hd);
alt scope_closes(hd) {
some(mut) { closing += [mut]; }
_ {}
}
}
sc = *tl;
}
@ -1177,6 +1195,7 @@ fn ns_for_def(d: def) -> namespace {
ast::def_const(_) { ns_value }
ast::def_arg(_, _) { ns_value }
ast::def_local(_) { ns_value }
ast::def_upvar(_, _, _) { ns_value }
ast::def_variant(_, _) { ns_value }
ast::def_ty(_) { 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
// collects the upvars and packages them up for build_environment.
fn build_closure(cx: &@block_ctxt, upvars: &@[ast::node_id], copying: bool) ->
{ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
fn build_closure(cx: &@block_ctxt, upvars: &@[ast::def], copying: bool)
-> {ptr: ValueRef, ptrty: ty::t, bcx: @block_ctxt} {
let closure_vals: [lval_result] = [];
let closure_tys: [ty::t] = [];
// 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)];
}
// Package up the upvars
for nid: ast::node_id in *upvars {
closure_vals += [trans_var(cx, cx.sp, nid)];
for def in *upvars {
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);
if !copying { ty = ty::mk_mut_ptr(bcx_tcx(cx), 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
// with the upvars and type descriptors.
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 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;
}
// Load the acutal upvars.
for upvar_id: ast::node_id in *upvars {
// Load the actual upvars.
for upvar_def in *upvars {
let upvarptr = GEP_tup_like(bcx, ty, llclosure, path + [i as int]);
bcx = upvarptr.bcx;
let llupvarptr = upvarptr.val;
if !copying { llupvarptr = Load(bcx, llupvarptr); }
let def_id = ast_util::def_id_of_def(bcx_tcx(bcx).
def_map.get(upvar_id));
let def_id = ast_util::def_id_of_def(upvar_def);
fcx.llupvars.insert(def_id.node, llupvarptr);
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 {
let ccx = bcx_ccx(cx);
alt freevars::def_lookup(bcx_tcx(cx), cx.fcx.id, id) {
some(ast::def_upvar(did, _)) {
fn trans_local_var(cx: &@block_ctxt, def: &ast::def) -> lval_result {
alt def {
ast::def_upvar(did, _, _) {
assert (cx.fcx.llupvars.contains_key(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));
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));
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));
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));
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);
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);
alt ty::struct(ccx.tcx, v_tyt.ty) {
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 {
assert (ccx.consts.contains_key(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));
}
}
some(ast::def_native_fn(did)) {
ast::def_native_fn(did) {
let tyt = ty::lookup_item_type(ccx.tcx, did);
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) ->
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,

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 {
alt cx.def_map.find(id) {
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> {
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] {
alt c {
@ -620,21 +607,11 @@ fn expr_to_constr_arg(tcx: ty::ctxt, e: &@expr) -> @constr_arg_use {
alt e.node {
expr_path(p) {
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,
carg_ident({ident: p.node.idents[0],
node: l_id.node}));
carg_ident({ident: p.node.idents[0], node: 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(_) {
tcx.sess.bug(~"exprs_to_constr_args: non-local variable " +
~"as pred arg");
@ -848,8 +825,9 @@ fn path_to_ident(cx: &ty::ctxt, p: &path) -> ident {
fn local_node_id_to_def_id_strict(fcx: &fn_ctxt, sp: &span, i: &node_id) ->
def_id {
alt local_node_id_to_def(fcx, i) {
some(def_local(d_id)) { ret d_id; }
some(def_arg(a_id, _)) { ret a_id; }
some(def_local(id)) | some(def_arg(id, _)) | some(def_upvar(id, _, _)) {
ret id;
}
some(_) {
fcx.ccx.tcx.sess.span_fatal(sp,
~"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> {
alt local_node_id_to_def(fcx, i) {
some(def_local(d_id)) { some(d_id) }
some(def_arg(a_id, _)) { some(a_id) }
some(def_local(id)) | some(def_arg(id, _)) | some(def_binding(id)) |
some(def_upvar(id, _, _)) { some(id) }
_ { none }
}
}
fn local_node_id_to_local_def_id(fcx: &fn_ctxt, i: &node_id) ->
option::t<node_id> {
alt local_node_id_to_def(fcx, i) {
some(def_local(d_id)) { some(d_id.node) }
some(def_arg(a_id, _)) { some(a_id.node) }
alt local_node_id_to_def_id(fcx, i) {
some(did) { some(did.node) }
_ { 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,
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) {
alt d {
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
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 {
def_local(d_id) {
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) {
let df = node_id_to_def_upvar_strict(fcx, id);
alt df {
handle_var_def(fcx, rslt, node_id_to_def_strict(fcx.ccx.tcx, id), name);
}
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, _) {
let i = bit_num(fcx, ninit(d_id.node, name));
use_var(fcx, d_id.node);
let i = bit_num(fcx, ninit(d_id.node, name));
require_and_preserve(i, rslt);
}
_ {/* nothing to check */ }
@ -369,8 +372,9 @@ fn find_pre_post_expr(fcx: &fn_ctxt, e: @expr) {
expr_fn(f) {
let rslt = expr_pp(fcx.ccx, e);
clear_pp(rslt);
let upvars = freevars::get_freevars(fcx.ccx.tcx, e.id);
for id: node_id in *upvars { handle_var(fcx, rslt, id, ~"upvar"); }
for def in *freevars::get_freevars(fcx.ccx.tcx, e.id) {
handle_var_def(fcx, rslt, def, ~"upvar");
}
}
expr_block(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) {
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_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 {
alt e.node {
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)) {
ret set_in_poststate_ident(fcx, loc.node,
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 fixup_result {
fix_ok(t); // fixup succeeded
fix_err(int); // fixup failed because a type variable was unresolved
}
type var_bindings =
{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_arg(_, _) { ret false; }
ast::def_local(_) { ret false; }
ast::def_upvar(_, _, _) { ret false; }
ast::def_variant(_, _) { ret true; }
ast::def_ty(_) { 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(_) {
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.
fcx.ccx.tcx.sess.unimpl(~"definition variant");
@ -1128,6 +1131,14 @@ fn visit_stmt(s: &@ast::stmt, wbcx: &wb_ctxt, v: &wb_vt) {
fn visit_expr(e: &@ast::expr, wbcx: &wb_ctxt, v: &wb_vt) {
if !wbcx.success { ret; }
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);
}
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,
mutable fixups: fixups,
ccx: ccx};
check_constraints(fcx, decl.constraints, decl.inputs);
check_block(fcx, body);
@ -2700,6 +2712,13 @@ fn check_fn(ccx: &@crate_ctxt, f: &ast::_fn, id: &ast::node_id,
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
// force any remaining type vars to be resolved.
// If we have an enclosing function scope, our type variables will be

View File

@ -44,11 +44,7 @@
def_use(def_id);
def_native_ty(def_id);
def_native_fn(def_id);
/* 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);
def_upvar(def_id, @def, bool /* writable */);
}
// 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_native_ty(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; }
}
}
// 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(~"native", ());
words.insert(~"fn", ());
words.insert(~"block", ());
words.insert(~"lambda", ());
words.insert(~"pure", ());
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
// mutate upvars from a lambda.
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
fn main() {
let i = 0;