Get rid of a lot of bind related cruft as part of Issue #2189.

This commit is contained in:
Michael Sullivan 2012-07-09 19:29:14 -07:00
parent 40fc1737b2
commit 95dd9f3204
3 changed files with 41 additions and 290 deletions

View File

@ -2660,7 +2660,7 @@ fn trans_lval(cx: block, e: @ast::expr) -> lval_result {
alt e.node {
ast::expr_path(_) {
let v = trans_path(cx, e.id);
ret lval_maybe_callee_to_lval(v, expr_ty(cx, e));
ret lval_maybe_callee_to_lval(v, e.span);
}
ast::expr_field(base, ident, _) {
ret trans_rec_field(cx, base, ident);
@ -2711,26 +2711,18 @@ fn non_gc_box_cast(cx: block, val: ValueRef) -> ValueRef {
PointerCast(cx, val, non_gc_t)
}
fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result {
let must_bind = alt c.env { self_env(_, _, _) { true } _ { false } };
if must_bind {
let n_args = ty::ty_fn_args(ty).len();
let args = vec::from_elem(n_args, none);
let space = alloc_ty(c.bcx, ty);
let bcx = closure::trans_bind_1(c.bcx, ty, c, args, ty,
save_in(space));
add_clean_temp(bcx, space, ty);
{bcx: bcx, val: space, kind: temporary}
} else {
alt check c.env {
is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} }
null_env {
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
null_env_ptr(c.bcx));
{bcx: c.bcx, val: llfn, kind: temporary}
}
}
fn lval_maybe_callee_to_lval(c: lval_maybe_callee, sp: span) -> lval_result {
alt c.env {
self_env(*) {
c.bcx.sess().span_bug(sp, "implicitly binding method call");
}
is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} }
null_env {
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
null_env_ptr(c.bcx));
{bcx: c.bcx, val: llfn, kind: temporary}
}
}
}
@ -3605,7 +3597,7 @@ fn trans_expr(bcx: block, e: @ast::expr, dest: dest) -> block {
ast::expr_field(base, _, _) {
if dest == ignore { ret trans_expr(bcx, base, ignore); }
let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e);
let lv = lval_maybe_callee_to_lval(callee, ty);
let lv = lval_maybe_callee_to_lval(callee, e.span);
revoke_clean(lv.bcx, lv.val);
memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty);
ret lv.bcx;

View File

