about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/comp/middle/trans/base.rs61
-rw-r--r--src/comp/middle/trans/closure.rs55
-rw-r--r--src/comp/middle/trans/impl.rs24
-rw-r--r--src/comp/middle/typeck.rs16
-rw-r--r--src/test/run-pass/bind-methods.rs20
5 files changed, 107 insertions, 69 deletions
diff --git a/src/comp/middle/trans/base.rs b/src/comp/middle/trans/base.rs
index 9bf815c459b..08e3e04cb91 100644
--- a/src/comp/middle/trans/base.rs
+++ b/src/comp/middle/trans/base.rs
@@ -2188,7 +2188,7 @@ type lval_result = {bcx: @block_ctxt, val: ValueRef, kind: lval_kind};
 enum callee_env {
     null_env,
     is_closure,
-    self_env(ValueRef),
+    self_env(ValueRef, ty::t),
     dict_env(ValueRef, ValueRef),
 }
 type lval_maybe_callee = {bcx: @block_ctxt,
@@ -2597,36 +2597,28 @@ fn trans_lval(cx: @block_ctxt, e: @ast::expr) -> lval_result {
     }
 }
 
-fn maybe_add_env(bcx: @block_ctxt, c: lval_maybe_callee)
-    -> (lval_kind, ValueRef) {
-    alt c.env {
-      is_closure { (c.kind, c.val) }
-      self_env(_) | dict_env(_, _) {
-        fail "Taking the value of a method does not work yet (issue #435)";
-      }
-      null_env {
-        let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
-        (temporary, create_real_fn_pair(bcx, llfnty, c.val,
-                                        null_env_ptr(bcx)))
-      }
-    }
-}
-
 fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result {
-    alt c.generic {
-      generic_full(gi) {
+    let must_bind = alt c.generic { generic_full(_) { true } _ { false } } ||
+        alt c.env { self_env(_, _) | dict_env(_, _) { true } _ { false } };
+    if must_bind {
         let n_args = ty::ty_fn_args(ty).len();
-        let args = vec::init_elt(n_args, none::<@ast::expr>);
+        let args = vec::init_elt(n_args, none);
         let space = alloc_ty(c.bcx, ty);
         let bcx = closure::trans_bind_1(space.bcx, ty, c, args, ty,
                                               save_in(space.val));
         add_clean_temp(bcx, space.val, ty);
-        ret {bcx: bcx, val: space.val, kind: temporary};
-      }
-      _ {
-        let (kind, val) = maybe_add_env(c.bcx, c);
-        ret {bcx: c.bcx, val: val, kind: kind};
-      }
+        {bcx: bcx, val: space.val, kind: temporary}
+    } else {
+        alt c.env {
+          is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} }
+          null_env {
+            let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
+            let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
+                                           null_env_ptr(c.bcx));
+            {bcx: c.bcx, val: llfn, kind: temporary}
+          }
+          _ { fail; }
+        }
     }
 }
 
@@ -2762,7 +2754,7 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty: TypeRef,
         if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
             val = Load(bcx, val);
         }
