Skip to content

Commit 57c7645

Browse files
committed
Allow static method calls to be bound
This allows you to take the value of, for example, `[1].len`, or bind it with `bind x.map(_)` syntax. I'm holding off on implementing this for dynamic methods (those on bounded type parameters or iface types) until it's clearer what we will do with monomorphization. Issue #435
1 parent f2e880b commit 57c7645

File tree

5 files changed

+107
-69
lines changed

5 files changed

+107
-69
lines changed

src/comp/middle/trans/base.rs

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,7 +2188,7 @@ type lval_result = {bcx: @block_ctxt, val: ValueRef, kind: lval_kind};
21882188
enum callee_env {
21892189
null_env,
21902190
is_closure,
2191-
self_env(ValueRef),
2191+
self_env(ValueRef, ty::t),
21922192
dict_env(ValueRef, ValueRef),
21932193
}
21942194
type lval_maybe_callee = {bcx: @block_ctxt,
@@ -2597,36 +2597,28 @@ fn trans_lval(cx: @block_ctxt, e: @ast::expr) -> lval_result {
25972597
}
25982598
}
25992599

2600-
fn maybe_add_env(bcx: @block_ctxt, c: lval_maybe_callee)
2601-
-> (lval_kind, ValueRef) {
2602-
alt c.env {
2603-
is_closure { (c.kind, c.val) }
2604-
self_env(_) | dict_env(_, _) {
2605-
fail "Taking the value of a method does not work yet (issue #435)";
2606-
}
2607-
null_env {
2608-
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
2609-
(temporary, create_real_fn_pair(bcx, llfnty, c.val,
2610-
null_env_ptr(bcx)))
2611-
}
2612-
}
2613-
}
2614-
26152600
fn lval_maybe_callee_to_lval(c: lval_maybe_callee, ty: ty::t) -> lval_result {
2616-
alt c.generic {
2617-
generic_full(gi) {
2601+
let must_bind = alt c.generic { generic_full(_) { true } _ { false } } ||
2602+
alt c.env { self_env(_, _) | dict_env(_, _) { true } _ { false } };
2603+
if must_bind {
26182604
let n_args = ty::ty_fn_args(ty).len();
2619-
let args = vec::init_elt(n_args, none::<@ast::expr>);
2605+
let args = vec::init_elt(n_args, none);
26202606
let space = alloc_ty(c.bcx, ty);
26212607
let bcx = closure::trans_bind_1(space.bcx, ty, c, args, ty,
26222608
save_in(space.val));
26232609
add_clean_temp(bcx, space.val, ty);
2624-
ret {bcx: bcx, val: space.val, kind: temporary};
2625-
}
2626-
_ {
2627-
let (kind, val) = maybe_add_env(c.bcx, c);
2628-
ret {bcx: c.bcx, val: val, kind: kind};
2629-
}
2610+
{bcx: bcx, val: space.val, kind: temporary}
2611+
} else {
2612+
alt c.env {
2613+
is_closure { {bcx: c.bcx, val: c.val, kind: c.kind} }
2614+
null_env {
2615+
let llfnty = llvm::LLVMGetElementType(val_ty(c.val));
2616+
let llfn = create_real_fn_pair(c.bcx, llfnty, c.val,
2617+
null_env_ptr(c.bcx));
2618+
{bcx: c.bcx, val: llfn, kind: temporary}
2619+
}
2620+
_ { fail; }
2621+
}
26302622
}
26312623
}
26322624

@@ -2762,7 +2754,7 @@ fn trans_arg_expr(cx: @block_ctxt, arg: ty::arg, lldestty: TypeRef,
27622754
if arg_mode == ast::by_val && (lv.kind == owned || !imm) {
27632755
val = Load(bcx, val);
27642756
}
2765-
} else if arg_mode == ast::by_copy {
2757+
} else if arg_mode == ast::by_copy {
27662758
let {bcx: cx, val: alloc} = alloc_ty(bcx, e_ty);
27672759
let last_use = ccx.last_uses.contains_key(e.id);
27682760
bcx = cx;
@@ -2926,16 +2918,21 @@ fn trans_call_inner(in_cx: @block_ctxt, fn_expr_ty: ty::t,
29262918
let cx = new_scope_block_ctxt(in_cx, "call");
29272919
Br(in_cx, cx.llbb);
29282920
let f_res = get_callee(cx);
2929-
let bcx = f_res.bcx;
2921+
let bcx = f_res.bcx, ccx = bcx_ccx(cx);
29302922

29312923
let faddr = f_res.val;
29322924
let llenv, dict_param = none;
29332925
alt f_res.env {
29342926
null_env {
2935-
llenv = llvm::LLVMGetUndef(T_opaque_box_ptr(bcx_ccx(cx)));
2927+
llenv = llvm::LLVMGetUndef(T_opaque_box_ptr(ccx));
2928+
}
2929+
self_env(e, _) {
2930+
llenv = PointerCast(bcx, e, T_opaque_box_ptr(ccx));
2931+
}
2932+
dict_env(dict, e) {
2933+
llenv = PointerCast(bcx, e, T_opaque_box_ptr(ccx));
2934+
dict_param = some(dict);
29362935
}
2937-
self_env(e) { llenv = e; }
2938-
dict_env(dict, e) { llenv = e; dict_param = some(dict); }
29392936
is_closure {
29402937
// It's a closure. Have to fetch the elements
29412938
if f_res.kind == owned {
@@ -3306,7 +3303,9 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
33063303
ret trans_call(bcx, f, args, e.id, dest);
33073304
}
33083305
ast::expr_field(_, _, _) {
3309-
fail "Taking the value of a method does not work yet (issue #435)";
3306+
let callee = trans_callee(bcx, e), ty = expr_ty(bcx, e);
3307+
let lv = lval_maybe_callee_to_lval(callee, ty);
3308+
ret memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, ty);
33103309
}
33113310
ast::expr_index(base, idx) {
33123311
// If it is here, it's not an lval, so this is a user-defined index op

src/comp/middle/trans/closure.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
494494
f_res: lval_maybe_callee,
495495
args: [option<@ast::expr>], pair_ty: ty::t,
496496
dest: dest) -> @block_ctxt {
497+
let ccx = bcx_ccx(cx);
497498
let bound: [@ast::expr] = [];
498499
for argopt: option<@ast::expr> in args {
499500
alt argopt { none { } some(e) { bound += [e]; } }
@@ -523,39 +524,37 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
523524
}
524525
}
525526
}
526-
lazily_emit_all_generic_info_tydesc_glues(bcx_ccx(cx), ginfo);
527+
lazily_emit_all_generic_info_tydesc_glues(ccx, ginfo);
527528
(ginfo.item_type, tds, ginfo.param_bounds)
528529
}
529530
_ { (outgoing_fty, [], @[]) }
530531
};
531532

532-
if bound.len() == 0u && lltydescs.len() == 0u {
533+
if bound.len() == 0u && lltydescs.len() == 0u &&
534+
(f_res.env == null_env || f_res.env == is_closure) {
533535
// Trivial 'binding': just return the closure
534536
let lv = lval_maybe_callee_to_lval(f_res, pair_ty);
535-
bcx = lv.bcx;
536-
ret memmove_ty(bcx, get_dest_addr(dest), lv.val, pair_ty);
537+
ret memmove_ty(lv.bcx, get_dest_addr(dest), lv.val, pair_ty);
537538
}
538-
let closure = alt f_res.env {
539-
null_env { none }
540-
_ { let (_, cl) = maybe_add_env(cx, f_res); some(cl) }
541-
};
542-
543-
// FIXME: should follow from a precondition on trans_bind_1
544-
let ccx = bcx_ccx(cx);
545-
check (type_has_static_size(ccx, outgoing_fty));
546539

547540
// Arrange for the bound function to live in the first binding spot
548541
// if the function is not statically known.
549-
let (env_vals, target_res) = alt closure {
550-
some(cl) {
542+
let (env_vals, target_info) = alt f_res.env {
543+
null_env { ([], target_static(f_res.val)) }
544+
is_closure {
551545
// Cast the function we are binding to be the type that the
552546
// closure will expect it to have. The type the closure knows
553547
// about has the type parameters substituted with the real types.
554548
let llclosurety = T_ptr(type_of(ccx, outgoing_fty));
555-
let src_loc = PointerCast(bcx, cl, llclosurety);
556-
([env_copy(src_loc, pair_ty, owned)], none)
549+
let src_loc = PointerCast(bcx, f_res.val, llclosurety);
550+
([env_copy(src_loc, pair_ty, owned)], target_closure)
551+
}
552+
self_env(slf, slf_t) {
553+
([env_copy(slf, slf_t, owned)], target_self(f_res.val))
554+
}
555+
dict_env(_, _) {
556+
ccx.sess.unimpl("binding of dynamic method calls");
557557
}
558-
none { ([], some(f_res.val)) }
559558
};
560559

561560
// Actually construct the closure
@@ -567,7 +566,7 @@ fn trans_bind_1(cx: @block_ctxt, outgoing_fty: ty::t,
567566
// Make thunk
568567
let llthunk = trans_bind_thunk(
569568
cx.fcx.ccx, cx.fcx.path, pair_ty, outgoing_fty_real, args,
570-
cdata_ty, *param_bounds, target_res);
569+
cdata_ty, *param_bounds, target_info);
571570

572571
// Fill the function pair
573572
fill_fn_pair(bcx, get_dest_addr(dest), llthunk.val, llbox);
@@ -722,6 +721,12 @@ fn make_opaque_cbox_free_glue(
722721
}
723722
}
724723

724+
enum target_info {
725+
target_closure,
726+
target_static(ValueRef),
727+
target_self(ValueRef),
728+
}
729+
725730
// pth is cx.path
726731
fn trans_bind_thunk(ccx: @crate_ctxt,
727732
path: path,
@@ -730,7 +735,7 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
730735
args: [option<@ast::expr>],
731736
cdata_ty: ty::t,
732737
param_bounds: [ty::param_bounds],
733-
target_fn: option<ValueRef>)
738+
target_info: target_info)
734739
-> {val: ValueRef, ty: TypeRef} {
735740

736741
// If we supported constraints on record fields, we could make the
@@ -800,11 +805,11 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
800805
// creating. (In our running example, target is the function f.) Pick
801806
// out the pointer to the target function from the environment. The
802807
// target function lives in the first binding spot.
803-
let (lltargetfn, lltargetenv, starting_idx) = alt target_fn {
804-
some(fptr) {
808+
let (lltargetfn, lltargetenv, starting_idx) = alt target_info {
809+
target_static(fptr) {
805810
(fptr, llvm::LLVMGetUndef(T_opaque_cbox_ptr(ccx)), 0)
806811
}
807-
none {
812+
target_closure {
808813
let {bcx: cx, val: pair} =
809814
GEP_tup_like(bcx, cdata_ty, llcdata,
810815
[0, abi::closure_body_bindings, 0]);
@@ -815,6 +820,12 @@ fn trans_bind_thunk(ccx: @crate_ctxt,
815820
bcx = cx;
816821
(lltargetfn, lltargetenv, 1)
817822
}
823+
target_self(fptr) {
824+
let rs = GEP_tup_like(bcx, cdata_ty, llcdata,
825+
[0, abi::closure_body_bindings, 0]);
826+
bcx = rs.bcx;
827+
(fptr, PointerCast(bcx, rs.val, T_opaque_cbox_ptr(ccx)), 1)
828+
}
818829
};
819830

820831
// And then, pick out the target function's own environment. That's what

src/comp/middle/trans/impl.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,9 @@ fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
6363
let tz = [], tr = [];
6464
let basety = expr_ty(bcx, base);
6565
let m_by_ref = ast::expl(ast::by_ref);
66-
let {bcx, val} =
67-
trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
68-
T_ptr(type_of_or_i8(bcx_ccx(bcx), basety)), tz,
69-
tr, base);
70-
rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
66+
trans_arg_expr(bcx, {mode: m_by_ref, ty: basety},
67+
T_ptr(type_of_or_i8(bcx_ccx(bcx), basety)), tz,
68+
tr, base)
7169
}
7270

7371
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,
10098
substs: option<([ty::t], typeck::dict_res)>)
10199
-> lval_maybe_callee {
102100
let {bcx, val} = trans_self_arg(bcx, base);
103-
{env: self_env(val) with lval_static_fn(bcx, did, callee_id, substs)}
101+
{env: self_env(val, node_id_type(bcx, base.id))
102+
with lval_static_fn(bcx, did, callee_id, substs)}
104103
}
105104

106105
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,
110109
{ty: fty, llty: T_fn([dict_ty] + inputs, output)}
111110
}
112111

113-
fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
112+
fn trans_vtable_callee(bcx: @block_ctxt, env: callee_env, dict: ValueRef,
114113
callee_id: ast::node_id, iface_id: ast::def_id,
115114
n_method: uint) -> lval_maybe_callee {
116115
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,
139138
origins: ccx.dict_map.find(callee_id)});
140139
}
141140
{bcx: bcx, val: mptr, kind: owned,
142-
env: dict_env(dict, self),
141+
env: env,
143142
generic: generic}
144143
}
145144