@ -90,9 +90,6 @@ import dvec::extensions;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
enum environment_value {
// Evaluate expr and store result in env (used for bind).
env_expr(@ast::expr, ty::t),
// Copy the value from this llvm ValueRef into the environment.
env_copy(ValueRef, ty::t, lval_kind),
@ -105,7 +102,6 @@ enum environment_value {
fn ev_to_str(ccx: @crate_ctxt, ev: environment_value) -> str {
alt ev {
env_expr(ex, _) { expr_to_str(ex) }
env_copy(v, t, lk) { #fmt("copy(%s,%s)", val_str(ccx.tn, v),
ty_to_str(ccx.tcx, t)) }
env_move(v, t, lk) { #fmt("move(%s,%s)", val_str(ccx.tn, v),
@ -123,7 +119,7 @@ fn mk_tuplified_uniq_cbox_ty(tcx: ty::ctxt, cdata_ty: ty::t) -> ty::t {
// Given a closure ty, emits a corresponding tuple ty
fn mk_closure_tys(tcx: ty::ctxt,
bound_values: ~[environment_value])
-> (ty::t, ~[ty::t]) {
-> ty::t {
let mut bound_tys = ~[];
// Compute the closed over data
@ -132,7 +128,6 @@ fn mk_closure_tys(tcx: ty::ctxt,
env_copy(_, t, _) { t }
env_move(_, t, _) { t }
env_ref(_, t, _) { t }
env_expr(_, t) { t }
});
}
let bound_data_ty = ty::mk_tup(tcx, bound_tys);
@ -140,7 +135,7 @@ fn mk_closure_tys(tcx: ty::ctxt,
let cdata_ty = ty::mk_tup(tcx, ~[ty::mk_tup(tcx, ~[]),
bound_data_ty]);
#debug["cdata_ty=%s", ty_to_str(tcx, cdata_ty)];
ret (cdata_ty, bound_tys);
ret cdata_ty;
}
fn allocate_cbox(bcx: block,
@ -196,8 +191,7 @@ fn store_environment(bcx: block,
let ccx = bcx.ccx(), tcx = ccx.tcx;
// compute the shape of the closure
let (cdata_ty, bound_tys) =
mk_closure_tys(tcx, bound_values);
let cdata_ty = mk_closure_tys(tcx, bound_values);
// allocate closure in the heap
let llbox = allocate_cbox(bcx, ck, cdata_ty);
@ -225,11 +219,6 @@ fn store_environment(bcx: block,
let bound_data = GEPi(bcx, llbox,
~[0u, abi::box_field_body, abi::closure_body_bindings, i]);
alt bv {
env_expr(e, _) {
bcx = base::trans_expr_save_in(bcx, e, bound_data);
add_clean_temp_mem(bcx, bound_data, bound_tys[i]);
vec::push(temp_cleanups, bound_data);
}
env_copy(val, ty, owned) {
let val1 = load_if_immediate(bcx, val, ty);
bcx = base::copy_val(bcx, INIT, bound_data, val1, ty);
@ -415,70 +404,6 @@ fn trans_expr_fn(bcx: block,
ret bcx;
}
fn trans_bind_1(cx: block, outgoing_fty: ty::t,
f_res: lval_maybe_callee,
args: ~[option<@ast::expr>], pair_ty: ty::t,
dest: dest) -> block {
let _icx = cx.insn_ctxt("closure::trans_bind1");
let ccx = cx.ccx();
let mut bound: ~[@ast::expr] = ~[];
for vec::each(args) |argopt| {
alt argopt { none { } some(e) { vec::push(bound, e); } }
}
let mut bcx = f_res.bcx;
if dest == ignore {
for vec::each(bound) |ex| { bcx = trans_expr(bcx, ex, ignore); }
ret bcx;
}
if bound.len() == 0u &&
(f_res.env == null_env || f_res.env == is_closure) {
// Trivial 'binding': just return the closure
let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, pair_ty);
ret lv.bcx;
}
// Arrange for the bound function to live in the first binding spot
// if the function is not statically known.
let (env_vals, target_info) = alt f_res.env {
null_env { (~[], target_static(f_res.val)) }
is_closure {
// Cast the function we are binding to be the type that the
// closure will expect it to have. The type the closure knows
// about has the type parameters substituted with the real types.
let llclosurety = T_ptr(type_of(ccx, outgoing_fty));
let src_loc = PointerCast(bcx, f_res.val, llclosurety);
(~[env_copy(src_loc, pair_ty, owned)], target_closure)
}
self_env(slf, slf_t, none) {
(~[env_copy(slf, slf_t, owned)], target_static_self(f_res.val))
}
self_env(_, slf_t, some(slf)) {
let cast = PointerCast(bcx, f_res.val, T_ptr(T_nil()));
(~[env_copy(cast, ty::mk_nil_ptr(ccx.tcx), owned_imm),
env_copy(slf, slf_t, owned_imm)], target_self)
}
};
// Actually construct the closure
let {llbox, cdata_ty, bcx} = store_environment(
bcx, vec::append(env_vals,
vec::map(bound, |x| {
env_expr(x, expr_ty(bcx, x))
})),
ty::ck_box);
// Make thunk
let llthunk = trans_bind_thunk(
cx.fcx.ccx, cx.fcx.path, pair_ty, outgoing_fty, args,
cdata_ty, target_info);
// Fill the function pair
fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox);
ret bcx;
}
fn make_fn_glue(
cx: block,
v: ValueRef,
@ -611,163 +536,3 @@ fn make_opaque_cbox_free_glue(
}
}
}
enum target_info {
target_closure,
target_static(ValueRef),
target_self,
target_static_self(ValueRef),
}
// pth is cx.path
fn trans_bind_thunk(ccx: @crate_ctxt,
path: path,
incoming_fty: ty::t,
outgoing_fty: ty::t,
args: ~[option<@ast::expr>],
cdata_ty: ty::t,
target_info: target_info)
-> {val: ValueRef, ty: TypeRef} {
let _icx = ccx.insn_ctxt("closure::trans_bind_thunk");
let tcx = ccx.tcx;
#debug["trans_bind_thunk[incoming_fty=%s,outgoing_fty=%s,\
cdata_ty=%s]/~",
ty_to_str(tcx, incoming_fty),
ty_to_str(tcx, outgoing_fty),
ty_to_str(tcx, cdata_ty)];
// Here we're not necessarily constructing a thunk in the sense of
// "function with no arguments". The result of compiling 'bind f(foo,
// bar, baz)' would be a thunk that, when called, applies f to those
// arguments and returns the result. But we're stretching the meaning of
// the word "thunk" here to also mean the result of compiling, say, 'bind
// f(foo, _, baz)', or any other bind expression that binds f and leaves
// some (or all) of the arguments unbound.
// Here, 'incoming_fty' is the type of the entire bind expression, while
// 'outgoing_fty' is the type of the function that is having some of its
// arguments bound. If f is a function that takes three arguments of type
// int and returns int, and we're translating, say, 'bind f(3, _, 5)',
// then outgoing_fty is the type of f, which is (int, int, int) -> int,
// and incoming_fty is the type of 'bind f(3, _, 5)', which is int -> int.
// Once translated, the entire bind expression will be the call f(foo,
// bar, baz) wrapped in a (so-called) thunk that takes 'bar' as its
// argument and that has bindings of 'foo' to 3 and 'baz' to 5 and a
// pointer to 'f' all saved in its environment. So, our job is to
// construct and return that thunk.
// Give the thunk a name, type, and value.
let s = mangle_internal_name_by_path_and_seq(ccx, path, @"thunk");
let llthunk_ty = get_pair_fn_ty(type_of(ccx, incoming_fty));
let llthunk = decl_internal_cdecl_fn(ccx.llmod, s, llthunk_ty);
// Create a new function context and block context for the thunk, and hold
// onto a pointer to the first block in the function for later use.
let fcx = new_fn_ctxt(ccx, path, llthunk, none);
let mut bcx = top_scope_block(fcx, none);
let lltop = bcx.llbb;
// Since we might need to construct derived tydescs that depend on
// our bound tydescs, we need to load tydescs out of the environment
// before derived tydescs are constructed. To do this, we load them
// in the load_env block.
let l_bcx = raw_block(fcx, fcx.llloadenv);
// The 'llenv' that will arrive in the thunk we're creating is an
// environment that will contain the values of its arguments and a
// pointer to the original function. This environment is always
// stored like an opaque box (see big comment at the header of the
// file), so we load the body body, which contains the type descr
// and cached data.
let llcdata = base::opaque_box_body(l_bcx, cdata_ty, fcx.llenv);
// "target", in this context, means the function that's having some of its
// arguments bound and that will be called inside the thunk we're
// creating. (In our running example, target is the function f.) Pick
// out the pointer to the target function from the environment. The
// target function lives in the first binding spot.
let (lltargetfn, lltargetenv, starting_idx) = alt target_info {
target_static(fptr) {
(fptr, llvm::LLVMGetUndef(T_opaque_cbox_ptr(ccx)), 0u)
}
target_closure {
let pair = GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, 0u]);
let lltargetenv =
Load(bcx, GEPi(bcx, pair, ~[0u, abi::fn_field_box]));
let lltargetfn = Load
(bcx, GEPi(bcx, pair, ~[0u, abi::fn_field_code]));
(lltargetfn, lltargetenv, 1u)
}
target_self {
let fptr = Load(bcx, GEPi(bcx, llcdata,
~[0u, abi::closure_body_bindings, 0u]));
let slfbox =
GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, 1u]);
let selfptr =
GEPi(bcx, Load(bcx, slfbox), ~[0u, abi::box_field_body]);
(fptr, PointerCast(bcx, selfptr, T_opaque_cbox_ptr(ccx)), 2u)
}
target_static_self(fptr) {
let slfptr =
GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, 0u]);
(fptr, PointerCast(bcx, slfptr, T_opaque_cbox_ptr(ccx)), 1u)
}
};
// And then, pick out the target function's own environment. That's what
// we'll use as the environment the thunk gets.
// Get the types of the arguments to f.
let outgoing_args = ty::ty_fn_args(outgoing_fty);
// Set up the three implicit arguments to the thunk.
let mut llargs: ~[ValueRef] = ~[fcx.llretptr, lltargetenv];
let mut a: uint = first_real_arg; // retptr, env come first
let mut b: uint = starting_idx;
let mut outgoing_arg_index: uint = 0u;
for vec::each(args) |arg| {
let out_arg = outgoing_args[outgoing_arg_index];
alt arg {
// Arg provided at binding time; thunk copies it from
// closure.
some(e) {
let mut val =
GEPi(bcx, llcdata, ~[0u, abi::closure_body_bindings, b]);
alt ty::resolved_mode(tcx, out_arg.mode) {
ast::by_val {
val = Load(bcx, val);
}
ast::by_copy {
let alloc = alloc_ty(bcx, out_arg.ty);
memmove_ty(bcx, alloc, val, out_arg.ty);
bcx = take_ty(bcx, alloc, out_arg.ty);
val = alloc;
}
ast::by_ref | ast::by_mutbl_ref | ast::by_move { }
}
vec::push(llargs, val);
b += 1u;
}
// Arg will be provided when the thunk is invoked.
none {
vec::push(llargs, llvm::LLVMGetParam(llthunk, a as c_uint));
a += 1u;
}
}
outgoing_arg_index += 1u;
}
// Cast the outgoing function to the appropriate type.
// This is necessary because the type of the function that we have
// in the closure does not know how many type descriptors the function
// needs to take.
let lltargetty = type_of_fn_from_ty(ccx, outgoing_fty);
let lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(lltargetty));
Call(bcx, lltargetfn, llargs);
build_return(bcx);
finish_fn(fcx, lltop);
ret {val: llthunk, ty: llthunk_ty};
}