-        } else if arg_mode == ast::by_copy {
+    } else if arg_mode == ast::by_copy {
         let {bcx: cx, val: alloc} = alloc_ty(bcx, e_ty);
         let last_use = ccx.last_uses.contains_key(e.id);
         bcx = cx;
@@ -2926,16 +2918,21 @@ fn trans_call_inner(in_cx: @block_ctxt, fn_expr_ty: ty::t,
     let cx = new_scope_block_ctxt(in_cx, "call");
     Br(in_cx, cx.llbb);
     let f_res = get_callee(cx);
-    let bcx = f_res.bcx;
+    let bcx = f_res.bcx, ccx = bcx_ccx(cx);
 
     let faddr = f_res.val;
     let llenv, dict_param = none;
     alt f_res.env {
       null_env {
-        llenv = llvm::LLVMGetUndef(T_opaque_box_ptr(bcx_ccx(cx)));
+        llenv = llvm::LLVMGetUndef(T_opaque_box_ptr(ccx));
+      }
+      self_env(e, _) {
+        llenv = PointerCast(bcx, e, T_opaque_box_ptr(ccx));
+      }
+      dict_env(dict, e) {
+        llenv = PointerCast(bcx, e, T_opaque_box_ptr(ccx));
+        dict_param = some(dict);
       }
-      self_env(e) { llenv = e; }
-      dict_env(dict, e) { llenv = e; dict_param = some(dict); }
       is_closure {
         // It's a closure. Have to fetch the elements
         if f_res.kind == owned {
@@ -3306,7 +3303,9 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
         ret trans_call(bcx, f, args, e.id, dest);
       }
       ast::expr_field(_, _, _) {
-        fail "Taking the value of a method does not work yet (issue #435)";
+        let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e);
+        let lv = lval_maybe_callee_to_lval(callee, ty);
+        ret memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty);
       }
       ast::expr_index(base, idx) {
         // If it is here, it's not an lval, so this is a user-defined index op
diff --git a/src/comp/middle/trans/closure.rs b/src/comp/middle/trans/closure.rs
index 16466eb092a..45e4bfadcbe 100644
--- a/src/comp/middle/trans/closure.rs
+++ b/src/comp/middle/trans/closure.rs
@@ -494,6 +494,7 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
                 f_res: lval_maybe_callee,
                 args: [option<@ast::expr>], pair_ty: ty::t,
                 dest: dest) -> @block_ctxt {
+    let ccx = bcx_ccx(cx);
     let bound: [@ast::expr] = [];
     for argopt: option<@ast::expr> in args {
         alt argopt { none { } some(e) { bound += [e]; } }
@@ -523,39 +524,37 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
                 }
             }
         }
-        lazily_emit_all_generic_info_tydesc_glues(bcx_ccx(cx), ginfo);
+        lazily_emit_all_generic_info_tydesc_glues(ccx, ginfo);
         (ginfo.item_type, tds, ginfo.param_bounds)
       }
       _ { (outgoing_fty, [], @[]) }
     };
 
-    if bound.len() == 0u && lltydescs.len() == 0u {
+    if bound.len() == 0u && lltydescs.len() == 0u &&
+       (f_res.env == null_env || f_res.env == is_closure) {
         // Trivial 'binding': just return the closure
         let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
-        bcx = lv.bcx;
-        ret memmove_ty(bcx, get_dest_addr(dest), lv.val, pair_ty);
+        ret memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, pair_ty);
     }
-    let closure = alt f_res.env {
-      null_env { none }
-      _ { let (_, cl) = maybe_add_env(cx, f_res); some(cl) }
-    };
-
-    // FIXME: should follow from a precondition on trans_bind_1
-    let ccx = bcx_ccx(cx);
-    check (type_has_static_size(ccx, outgoing_fty));
 
     // Arrange for the bound function to live in the first binding spot
     // if the function is not statically known.
-    let (env_vals, target_res) = alt closure {
-      some(cl) {
+    let (env_vals, target_info) = alt f_res.env {
+      null_env { ([], target_static(f_res.val)) }
+      is_closure {
         // Cast the function we are binding to be the type that the
         // closure will expect it to have. The type the closure knows
         // about has the type parameters substituted with the real types.
         let llclosurety = T_ptr(type_of(ccx, outgoing_fty));
-        let src_loc = PointerCast(bcx, cl, llclosurety);
-        ([env_copy(src_loc, pair_ty, owned)], none)
+        let src_loc = PointerCast(bcx, f_res.val, llclosurety);
+        ([env_copy(src_loc, pair_ty, owned)], target_closure)
+      }
+      self_env(slf, slf_t) {
+        ([env_copy(slf, slf_t, owned)], target_self(f_res.val))
+      }
+      dict_env(_, _) {
+        ccx.sess.unimpl("binding of dynamic method calls");
       }
-      none { ([], some(f_res.val)) }
     };
 
     // Actually construct the closure
@@ -567,7 +566,7 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
     // Make thunk
     let llthunk = trans_bind_thunk(
         cx.fcx.ccx, cx.fcx.path, pair_ty, outgoing_fty_real, args,
-        cdata_ty, *param_bounds, target_res);
+        cdata_ty, *param_bounds, target_info);
 
     // Fill the function pair
     fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox);
