Skip to content

Commit 87b064b

Browse files
committed
First stab at operator overloading
When no built-in interpretation is found for one of the operators mentioned below, the typechecker will try to turn it into a method call with the name written next to it. For binary operators, the method will be called on the LHS with the RHS as only parameter. Binary: + op_add - op_sub * op_mul / op_div % op_rem & op_and | op_or ^ op_xor << op_shift_left >> op_shift_right >>> op_ashift_right Unary: - op_neg ! op_not Overloading of the indexing ([]) operator isn't finished yet. Issue #1520
1 parent 1792d9e commit 87b064b

File tree

10 files changed

+269
-142
lines changed

10 files changed

+269
-142
lines changed

src/comp/driver/driver.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,12 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
162162
let freevars =
163163
time(time_passes, "freevar finding",
164164
bind freevars::annotate_freevars(def_map, crate));
165-
time(time_passes, "const checking",
166-
bind middle::check_const::check_crate(sess, crate));
167165
let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars);
168166
let (method_map, dict_map) =
169167
time(time_passes, "typechecking",
170168
bind typeck::check_crate(ty_cx, impl_map, crate));
169+
time(time_passes, "const checking",
170+
bind middle::check_const::check_crate(sess, crate, method_map));
171171

172172
if upto == cu_typeck { ret {crate: crate, tcx: some(ty_cx)}; }
173173

src/comp/middle/check_const.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import syntax::ast::*;
22
import syntax::{visit, ast_util};
33
import driver::session::session;
44

5-
fn check_crate(sess: session, crate: @crate) {
5+
fn check_crate(sess: session, crate: @crate, method_map: typeck::method_map) {
66
visit::visit_crate(*crate, false, visit::mk_vt(@{
77
visit_item: check_item,
88
visit_pat: check_pat,
9-
visit_expr: bind check_expr(sess, _, _, _)
9+
visit_expr: bind check_expr(sess, method_map, _, _, _)
1010
with *visit::default_visitor()
1111
}));
1212
sess.abort_if_errors();
@@ -41,7 +41,8 @@ fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) {
4141
}
4242
}
4343

