Implement autoderef for function calls.

This is important since we are going to be making functions noncopyable
soon, which means we'll be seeing a lot of boxed functions.
(*f)(...) is really just too heavyweight.

Doing the autodereferencing was a very little bit tricky since
trans_call works with an *lval* of the function whereas existing
autoderef code was not for lvals.
This commit is contained in:
Michael Sullivan 2011-07-01 02:52:05 -07:00 committed by Marijn Haverbeke
parent ee45d54a4e
commit 7b1b5d5a8a
5 changed files with 69 additions and 23 deletions

View File

@ -663,7 +663,7 @@ fn def_is_local(&ast::def d, bool objfields_count) -> bool {
}
fn fty_args(&ctx cx, ty::t fty) -> ty::arg[] {
ret alt (ty::struct(*cx.tcx, fty)) {
ret alt (ty::struct(*cx.tcx, ty::type_autoderef(*cx.tcx, fty))) {
case (ty::ty_fn(_, ?args, _, _, _)) { args }
case (ty::ty_native_fn(_, ?args, _)) { args }
};

View File

@ -4099,12 +4099,18 @@ fn trans_eager_binop(&@block_ctxt cx, ast::binop op, &ty::t intype,
}
}
fn autoderef(&@block_ctxt cx, ValueRef v, &ty::t t) -> result {
fn autoderef_lval(&@block_ctxt cx, ValueRef v, &ty::t t, bool is_lval)
-> result {
let ValueRef v1 = v;
let ty::t t1 = t;
while (true) {
alt (ty::struct(cx.fcx.lcx.ccx.tcx, t1)) {
case (ty::ty_box(?mt)) {
// If we are working with an lval, we want to
// unconditionally load at the top of the loop
// to get rid of the extra indirection
if (is_lval) { v1 = cx.build.Load(v1); }
auto body =
cx.build.GEP(v1,
[C_int(0), C_int(abi::box_rc_field_body)]);
@ -4118,7 +4124,11 @@ fn autoderef(&@block_ctxt cx, ValueRef v, &ty::t t) -> result {
auto llty = type_of(cx.fcx.lcx.ccx, cx.sp, mt.ty);
v1 = cx.build.PointerCast(body, T_ptr(llty));
} else { v1 = body; }
v1 = load_if_immediate(cx, v1, t1);
// But if we aren't working with an lval, we get rid of
// a layer of indirection at the bottom of the loop so
// that it is gone when we return...
if (!is_lval) { v1 = load_if_immediate(cx, v1, t1); }
}
case (_) { break; }
}
@ -4126,6 +4136,10 @@ fn autoderef(&@block_ctxt cx, ValueRef v, &ty::t t) -> result {
ret rslt(cx, v1);
}
fn autoderef(&@block_ctxt cx, ValueRef v, &ty::t t) -> result {
ret autoderef_lval(cx, v, t, false);
}
fn trans_binary(&@block_ctxt cx, ast::binop op, &@ast::expr a, &@ast::expr b)
-> result {
@ -5637,17 +5651,33 @@ fn trans_call(&@block_ctxt cx, &@ast::expr f, &option::t[ValueRef] lliterbody,
// with trans_call.
auto f_res = trans_lval(cx, f);
let ty::t fn_ty;
alt (f_res.method_ty) {
case (some(?meth)) {
// self-call
fn_ty = meth;
}
case (_) {
fn_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, f);
}
}
auto bcx = f_res.res.bcx;
auto faddr = f_res.res.val;
auto llenv = C_null(T_opaque_closure_ptr(cx.fcx.lcx.ccx.tn));
alt (f_res.llobj) {
case (some(_)) {
// It's a vtbl entry.
faddr = f_res.res.bcx.build.Load(faddr);
faddr = bcx.build.Load(faddr);
}
case (none) {
// It's a closure.
auto bcx = f_res.res.bcx;
auto pair = faddr;
// It's a closure. We have to autoderef.
auto res = autoderef_lval(bcx, f_res.res.val, fn_ty, true);
bcx = res.bcx;
fn_ty = ty::type_autoderef(bcx.fcx.lcx.ccx.tcx, fn_ty);
auto pair = res.val;
faddr =
bcx.build.GEP(pair, [C_int(0), C_int(abi::fn_field_code)]);
faddr = bcx.build.Load(faddr);
@ -5656,19 +5686,12 @@ fn trans_call(&@block_ctxt cx, &@ast::expr f, &option::t[ValueRef] lliterbody,
llenv = bcx.build.Load(llclosure);
}
}
let ty::t fn_ty;
alt (f_res.method_ty) {
case (some(?meth)) {
// self-call
fn_ty = meth;
}
case (_) { fn_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, f); }
}
auto ret_ty = ty::node_id_to_type(cx.fcx.lcx.ccx.tcx, id);
auto args_res =
trans_args(f_res.res.bcx, llenv, f_res.llobj, f_res.generic,
trans_args(bcx, llenv, f_res.llobj, f_res.generic,
lliterbody, args, fn_ty);
auto bcx = args_res._0;
bcx = args_res._0;
auto llargs = args_res._1;
auto llretslot = args_res._2;
/*

View File

@ -1336,17 +1336,21 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
// expressions.
fn check_call_or_bind(&@fn_ctxt fcx, &span sp, &@ast::expr f,
&vec[option::t[@ast::expr]] args) {
&vec[option::t[@ast::expr]] args, bool is_call) {
// Check the function.
check_expr(fcx, f);
// Get the function type.
auto fty = expr_ty(fcx.ccx.tcx, f);
// Grab the argument types and the return type.
// We want to autoderef calls but not binds
auto fty_stripped =
if (is_call) { strip_boxes(fcx, sp, fty) } else { fty };
// Grab the argument types and the return type.
auto arg_tys;
alt (structure_of(fcx, sp, fty)) {
alt (structure_of(fcx, sp, fty_stripped)) {
case (ty::ty_fn(_, ?arg_tys_0, _, _, _)) { arg_tys = arg_tys_0; }
case (ty::ty_native_fn(_, ?arg_tys_0, _)) { arg_tys = arg_tys_0; }
case (_) {
@ -1410,7 +1414,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
}
// Call the generic checker.
check_call_or_bind(fcx, sp, f, args_opt_0);
check_call_or_bind(fcx, sp, f, args_opt_0, true);
}
// A generic function for checking for or for-each loops
@ -1806,7 +1810,7 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
case (ast::expr_bind(?f, ?args)) {
// Call the generic checker.
check_call_or_bind(fcx, expr.span, f, args);
check_call_or_bind(fcx, expr.span, f, args, false);
// Pull the argument and return types out.
auto proto_1;
@ -1855,7 +1859,8 @@ fn check_expr(&@fn_ctxt fcx, &@ast::expr expr) {
// Pull the return type out of the type of the function.
auto rt_1;
auto fty = ty::expr_ty(fcx.ccx.tcx, f);
auto fty = strip_boxes(fcx, expr.span,
ty::expr_ty(fcx.ccx.tcx, f));
alt (structure_of(fcx, expr.span, fty)) {
case (ty::ty_fn(_, _, ?rt, _, _)) { rt_1 = rt; }
case (ty::ty_native_fn(_, _, ?rt)) { rt_1 = rt; }

View File

@ -0,0 +1,7 @@
// error-pattern: mismatched types
fn add1(int i) -> int { ret i+1; }
fn main() {
auto f = @add1;
auto g = bind f(5);
}

View File

@ -0,0 +1,11 @@
// xfail-stage0
fn add1(int i) -> int { ret i+1; }
fn main() {
auto f = @add1;
auto g = @f;
auto h = @@@add1;
assert(f(5) == 6);
assert(g(8) == 9);
assert(h(0x1badd00d) == 0x1badd00e);
}