@@ -722,6 +721,12 @@ fn make_opaque_cbox_free_glue(
     }
 }
 
+enum target_info {
+    target_closure,
+    target_static(ValueRef),
+    target_self(ValueRef),
+}
+
 // pth is cx.path
 fn trans_bind_thunk(ccx: @crate_ctxt,
                     path: path,
@@ -730,7 +735,7 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
                     args: [option<@ast::expr>],
                     cdata_ty: ty::t,
                     param_bounds: [ty::param_bounds],
-                    target_fn: option<ValueRef>)
+                    target_info: target_info)
     -> {val: ValueRef, ty: TypeRef} {
 
     // If we supported constraints on record fields, we could make the
@@ -800,11 +805,11 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
     // creating.  (In our running example, target is the function f.)  Pick
     // out the pointer to the target function from the environment. The
     // target function lives in the first binding spot.
-    let (lltargetfn, lltargetenv, starting_idx) = alt target_fn {
-      some(fptr) {
+    let (lltargetfn, lltargetenv, starting_idx) = alt target_info {
+      target_static(fptr) {
         (fptr, llvm::LLVMGetUndef(T_opaque_cbox_ptr(ccx)), 0)
       }
-      none {
+      target_closure {
         let {bcx: cx, val: pair} =
             GEP_tup_like(bcx, cdata_ty, llcdata,
                          [0, abi::closure_body_bindings, 0]);
@@ -815,6 +820,12 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
         bcx = cx;
         (lltargetfn, lltargetenv, 1)
       }
+      target_self(fptr) {
+        let rs = GEP_tup_like(bcx, cdata_ty, llcdata,
+                              [0, abi::closure_body_bindings, 0]);
+        bcx = rs.bcx;
+        (fptr, PointerCast(bcx, rs.val, T_opaque_cbox_ptr(ccx)), 1)
+      }
     };
 
     // And then, pick out the target function's own environment.  That's what
diff --git a/src/comp/middle/trans/impl.rs b/src/comp/middle/trans/impl.rs
index e72bbe04ae8..3da3f0b2360 100644
--- a/src/comp/middle/trans/impl.rs
+++ b/src/comp/middle/trans/impl.rs
@@ -63,11 +63,9 @@ fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
     let tz = [], tr = [];
     let basety = expr_ty(bcx, base);
     let m_by_ref = ast::expl(ast::by_ref);
-    let {bcx, val} =
-        trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
-                       T_ptr(type_of_or_i8(bcx_ccx(bcx), basety)), tz,
-                       tr, base);
-    rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
+    trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
+                   T_ptr(type_of_or_i8(bcx_ccx(bcx), basety)), tz,
+                   tr, base)
 }
 
 fn trans_method_callee(bcx: @block_ctxt, callee_id: ast::node_id,
@@ -100,7 +98,8 @@ fn trans_static_callee(bcx: @block_ctxt, callee_id: ast::node_id,
                        substs: option<([ty::t], typeck::dict_res)>)
     -> lval_maybe_callee {
     let {bcx, val} = trans_self_arg(bcx, base);
-    {env: self_env(val) with lval_static_fn(bcx, did, callee_id, substs)}
+    {env: self_env(val, node_id_type(bcx, base.id))
+     with lval_static_fn(bcx, did, callee_id, substs)}
 }
 
 fn wrapper_fn_ty(ccx: @crate_ctxt, dict_ty: TypeRef, fty: ty::t,
@@ -110,7 +109,7 @@ fn wrapper_fn_ty(ccx: @crate_ctxt, dict_ty: TypeRef, fty: ty::t,
     {ty: fty, llty: T_fn([dict_ty] + inputs, output)}
 }
 
-fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
+fn trans_vtable_callee(bcx: @block_ctxt, env: callee_env, dict: ValueRef,
                        callee_id: ast::node_id, iface_id: ast::def_id,
                        n_method: uint) -> lval_maybe_callee {
     let bcx = bcx, ccx = bcx_ccx(bcx), tcx = ccx.tcx;
@@ -139,7 +138,7 @@ fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
                                 origins: ccx.dict_map.find(callee_id)});
     }
     {bcx: bcx, val: mptr, kind: owned,
-     env: dict_env(dict, self),
+     env: env,
      generic: generic}
 }
 