44-
fn check_expr(sess: session, e: @expr, &&is_const: bool, v: visit::vt<bool>) {
44+
fn check_expr(sess: session, method_map: typeck::method_map, e: @expr,
45+
&&is_const: bool, v: visit::vt<bool>) {
4546
if is_const {
4647
alt e.node {
4748
expr_unary(box(_), _) | expr_unary(uniq(_), _) |
@@ -54,7 +55,13 @@ fn check_expr(sess: session, e: @expr, &&is_const: bool, v: visit::vt<bool>) {
5455
sess.span_err(e.span,
5556
"string constants are not supported");
5657
}
57-
expr_lit(_) | expr_binary(_, _, _) | expr_unary(_, _) {}
58+
expr_binary(_, _, _) | expr_unary(_, _) {
59+
if method_map.contains_key(e.id) {
60+
sess.span_err(e.span, "user-defined operators are not \
61+
allowed in constant expressions");
62+
}
63+
}
64+
expr_lit(_) {}
5865
_ {
5966
sess.span_err(e.span,
6067
"constant contains unimplemented expression type");

src/comp/middle/resolve.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1983,9 +1983,9 @@ fn visit_mod_with_impl_scope(e: @env, m: ast::_mod, s: span, sc: iscopes,
19831983

19841984
fn resolve_impl_in_expr(e: @env, x: @ast::expr, sc: iscopes, v: vt<iscopes>) {
19851985
alt x.node {
1986-
ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) {
1987-
e.impl_map.insert(x.id, sc);
1988-
}
1986+
ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) |
1987+
ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
1988+
ast::expr_assign_op(_, _, _) { e.impl_map.insert(x.id, sc); }
19891989
_ {}
19901990
}
19911991
visit::visit_expr(x, sc, v);

src/comp/middle/trans.rs

+66-24
Original file line numberDiff line numberDiff line change
@@ -2072,7 +2072,19 @@ fn node_type(cx: @crate_ctxt, sp: span, id: ast::node_id) -> TypeRef {
20722072
}
20732073

20742074
fn trans_unary(bcx: @block_ctxt, op: ast::unop, e: @ast::expr,
2075-
id: ast::node_id, dest: dest) -> @block_ctxt {
2075+
un_expr: @ast::expr, dest: dest) -> @block_ctxt {
2076+
// Check for user-defined method call
2077+
alt bcx_ccx(bcx).method_map.find(un_expr.id) {
2078+
some(origin) {
2079+
let callee_id = ast_util::op_expr_callee_id(un_expr);
2080+
let fty = ty::node_id_to_monotype(bcx_tcx(bcx), callee_id);
2081+
ret trans_call_inner(bcx, fty, {|bcx|
2082+
trans_impl::trans_method_callee(bcx, callee_id, e, origin)
2083+
}, [], un_expr.id, dest);
2084+
}
2085+
_ {}
2086+
}
2087+
20762088
if dest == ignore { ret trans_expr(bcx, e, ignore); }
20772089
let e_ty = ty::expr_ty(bcx_tcx(bcx), e);
20782090
alt op {
@@ -2104,7 +2116,7 @@ fn trans_unary(bcx: @block_ctxt, op: ast::unop, e: @ast::expr,
21042116
ret store_in_dest(bcx, box, dest);
21052117
}
21062118
ast::uniq(_) {
2107-
ret trans_uniq::trans_uniq(bcx, e, id, dest);
2119+
ret trans_uniq::trans_uniq(bcx, e, un_expr.id, dest);
21082120
}
21092121
ast::deref {
21102122
bcx_ccx(bcx).sess.bug("deref expressions should have been \
@@ -2193,12 +2205,26 @@ fn trans_eager_binop(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
21932205
ret store_in_dest(cx, val, dest);
21942206
}
21952207

2196-
fn trans_assign_op(bcx: @block_ctxt, op: ast::binop, dst: @ast::expr,
2197-
src: @ast::expr) -> @block_ctxt {
2208+
fn trans_assign_op(bcx: @block_ctxt, ex: @ast::expr, op: ast::binop,
2209+
dst: @ast::expr, src: @ast::expr) -> @block_ctxt {
21982210
let tcx = bcx_tcx(bcx);
21992211
let t = ty::expr_ty(tcx, src);
22002212
let lhs_res = trans_lval(bcx, dst);
22012213
assert (lhs_res.kind == owned);
2214+
2215+
// A user-defined operator method
2216+
alt bcx_ccx(bcx).method_map.find(ex.id) {
2217+
some(origin) {
2218+
let callee_id = ast_util::op_expr_callee_id(ex);
2219+
let fty = ty::node_id_to_monotype(bcx_tcx(bcx), callee_id);
2220+
ret trans_call_inner(bcx, fty, {|bcx|
2221+
// FIXME provide the already-computed address, not the expr
2222+
trans_impl::trans_method_callee(bcx, callee_id, src, origin)
2223+
}, [dst], ex.id, save_in(lhs_res.val));
2224+
}
2225+
_ {}
2226+
}
2227+
22022228
// Special case for `+= [x]`
22032229
alt ty::struct(tcx, t) {
22042230
ty::ty_vec(_) {
@@ -2305,20 +2331,34 @@ fn trans_lazy_binop(bcx: @block_ctxt, op: ast::binop, a: @ast::expr,
23052331
ret store_in_dest(join_cx, phi, dest);
23062332
}
23072333

2308-
fn trans_binary(cx: @block_ctxt, op: ast::binop, a: @ast::expr, b: @ast::expr,
2309-
dest: dest) -> @block_ctxt {
2334+
2335+
2336+
fn trans_binary(bcx: @block_ctxt, op: ast::binop, a: @ast::expr,
2337+
b: @ast::expr, dest: dest, ex: @ast::expr) -> @block_ctxt {
2338+
// User-defined operators
2339+
alt bcx_ccx(bcx).method_map.find(ex.id) {
2340+
some(origin) {
2341+
let callee_id = ast_util::op_expr_callee_id(ex);
2342+
let fty = ty::node_id_to_monotype(bcx_tcx(bcx), callee_id);
2343+
ret trans_call_inner(bcx, fty, {|bcx|
2344+
trans_impl::trans_method_callee(bcx, callee_id, a, origin)
2345+
}, [b], ex.id, dest);
2346+
}
2347+
_ {}
2348+
}
2349+
23102350
// First couple cases are lazy:
23112351
alt op {
23122352
ast::and | ast::or {
2313-
ret trans_lazy_binop(cx, op, a, b, dest);
2353+
ret trans_lazy_binop(bcx, op, a, b, dest);
23142354
}
23152355
_ {
23162356
// Remaining cases are eager:
2317-
let lhs = trans_temp_expr(cx, a);
2357+
let lhs = trans_temp_expr(bcx, a);
23182358
let rhs = trans_temp_expr(lhs.bcx, b);
23192359
ret trans_eager_binop(rhs.bcx, op, lhs.val,
2320-
ty::expr_ty(bcx_tcx(cx), a), rhs.val,
2321-
ty::expr_ty(bcx_tcx(cx), b), dest);
2360+
ty::expr_ty(bcx_tcx(bcx), a), rhs.val,
2361+
ty::expr_ty(bcx_tcx(bcx), b), dest);
23222362
}
23232363
}
23242364
}
@@ -2746,15 +2786,8 @@ fn trans_callee(bcx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee {
27462786
// Lval means this is a record field, so not a method
27472787
if !expr_is_lval(bcx, e) {
27482788
alt bcx_ccx(bcx).method_map.find(e.id) {
2749-
some(typeck::method_static(did)) { // An impl method
2750-
ret trans_impl::trans_static_callee(bcx, e, base, did);
2751-
}
2752-
some(typeck::method_param(iid, off, p, b)) {
2753-
ret trans_impl::trans_param_callee(
2754-
bcx, e, base, iid, off, p, b);
2755-
}
2756-
some(typeck::method_iface(off)) {
2757-
ret trans_impl::trans_iface_callee(bcx, e, base, off);
2789+
some(origin) { // An impl method
2790+
ret trans_impl::trans_method_callee(bcx, e.id, base, origin);
27582791
}
27592792
}
27602793
}
@@ -3132,15 +3165,22 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef,
31323165
fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
31333166
args: [@ast::expr], id: ast::node_id, dest: dest)
31343167
-> @block_ctxt {
3168+
trans_call_inner(in_cx, ty::expr_ty(bcx_tcx(in_cx), f),
3169+
{|cx| trans_callee(cx, f)}, args, id, dest)
3170+
}
3171+
3172+
fn trans_call_inner(in_cx: @block_ctxt, fn_expr_ty: ty::t,
3173+
get_callee: fn(@block_ctxt) -> lval_maybe_callee,
3174+
args: [@ast::expr], id: ast::node_id, dest: dest)
3175+
-> @block_ctxt {
31353176
// NB: 'f' isn't necessarily a function; it might be an entire self-call
31363177
// expression because of the hack that allows us to process self-calls
31373178
// with trans_call.
31383179
let tcx = bcx_tcx(in_cx);
3139-
let fn_expr_ty = ty::expr_ty(tcx, f);
31403180

31413181
let cx = new_scope_block_ctxt(in_cx, "call");
31423182
Br(in_cx, cx.llbb);
3143-
let f_res = trans_callee(cx, f);
3183+
let f_res = get_callee(cx);
31443184
let bcx = f_res.bcx;
31453185

31463186
let faddr = f_res.val;
@@ -3478,10 +3518,12 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
34783518
ast::expr_tup(args) { ret trans_tup(bcx, args, e.id, dest); }
34793519
ast::expr_lit(lit) { ret trans_lit(bcx, *lit, dest); }
34803520
ast::expr_vec(args, _) { ret tvec::trans_vec(bcx, args, e.id, dest); }
3481-
ast::expr_binary(op, x, y) { ret trans_binary(bcx, op, x, y, dest); }
3521+
ast::expr_binary(op, x, y) {
3522+
ret trans_binary(bcx, op, x, y, dest, e);
3523+
}
34823524
ast::expr_unary(op, x) {
34833525
assert op != ast::deref; // lvals are handled above
3484-
ret trans_unary(bcx, op, x, e.id, dest);
3526+
ret trans_unary(bcx, op, x, e, dest);
34853527
}
34863528
ast::expr_fn(proto, decl, body, cap_clause) {
34873529
ret trans_closure::trans_expr_fn(
@@ -3620,7 +3662,7 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
36203662
}
36213663
ast::expr_assign_op(op, dst, src) {
36223664
assert dest == ignore;
3623-
ret trans_assign_op(bcx, op, dst, src);
3665+
ret trans_assign_op(bcx, e, op, dst, src);
36243666
}
36253667
}
36263668
}

src/comp/middle/trans_impl.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,28 @@ fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
6262
rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
6363
}
6464

65+
fn trans_method_callee(bcx: @block_ctxt, callee_id: ast::node_id,
66+
self: @ast::expr, origin: typeck::method_origin)
67+
-> lval_maybe_callee {
68+
alt origin {
69+
typeck::method_static(did) {
70+
trans_static_callee(bcx, callee_id, self, did)
71+
}
72+
typeck::method_param(iid, off, p, b) {
73+
trans_param_callee(bcx, callee_id, self, iid, off, p, b)
74+
}
75+
typeck::method_iface(off) {
76+
trans_iface_callee(bcx, callee_id, self, off)
77+
}
78+
}
79+
}
80+
6581
// Method callee where the method is statically known
66-
fn trans_static_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
67-
did: ast::def_id) -> lval_maybe_callee {
82+
fn trans_static_callee(bcx: @block_ctxt, callee_id: ast::node_id,
83+
base: @ast::expr, did: ast::def_id)
84+
-> lval_maybe_callee {
6885
let {bcx, val} = trans_self_arg(bcx, base);
69-
{env: self_env(val) with lval_static_fn(bcx, did, e.id)}
86+
{env: self_env(val) with lval_static_fn(bcx, did, callee_id)}
7087
}
7188

7289
fn wrapper_fn_ty(ccx: @crate_ctxt, dict_ty: TypeRef, m: ty::method)
@@ -79,7 +96,7 @@ fn wrapper_fn_ty(ccx: @crate_ctxt, dict_ty: TypeRef, m: ty::method)
7996
}
8097

8198
fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
82-
fld_expr: @ast::expr, iface_id: ast::def_id,
99+
callee_id: ast::node_id, iface_id: ast::def_id,
83100
n_method: uint) -> lval_maybe_callee {
84101
let bcx = bcx, ccx = bcx_ccx(bcx), tcx = ccx.tcx;
85102
let method = ty::iface_methods(tcx, iface_id)[n_method];
@@ -90,7 +107,7 @@ fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
90107
let generic = none;
91108
if vec::len(*method.tps) > 0u || ty::type_contains_params(tcx, fty) {
92109
let tydescs = [], tis = [];
93-
let tptys = ty::node_id_to_type_params(tcx, fld_expr.id);
110+
let tptys = ty::node_id_to_type_params(tcx, callee_id);
94111
for t in vec::tail_n(tptys, vec::len(tptys) - vec::len(*method.tps)) {
95112
let ti = none;
96113
let td = get_tydesc(bcx, t, true, ti).result;
@@ -102,24 +119,24 @@ fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
102119
static_tis: tis,
103120
tydescs: tydescs,
104121
param_bounds: method.tps,
105-
origins: bcx_ccx(bcx).dict_map.find(fld_expr.id)});
122+
origins: bcx_ccx(bcx).dict_map.find(callee_id)});
106123
}
107124
{bcx: bcx, val: mptr, kind: owned,
108125
env: dict_env(dict, self),
109126
generic: generic}
110127
}
111128

112129
// Method callee where the dict comes from a type param
113-
fn trans_param_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
130+
fn trans_param_callee(bcx: @block_ctxt, callee_id: ast::node_id,
114131
base: @ast::expr, iface_id: ast::def_id, n_method: uint,
115132
n_param: uint, n_bound: uint) -> lval_maybe_callee {
116133
let {bcx, val} = trans_self_arg(bcx, base);
117134
let dict = option::get(bcx.fcx.lltyparams[n_param].dicts)[n_bound];
118-
trans_vtable_callee(bcx, val, dict, fld_expr, iface_id, n_method)
135+
trans_vtable_callee(bcx, val, dict, callee_id, iface_id, n_method)
119136
}
120137

121138
// Method callee where the dict comes from a boxed iface
122-
fn trans_iface_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
139+
fn trans_iface_callee(bcx: @block_ctxt, callee_id: ast::node_id,
123140
base: @ast::expr, n_method: uint)
124141
-> lval_maybe_callee {
125142
let tcx = bcx_tcx(bcx);
@@ -133,7 +150,7 @@ fn trans_iface_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
133150
let iface_id = alt ty::struct(tcx, ty::expr_ty(tcx, base)) {
134151
ty::ty_iface(did, _) { did }
135152
};
136-
trans_vtable_callee(bcx, self, dict, fld_expr, iface_id, n_method)
153+
trans_vtable_callee(bcx, self, dict, callee_id, iface_id, n_method)
137154
}
138155

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

0 commit comments

Comments
 (0)