1
Fork 0

Saner approach to lvalues and callable values in trans

LValues no longer carry information about generics and objs, instead
there's an extended form of lvalue, lval_maybe_callee, only used by
call and bind, which holds this info.

This makes it possible to take the value of a method and get a working
closure, and will (with some more work) allow us to call statically
known functions without loading from their pair.

Closes #435
Closes #758
This commit is contained in:
Marijn Haverbeke 2011-09-16 16:27:34 +02:00
parent d9c664e3fa
commit 575aae407b
3 changed files with 220 additions and 197 deletions

View file

@ -2048,7 +2048,7 @@ fn copy_val_no_check(cx: @block_ctxt, action: copy_action, dst: ValueRef,
// doesn't need to be dropped. // doesn't need to be dropped.
fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef, fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef,
src: lval_result, t: ty::t) -> @block_ctxt { src: lval_result, t: ty::t) -> @block_ctxt {
let src_val = src.res.val; let src_val = src.val;
let tcx = bcx_tcx(cx); let tcx = bcx_tcx(cx);
if ty::type_is_scalar(tcx, t) || ty::type_is_native(tcx, t) { if ty::type_is_scalar(tcx, t) || ty::type_is_native(tcx, t) {
if src.is_mem { src_val = Load(cx, src_val); } if src.is_mem { src_val = Load(cx, src_val); }
@ -2060,7 +2060,7 @@ fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef,
if src.is_mem { src_val = Load(cx, src_val); } if src.is_mem { src_val = Load(cx, src_val); }
if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); } if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); }
Store(cx, src_val, dst); Store(cx, src_val, dst);
if src.is_mem { ret zero_alloca(cx, src.res.val, t).bcx; } if src.is_mem { ret zero_alloca(cx, src.val, t).bcx; }
// If we're here, it must be a temporary. // If we're here, it must be a temporary.
ret revoke_clean(cx, src_val, t); ret revoke_clean(cx, src_val, t);
@ -2083,7 +2083,7 @@ fn move_val_if_temp(cx: @block_ctxt, action: copy_action, dst: ValueRef,
// Lvals in memory are not temporaries. Copy them. // Lvals in memory are not temporaries. Copy them.
if src.is_mem { if src.is_mem {
ret copy_val(cx, action, dst, load_if_immediate(cx, src.res.val, t), ret copy_val(cx, action, dst, load_if_immediate(cx, src.val, t),
t); t);
} }
ret move_val(cx, action, dst, src, t); ret move_val(cx, action, dst, src, t);
@ -2165,8 +2165,8 @@ fn trans_unary(cx: @block_ctxt, op: ast::unop, e: @ast::expr,
} }
ast::box(_) { ast::box(_) {
let lv = trans_lval(cx, e); let lv = trans_lval(cx, e);
let box_ty = node_id_type(bcx_ccx(lv.res.bcx), id); let box_ty = node_id_type(bcx_ccx(lv.bcx), id);
let sub = trans_malloc_boxed(lv.res.bcx, e_ty); let sub = trans_malloc_boxed(lv.bcx, e_ty);
let body = sub.body; let body = sub.body;
add_clean_temp(cx, sub.box, box_ty); add_clean_temp(cx, sub.box, box_ty);
@ -2562,7 +2562,7 @@ fn build_environment(bcx: @block_ctxt, lltydescs: [ValueRef],
bcx = bound.bcx; bcx = bound.bcx;
if copying { if copying {
bcx = move_val_if_temp(bcx, INIT, bound.val, lv, bound_tys[i]); bcx = move_val_if_temp(bcx, INIT, bound.val, lv, bound_tys[i]);
} else { Store(bcx, lv.res.val, bound.val); } } else { Store(bcx, lv.val, bound.val); }
i += 1u; i += 1u;
} }
@ -2766,8 +2766,8 @@ fn trans_for_each(cx: @block_ctxt, local: @ast::local, seq: @ast::expr,
ast::expr_call(f, args) { ast::expr_call(f, args) {
let pair = let pair =
create_real_fn_pair(cx, iter_body_llty, lliterbody, llenv.ptr); create_real_fn_pair(cx, iter_body_llty, lliterbody, llenv.ptr);
r = trans_call(cx, f, some(pair), args, seq.id).res; let r = trans_call(cx, f, some(pair), args, seq.id);
ret rslt(r.bcx, C_nil()); ret rslt(r.res.bcx, C_nil());
} }
} }
} }
@ -2813,24 +2813,26 @@ type generic_info =
static_tis: [option::t<@tydesc_info>], static_tis: [option::t<@tydesc_info>],
tydescs: [ValueRef]}; tydescs: [ValueRef]};
type lval_result = type lval_result = {bcx: @block_ctxt,
{res: result, val: ValueRef,
is_mem: bool, is_mem: bool};
generic: option::t<generic_info>, tag callee_env { some_env(ValueRef); null_env; is_closure; }
llobj: option::t<ValueRef>}; type lval_maybe_callee = {bcx: @block_ctxt,
val: ValueRef,
is_mem: bool,
env: callee_env,
generic: option::t<generic_info>};
fn lval_mem(cx: @block_ctxt, val: ValueRef) -> lval_result { fn lval_mem(bcx: @block_ctxt, val: ValueRef) -> lval_result {
ret {res: rslt(cx, val), ret {bcx: bcx, val: val, is_mem: true};
is_mem: true, }
generic: none::<generic_info>, fn lval_val(bcx: @block_ctxt, val: ValueRef) -> lval_result {
llobj: none::<ValueRef>}; ret {bcx: bcx, val: val, is_mem: false};
} }
fn lval_val(cx: @block_ctxt, val: ValueRef) -> lval_result { fn lval_no_env(bcx: @block_ctxt, val: ValueRef, is_mem: bool)
ret {res: rslt(cx, val), -> lval_maybe_callee {
is_mem: false, ret {bcx: bcx, val: val, is_mem: is_mem, env: is_closure, generic: none};
generic: none::<generic_info>,
llobj: none::<ValueRef>};
} }
fn trans_external_path(cx: @block_ctxt, did: ast::def_id, fn trans_external_path(cx: @block_ctxt, did: ast::def_id,
@ -2841,35 +2843,31 @@ fn trans_external_path(cx: @block_ctxt, did: ast::def_id,
type_of_ty_param_kinds_and_ty(lcx, cx.sp, tpt)); type_of_ty_param_kinds_and_ty(lcx, cx.sp, tpt));
} }
fn lval_generic_fn(cx: @block_ctxt, tpt: ty::ty_param_kinds_and_ty, fn lval_static_fn(bcx: @block_ctxt, tpt: ty::ty_param_kinds_and_ty,
fn_id: ast::def_id, id: ast::node_id) -> lval_result { fn_id: ast::def_id, id: ast::node_id) -> lval_maybe_callee {
let lv; let val = if fn_id.crate == ast::local_crate {
if fn_id.crate == ast::local_crate {
// Internal reference. // Internal reference.
assert (bcx_ccx(cx).fn_pairs.contains_key(fn_id.node)); assert (bcx_ccx(bcx).fn_pairs.contains_key(fn_id.node));
lv = lval_val(cx, bcx_ccx(cx).fn_pairs.get(fn_id.node)); bcx_ccx(bcx).fn_pairs.get(fn_id.node)
} else { } else {
// External reference. // External reference.
lv = lval_val(cx, trans_external_path(cx, fn_id, tpt)); trans_external_path(bcx, fn_id, tpt)
} };
let tys = ty::node_id_to_type_params(bcx_tcx(cx), id); let tys = ty::node_id_to_type_params(bcx_tcx(bcx), id);
let gen = none;
if std::vec::len::<ty::t>(tys) != 0u { if std::vec::len::<ty::t>(tys) != 0u {
let bcx = lv.res.bcx; let tydescs = [], tis = [];
let tydescs: [ValueRef] = []; for t in tys {
let tis: [option::t<@tydesc_info>] = [];
for t: ty::t in tys {
// TODO: Doesn't always escape. // TODO: Doesn't always escape.
let ti = none;
let ti = none::<@tydesc_info>;
let td = get_tydesc(bcx, t, true, tps_normal, ti).result; let td = get_tydesc(bcx, t, true, tps_normal, ti).result;
tis += [ti]; tis += [ti];
bcx = td.bcx; bcx = td.bcx;
tydescs += [td.val]; tydescs += [td.val];
} }
let gen = {item_type: tpt.ty, static_tis: tis, tydescs: tydescs}; gen = some({item_type: tpt.ty, static_tis: tis, tydescs: tydescs});
lv = {res: rslt(bcx, lv.res.val), generic: some(gen) with lv};
} }
ret lv; ret {bcx: bcx, val: val, is_mem: true, env: is_closure, generic: gen};
} }
fn lookup_discriminant(lcx: @local_ctxt, vid: ast::def_id) -> ValueRef { fn lookup_discriminant(lcx: @local_ctxt, vid: ast::def_id) -> ValueRef {
@ -2922,21 +2920,25 @@ fn trans_local_var(cx: @block_ctxt, def: ast::def) -> lval_result {
} }
} }
fn trans_var(cx: @block_ctxt, sp: span, def: ast::def, id: ast::node_id) -> fn trans_path(cx: @block_ctxt, p: ast::path, id: ast::node_id)
lval_result { -> lval_maybe_callee {
ret trans_var(cx, p.span, bcx_tcx(cx).def_map.get(id), id);
}
fn trans_var(cx: @block_ctxt, sp: span, def: ast::def, id: ast::node_id)
-> lval_maybe_callee {
let ccx = bcx_ccx(cx); let ccx = bcx_ccx(cx);
alt def { alt def {
ast::def_fn(did, _) | ast::def_native_fn(did) { ast::def_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_static_fn(cx, tyt, did, id);
} }
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(_, _, _, _, _) {
// N-ary variant. // N-ary variant.
ret lval_static_fn(cx, v_tyt, vid, id);
ret lval_generic_fn(cx, v_tyt, vid, id);
} }
_ { _ {
// Nullary variant. // Nullary variant.
@ -2952,43 +2954,44 @@ fn trans_var(cx: @block_ctxt, sp: span, def: ast::def, id: ast::node_id) ->
let lldiscrimptr = GEP(bcx, lltagptr, [C_int(0), C_int(0)]); let lldiscrimptr = GEP(bcx, lltagptr, [C_int(0), C_int(0)]);
Store(bcx, lldiscrim, lldiscrimptr); Store(bcx, lldiscrim, lldiscrimptr);
} }
ret lval_val(bcx, lltagptr); ret lval_no_env(bcx, lltagptr, false);
} }
} }
} }
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_no_env(cx, ccx.consts.get(did.node), true);
} else { } else {
let tp = ty::node_id_to_monotype(ccx.tcx, id); let tp = ty::node_id_to_monotype(ccx.tcx, id);
let k: [ast::kind] = []; let k: [ast::kind] = [];
ret lval_val(cx, let val = trans_external_path(cx, did, {kinds: k, ty: tp});
load_if_immediate(cx, ret lval_no_env(cx, load_if_immediate(cx, val, tp), false);
trans_external_path(cx, did,
{kinds: k,
ty: tp}),
tp));
} }
} }
_ { ret trans_local_var(cx, def); } _ {
let loc = trans_local_var(cx, def);
ret lval_no_env(loc.bcx, loc.val, loc.is_mem);
}
} }
} }
fn trans_path(cx: @block_ctxt, p: ast::path, id: ast::node_id) -> fn trans_field(cx: @block_ctxt, sp: span, base: @ast::expr,
lval_result { field: ast::ident) -> lval_maybe_callee {
ret trans_var(cx, p.span, bcx_tcx(cx).def_map.get(id), id); let {bcx, val} = trans_expr(cx, base);
ret trans_field_inner(bcx, sp, val, ty::expr_ty(bcx_tcx(cx), base),
field);
} }
fn trans_field(cx: @block_ctxt, sp: span, v: ValueRef, t0: ty::t, fn trans_field_inner(cx: @block_ctxt, sp: span, v: ValueRef, t0: ty::t,
field: ast::ident) -> lval_result { field: ast::ident) -> lval_maybe_callee {
let r = autoderef(cx, v, t0); let r = autoderef(cx, v, t0);
let t = r.ty; let t = r.ty;
alt ty::struct(bcx_tcx(cx), t) { alt ty::struct(bcx_tcx(cx), t) {
ty::ty_rec(fields) { ty::ty_rec(fields) {
let ix: uint = ty::field_idx(bcx_ccx(cx).sess, sp, field, fields); let ix: uint = ty::field_idx(bcx_ccx(cx).sess, sp, field, fields);
let v = GEP_tup_like(r.bcx, t, r.val, [0, ix as int]); let v = GEP_tup_like(r.bcx, t, r.val, [0, ix as int]);
ret lval_mem(v.bcx, v.val); ret lval_no_env(v.bcx, v.val, true);
} }
ty::ty_obj(methods) { ty::ty_obj(methods) {
let ix: uint = ty::method_idx(bcx_ccx(cx).sess, sp, field, methods); let ix: uint = ty::method_idx(bcx_ccx(cx).sess, sp, field, methods);
@ -3012,9 +3015,9 @@ fn trans_field(cx: @block_ctxt, sp: span, v: ValueRef, t0: ty::t,
type_of_fn(ccx, sp, ty::ty_fn_proto(tcx, fn_ty), type_of_fn(ccx, sp, ty::ty_fn_proto(tcx, fn_ty),
true, ret_ref, ty::ty_fn_args(tcx, fn_ty), true, ret_ref, ty::ty_fn_args(tcx, fn_ty),
ret_ty, 0u); ret_ty, 0u);
v = PointerCast(r.bcx, v, T_ptr(T_ptr(ll_fn_ty))); v = Load(r.bcx, PointerCast(r.bcx, v, T_ptr(T_ptr(ll_fn_ty))));
let lvo = lval_mem(r.bcx, v); ret {bcx: r.bcx, val: v, is_mem: true,
ret {llobj: some::<ValueRef>(r.val) with lvo}; env: some_env(r.val), generic: none};
} }
_ { bcx_ccx(cx).sess.unimpl("field variant in trans_field"); } _ { bcx_ccx(cx).sess.unimpl("field variant in trans_field"); }
} }
@ -3069,16 +3072,38 @@ fn trans_index(cx: @block_ctxt, sp: span, base: @ast::expr, idx: @ast::expr,
ret lval_mem(next_cx, elt); ret lval_mem(next_cx, elt);
} }
// The additional bool returned indicates whether it's mem (that is fn trans_callee(cx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee {
// represented as an alloca or heap, hence needs a 'load' to be used as an
// immediate).
fn trans_lval_gen(cx: @block_ctxt, e: @ast::expr) -> lval_result {
alt e.node { alt e.node {
ast::expr_path(p) { ret trans_path(cx, p, e.id); } ast::expr_path(p) { ret trans_path(cx, p, e.id); }
ast::expr_field(base, ident) { ast::expr_field(base, ident) {
let r = trans_expr(cx, base); ret trans_field(cx, e.span, base, ident);
let t = ty::expr_ty(bcx_tcx(cx), base); }
ret trans_field(r.bcx, e.span, r.val, t, ident); ast::expr_self_method(ident) {
alt cx.fcx.llself {
some(pair) {
ret trans_field_inner(cx, e.span, pair.v, pair.t, ident);
}
}
}
_ {
let lv = trans_lval(cx, e);
ret lval_no_env(lv.bcx, lv.val, lv.is_mem);
}
}
}
// The additional bool returned indicates whether it's mem (that is
// represented as an alloca or heap, hence needs a 'load' to be used as an
// immediate).
fn trans_lval(cx: @block_ctxt, e: @ast::expr) -> lval_result {
alt e.node {
ast::expr_path(p) {
let v = trans_path(cx, p, e.id);
ret lval_maybe_callee_to_lval(v, ty::expr_ty(bcx_tcx(cx), e));
}
ast::expr_field(base, ident) {
let f = trans_field(cx, e.span, base, ident);
ret lval_maybe_callee_to_lval(f, ty::expr_ty(bcx_tcx(cx), e));
} }
ast::expr_index(base, idx) { ast::expr_index(base, idx) {
ret trans_index(cx, e.span, base, idx, e.id); ret trans_index(cx, e.span, base, idx, e.id);
@ -3110,13 +3135,6 @@ fn trans_lval_gen(cx: @block_ctxt, e: @ast::expr) -> lval_result {
}; };
ret lval_mem(sub.bcx, val); ret lval_mem(sub.bcx, val);
} }
ast::expr_self_method(ident) {
alt copy cx.fcx.llself {
some(pair) {
ret trans_field(cx, e.span, pair.v, pair.t, ident);
}
}
}
ast::expr_call(f, args) { ast::expr_call(f, args) {
let {res: {bcx, val}, by_ref} = let {res: {bcx, val}, by_ref} =
trans_call(cx, f, none, args, e.id); trans_call(cx, f, none, args, e.id);
@ -3124,25 +3142,38 @@ fn trans_lval_gen(cx: @block_ctxt, e: @ast::expr) -> lval_result {
else { ret lval_val(bcx, val); } else { ret lval_val(bcx, val); }
} }
_ { _ {
ret {res: trans_expr(cx, e), let res = trans_expr(cx, e);
is_mem: false, ret lval_val(res.bcx, res.val);
generic: none,
llobj: none};
} }
} }
} }
fn trans_lval(cx: @block_ctxt, e: @ast::expr) -> lval_result { fn maybe_add_env(bcx: @block_ctxt, c: lval_maybe_callee)
let lv = trans_lval_gen(cx, e); -> (bool, ValueRef) {
alt lv.generic { if c.env == is_closure {
(c.is_mem, c.val)
} else {
let env = alt c.env {
null_env. { C_null(T_opaque_closure_ptr(*bcx_ccx(bcx))) }
some_env(e) { e }
};
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
(false, create_real_fn_pair(bcx, llfnty, c.val, env))
}
}
fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result {
alt c.generic {
some(gi) { some(gi) {
let t = ty::expr_ty(bcx_tcx(cx), e); let n_args = std::vec::len(ty::ty_fn_args(bcx_tcx(c.bcx), ty));
let n_args = std::vec::len(ty::ty_fn_args(bcx_tcx(cx), t));
let args = std::vec::init_elt(none::<@ast::expr>, n_args); let args = std::vec::init_elt(none::<@ast::expr>, n_args);
let bound = trans_bind_1(lv.res.bcx, e, lv, args, e.id); let {bcx, val} = trans_bind_1(c.bcx, ty, c, args, ty);
ret lval_val(bound.bcx, bound.val); ret lval_val(bcx, val);
}
none. {
let (is_mem, val) = maybe_add_env(c.bcx, c);
ret {bcx: c.bcx, val: val, is_mem: is_mem};
} }
none. { ret lv; }
} }
} }
@ -3294,24 +3325,26 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
// creating. (In our running example, target is the function f.) Pick // creating. (In our running example, target is the function f.) Pick
// out the pointer to the target function from the environment. The // out the pointer to the target function from the environment. The
// target function lives in the first binding spot. // target function lives in the first binding spot.
let (lltarget, starting_idx) = let (lltargetfn, lltargetenv, starting_idx) = alt target_fn {
alt target_fn { some(fptr) {
some(lltarget) { (lltarget, 0) } (fptr, C_null(T_opaque_closure_ptr(*bcx_ccx(bcx))), 0)
none. { }
let lltarget = none. {
GEP_tup_like(bcx, closure_ty, llclosure, let {bcx: cx, val: pair} =
[0, abi::box_rc_field_body, GEP_tup_like(bcx, closure_ty, llclosure,
abi::closure_elt_bindings, 0]); [0, abi::box_rc_field_body,
bcx = lltarget.bcx;; abi::closure_elt_bindings, 0]);
(lltarget.val, 1) let lltargetenv =
} Load(cx, GEP(cx, pair, [C_int(0), C_int(abi::fn_field_box)]));
}; let lltargetfn = Load
(cx, GEP(cx, pair, [C_int(0), C_int(abi::fn_field_code)]));
bcx = cx;
(lltargetfn, lltargetenv, 1)
}
};
// And then, pick out the target function's own environment. That's what // And then, pick out the target function's own environment. That's what
// we'll use as the environment the thunk gets. // we'll use as the environment the thunk gets.
let lltargetclosure =
GEP(bcx, lltarget, [C_int(0), C_int(abi::fn_field_box)]);
lltargetclosure = Load(bcx, lltargetclosure);
// Get f's return type, which will also be the return type of the entire // Get f's return type, which will also be the return type of the entire
// bind expression. // bind expression.
@ -3331,7 +3364,7 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
} }
// Set up the three implicit arguments to the thunk. // Set up the three implicit arguments to the thunk.
let llargs: [ValueRef] = [llretptr, fcx.lltaskptr, lltargetclosure]; let llargs: [ValueRef] = [llretptr, fcx.lltaskptr, lltargetenv];
// Copy in the type parameters. // Copy in the type parameters.
let i: uint = 0u; let i: uint = 0u;
@ -3356,10 +3389,6 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
let out_arg = outgoing_args[outgoing_arg_index]; let out_arg = outgoing_args[outgoing_arg_index];
let llout_arg_ty = llout_arg_tys[outgoing_arg_index]; let llout_arg_ty = llout_arg_tys[outgoing_arg_index];
alt arg { alt arg {
// Arg provided at binding time; thunk copies it from // Arg provided at binding time; thunk copies it from
// closure. // closure.
some(e) { some(e) {
@ -3378,8 +3407,6 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
b += 1; b += 1;
} }
// Arg will be provided when the thunk is invoked. // Arg will be provided when the thunk is invoked.
none. { none. {
let arg: ValueRef = llvm::LLVMGetParam(llthunk, a); let arg: ValueRef = llvm::LLVMGetParam(llthunk, a);
@ -3393,18 +3420,13 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
outgoing_arg_index += 1u; outgoing_arg_index += 1u;
} }
let lltargetfn =
GEP(bcx, lltarget, [C_int(0), C_int(abi::fn_field_code)]);
// Cast the outgoing function to the appropriate type. // Cast the outgoing function to the appropriate type.
// This is necessary because the type of the function that we have // This is necessary because the type of the function that we have
// in the closure does not know how many type descriptors the function // in the closure does not know how many type descriptors the function
// needs to take. // needs to take.
let lltargetty = let lltargetty =
type_of_fn_from_ty(bcx_ccx(bcx), sp, outgoing_fty, ty_param_count); type_of_fn_from_ty(bcx_ccx(bcx), sp, outgoing_fty, ty_param_count);
lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(T_ptr(lltargetty))); lltargetfn = PointerCast(bcx, lltargetfn, T_ptr(lltargetty));
lltargetfn = Load(bcx, lltargetfn);
FastCall(bcx, lltargetfn, llargs); FastCall(bcx, lltargetfn, llargs);
build_return(bcx); build_return(bcx);
finish_fn(fcx, lltop); finish_fn(fcx, lltop);
@ -3413,19 +3435,20 @@ fn trans_bind_thunk(cx: @local_ctxt, sp: span, incoming_fty: ty::t,
fn trans_bind(cx: @block_ctxt, f: @ast::expr, args: [option::t<@ast::expr>], fn trans_bind(cx: @block_ctxt, f: @ast::expr, args: [option::t<@ast::expr>],
id: ast::node_id) -> result { id: ast::node_id) -> result {
let f_res = trans_lval_gen(cx, f); let f_res = trans_callee(cx, f);
ret trans_bind_1(cx, f, f_res, args, id); ret trans_bind_1(cx, ty::expr_ty(bcx_tcx(cx), f), f_res, args,
ty::node_id_to_type(bcx_tcx(cx), id));
} }
fn trans_bind_1(cx: @block_ctxt, f: @ast::expr, f_res: lval_result, fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
args: [option::t<@ast::expr>], id: ast::node_id) -> result { f_res: lval_maybe_callee,
args: [option::t<@ast::expr>], pair_ty: ty::t) -> result {
let bound: [@ast::expr] = []; let bound: [@ast::expr] = [];
for argopt: option::t<@ast::expr> in args { for argopt: option::t<@ast::expr> in args {
alt argopt { none. { } some(e) { bound += [e]; } } alt argopt { none. { } some(e) { bound += [e]; } }
} }
// Figure out which tydescs we need to pass, if any. // Figure out which tydescs we need to pass, if any.
let outgoing_fty: ty::t = ty::expr_ty(bcx_tcx(cx), f);
let outgoing_fty_real; // the type with typarams still in it let outgoing_fty_real; // the type with typarams still in it
let lltydescs: [ValueRef]; let lltydescs: [ValueRef];
alt f_res.generic { alt f_res.generic {
@ -3439,10 +3462,15 @@ fn trans_bind_1(cx: @block_ctxt, f: @ast::expr, f_res: lval_result,
let ty_param_count = std::vec::len(lltydescs); let ty_param_count = std::vec::len(lltydescs);
if std::vec::len(bound) == 0u && ty_param_count == 0u { if std::vec::len(bound) == 0u && ty_param_count == 0u {
// Trivial 'binding': just return the static pair-ptr. // Trivial 'binding': just return the closure
ret f_res.res; let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
ret rslt(lv.bcx, lv.val);
} }
let bcx = f_res.res.bcx; let bcx = f_res.bcx;
let (is_mem, closure) = alt f_res.env {
null_env. { (true, none) }
_ { let (mem, cl) = maybe_add_env(cx, f_res); (mem, some(cl)) }
};
// FIXME: should follow from a precondition on trans_bind_1 // FIXME: should follow from a precondition on trans_bind_1
let ccx = bcx_ccx(cx); let ccx = bcx_ccx(cx);
@ -3450,22 +3478,24 @@ fn trans_bind_1(cx: @block_ctxt, f: @ast::expr, f_res: lval_result,
// Arrange for the bound function to live in the first binding spot // Arrange for the bound function to live in the first binding spot
// if the function is not statically known. // if the function is not statically known.
let (bound_tys, bound_vals, target_res) = let (bound_tys, bound_vals, target_res) = alt closure {
if f_res.is_mem { some(cl) {
// Cast the function we are binding to be the type that the // Cast the function we are binding to be the type that the
// closure will expect it to have. The type the closure knows // closure will expect it to have. The type the closure knows
// about has the type parameters substituted with the real types. // about has the type parameters substituted with the real types.
let sp = cx.sp; let sp = cx.sp;
let llclosurety = T_ptr(type_of(ccx, sp, outgoing_fty)); let llclosurety = T_ptr(type_of(ccx, sp, outgoing_fty));
let src_loc = PointerCast(bcx, f_res.res.val, llclosurety); let src_loc = PointerCast(bcx, cl, llclosurety);
let bound_f = {res: {bcx: bcx, val: src_loc} with f_res}; let bound_f = {bcx: bcx, val: src_loc, is_mem: is_mem};
([outgoing_fty], [bound_f], none) ([outgoing_fty], [bound_f], none)
} else { ([], [], some(f_res.res.val)) }; }
none. { ([], [], some(f_res.val)) }
};
// Translate the bound expressions. // Translate the bound expressions.
for e: @ast::expr in bound { for e: @ast::expr in bound {
let lv = trans_lval(bcx, e); let lv = trans_lval(bcx, e);
bcx = lv.res.bcx; bcx = lv.bcx;
bound_vals += [lv]; bound_vals += [lv];
bound_tys += [ty::expr_ty(bcx_tcx(cx), e)]; bound_tys += [ty::expr_ty(bcx_tcx(cx), e)];
} }
@ -3476,8 +3506,6 @@ fn trans_bind_1(cx: @block_ctxt, f: @ast::expr, f_res: lval_result,
bcx = closure.bcx; bcx = closure.bcx;
// Make thunk // Make thunk
// The type of the entire bind expression.
let pair_ty = node_id_type(bcx_ccx(cx), id);
let llthunk = let llthunk =
trans_bind_thunk(cx.fcx.lcx, cx.sp, pair_ty, outgoing_fty_real, args, trans_bind_thunk(cx.fcx.lcx, cx.sp, pair_ty, outgoing_fty_real, args,
closure.ptrty, ty_param_count, target_res); closure.ptrty, ty_param_count, target_res);
@ -3497,8 +3525,8 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty0: TypeRef,
let e_ty = ty::expr_ty(ccx.tcx, e); let e_ty = ty::expr_ty(ccx.tcx, e);
let is_bot = ty::type_is_bot(ccx.tcx, e_ty); let is_bot = ty::type_is_bot(ccx.tcx, e_ty);
let lv = trans_lval(cx, e); let lv = trans_lval(cx, e);
let bcx = lv.res.bcx; let bcx = lv.bcx;
let val = lv.res.val; let val = lv.val;
if is_bot { if is_bot {
// For values of type _|_, we generate an // For values of type _|_, we generate an
// "undef" value, as such a value should never // "undef" value, as such a value should never
@ -3537,8 +3565,8 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty0: TypeRef,
if lv.is_mem { if lv.is_mem {
// Use actual ty, not declared ty -- anything else doesn't make // Use actual ty, not declared ty -- anything else doesn't make
// sense if declared ty is a ty param // sense if declared ty is a ty param
to_zero += [{v: lv.res.val, t: e_ty}]; to_zero += [{v: lv.val, t: e_ty}];
} else { to_revoke += [{v: lv.res.val, t: e_ty}]; } } else { to_revoke += [{v: lv.val, t: e_ty}]; }
} }
ret rslt(bcx, val); ret rslt(bcx, val);
} }
@ -3677,27 +3705,23 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(bcx_tcx(in_cx), let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(bcx_tcx(in_cx),
fn_expr_ty)); fn_expr_ty));
let cx = new_scope_block_ctxt(in_cx, "call"); let cx = new_scope_block_ctxt(in_cx, "call");
let f_res = trans_lval_gen(cx, f); let f_res = trans_callee(cx, f);
let bcx = f_res.res.bcx; let bcx = f_res.bcx;
let faddr = f_res.res.val; let faddr = f_res.val;
let llenv = C_null(T_opaque_closure_ptr(*bcx_ccx(cx))); let llenv = alt f_res.env {
alt f_res.llobj { null_env. { C_null(T_opaque_closure_ptr(*bcx_ccx(cx))) }
some(ob) { some_env(e) { e }
// It's a vtbl entry. is_closure. {
faddr = Load(bcx, faddr); // It's a closure. Have to fetch the elements
llenv = ob;
}
none. {
// It's a closure. We have to autoderef.
if f_res.is_mem { faddr = load_if_immediate(bcx, faddr, fn_expr_ty); } if f_res.is_mem { faddr = load_if_immediate(bcx, faddr, fn_expr_ty); }
let pair = faddr; let pair = faddr;
faddr = GEP(bcx, pair, [C_int(0), C_int(abi::fn_field_code)]); faddr = GEP(bcx, pair, [C_int(0), C_int(abi::fn_field_code)]);
faddr = Load(bcx, faddr); faddr = Load(bcx, faddr);
let llclosure = GEP(bcx, pair, [C_int(0), C_int(abi::fn_field_box)]); let llclosure = GEP(bcx, pair, [C_int(0), C_int(abi::fn_field_box)]);
llenv = Load(bcx, llclosure); Load(bcx, llclosure)
} }
} };
let ret_ty = ty::node_id_to_type(bcx_tcx(cx), id); let ret_ty = ty::node_id_to_type(bcx_tcx(cx), id);
let args_res = let args_res =
@ -3861,7 +3885,7 @@ fn trans_tup(cx: @block_ctxt, elts: [@ast::expr], id: ast::node_id) ->
for e in elts { for e in elts {
let e_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, e); let e_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, e);
let src = trans_lval(bcx, e); let src = trans_lval(bcx, e);
bcx = src.res.bcx; bcx = src.bcx;
let dst_res = GEP_tup_like(bcx, t, tup_val, [0, i]); let dst_res = GEP_tup_like(bcx, t, tup_val, [0, i]);
bcx = move_val_if_temp(dst_res.bcx, INIT, dst_res.val, src, e_ty); bcx = move_val_if_temp(dst_res.bcx, INIT, dst_res.val, src, e_ty);
i += 1; i += 1;
@ -3899,7 +3923,7 @@ fn trans_rec(cx: @block_ctxt, fields: [ast::field],
expr_provided = true; expr_provided = true;
let lv = trans_lval(bcx, f.node.expr); let lv = trans_lval(bcx, f.node.expr);
bcx = bcx =
move_val_if_temp(lv.res.bcx, INIT, dst_res.val, lv, e_ty); move_val_if_temp(lv.bcx, INIT, dst_res.val, lv, e_ty);
break; break;
} }
} }
@ -3979,13 +4003,13 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
ast::expr_copy(a) { ast::expr_copy(a) {
let e_ty = ty::expr_ty(bcx_tcx(cx), a); let e_ty = ty::expr_ty(bcx_tcx(cx), a);
let lv = trans_lval(cx, a); let lv = trans_lval(cx, a);
let bcx = lv.res.bcx; let bcx = lv.bcx;
if !lv.is_mem { ret lv.res; } if !lv.is_mem { ret {bcx: lv.bcx, val: lv.val}; }
let r = if type_is_immediate(bcx_ccx(cx), e_ty) { let r = if type_is_immediate(bcx_ccx(cx), e_ty) {
rslt(bcx, Load(bcx, lv.res.val)) rslt(bcx, Load(bcx, lv.val))
} else { } else {
let {bcx, val: dest} = alloc_ty(bcx, e_ty); let {bcx, val: dest} = alloc_ty(bcx, e_ty);
bcx = copy_val(bcx, INIT, dest, lv.res.val, e_ty); bcx = copy_val(bcx, INIT, dest, lv.val, e_ty);
rslt(bcx, dest) rslt(bcx, dest)
}; };
add_clean_temp(bcx, r.val, e_ty); add_clean_temp(bcx, r.val, e_ty);
@ -3994,43 +4018,43 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
ast::expr_move(dst, src) { ast::expr_move(dst, src) {
let lhs_res = trans_lval(cx, dst); let lhs_res = trans_lval(cx, dst);
assert (lhs_res.is_mem); assert (lhs_res.is_mem);
// FIXME Fill in lhs_res.res.bcx.sp // FIXME Fill in lhs_res.bcx.sp
let rhs_res = trans_lval(lhs_res.res.bcx, src); let rhs_res = trans_lval(lhs_res.bcx, src);
let t = ty::expr_ty(bcx_tcx(cx), src); let t = ty::expr_ty(bcx_tcx(cx), src);
// FIXME: calculate copy init-ness in typestate. // FIXME: calculate copy init-ness in typestate.
let bcx = let bcx =
move_val(rhs_res.res.bcx, DROP_EXISTING, lhs_res.res.val, rhs_res, move_val(rhs_res.bcx, DROP_EXISTING, lhs_res.val, rhs_res,
t); t);
ret rslt(bcx, C_nil()); ret rslt(bcx, C_nil());
} }
ast::expr_assign(dst, src) { ast::expr_assign(dst, src) {
let lhs_res = trans_lval(cx, dst); let lhs_res = trans_lval(cx, dst);
assert (lhs_res.is_mem); assert (lhs_res.is_mem);
// FIXME Fill in lhs_res.res.bcx.sp // FIXME Fill in lhs_res.bcx.sp
let rhs = trans_lval(lhs_res.res.bcx, src); let rhs = trans_lval(lhs_res.bcx, src);
let t = ty::expr_ty(bcx_tcx(cx), src); let t = ty::expr_ty(bcx_tcx(cx), src);
// FIXME: calculate copy init-ness in typestate. // FIXME: calculate copy init-ness in typestate.
let bcx = let bcx =
move_val_if_temp(rhs.res.bcx, DROP_EXISTING, lhs_res.res.val, rhs, move_val_if_temp(rhs.bcx, DROP_EXISTING, lhs_res.val, rhs,
t); t);
ret rslt(bcx, C_nil()); ret rslt(bcx, C_nil());
} }
ast::expr_swap(dst, src) { ast::expr_swap(dst, src) {
let lhs_res = trans_lval(cx, dst); let lhs_res = trans_lval(cx, dst);
assert (lhs_res.is_mem); assert (lhs_res.is_mem);
// FIXME Fill in lhs_res.res.bcx.sp // FIXME Fill in lhs_res.bcx.sp
let rhs_res = trans_lval(lhs_res.res.bcx, src); let rhs_res = trans_lval(lhs_res.bcx, src);
let t = ty::expr_ty(bcx_tcx(cx), src); let t = ty::expr_ty(bcx_tcx(cx), src);
let {bcx: bcx, val: tmp_alloc} = alloc_ty(rhs_res.res.bcx, t); let {bcx: bcx, val: tmp_alloc} = alloc_ty(rhs_res.bcx, t);
// Swap through a temporary. // Swap through a temporary.
bcx = move_val(bcx, INIT, tmp_alloc, lhs_res, t); bcx = move_val(bcx, INIT, tmp_alloc, lhs_res, t);
bcx = move_val(bcx, INIT, lhs_res.res.val, rhs_res, t); bcx = move_val(bcx, INIT, lhs_res.val, rhs_res, t);
bcx = bcx =
move_val(bcx, INIT, rhs_res.res.val, lval_mem(bcx, tmp_alloc), t); move_val(bcx, INIT, rhs_res.val, lval_mem(bcx, tmp_alloc), t);
ret rslt(bcx, C_nil()); ret rslt(bcx, C_nil());
} }
ast::expr_assign_op(op, dst, src) { ast::expr_assign_op(op, dst, src) {
@ -4045,8 +4069,8 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
alt src.node { alt src.node {
ast::expr_vec(args, _) { ast::expr_vec(args, _) {
let bcx = let bcx =
tvec::trans_append_literal(lhs_res.res.bcx, tvec::trans_append_literal(lhs_res.bcx,
lhs_res.res.val, t, args); lhs_res.val, t, args);
ret rslt(bcx, C_nil()); ret rslt(bcx, C_nil());
} }
_ { } _ { }
@ -4055,24 +4079,24 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
_ { } _ { }
} }
// FIXME Fill in lhs_res.res.bcx.sp // FIXME Fill in lhs_res.bcx.sp
let rhs_res = trans_expr(lhs_res.res.bcx, src); let rhs_res = trans_expr(lhs_res.bcx, src);
if ty::type_is_sequence(tcx, t) { if ty::type_is_sequence(tcx, t) {
alt op { alt op {
ast::add. { ast::add. {
ret tvec::trans_append(rhs_res.bcx, t, lhs_res.res.val, ret tvec::trans_append(rhs_res.bcx, t, lhs_res.val,
rhs_res.val); rhs_res.val);
} }
_ { } _ { }
} }
} }
let lhs_val = load_if_immediate(rhs_res.bcx, lhs_res.res.val, t); let lhs_val = load_if_immediate(rhs_res.bcx, lhs_res.val, t);
let v = let v =
trans_eager_binop(rhs_res.bcx, op, lhs_val, t, rhs_res.val, t); trans_eager_binop(rhs_res.bcx, op, lhs_val, t, rhs_res.val, t);
// FIXME: calculate copy init-ness in typestate. // FIXME: calculate copy init-ness in typestate.
// This is always a temporary, so can always be safely moved // This is always a temporary, so can always be safely moved
let bcx = let bcx =
move_val(v.bcx, DROP_EXISTING, lhs_res.res.val, move_val(v.bcx, DROP_EXISTING, lhs_res.val,
lval_val(v.bcx, v.val), t); lval_val(v.bcx, v.val), t);
ret rslt(bcx, C_nil()); ret rslt(bcx, C_nil());
} }
@ -4127,9 +4151,9 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
ast::expr_path(_) | ast::expr_unary(ast::deref., _) { ast::expr_path(_) | ast::expr_unary(ast::deref., _) {
let t = ty::expr_ty(bcx_tcx(cx), e); let t = ty::expr_ty(bcx_tcx(cx), e);
let sub = trans_lval(cx, e); let sub = trans_lval(cx, e);
let v = sub.res.val; let v = sub.val;
if sub.is_mem { v = load_if_immediate(sub.res.bcx, v, t); } if sub.is_mem { v = load_if_immediate(sub.bcx, v, t); }
ret rslt(sub.res.bcx, v); ret rslt(sub.bcx, v);
} }
ast::expr_unary(op, x) { ast::expr_unary(op, x) {
ret trans_unary(cx, op, x, e.id); ret trans_unary(cx, op, x, e.id);
@ -4419,10 +4443,10 @@ fn trans_ret(cx: @block_ctxt, e: option::t<@ast::expr>) -> result {
some(x) { some(x) {
let t = ty::expr_ty(bcx_tcx(cx), x); let t = ty::expr_ty(bcx_tcx(cx), x);
let lv = trans_lval(cx, x); let lv = trans_lval(cx, x);
bcx = lv.res.bcx; bcx = lv.bcx;
if ast_util::ret_by_ref(cx.fcx.ret_style) { if ast_util::ret_by_ref(cx.fcx.ret_style) {
assert lv.is_mem; assert lv.is_mem;
Store(bcx, lv.res.val, cx.fcx.llretptr); Store(bcx, lv.val, cx.fcx.llretptr);
} else { } else {
let is_local = alt x.node { let is_local = alt x.node {
ast::expr_path(p) { ast::expr_path(p) {
@ -4491,11 +4515,11 @@ fn init_local(bcx: @block_ctxt, local: @ast::local) -> @block_ctxt {
// the value. // the value.
ty = node_id_type(bcx_ccx(bcx), init.expr.id); ty = node_id_type(bcx_ccx(bcx), init.expr.id);
let sub = trans_lval(bcx, init.expr); let sub = trans_lval(bcx, init.expr);
bcx = move_val_if_temp(sub.res.bcx, INIT, llptr, sub, ty); bcx = move_val_if_temp(sub.bcx, INIT, llptr, sub, ty);
} }
ast::init_move. { ast::init_move. {
let sub = trans_lval(bcx, init.expr); let sub = trans_lval(bcx, init.expr);
bcx = move_val(sub.res.bcx, INIT, llptr, sub, ty); bcx = move_val(sub.bcx, INIT, llptr, sub, ty);
} }
} }
} }
@ -4545,9 +4569,8 @@ fn init_ref_local(bcx: @block_ctxt, local: @ast::local) -> @block_ctxt {
let init_expr = option::get(local.node.init).expr; let init_expr = option::get(local.node.init).expr;
let val = trans_lval(bcx, init_expr); let val = trans_lval(bcx, init_expr);
assert val.is_mem; assert val.is_mem;
ret trans_alt::bind_irrefutable_pat(val.res.bcx, local.node.pat, ret trans_alt::bind_irrefutable_pat(val.bcx, local.node.pat,
val.res.val, val.val, bcx.fcx.lllocals, false);
bcx.fcx.lllocals, false);
} }
fn zero_alloca(cx: @block_ctxt, llptr: ValueRef, t: ty::t) -> result { fn zero_alloca(cx: @block_ctxt, llptr: ValueRef, t: ty::t) -> result {
@ -4813,7 +4836,7 @@ fn trans_block(cx: @block_ctxt, b: ast::blk, output: out_method) -> result {
if is_terminated(bcx) || ty::type_is_bot(ccx.tcx, r_ty) { ret r; } if is_terminated(bcx) || ty::type_is_bot(ccx.tcx, r_ty) { ret r; }
} else { } else {
let lv = trans_lval(bcx, e); let lv = trans_lval(bcx, e);
r = lv.res; r = {bcx: lv.bcx, val: lv.val};
bcx = r.bcx; bcx = r.bcx;
if is_terminated(bcx) || ty::type_is_bot(ccx.tcx, r_ty) { ret r; } if is_terminated(bcx) || ty::type_is_bot(ccx.tcx, r_ty) { ret r; }
alt output { alt output {

View file

@ -117,7 +117,7 @@ fn trans_vec(bcx: @block_ctxt, args: [@ast::expr], id: ast::node_id) ->
let i = 0u; let i = 0u;
for e in args { for e in args {
let lv = trans_lval(bcx, e); let lv = trans_lval(bcx, e);
bcx = lv.res.bcx; bcx = lv.bcx;
let lleltptr = let lleltptr =
if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) { if ty::type_has_dynamic_size(bcx_tcx(bcx), unit_ty) {
InBoundsGEP(bcx, dataptr, [Mul(bcx, C_uint(i), llunitsz)]) InBoundsGEP(bcx, dataptr, [Mul(bcx, C_uint(i), llunitsz)])

View file

@ -1,6 +1,6 @@
// Test case for issue #435. // Test case for issue #435.
obj foo() { obj foo(x: int) {
fn add5(n: int) -> int { ret n + 5; } fn add5(n: int) -> int { ret n + x; }
} }
fn add5(n: int) -> int { ret n + 5; } fn add5(n: int) -> int { ret n + 5; }
@ -10,7 +10,7 @@ fn main() {
assert (add5(7) == 12); assert (add5(7) == 12);
assert (fiveplusseven() == 12); assert (fiveplusseven() == 12);
let my_foo = foo(); let my_foo = foo(5);
let fiveplusseven_too = bind my_foo.add5(7); let fiveplusseven_too = bind my_foo.add5(7);
assert (my_foo.add5(7) == 12); assert (my_foo.add5(7) == 12);
assert (fiveplusseven_too() == 12); assert (fiveplusseven_too() == 12);