about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Sullivan <sully@msully.net>2011-07-01 02:52:05 -0700
committerMarijn Haverbeke <marijnh@gmail.com>2011-07-01 12:39:58 +0200
commit7b1b5d5a8a9aba6a682f95b7c738945be8e1fca2 (patch)
tree6761fa6e9c26cb08259b4e656269493ab2ae699d /src
parentee45d54a4e14c8ae809b0b6c3111260a3f39ba40 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/comp/middle/alias.rs2
-rw-r--r--src/comp/middle/trans.rs55
-rw-r--r--src/comp/middle/typeck.rs17
-rw-r--r--src/test/compile-fail/auto-deref-bind.rs7
-rw-r--r--src/test/run-pass/auto-deref-fn.rs11
5 files changed, 69 insertions, 23 deletions
diff --git a/src/comp/middle/alias.rs b/src/comp/middle/alias.rs
index 20bed30a7d2..9bd1473e4b1 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 89b147bd473..f83ca389852 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 b9d5860d0e9..3cf16870d86 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 00000000000..51f76be6289
--- /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 00000000000..6959e8e109f
--- /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);
+}