@@ -181,7 +180,8 @@ fn trans_param_callee(bcx: @block_ctxt, callee_id: ast::node_id,
                       n_param: uint, n_bound: uint) -> lval_maybe_callee {
     let {bcx, val} = trans_self_arg(bcx, base);
     let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound];
-    trans_vtable_callee(bcx, val, dict, callee_id, iface_id, n_method)
+    trans_vtable_callee(bcx, dict_env(dict, val), dict,
+                        callee_id, iface_id, n_method)
 }
 
 // Method callee where the dict comes from a boxed iface
@@ -193,9 +193,9 @@ fn trans_iface_callee(bcx: @block_ctxt, callee_id: ast::node_id,
                                      T_ptr(T_ptr(T_dict()))));
     let box = Load(bcx, GEPi(bcx, val, [0, 1]));
     // FIXME[impl] I doubt this is alignment-safe
-    let self = PointerCast(bcx, GEPi(bcx, box, [0, abi::box_field_body]),
-                           T_opaque_cbox_ptr(bcx_ccx(bcx)));
-    trans_vtable_callee(bcx, self, dict, callee_id, iface_id, n_method)
+    let self = GEPi(bcx, box, [0, abi::box_field_body]);
+    trans_vtable_callee(bcx, dict_env(dict, self), dict,
+                        callee_id, iface_id, n_method)
 }
 
 fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} {
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index 7da03561ea0..caa4079a9d4 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -1674,7 +1674,8 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
                 alt vec::position(*ifce_methods, {|m| m.ident == name}) {
                   some(pos) {
                     let m = ifce_methods[pos];
-                    ret some({method_ty: ty::mk_fn(tcx, m.fty),
+                    ret some({method_ty: ty::mk_fn(tcx, {proto: ast::proto_box
+                                                         with m.fty}),
                               n_tps: vec::len(*m.tps),
                               substs: tps,
                               origin: method_param(iid, pos, n, bound_n),
@@ -1694,7 +1695,7 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
         let i = 0u;
         for m in *ty::iface_methods(tcx, did) {
             if m.ident == name {
-                let fty = ty::mk_fn(tcx, m.fty);
+                let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});
                 if ty::type_has_vars(fty) {
                     tcx.sess.span_fatal(
                         expr.span, "can not call a method that contains a \
@@ -1717,13 +1718,20 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
             alt tcx.items.get(did.node) {
               ast_map::node_method(m, _, _) {
                 let mt = ty_of_method(tcx, m_check, m);
-                ty::mk_fn(tcx, mt.fty)
+                ty::mk_fn(tcx, {proto: ast::proto_box with mt.fty})
               }
               _ {
                   tcx.sess.bug("Undocumented invariant in ty_from_did");
               }
             }
-        } else { csearch::get_type(tcx, did).ty }
+        } else {
+            alt ty::get(csearch::get_type(tcx, did).ty).struct {
+              ty::ty_fn(fty) {
+                ty::mk_fn(tcx, {proto: ast::proto_box with fty})
+              }
+              _ { fail; }
+            }
+        }
     }
 
     let result = none, complained = false;
diff --git a/src/test/run-pass/bind-methods.rs b/src/test/run-pass/bind-methods.rs
new file mode 100644
index 00000000000..2eb20ed0e9d
--- /dev/null
+++ b/src/test/run-pass/bind-methods.rs
@@ -0,0 +1,20 @@
+iface foo {
+    fn foo() -> int;
+    fn bar(p: int) -> int;
+}
+impl of foo for int {
+    fn foo() -> int { self }
+    fn bar(p: int) -> int { p * self.foo() }
+}
+impl <T: foo> of foo for [T] {
+    fn foo() -> int { vec::foldl(0, self, {|a, b| a + b.foo()}) }
+    fn bar(p: int) -> int { p + self.len() as int }
+}
+
+fn main() {
+    let x = [1, 2, 3];
+    let y = x.foo, z = [4, 5, 6].foo;
+    assert y() + z() == 21;
+    let a = x.bar, b = bind [4, 5, 6].bar(_);
+    assert a(1) + b(2) + z() == 24;
+}