Allow operator overloading of the indexing operator

The method `op_index` (which takes a single argument) is used for
this.

Issue #1520
This commit is contained in:
Marijn Haverbeke 2012-01-26 15:23:04 +01:00
parent 87b064b249
commit 888262b337
7 changed files with 88 additions and 57 deletions

View file

@ -1983,9 +1983,12 @@ fn visit_mod_with_impl_scope(e: @env, m: ast::_mod, s: span, sc: iscopes,
fn resolve_impl_in_expr(e: @env, x: @ast::expr, sc: iscopes, v: vt<iscopes>) {
alt x.node {
// Store the visible impls in all exprs that might need them
ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) |
ast::expr_binary(_, _, _) | ast::expr_unary(_, _) |
ast::expr_assign_op(_, _, _) { e.impl_map.insert(x.id, sc); }
ast::expr_assign_op(_, _, _) | ast::expr_index(_, _) {
e.impl_map.insert(x.id, sc);
}
_ {}
}
visit::visit_expr(x, sc, v);

View file

@ -2724,9 +2724,8 @@ fn trans_rec_field(bcx: @block_ctxt, base: @ast::expr,
ret {bcx: bcx, val: val, kind: owned};
}
fn trans_index(cx: @block_ctxt, sp: span, base: @ast::expr, idx: @ast::expr,
id: ast::node_id) -> lval_result {
// Is this an interior vector?
fn trans_index(cx: @block_ctxt, ex: @ast::expr, base: @ast::expr,
idx: @ast::expr) -> lval_result {
let base_ty = ty::expr_ty(bcx_tcx(cx), base);
let exp = trans_temp_expr(cx, base);
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,
ix_val = Trunc(bcx, ix.val, ccx.int_type);
} else { ix_val = ix.val; }
let unit_ty = node_id_type(bcx_ccx(cx), id);
let unit_ty = node_id_type(bcx_ccx(cx), ex.id);
let unit_sz = size_of(bcx, unit_ty);
bcx = unit_sz.bcx;
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,
CondBr(bcx, bounds_check, next_cx.llbb, fail_cx.llbb);
// fail: bad bounds check.
trans_fail(fail_cx, some::<span>(sp), "bounds check");
trans_fail(fail_cx, some(ex.span), "bounds check");
let elt =
if check type_has_static_size(ncx, unit_ty) {
let elt_1 = GEP(next_cx, body, [ix_val]);
let llunitty = type_of(ncx, sp, unit_ty);
let llunitty = type_of(ncx, ex.span, unit_ty);
PointerCast(next_cx, elt_1, T_ptr(llunitty))
} else {
body = PointerCast(next_cx, body, T_ptr(T_i8()));
@ -2812,7 +2811,7 @@ fn trans_lval(cx: @block_ctxt, e: @ast::expr) -> lval_result {
ret trans_rec_field(cx, base, ident);
}
ast::expr_index(base, idx) {
ret trans_index(cx, e.span, base, idx, e.id);
ret trans_index(cx, e, base, idx);
}
ast::expr_unary(ast::deref, base) {
let ccx = bcx_ccx(cx);
@ -3560,6 +3559,15 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
ast::expr_field(_, _, _) {
fail "Taking the value of a method does not work yet (issue #435)";
}
ast::expr_index(base, idx) {
// If it is here, it's not an lval, so this is a user-defined index op
let origin = bcx_ccx(bcx).method_map.get(e.id);
let callee_id = ast_util::op_expr_callee_id(e);
let fty = ty::node_id_to_monotype(tcx, callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
trans_impl::trans_method_callee(bcx, callee_id, base, origin)
}, [idx], e.id, dest);
}
// These return nothing
ast::expr_break {

View file

@ -1607,9 +1607,10 @@ fn expr_has_ty_params(cx: ctxt, expr: @ast::expr) -> bool {
fn expr_is_lval(method_map: typeck::method_map, e: @ast::expr) -> bool {
alt e.node {
ast::expr_path(_) | ast::expr_index(_, _) |
ast::expr_unary(ast::deref, _) { true }
ast::expr_field(base, ident, _) { !method_map.contains_key(e.id) }
ast::expr_path(_) | ast::expr_unary(ast::deref, _) { true }
ast::expr_field(_, _, _) | ast::expr_index(_, _) {
!method_map.contains_key(e.id)
}
_ { false }
}
}

View file

@ -1780,6 +1780,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
_ { none }
}
}
fn lookup_op_method(fcx: @fn_ctxt, op_ex: @ast::expr, self_t: ty::t,
opname: str,
args: [option::t<@ast::expr>]) -> option::t<ty::t> {
let isc = fcx.ccx.impl_map.get(op_ex.id);
alt lookup_method(fcx, isc, opname, self_t, op_ex.span) {
some({method_ty, n_tps: 0u, substs, origin}) {
let callee_id = ast_util::op_expr_callee_id(op_ex);
write::ty_fixup(fcx, callee_id, {substs: some(substs),
ty: method_ty});
check_call_or_bind(fcx, op_ex.span, method_ty, args);
fcx.ccx.method_map.insert(op_ex.id, origin);
some(ty::ty_fn_ret(fcx.ccx.tcx, method_ty))
}
_ { none }
}
}
fn check_binop(fcx: @fn_ctxt, ex: @ast::expr, ty: ty::t,
op: ast::binop, rhs: @ast::expr) -> ty::t {
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,
};
}
let isc = fcx.ccx.impl_map.get(ex.id);
alt binop_method(op) {
some(name) {
alt lookup_method(fcx, isc, name, resolved_t, ex.span) {
some({method_ty, n_tps: 0u, substs, origin}) {
let callee_id = ast_util::op_expr_callee_id(ex);
write::ty_fixup(fcx, callee_id, {substs: some(substs),
ty: method_ty});
check_call_or_bind(fcx, ex.span, method_ty, [some(rhs)]);
fcx.ccx.method_map.insert(ex.id, origin);
ret ty::ty_fn_ret(tcx, method_ty);
}
alt lookup_op_method(fcx, ex, resolved_t, name, [some(rhs)]) {
some(ret_ty) { ret ret_ty; }
_ {}
}
}
none {}
_ {}
}
tcx.sess.span_err(
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,
}
fn check_user_unop(fcx: @fn_ctxt, op_str: str, mname: str,
ex: @ast::expr, rhs_t: ty::t) -> ty::t {
let isc = fcx.ccx.impl_map.get(ex.id);
let tcx = fcx.ccx.tcx;
alt lookup_method(fcx, isc, mname, rhs_t, ex.span) {
some({method_ty, n_tps: 0u, substs, origin}) {
let callee_id = ast_util::op_expr_callee_id(ex);
write::ty_fixup(fcx, callee_id, {substs: some(substs),
ty: method_ty});
check_call_or_bind(fcx, ex.span, method_ty, []);
fcx.ccx.method_map.insert(ex.id, origin);
ret ty::ty_fn_ret(tcx, method_ty);
alt lookup_op_method(fcx, ex, rhs_t, mname, []) {
some(ret_ty) { ret_ty }
_ {
fcx.ccx.tcx.sess.span_err(
ex.span, #fmt["cannot apply unary operator `%s` to type `%s`",
op_str, ty_to_str(fcx.ccx.tcx, rhs_t)]);
rhs_t
}
_ {}
}
tcx.sess.span_err(
ex.span, #fmt["can not apply unary operator `%s` to type `%s`",
op_str, ty_to_str(tcx, rhs_t)]);
rhs_t
}
let tcx = fcx.ccx.tcx;
@ -1888,7 +1888,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
ty::ty_ptr(inner) {
oper_t = inner.ty;
require_unsafe(fcx.ccx.tcx.sess, fcx.purity, expr.span);
require_unsafe(tcx.sess, fcx.purity, expr.span);
}
_ {
tcx.sess.span_fatal(expr.span,
@ -1989,7 +1989,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
ast::expr_copy(a) {
bot = check_expr_with_unifier(fcx, a, unify, expected);
let tpot =
ty::node_id_to_ty_param_substs_opt_and_ty(fcx.ccx.tcx, a.id);
ty::node_id_to_ty_param_substs_opt_and_ty(tcx, a.id);
write::ty_fixup(fcx, id, tpot);
}
@ -2073,9 +2073,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let proto = alt ty::struct(tcx, expected) {
ty::ty_fn({proto, _}) { proto }
_ {
fcx.ccx.tcx.sess.span_warn(
expr.span,
"unable to infer kind of closure, defaulting to block");
tcx.sess.span_warn(expr.span, "unable to infer kind of closure, \
defaulting to block");
ast::proto_block
}
};
@ -2190,10 +2189,10 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
vec::reserve(elt_ts, vec::len(elts));
for e in elts {
check_expr(fcx, e);
let ety = expr_ty(fcx.ccx.tcx, e);
let ety = expr_ty(tcx, e);
elt_ts += [ety];
}
let typ = ty::mk_tup(fcx.ccx.tcx, elt_ts);
let typ = ty::mk_tup(tcx, elt_ts);
write::ty_only_fixup(fcx, id, typ);
}
ast::expr_rec(fields, base) {
@ -2312,26 +2311,39 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
ast::expr_index(base, idx) {
bot |= check_expr(fcx, base);
let base_t = expr_ty(tcx, base);
base_t = do_autoderef(fcx, expr.span, base_t);
let raw_base_t = expr_ty(tcx, base);
let base_t = do_autoderef(fcx, expr.span, raw_base_t);
bot |= check_expr(fcx, idx);
let idx_t = expr_ty(tcx, idx);
if !type_is_integral(fcx, idx.span, idx_t) {
tcx.sess.span_err(idx.span,
"mismatched types: expected \
`integer` but found `"
+ ty_to_str(tcx, idx_t) + "`");
fn require_integral(fcx: @fn_ctxt, sp: span, t: ty::t) {
if !type_is_integral(fcx, sp, t) {
fcx.ccx.tcx.sess.span_err(sp, "mismatched types: expected \
`integer` but found `"
+ ty_to_str(fcx.ccx.tcx, t) + "`");
}
}
alt structure_of(fcx, expr.span, base_t) {
ty::ty_vec(mt) { write::ty_only_fixup(fcx, id, mt.ty); }
ty::ty_vec(mt) {
require_integral(fcx, idx.span, idx_t);
write::ty_only_fixup(fcx, id, mt.ty);
}
ty::ty_str {
require_integral(fcx, idx.span, idx_t);
let typ = ty::mk_mach_uint(tcx, ast::ty_u8);
write::ty_only_fixup(fcx, id, typ);
}
_ {
tcx.sess.span_fatal(expr.span,
"vector-indexing bad type: " +
ty_to_str(tcx, base_t));
let resolved = structurally_resolved_type(fcx, expr.span,
raw_base_t);
alt lookup_op_method(fcx, expr, resolved, "op_index",
[some(idx)]) {
some(ret_ty) { write::ty_only_fixup(fcx, id, ret_ty); }
_ {
tcx.sess.span_fatal(
expr.span, "cannot index a value of type `" +
ty_to_str(tcx, base_t) + "`");
}
}
}
}
}
@ -2922,7 +2934,8 @@ mod dict {
}
// Must resolve bounds on methods with bounded params
ast::expr_field(_, _, _) | ast::expr_binary(_, _, _) |
ast::expr_unary(_, _) | ast::expr_assign_op(_, _, _) {
ast::expr_unary(_, _) | ast::expr_assign_op(_, _, _) |
ast::expr_index(_, _) {
alt cx.method_map.find(ex.id) {
some(method_static(did)) {
let bounds = ty::lookup_item_type(cx.tcx, did).bounds;

View file

@ -1008,6 +1008,7 @@ fn parse_dot_or_call_expr_with(p: parser, e0: pexpr) -> pexpr {
let ix = parse_expr(p);
hi = ix.span.hi;
expect(p, token::RBRACKET);
p.get_id(); // see ast_util::op_expr_callee_id
e = mk_pexpr(p, lo, hi, ast::expr_index(to_expr(e), ix));
}

View file

@ -1,3 +1,3 @@
// error-pattern:can not apply unary operator `-` to type `str`
// error-pattern:cannot apply unary operator `-` to type `str`
fn main() { -"foo"; }

View file

@ -7,6 +7,9 @@ impl add_point for point {
fn op_neg() -> point {
{x: -self.x, y: -self.y}
}
fn op_index(x: bool) -> int {
x ? self.x : self.y
}
}
fn main() {
@ -14,4 +17,6 @@ fn main() {
p += {x: 1, y: 2};
assert p + {x: 5, y: 5} == {x: 16, y: 27};
assert -p == {x: -11, y: -22};
assert p[true] == 11;
assert p[false] == 22;
}