@@ -181,7 +180,8 @@ fn trans_param_callee(bcx: @block_ctxt, callee_id: ast::node_id,
181180
n_param: uint, n_bound: uint) -> lval_maybe_callee {
182181
let {bcx, val} = trans_self_arg(bcx, base);
183182
let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound];
184-
trans_vtable_callee(bcx, val, dict, callee_id, iface_id, n_method)
183+
trans_vtable_callee(bcx, dict_env(dict, val), dict,
184+
callee_id, iface_id, n_method)
185185
}
186186

187187
// 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,
193193
T_ptr(T_ptr(T_dict()))));
194194
let box = Load(bcx, GEPi(bcx, val, [0, 1]));
195195
// FIXME[impl] I doubt this is alignment-safe
196-
let self = PointerCast(bcx, GEPi(bcx, box, [0, abi::box_field_body]),
197-
T_opaque_cbox_ptr(bcx_ccx(bcx)));
198-
trans_vtable_callee(bcx, self, dict, callee_id, iface_id, n_method)
196+
let self = GEPi(bcx, box, [0, abi::box_field_body]);
197+
trans_vtable_callee(bcx, dict_env(dict, self), dict,
198+
callee_id, iface_id, n_method)
199199
}
200200

201201
fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} {

src/comp/middle/typeck.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,7 +1674,8 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
16741674
alt vec::position(*ifce_methods, {|m| m.ident == name}) {
16751675
some(pos) {
16761676
let m = ifce_methods[pos];
1677-
ret some({method_ty: ty::mk_fn(tcx, m.fty),
1677+
ret some({method_ty: ty::mk_fn(tcx, {proto: ast::proto_box
1678+
with m.fty}),
16781679
n_tps: vec::len(*m.tps),
16791680
substs: tps,
16801681
origin: method_param(iid, pos, n, bound_n),
@@ -1694,7 +1695,7 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
16941695
let i = 0u;
16951696
for m in *ty::iface_methods(tcx, did) {
16961697
if m.ident == name {
1697-
let fty = ty::mk_fn(tcx, m.fty);
1698+
let fty = ty::mk_fn(tcx, {proto: ast::proto_box with m.fty});
16981699
if ty::type_has_vars(fty) {
16991700
tcx.sess.span_fatal(
17001701
expr.span, "can not call a method that contains a \
@@ -1717,13 +1718,20 @@ fn lookup_method_inner(fcx: @fn_ctxt, expr: @ast::expr,
17171718
alt tcx.items.get(did.node) {
17181719
ast_map::node_method(m, _, _) {
17191720
let mt = ty_of_method(tcx, m_check, m);
1720-
ty::mk_fn(tcx, mt.fty)
1721+
ty::mk_fn(tcx, {proto: ast::proto_box with mt.fty})
17211722
}
17221723
_ {
17231724
tcx.sess.bug("Undocumented invariant in ty_from_did");
17241725
}
17251726
}
1726-
} else { csearch::get_type(tcx, did).ty }
1727+
} else {
1728+
alt ty::get(csearch::get_type(tcx, did).ty).struct {
1729+
ty::ty_fn(fty) {
1730+
ty::mk_fn(tcx, {proto: ast::proto_box with fty})
1731+
}
1732+
_ { fail; }
1733+
}
1734+
}
17271735
}
17281736

17291737
let result = none, complained = false;

src/test/run-pass/bind-methods.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
iface foo {
2+
fn foo() -> int;
3+
fn bar(p: int) -> int;
4+
}
5+
impl of foo for int {
6+
fn foo() -> int { self }
7+
fn bar(p: int) -> int { p * self.foo() }
8+
}
9+
impl <T: foo> of foo for [T] {
10+
fn foo() -> int { vec::foldl(0, self, {|a, b| a + b.foo()}) }
11+
fn bar(p: int) -> int { p + self.len() as int }
12+
}
13+
14+
fn main() {
15+
let x = [1, 2, 3];
16+
let y = x.foo, z = [4, 5, 6].foo;
17+
assert y() + z() == 21;
18+
let a = x.bar, b = bind [4, 5, 6].bar(_);
19+
assert a(1) + b(2) + z() == 24;
20+
}

0 commit comments

Comments
 (0)