rustc: Instantiate "caller" regions when calling functions

This commit is contained in:
Patrick Walton 2012-03-13 17:31:04 -07:00
parent aeb445b2ea
commit fc50abe6c5
2 changed files with 64 additions and 12 deletions

View file

@ -38,7 +38,9 @@ type region_map = {
/* Mapping from a region name to its function. */
region_name_to_fn: hashmap<ast::def_id,ast::node_id>,
/* Mapping from an AST type node to the region that `&` resolves to. */
ast_type_to_inferred_region: hashmap<ast::node_id,ty::region>
ast_type_to_inferred_region: hashmap<ast::node_id,ty::region>,
/* Mapping from a call site (or `bind` site) to its containing block. */
call_site_to_block: hashmap<ast::node_id,ast::node_id>
};
type ctxt = {
@ -237,6 +239,16 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
in_alt: false with cx};
visit::visit_expr(expr, new_cx, visitor);
}
ast::expr_call(_, _, _) | ast::expr_bind(_, _) {
// Record the block that this call appears in.
alt cx.parent {
pa_block(blk_id) {
cx.region_map.call_site_to_block.insert(expr.id, blk_id);
}
_ { cx.sess.span_bug(expr.span, "expr outside of block?!"); }
}
visit::visit_expr(expr, cx, visitor);
}
_ { visit::visit_expr(expr, cx, visitor); }
}
}
@ -263,7 +275,8 @@ fn resolve_crate(sess: session, def_map: resolve::def_map, crate: @ast::crate)
local_blocks: map::new_int_hash(),
region_name_to_fn: new_def_hash(),
ast_type_to_inferred_region:
map::new_int_hash()},
map::new_int_hash(),
call_site_to_block: map::new_int_hash()},
mut bindings: @list::nil,
mut queued_locals: [],
parent: pa_crate,

View file

@ -1970,8 +1970,42 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
// A generic function to factor out common logic from call and bind
// expressions.
fn check_call_or_bind(fcx: @fn_ctxt, sp: span, fty: ty::t,
args: [option<@ast::expr>]) -> bool {
fn check_call_or_bind(fcx: @fn_ctxt, sp: span, id: ast::node_id,
fty: ty::t, args: [option<@ast::expr>]) -> bool {
// Replaces "caller" regions in the arguments with the local region.
fn instantiate_caller_regions(fcx: @fn_ctxt, id: ast::node_id,
args: [ty::arg]) -> [ty::arg] {
let site_to_block = fcx.ccx.tcx.region_map.call_site_to_block;
let block_id = alt site_to_block.find(id) {
none {
// This can happen for those expressions that are
// synthesized during typechecking; e.g. during
// check_constraints().
ret args;
}
some(block_id) { block_id }
};
let region = ty::re_block(block_id);
ret vec::map(args) {|arg|
if ty::type_has_rptrs(arg.ty) {
let ty = ty::fold_ty(fcx.ccx.tcx, ty::fm_rptr({|r|
alt r {
ty::re_caller(_) {
// FIXME: We should not recurse into nested
// function types here.
region
}
_ { r }
}
}), arg.ty);
{ty: ty with arg}
} else {
arg
}
};
}
let sty = structure_of(fcx, sp, fty);
// Grab the argument types
let arg_tys = alt sty {
@ -2009,6 +2043,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
arg_tys = vec::from_elem(supplied_arg_count, dummy);
}
arg_tys = instantiate_caller_regions(fcx, id, arg_tys);
// Check the arguments.
// We do this in a pretty awful way: first we typecheck any arguments
// that are not anonymous functions, then we typecheck the anonymous
@ -2049,7 +2085,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
// A generic function for checking call expressions
fn check_call(fcx: @fn_ctxt, sp: span, f: @ast::expr, args: [@ast::expr])
fn check_call(fcx: @fn_ctxt, sp: span, id: ast::node_id, f: @ast::expr,
args: [@ast::expr])
-> bool {
let args_opt_0: [option<@ast::expr>] = [];
for arg: @ast::expr in args {
@ -2058,13 +2095,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let bot = check_expr(fcx, f);
// Call the generic checker.
bot | check_call_or_bind(fcx, sp, expr_ty(fcx.ccx.tcx, f), args_opt_0)
bot | check_call_or_bind(fcx, sp, id, expr_ty(fcx.ccx.tcx, f),
args_opt_0)
}
// A generic function for doing all of the checking for call expressions
fn check_call_full(fcx: @fn_ctxt, sp: span, f: @ast::expr,
args: [@ast::expr], id: ast::node_id) -> bool {
let bot = check_call(fcx, sp, f, args);
fn check_call_full(fcx: @fn_ctxt, sp: span, id: ast::node_id,
f: @ast::expr, args: [@ast::expr]) -> bool {
let bot = check_call(fcx, sp, id, f, args);
/* here we're kind of hosed, as f can be any expr
need to restrict it to being an explicit expr_path if we're
inside a pure function, and need an environment mapping from
@ -2145,7 +2183,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
alt lookup_method(fcx, op_ex, callee_id, opname, self_t, []) {
some(origin) {
let method_ty = ty::node_id_to_type(fcx.ccx.tcx, callee_id);
check_call_or_bind(fcx, op_ex.span, method_ty, args);
check_call_or_bind(fcx, op_ex.span, op_ex.id, method_ty, args);
fcx.ccx.method_map.insert(op_ex.id, origin);
some(ty::ty_fn_ret(method_ty))
}
@ -2472,7 +2510,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
ast::expr_bind(f, args) {
// Call the generic checker.
bot = check_expr(fcx, f);
bot |= check_call_or_bind(fcx, expr.span, expr_ty(tcx, f), args);
bot |= check_call_or_bind(fcx, expr.span, expr.id, expr_ty(tcx, f),
args);
// Pull the argument and return types out.
let proto, arg_tys, rt, cf, constrs;
@ -2518,7 +2557,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
write_ty(tcx, id, ft);
}
ast::expr_call(f, args, _) {
bot = check_call_full(fcx, expr.span, f, args, expr.id);
bot = check_call_full(fcx, expr.span, expr.id, f, args);
}
ast::expr_cast(e, t) {
bot = check_expr(fcx, e);