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:
parent
ee45d54a4e
commit
7b1b5d5a8a
|
@ -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 }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
/*
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue