Skip to content

Commit 888262b

Browse files
committed
Allow operator overloading of the indexing operator
The method `op_index` (which takes a single argument) is used for this. Issue #1520
1 parent 87b064b commit 888262b

File tree

7 files changed

+88
-57
lines changed

7 files changed

+88
-57
lines changed

src/comp/middle/resolve.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1983,9 +1983,12 @@ 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+
// Store the visible impls in all exprs that might need them
19861987
ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) |
19871988
ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
1988-
ast::expr_assign_op(_, _, _) { e.impl_map.insert(x.id, sc); }
1989+
ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
1990+
e.impl_map.insert(x.id, sc);
1991+
}
19891992
_ {}
19901993
}
19911994
visit::visit_expr(x, sc, v);

src/comp/middle/trans.rs

+15-7
Original file line numberDiff line numberDiff line change
@@ -2724,9 +2724,8 @@ fn trans_rec_field(bcx: @block_ctxt, base: @ast::expr,
27242724
ret {bcx: bcx, val: val, kind: owned};
27252725
}
27262726

2727-
fn trans_index(cx: @block_ctxt, sp: span, base: @ast::expr, idx: @ast::expr,
2728-
id: ast::node_id) -> lval_result {
2729-
// Is this an interior vector?
2727+
fn trans_index(cx: @block_ctxt, ex: @ast::expr, base: @ast::expr,
2728+
idx: @ast::expr) -> lval_result {
27302729
let base_ty = ty::expr_ty(bcx_tcx(cx), base);
27312730
let exp = trans_temp_expr(cx, base);
27322731
let lv = autoderef(exp.bcx, exp.val, base_ty);
@@ -2745,7 +2744,7 @@ fn trans_index(cx: @block_ctxt, sp: span, base: @ast::expr, idx: @ast::expr,
27452744
ix_val = Trunc(bcx, ix.val, ccx.int_type);
27462745
} else { ix_val = ix.val; }
27472746

2748-
let unit_ty = node_id_type(bcx_ccx(cx), id);
2747+
let unit_ty = node_id_type(bcx_ccx(cx), ex.id);
27492748
let unit_sz = size_of(bcx, unit_ty);
27502749
bcx = unit_sz.bcx;
27512750
maybe_name_value(bcx_ccx(cx), unit_sz.val, "unit_sz");
@@ -2760,11 +2759,11 @@ fn trans_index(cx: @block_ctxt, sp: span, base: @ast::expr, idx: @ast::expr,
27602759
CondBr(bcx, bounds_check, next_cx.llbb, fail_cx.llbb);
27612760
// fail: bad bounds check.
27622761

2763-
trans_fail(fail_cx, some::<span>(sp), "bounds check");
2762+
trans_fail(fail_cx, some(ex.span), "bounds check");
27642763
let elt =
27652764
if check type_has_static_size(ncx, unit_ty) {
27662765
let elt_1 = GEP(next_cx, body, [ix_val]);
2767-
let llunitty = type_of(ncx, sp, unit_ty);
2766+
let llunitty = type_of(ncx, ex.span, unit_ty);
27682767
PointerCast(next_cx, elt_1, T_ptr(llunitty))
27692768
} else {
27702769
body = PointerCast(next_cx, body, T_ptr(T_i8()));
@@ -2812,7 +2811,7 @@ fn trans_lval(cx: @block_ctxt, e: @ast::expr) -> lval_result {
28122811
ret trans_rec_field(cx, base, ident);
28132812
}
28142813
ast::expr_index(base, idx) {
2815-
ret trans_index(cx, e.span, base, idx, e.id);
2814+
ret trans_index(cx, e, base, idx);
28162815
}
28172816
ast::expr_unary(ast::deref, base) {
28182817
let ccx = bcx_ccx(cx);
@@ -3560,6 +3559,15 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
35603559
ast::expr_field(_, _, _) {
35613560
fail "Taking the value of a method does not work yet (issue #435)";
35623561
}
3562+
ast::expr_index(base, idx) {
3563+
// If it is here, it's not an lval, so this is a user-defined index op
3564+
let origin = bcx_ccx(bcx).method_map.get(e.id);
3565+
let callee_id = ast_util::op_expr_callee_id(e);
3566+
let fty = ty::node_id_to_monotype(tcx, callee_id);
3567+
ret trans_call_inner(bcx, fty, {|bcx|
3568+
trans_impl::trans_method_callee(bcx, callee_id, base, origin)
3569+
}, [idx], e.id, dest);
3570+
}
35633571

35643572
// These return nothing
35653573
ast::expr_break {

src/comp/middle/ty.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -1607,9 +1607,10 @@ fn expr_has_ty_params(cx: ctxt, expr: @ast::expr) -> bool {
16071607

16081608
fn expr_is_lval(method_map: typeck::method_map, e: @ast::expr) -> bool {
16091609
alt e.node {
1610-
ast::expr_path(_) | ast::expr_index(_, _) |
1611-
ast::expr_unary(ast::deref, _) { true }
1612-
ast::expr_field(base, ident, _) { !method_map.contains_key(e.id) }
1610+
ast::expr_path(_) | ast::expr_unary(ast::deref, _) { true }
1611+
ast::expr_field(_, _, _) | ast::expr_index(_, _) {
1612+
!method_map.contains_key(e.id)
1613+
}
16131614
_ { false }
16141615
}
16151616
}

src/comp/middle/typeck.rs

+58-45
Original file line numberDiff line numberDiff line change
@@ -1780,6 +1780,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
17801780
_ { none }
17811781
}
17821782
}
1783+
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr, self_t: ty::t,
1784+
opname: str,
1785+
args: [option::t<@ast::expr>]) -> option::t<ty::t> {
1786+
let isc = fcx.ccx.impl_map.get(op_ex.id);
1787+
alt lookup_method(fcx, isc, opname, self_t, op_ex.span) {
1788+
some({method_ty, n_tps: 0u, substs, origin}) {
1789+
let callee_id = ast_util::op_expr_callee_id(op_ex);
1790+
write::ty_fixup(fcx, callee_id, {substs: some(substs),
1791+
ty: method_ty});
1792+
check_call_or_bind(fcx, op_ex.span, method_ty, args);
1793+
fcx.ccx.method_map.insert(op_ex.id, origin);
1794+
some(ty::ty_fn_ret(fcx.ccx.tcx, method_ty))
1795+
}
1796+
_ { none }
1797+
}
1798+
}
17831799
fn check_binop(fcx: @fn_ctxt, ex: @ast::expr, ty: ty::t,
17841800
op: ast::binop, rhs: @ast::expr) -> ty::t {
17851801
let resolved_t = structurally_resolved_type(fcx, ex.span, ty);
@@ -1792,22 +1808,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
17921808
};
17931809
}
17941810

1795-
let isc = fcx.ccx.impl_map.get(ex.id);
17961811
alt binop_method(op) {
17971812
some(name) {
1798-
alt lookup_method(fcx, isc, name, resolved_t, ex.span) {
1799-
some({method_ty, n_tps: 0u, substs, origin}) {
1800-
let callee_id = ast_util::op_expr_callee_id(ex);
1801-
write::ty_fixup(fcx, callee_id, {substs: some(substs),
1802-
ty: method_ty});
1803-
check_call_or_bind(fcx, ex.span, method_ty, [some(rhs)]);
1804-
fcx.ccx.method_map.insert(ex.id, origin);
1805-
ret ty::ty_fn_ret(tcx, method_ty);
1806-
}
1813+
alt lookup_op_method(fcx, ex, resolved_t, name, [some(rhs)]) {
1814+
some(ret_ty) { ret ret_ty; }
18071815
_ {}
18081816
}
18091817
}
1810-
none {}
1818+
_ {}
18111819
}
18121820
tcx.sess.span_err(
18131821
ex.span, "binary operation " + ast_util::binop_to_str(op) +
@@ -1817,23 +1825,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
18171825
}
18181826
fn check_user_unop(fcx: @fn_ctxt, op_str: str, mname: str,
18191827
ex: @ast::expr, rhs_t: ty::t) -> ty::t {
1820-
let isc = fcx.ccx.impl_map.get(ex.id);
1821-
let tcx = fcx.ccx.tcx;
1822-
alt lookup_method(fcx, isc, mname, rhs_t, ex.span) {
1823-
some({method_ty, n_tps: 0u, substs, origin}) {
1824-
let callee_id = ast_util::op_expr_callee_id(ex);
1825-
write::ty_fixup(fcx, callee_id, {substs: some(substs),
1826-
ty: method_ty});
1827-
check_call_or_bind(fcx, ex.span, method_ty, []);
1828-
fcx.ccx.method_map.insert(ex.id, origin);
1829-
ret ty::ty_fn_ret(tcx, method_ty);
1828+
alt lookup_op_method(fcx, ex, rhs_t, mname, []) {
1829+
some(ret_ty) { ret_ty }
1830+
_ {
1831+
fcx.ccx.tcx.sess.span_err(
1832+
ex.span, #fmt["cannot apply unary operator `%s` to type `%s`",
1833+
op_str, ty_to_str(fcx.ccx.tcx, rhs_t)]);
1834+
rhs_t
18301835
}
1831-
_ {}
18321836
}
1833-
tcx.sess.span_err(
1834-
ex.span, #fmt["can not apply unary operator `%s` to type `%s`",
1835-
op_str, ty_to_str(tcx, rhs_t)]);
1836-
rhs_t
18371837
}
18381838

18391839
let tcx = fcx.ccx.tcx;
@@ -1888,7 +1888,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
18881888
}
18891889
ty::ty_ptr(inner) {
18901890
oper_t = inner.ty;
1891-
require_unsafe(fcx.ccx.tcx.sess, fcx.purity, expr.span);
1891+
require_unsafe(tcx.sess, fcx.purity, expr.span);
18921892
}
18931893
_ {
18941894
tcx.sess.span_fatal(expr.span,
@@ -1989,7 +1989,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
19891989
ast::expr_copy(a) {
19901990
bot = check_expr_with_unifier(fcx, a, unify, expected);
19911991
let tpot =
1992-
ty::node_id_to_ty_param_substs_opt_and_ty(fcx.ccx.tcx, a.id);
1992+
ty::node_id_to_ty_param_substs_opt_and_ty(tcx, a.id);
19931993
write::ty_fixup(fcx, id, tpot);
19941994

19951995
}
@@ -2073,9 +2073,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
20732073
let proto = alt ty::struct(tcx, expected) {
20742074
ty::ty_fn({proto, _}) { proto }
20752075
_ {
2076-
fcx.ccx.tcx.sess.span_warn(
2077-
expr.span,
2078-
"unable to infer kind of closure, defaulting to block");
2076+
tcx.sess.span_warn(expr.span, "unable to infer kind of closure, \
2077+
defaulting to block");
20792078
ast::proto_block
20802079
}
20812080
};
@@ -2190,10 +2189,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
21902189
vec::reserve(elt_ts, vec::len(elts));
21912190
for e in elts {
21922191
check_expr(fcx, e);
2193-
let ety = expr_ty(fcx.ccx.tcx, e);
2192+
let ety = expr_ty(tcx, e);
21942193
elt_ts += [ety];
21952194
}
2196-
let typ = ty::mk_tup(fcx.ccx.tcx, elt_ts);
2195+
let typ = ty::mk_tup(tcx, elt_ts);
21972196
write::ty_only_fixup(fcx, id, typ);
21982197
}
21992198
ast::expr_rec(fields, base) {
@@ -2312,26 +2311,39 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
23122311
}
23132312
ast::expr_index(base, idx) {
23142313
bot |= check_expr(fcx, base);
2315-
let base_t = expr_ty(tcx, base);
2316-
base_t = do_autoderef(fcx, expr.span, base_t);
2314+
let raw_base_t = expr_ty(tcx, base);
2315+
let base_t = do_autoderef(fcx, expr.span, raw_base_t);
23172316
bot |= check_expr(fcx, idx);
23182317
let idx_t = expr_ty(tcx, idx);
2319-
if !type_is_integral(fcx, idx.span, idx_t) {
2320-
tcx.sess.span_err(idx.span,
2321-
"mismatched types: expected \
2322-
`integer` but found `"
2323-
+ ty_to_str(tcx, idx_t) + "`");
2318+
fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) {
2319+
if !type_is_integral(fcx, sp, t) {
2320+
fcx.ccx.tcx.sess.span_err(sp, "mismatched types: expected \
2321+
`integer` but found `"
2322+
+ ty_to_str(fcx.ccx.tcx, t) + "`");
2323+
}
23242324
}
23252325
alt structure_of(fcx, expr.span, base_t) {
2326-
ty::ty_vec(mt) { write::ty_only_fixup(fcx, id, mt.ty); }
2326+
ty::ty_vec(mt) {
2327+
require_integral(fcx, idx.span, idx_t);
2328+
write::ty_only_fixup(fcx, id, mt.ty);
2329+
}
23272330
ty::ty_str {
2331+
require_integral(fcx, idx.span, idx_t);
23282332
let typ = ty::mk_mach_uint(tcx, ast::ty_u8);
23292333
write::ty_only_fixup(fcx, id, typ);
23302334
}
23312335
_ {
2332-
tcx.sess.span_fatal(expr.span,
2333-
"vector-indexing bad type: " +
2334-
ty_to_str(tcx, base_t));
2336+
let resolved = structurally_resolved_type(fcx, expr.span,
2337+
raw_base_t);
2338+
alt lookup_op_method(fcx, expr, resolved, "op_index",
2339+
[some(idx)]) {
2340+
some(ret_ty) { write::ty_only_fixup(fcx, id, ret_ty); }
2341+
_ {
2342+
tcx.sess.span_fatal(
2343+
expr.span, "cannot index a value of type `" +
2344+
ty_to_str(tcx, base_t) + "`");
2345+
}
2346+
}
23352347
}
23362348
}
23372349
}
@@ -2922,7 +2934,8 @@ mod dict {
29222934
}
29232935
// Must resolve bounds on methods with bounded params
29242936
ast::expr_field(_, _, _) | ast::expr_binary(_, _, _) |
2925-
ast::expr_unary(_, _) | ast::expr_assign_op(_, _, _) {
2937+
ast::expr_unary(_, _) | ast::expr_assign_op(_, _, _) |
2938+
ast::expr_index(_, _) {
29262939
alt cx.method_map.find(ex.id) {
29272940
some(method_static(did)) {
29282941
let bounds = ty::lookup_item_type(cx.tcx, did).bounds;

src/comp/syntax/parse/parser.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,7 @@ fn parse_dot_or_call_expr_with(p: parser, e0: pexpr) -> pexpr {
10081008
let ix = parse_expr(p);
10091009
hi = ix.span.hi;
10101010
expect(p, token::RBRACKET);
1011+
p.get_id(); // see ast_util::op_expr_callee_id
10111012
e = mk_pexpr(p, lo, hi, ast::expr_index(to_expr(e), ix));
10121013
}
10131014

src/test/compile-fail/minus-string.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
// error-pattern:can not apply unary operator `-` to type `str`
1+
// error-pattern:cannot apply unary operator `-` to type `str`
22

33
fn main() { -"foo"; }

src/test/run-pass/operator-overloading.rs

+5
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ impl add_point for point {
77
fn op_neg() -> point {
88
{x: -self.x, y: -self.y}
99
}
10+
fn op_index(x: bool) -> int {
11+
x ? self.x : self.y
12+
}
1013
}
1114

1215
fn main() {
1316
let p = {x: 10, y: 20};
1417
p += {x: 1, y: 2};
1518
assert p + {x: 5, y: 5} == {x: 16, y: 27};
1619
assert -p == {x: -11, y: -22};
20+
assert p[true] == 11;
21+
assert p[false] == 22;
1722
}

0 commit comments

Comments
 (0)