From 7b1b5d5a8a9aba6a682f95b7c738945be8e1fca2 Mon Sep 17 00:00:00 2001 From: Michael Sullivan Date: Fri, 1 Jul 2011 02:52:05 -0700 Subject: [PATCH] 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. --- src/comp/middle/alias.rs | 2 +- src/comp/middle/trans.rs | 55 +++++++++++++++++------- src/comp/middle/typeck.rs | 17 +++++--- src/test/compile-fail/auto-deref-bind.rs | 7 +++ src/test/run-pass/auto-deref-fn.rs | 11 +++++ 5 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 src/test/compile-fail/auto-deref-bind.rs create mode 100644 src/test/run-pass/auto-deref-fn.rs diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs index 20bed30a7d2a..9bd1473e4b12 100644 --- a/src/comp/middle/alias.rs +++ b/src/comp/middle/alias.rs @@ -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 } }; diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index 89b147bd4736..f83ca3898520 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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; /* diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index b9d5860d0e9a..3cf16870d869 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -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; } diff --git a/src/test/compile-fail/auto-deref-bind.rs b/src/test/compile-fail/auto-deref-bind.rs new file mode 100644 index 000000000000..51f76be6289d --- /dev/null +++ b/src/test/compile-fail/auto-deref-bind.rs @@ -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); +} diff --git a/src/test/run-pass/auto-deref-fn.rs b/src/test/run-pass/auto-deref-fn.rs new file mode 100644 index 000000000000..6959e8e109f3 --- /dev/null +++ b/src/test/run-pass/auto-deref-fn.rs @@ -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); +}