View File

@ -710,18 +710,18 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
#debug(">> typechecking expr %d (%s)",
expr.id, syntax::print::pprust::expr_to_str(expr));
// A generic function to factor out common logic from call and bind
// expressions.
fn check_call_or_bind(
// A generic function to factor out common logic from call and
// overloaded operations
fn check_call_inner(
fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id, in_fty: ty::t,
args: ~[option<@ast::expr>]) -> {fty: ty::t, bot: bool} {
args: ~[@ast::expr]) -> {fty: ty::t, bot: bool} {
let mut bot = false;
// Replace all region parameters in the arguments and return
// type with fresh region variables.
#debug["check_call_or_bind: before universal quant., in_fty=%s",
#debug["check_call_inner: before universal quant., in_fty=%s",
fcx.infcx.ty_to_str(in_fty)];
// This is subtle: we expect `fty` to be a function type, which
@ -747,7 +747,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
};
let fty = ty::mk_fn(fcx.tcx(), fn_ty);
#debug["check_call_or_bind: after universal quant., fty=%s",
#debug["check_call_inner: after universal quant., fty=%s",
fcx.infcx.ty_to_str(fty)];
let supplied_arg_count = vec::len(args);
@ -782,23 +782,18 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// of arguments when we typecheck the functions. This isn't really the
// right way to do this.
for [false, true]/_.each |check_blocks| {
for args.eachi |i, a_opt| {
alt a_opt {
some(a) {
let is_block = alt a.node {
ast::expr_fn_block(*) { true }
_ { false }
};
if is_block == check_blocks {
let arg_ty = arg_tys[i];
bot |= check_expr_with_unifier(
fcx, a, some(arg_ty),
|| demand::assign(fcx, a.span, call_expr_id,
arg_ty, a)
for args.eachi |i, a| {
let is_block = alt a.node {
ast::expr_fn_block(*) { true }
_ { false }
};
if is_block == check_blocks {
let arg_ty = arg_tys[i];
bot |= check_expr_with_unifier(
fcx, a, some(arg_ty),
|| demand::assign(fcx, a.span, call_expr_id,
arg_ty, a)
);
}
}
none { }
}
}
}
@ -824,9 +819,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
// Call the generic checker.
let fty = {
let args_opt = args.map(|arg| some(arg));
let r = check_call_or_bind(fcx, sp, call_expr_id,
fn_ty, args_opt);
let r = check_call_inner(fcx, sp, call_expr_id,
fn_ty, args);
bot |= r.bot;
r.fty
};
@ -890,7 +884,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
}
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr,
self_ex: @ast::expr, self_t: ty::t,
opname: str, args: ~[option<@ast::expr>])
opname: str, args: ~[@ast::expr])
-> option<(ty::t, bool)> {
let callee_id = ast_util::op_expr_callee_id(op_ex);
let lkup = method::lookup(fcx, op_ex, self_ex, op_ex.id,
@ -899,8 +893,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
some(origin) {
let {fty: method_ty, bot: bot} = {
let method_ty = fcx.node_ty(callee_id);
check_call_or_bind(fcx, op_ex.span, op_ex.id,
method_ty, args)
check_call_inner(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), bot))
@ -965,7 +959,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
some(name) {
alt lookup_op_method(fcx, ex,
lhs_expr, lhs_resolved_t,
name, ~[some(rhs)]) {
name, ~[rhs]) {
some(pair) { ret pair; }
_ {}
}
@ -1589,7 +1583,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
let resolved = structurally_resolved_type(fcx, expr.span,
raw_base_t);
alt lookup_op_method(fcx, expr, base, resolved, "[]",
~[some(idx)]) {
~[idx]) {
some((ret_ty, _)) { fcx.write_ty(id, ret_ty); }
_ {
tcx.sess.span_fatal(