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) + 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 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; +}