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
This commit is contained in:
Marijn Haverbeke 2012-01-26 12:26:14 +01:00
parent 1792d9ec96
commit 87b064b249
10 changed files with 269 additions and 142 deletions

View file

@ -162,12 +162,12 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
let freevars =
time(time_passes, "freevar finding",
bind freevars::annotate_freevars(def_map, crate));
time(time_passes, "const checking",
bind middle::check_const::check_crate(sess, crate));
let ty_cx = ty::mk_ctxt(sess, def_map, ast_map, freevars);
let (method_map, dict_map) =
time(time_passes, "typechecking",
bind typeck::check_crate(ty_cx, impl_map, crate));
time(time_passes, "const checking",
bind middle::check_const::check_crate(sess, crate, method_map));
if upto == cu_typeck { ret {crate: crate, tcx: some(ty_cx)}; }

View file

@ -2,11 +2,11 @@ import syntax::ast::*;
import syntax::{visit, ast_util};
import driver::session::session;
fn check_crate(sess: session, crate: @crate) {
fn check_crate(sess: session, crate: @crate, method_map: typeck::method_map) {
visit::visit_crate(*crate, false, visit::mk_vt(@{
visit_item: check_item,
visit_pat: check_pat,
visit_expr: bind check_expr(sess, _, _, _)
visit_expr: bind check_expr(sess, method_map, _, _, _)
with *visit::default_visitor()
}));
sess.abort_if_errors();
@ -41,7 +41,8 @@ fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) {
}
}
fn check_expr(sess: session, e: @expr, &&is_const: bool, v: visit::vt<bool>) {
fn check_expr(sess: session, method_map: typeck::method_map, e: @expr,
&&is_const: bool, v: visit::vt<bool>) {
if is_const {
alt e.node {
expr_unary(box(_), _) | expr_unary(uniq(_), _) |
@ -54,7 +55,13 @@ fn check_expr(sess: session, e: @expr, &&is_const: bool, v: visit::vt<bool>) {
sess.span_err(e.span,
"string constants are not supported");
}
expr_lit(_) | expr_binary(_, _, _) | expr_unary(_, _) {}
expr_binary(_, _, _) | expr_unary(_, _) {
if method_map.contains_key(e.id) {
sess.span_err(e.span, "user-defined operators are not \
allowed in constant expressions");
}
}
expr_lit(_) {}
_ {
sess.span_err(e.span,
"constant contains unimplemented expression type");

View file

@ -1983,9 +1983,9 @@ 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 {
ast::expr_field(_, _, _) | ast::expr_path(_) | ast::expr_cast(_, _) {
e.impl_map.insert(x.id, sc);
}
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); }
_ {}
}
visit::visit_expr(x, sc, v);

View file

@ -2072,7 +2072,19 @@ fn node_type(cx: @crate_ctxt, sp: span, id: ast::node_id) -> TypeRef {
}
fn trans_unary(bcx: @block_ctxt, op: ast::unop, e: @ast::expr,
id: ast::node_id, dest: dest) -> @block_ctxt {
un_expr: @ast::expr, dest: dest) -> @block_ctxt {
// Check for user-defined method call
alt bcx_ccx(bcx).method_map.find(un_expr.id) {
some(origin) {
let callee_id = ast_util::op_expr_callee_id(un_expr);
let fty = ty::node_id_to_monotype(bcx_tcx(bcx), callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
trans_impl::trans_method_callee(bcx, callee_id, e, origin)
}, [], un_expr.id, dest);
}
_ {}
}
if dest == ignore { ret trans_expr(bcx, e, ignore); }
let e_ty = ty::expr_ty(bcx_tcx(bcx), e);
alt op {
@ -2104,7 +2116,7 @@ fn trans_unary(bcx: @block_ctxt, op: ast::unop, e: @ast::expr,
ret store_in_dest(bcx, box, dest);
}
ast::uniq(_) {
ret trans_uniq::trans_uniq(bcx, e, id, dest);
ret trans_uniq::trans_uniq(bcx, e, un_expr.id, dest);
}
ast::deref {
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,
ret store_in_dest(cx, val, dest);
}
fn trans_assign_op(bcx: @block_ctxt, op: ast::binop, dst: @ast::expr,
src: @ast::expr) -> @block_ctxt {
fn trans_assign_op(bcx: @block_ctxt, ex: @ast::expr, op: ast::binop,
dst: @ast::expr, src: @ast::expr) -> @block_ctxt {
let tcx = bcx_tcx(bcx);
let t = ty::expr_ty(tcx, src);
let lhs_res = trans_lval(bcx, dst);
assert (lhs_res.kind == owned);
// A user-defined operator method
alt bcx_ccx(bcx).method_map.find(ex.id) {
some(origin) {
let callee_id = ast_util::op_expr_callee_id(ex);
let fty = ty::node_id_to_monotype(bcx_tcx(bcx), callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
// FIXME provide the already-computed address, not the expr
trans_impl::trans_method_callee(bcx, callee_id, src, origin)
}, [dst], ex.id, save_in(lhs_res.val));
}
_ {}
}
// Special case for `+= [x]`
alt ty::struct(tcx, t) {
ty::ty_vec(_) {
@ -2305,20 +2331,34 @@ fn trans_lazy_binop(bcx: @block_ctxt, op: ast::binop, a: @ast::expr,
ret store_in_dest(join_cx, phi, dest);
}
fn trans_binary(cx: @block_ctxt, op: ast::binop, a: @ast::expr, b: @ast::expr,
dest: dest) -> @block_ctxt {
fn trans_binary(bcx: @block_ctxt, op: ast::binop, a: @ast::expr,
b: @ast::expr, dest: dest, ex: @ast::expr) -> @block_ctxt {
// User-defined operators
alt bcx_ccx(bcx).method_map.find(ex.id) {
some(origin) {
let callee_id = ast_util::op_expr_callee_id(ex);
let fty = ty::node_id_to_monotype(bcx_tcx(bcx), callee_id);
ret trans_call_inner(bcx, fty, {|bcx|
trans_impl::trans_method_callee(bcx, callee_id, a, origin)
}, [b], ex.id, dest);
}
_ {}
}
// First couple cases are lazy:
alt op {
ast::and | ast::or {
ret trans_lazy_binop(cx, op, a, b, dest);
ret trans_lazy_binop(bcx, op, a, b, dest);
}
_ {
// Remaining cases are eager:
let lhs = trans_temp_expr(cx, a);
let lhs = trans_temp_expr(bcx, a);
let rhs = trans_temp_expr(lhs.bcx, b);
ret trans_eager_binop(rhs.bcx, op, lhs.val,
ty::expr_ty(bcx_tcx(cx), a), rhs.val,
ty::expr_ty(bcx_tcx(cx), b), dest);
ty::expr_ty(bcx_tcx(bcx), a), rhs.val,
ty::expr_ty(bcx_tcx(bcx), b), dest);
}
}
}
@ -2746,15 +2786,8 @@ fn trans_callee(bcx: @block_ctxt, e: @ast::expr) -> lval_maybe_callee {
// Lval means this is a record field, so not a method
if !expr_is_lval(bcx, e) {
alt bcx_ccx(bcx).method_map.find(e.id) {
some(typeck::method_static(did)) { // An impl method
ret trans_impl::trans_static_callee(bcx, e, base, did);
}
some(typeck::method_param(iid, off, p, b)) {
ret trans_impl::trans_param_callee(
bcx, e, base, iid, off, p, b);
}
some(typeck::method_iface(off)) {
ret trans_impl::trans_iface_callee(bcx, e, base, off);
some(origin) { // An impl method
ret trans_impl::trans_method_callee(bcx, e.id, base, origin);
}
}
}
@ -3132,15 +3165,22 @@ fn trans_args(cx: @block_ctxt, llenv: ValueRef,
fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
args: [@ast::expr], id: ast::node_id, dest: dest)
-> @block_ctxt {
trans_call_inner(in_cx, ty::expr_ty(bcx_tcx(in_cx), f),
{|cx| trans_callee(cx, f)}, args, id, dest)
}
fn trans_call_inner(in_cx: @block_ctxt, fn_expr_ty: ty::t,
get_callee: fn(@block_ctxt) -> lval_maybe_callee,
args: [@ast::expr], id: ast::node_id, dest: dest)
-> @block_ctxt {
// NB: 'f' isn't necessarily a function; it might be an entire self-call
// expression because of the hack that allows us to process self-calls
// with trans_call.
let tcx = bcx_tcx(in_cx);
let fn_expr_ty = ty::expr_ty(tcx, f);
let cx = new_scope_block_ctxt(in_cx, "call");
Br(in_cx, cx.llbb);
let f_res = trans_callee(cx, f);
let f_res = get_callee(cx);
let bcx = f_res.bcx;
let faddr = f_res.val;
@ -3478,10 +3518,12 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
ast::expr_tup(args) { ret trans_tup(bcx, args, e.id, dest); }
ast::expr_lit(lit) { ret trans_lit(bcx, *lit, dest); }
ast::expr_vec(args, _) { ret tvec::trans_vec(bcx, args, e.id, dest); }
ast::expr_binary(op, x, y) { ret trans_binary(bcx, op, x, y, dest); }
ast::expr_binary(op, x, y) {
ret trans_binary(bcx, op, x, y, dest, e);
}
ast::expr_unary(op, x) {
assert op != ast::deref; // lvals are handled above
ret trans_unary(bcx, op, x, e.id, dest);
ret trans_unary(bcx, op, x, e, dest);
}
ast::expr_fn(proto, decl, body, cap_clause) {
ret trans_closure::trans_expr_fn(
@ -3620,7 +3662,7 @@ fn trans_expr(bcx: @block_ctxt, e: @ast::expr, dest: dest) -> @block_ctxt {
}
ast::expr_assign_op(op, dst, src) {
assert dest == ignore;
ret trans_assign_op(bcx, op, dst, src);
ret trans_assign_op(bcx, e, op, dst, src);
}
}
}

View file

@ -62,11 +62,28 @@ fn trans_self_arg(bcx: @block_ctxt, base: @ast::expr) -> result {
rslt(bcx, PointerCast(bcx, val, T_opaque_cbox_ptr(bcx_ccx(bcx))))
}
fn trans_method_callee(bcx: @block_ctxt, callee_id: ast::node_id,
self: @ast::expr, origin: typeck::method_origin)
-> lval_maybe_callee {
alt origin {
typeck::method_static(did) {
trans_static_callee(bcx, callee_id, self, did)
}
typeck::method_param(iid, off, p, b) {
trans_param_callee(bcx, callee_id, self, iid, off, p, b)
}
typeck::method_iface(off) {
trans_iface_callee(bcx, callee_id, self, off)
}
}
}
// Method callee where the method is statically known
fn trans_static_callee(bcx: @block_ctxt, e: @ast::expr, base: @ast::expr,
did: ast::def_id) -> lval_maybe_callee {
fn trans_static_callee(bcx: @block_ctxt, callee_id: ast::node_id,
base: @ast::expr, did: ast::def_id)
-> lval_maybe_callee {
let {bcx, val} = trans_self_arg(bcx, base);
{env: self_env(val) with lval_static_fn(bcx, did, e.id)}
{env: self_env(val) with lval_static_fn(bcx, did, callee_id)}
}
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)
}
fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
fld_expr: @ast::expr, iface_id: ast::def_id,
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;
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,
let generic = none;
if vec::len(*method.tps) > 0u || ty::type_contains_params(tcx, fty) {
let tydescs = [], tis = [];
let tptys = ty::node_id_to_type_params(tcx, fld_expr.id);
let tptys = ty::node_id_to_type_params(tcx, callee_id);
for t in vec::tail_n(tptys, vec::len(tptys) - vec::len(*method.tps)) {
let ti = none;
let td = get_tydesc(bcx, t, true, ti).result;
@ -102,7 +119,7 @@ fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
static_tis: tis,
tydescs: tydescs,
param_bounds: method.tps,
origins: bcx_ccx(bcx).dict_map.find(fld_expr.id)});
origins: bcx_ccx(bcx).dict_map.find(callee_id)});
}
{bcx: bcx, val: mptr, kind: owned,
env: dict_env(dict, self),
@ -110,16 +127,16 @@ fn trans_vtable_callee(bcx: @block_ctxt, self: ValueRef, dict: ValueRef,
}
// Method callee where the dict comes from a type param
fn trans_param_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
fn trans_param_callee(bcx: @block_ctxt, callee_id: ast::node_id,
base: @ast::expr, iface_id: ast::def_id, n_method: uint,
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, fld_expr, iface_id, n_method)
trans_vtable_callee(bcx, val, dict, callee_id, iface_id, n_method)
}
// Method callee where the dict comes from a boxed iface
fn trans_iface_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
fn trans_iface_callee(bcx: @block_ctxt, callee_id: ast::node_id,
base: @ast::expr, n_method: uint)
-> lval_maybe_callee {
let tcx = bcx_tcx(bcx);
@ -133,7 +150,7 @@ fn trans_iface_callee(bcx: @block_ctxt, fld_expr: @ast::expr,
let iface_id = alt ty::struct(tcx, ty::expr_ty(tcx, base)) {
ty::ty_iface(did, _) { did }
};
trans_vtable_callee(bcx, self, dict, fld_expr, iface_id, n_method)
trans_vtable_callee(bcx, self, dict, callee_id, iface_id, n_method)
}
fn llfn_arg_tys(ft: TypeRef) -> {inputs: [TypeRef], output: TypeRef} {

View file

@ -429,14 +429,14 @@ fn ty_of_native_item(tcx: ty::ctxt, mode: mode, it: @ast::native_item)
alt it.node {
ast::native_item_fn(fn_decl, params) {
ret ty_of_native_fn_decl(tcx, mode, fn_decl, params,
ast_util::local_def(it.id));
local_def(it.id));
}
ast::native_item_ty {
alt tcx.tcache.find(local_def(it.id)) {
some(tpt) { ret tpt; }
none { }
}
let t = ty::mk_native(tcx, ast_util::local_def(it.id));
let t = ty::mk_native(tcx, local_def(it.id));
let t = ty::mk_named(tcx, t, @it.ident);
let tpt = {bounds: @[], ty: t};
tcx.tcache.insert(local_def(it.id), tpt);
@ -1613,25 +1613,14 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
// A generic function to factor out common logic from call and bind
// expressions.
fn check_call_or_bind(fcx: @fn_ctxt, sp: span, f: @ast::expr,
fn check_call_or_bind(fcx: @fn_ctxt, sp: span, fty: ty::t,
args: [option::t<@ast::expr>]) -> bool {
// Check the function.
let bot = check_expr(fcx, f);
// Get the function type.
let fty = expr_ty(fcx.ccx.tcx, f);
let sty = structure_of(fcx, sp, fty);
// Grab the argument types
let arg_tys =
alt sty {
ty::ty_fn({inputs: arg_tys, _}) {
arg_tys
}
let arg_tys = alt sty {
ty::ty_fn({inputs: arg_tys, _}) { arg_tys }
_ {
fcx.ccx.tcx.sess.span_fatal(f.span,
"mismatched types: \
fcx.ccx.tcx.sess.span_fatal(sp, "mismatched types: \
expected function or native \
function but found "
+ ty_to_str(fcx.ccx.tcx, fty))
@ -1642,16 +1631,12 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let expected_arg_count = vec::len(arg_tys);
let supplied_arg_count = vec::len(args);
if expected_arg_count != supplied_arg_count {
fcx.ccx.tcx.sess.span_err(sp,
#fmt["this function takes %u \
parameter%s but %u parameter%s supplied",
expected_arg_count,
if expected_arg_count == 1u {
""
} else { "s" }, supplied_arg_count,
if supplied_arg_count == 1u {
" was"
} else { "s were" }]);
fcx.ccx.tcx.sess.span_err(
sp, #fmt["this function takes %u parameter%s but %u \
parameter%s supplied", expected_arg_count,
expected_arg_count == 1u ? "" : "s",
supplied_arg_count,
supplied_arg_count == 1u ? " was" : "s were"]);
// HACK: build an arguments list with dummy arguments to
// check against
let dummy = {mode: ast::by_ref, ty: ty::mk_bot(fcx.ccx.tcx)};
@ -1667,19 +1652,16 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let check_args = fn@(check_blocks: bool) -> bool {
let i = 0u;
let bot = false;
for a_opt: option::t<@ast::expr> in args {
for a_opt in args {
alt a_opt {
some(a) {
let is_block =
alt a.node {
let is_block = alt a.node {
ast::expr_fn_block(_, _) { true }
_ { false }
};
if is_block == check_blocks {
bot |=
check_expr_with_unifier(fcx, a,
demand::simple,
arg_tys[i].ty);
bot |= check_expr_with_unifier(
fcx, a, demand::simple, arg_tys[i].ty);
}
}
none { }
@ -1688,10 +1670,7 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
ret bot;
};
bot |= check_args(false);
bot |= check_args(true);
ret bot;
check_args(false) | check_args(true)
}
// A generic function for checking assignment expressions
@ -1711,8 +1690,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
args_opt_0 += [some::<@ast::expr>(arg)];
}
let bot = check_expr(fcx, f);
// Call the generic checker.
ret check_call_or_bind(fcx, sp, f, args_opt_0);
bot | check_call_or_bind(fcx, sp, expr_ty(fcx.ccx.tcx, f), args_opt_0)
}
// A generic function for doing all of the checking for call expressions
@ -1784,19 +1764,77 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
ret if_bot;
}
// Checks the compatibility
fn check_binop_type_compat(fcx: @fn_ctxt, span: span, ty: ty::t,
binop: ast::binop) {
let resolved_t = resolve_type_vars_if_possible(fcx, ty);
if !ty::is_binopable(fcx.ccx.tcx, resolved_t, binop) {
let binopstr = ast_util::binop_to_str(binop);
let t_str = ty_to_str(fcx.ccx.tcx, resolved_t);
let errmsg =
"binary operation " + binopstr +
" cannot be applied to type `" + t_str + "`";
fcx.ccx.tcx.sess.span_err(span, errmsg);
fn binop_method(op: ast::binop) -> option::t<str> {
alt op {
ast::add { some("op_add") }
ast::subtract { some("op_sub") }
ast::mul { some("op_mul") }
ast::div { some("op_div") }
ast::rem { some("op_rem") }
ast::bitxor { some("op_xor") }
ast::bitand { some("op_and") }
ast::bitor { some("op_or") }
ast::lsl { some("op_shift_left") }
ast::lsr { some("op_shift_right") }
ast::asr { some("op_ashift_right") }
_ { 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);
let tcx = fcx.ccx.tcx;
if ty::is_binopable(tcx, resolved_t, op) {
ret alt op {
ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
ast::gt { ty::mk_bool(tcx) }
_ { resolved_t }
};
}
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);
}
_ {}
}
}
none {}
}
tcx.sess.span_err(
ex.span, "binary operation " + ast_util::binop_to_str(op) +
" cannot be applied to type `" + ty_to_str(tcx, resolved_t) +
"`");
resolved_t
}
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);
}
_ {}
}
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;
let id = expr.id;
@ -1813,14 +1851,15 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
let rhs_bot = check_expr_with(fcx, rhs, lhs_t);
if !ast_util::lazy_binop(binop) { bot |= rhs_bot; }
check_binop_type_compat(fcx, expr.span, lhs_t, binop);
let t = alt binop {
ast::eq | ast::lt | ast::le | ast::ne | ast::ge |
ast::gt { ty::mk_bool(tcx) }
_ { lhs_t }
};
write::ty_only_fixup(fcx, id, t);
let result = check_binop(fcx, expr, lhs_t, binop, rhs);
write::ty_only_fixup(fcx, id, result);
}
ast::expr_assign_op(op, lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
bot = check_assignment(fcx, expr.span, lhs, rhs, id);
let lhs_t = ty::expr_ty(tcx, lhs);
let result = check_binop(fcx, expr, lhs_t, op, rhs);
demand::simple(fcx, expr.span, result, lhs_t);
}
ast::expr_unary(unop, oper) {
bot = check_expr(fcx, oper);
@ -1860,22 +1899,17 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
}
ast::not {
if !type_is_integral(fcx, oper.span, oper_t) &&
structure_of(fcx, oper.span, oper_t) != ty::ty_bool {
tcx.sess.span_err(expr.span,
#fmt["mismatched types: expected `bool` \
or `integer` but found `%s`",
ty_to_str(tcx, oper_t)]);
oper_t = structurally_resolved_type(fcx, oper.span, oper_t);
if !(ty::type_is_integral(tcx, oper_t) ||
ty::struct(tcx, oper_t) == ty::ty_bool) {
oper_t = check_user_unop(fcx, "!", "op_not", expr, oper_t);
}
}
ast::neg {
oper_t = structurally_resolved_type(fcx, oper.span, oper_t);
if !(ty::type_is_integral(tcx, oper_t) ||
ty::type_is_fp(tcx, oper_t)) {
tcx.sess.span_err(expr.span,
"applying unary minus to \
non-numeric type `"
+ ty_to_str(tcx, oper_t) + "`");
oper_t = check_user_unop(fcx, "-", "op_neg", expr, oper_t);
}
}
}
@ -1971,11 +2005,6 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
require_impure(tcx.sess, fcx.purity, expr.span);
bot = check_assignment(fcx, expr.span, lhs, rhs, id);
}
ast::expr_assign_op(op, lhs, rhs) {
require_impure(tcx.sess, fcx.purity, expr.span);
bot = check_assignment(fcx, expr.span, lhs, rhs, id);
check_binop_type_compat(fcx, expr.span, expr_ty(tcx, lhs), op);
}
ast::expr_if(cond, thn, elsopt) {
bot =
check_expr_with(fcx, cond, ty::mk_bool(tcx)) |
@ -2069,7 +2098,8 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
}
ast::expr_bind(f, args) {
// Call the generic checker.
bot = check_call_or_bind(fcx, expr.span, f, args);
bot = check_expr(fcx, f);
bot |= check_call_or_bind(fcx, expr.span, expr_ty(tcx, f), args);
// Pull the argument and return types out.
let proto, arg_tys, rt, cf, constrs;
@ -2891,14 +2921,19 @@ mod dict {
}
}
// Must resolve bounds on methods with bounded params
ast::expr_field(_, _, _) {
ast::expr_field(_, _, _) | ast::expr_binary(_, _, _) |
ast::expr_unary(_, _) | ast::expr_assign_op(_, _, _) {
alt cx.method_map.find(ex.id) {
some(method_static(did)) {
let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
if has_iface_bounds(*bounds) {
let ts = ty::node_id_to_type_params(cx.tcx, ex.id);
let callee_id = alt ex.node {
ast::expr_field(_, _, _) { ex.id }
_ { ast_util::op_expr_callee_id(ex) }
};
let ts = ty::node_id_to_type_params(cx.tcx, callee_id);
let iscs = cx.impl_map.get(ex.id);
cx.dict_map.insert(ex.id, lookup_dicts(
cx.dict_map.insert(callee_id, lookup_dicts(
fcx, iscs, ex.span, bounds, ts));
}
}

View file

@ -326,6 +326,11 @@ fn ident_to_path(s: span, i: ident) -> @path {
@respan(s, {global: false, idents: [i], types: []})
}
// Provides an extra node_id to hang callee information on, in case the
// operator is deferred to a user-supplied method. The parser is responsible
// for reserving this id.
fn op_expr_callee_id(e: @expr) -> node_id { e.id - 1 }
// Local Variables:
// mode: rust
// fill-column: 78;

View file

@ -1048,6 +1048,7 @@ fn parse_prefix_expr(p: parser) -> pexpr {
p.bump();
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
p.get_id(); // see ast_util::op_expr_callee_id
ex = ast::expr_unary(ast::not, e);
}
token::BINOP(b) {
@ -1056,6 +1057,7 @@ fn parse_prefix_expr(p: parser) -> pexpr {
p.bump();
let e = to_expr(parse_prefix_expr(p));
hi = e.span.hi;
p.get_id(); // see ast_util::op_expr_callee_id
ex = ast::expr_unary(ast::neg, e);
}
token::STAR {
@ -1146,6 +1148,7 @@ fn parse_more_binops(p: parser, plhs: pexpr, min_prec: int) ->
p.bump();
let expr = parse_prefix_expr(p);
let rhs = parse_more_binops(p, expr, cur.prec);
p.get_id(); // see ast_util::op_expr_callee_id
let bin = mk_pexpr(p, lhs.span.lo, rhs.span.hi,
ast::expr_binary(cur.op, lhs, rhs));
ret parse_more_binops(p, bin, min_prec);
@ -1186,6 +1189,7 @@ fn parse_assign_expr(p: parser) -> @ast::expr {
token::LSR { aop = ast::lsr; }
token::ASR { aop = ast::asr; }
}
p.get_id(); // see ast_util::op_expr_callee_id
ret mk_expr(p, lo, rhs.span.hi, ast::expr_assign_op(aop, lhs, rhs));
}
token::LARROW {

View file

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

View file

@ -0,0 +1,17 @@
type point = {x: int, y: int};
impl add_point for point {
fn op_add(other: point) -> point {
{x: self.x + other.x, y: self.y + other.y}
}
fn op_neg() -> point {
{x: -self.x, y: -self.y}
}
}
fn main() {
let p = {x: 10, y: 20};
p += {x: 1, y: 2};
assert p + {x: 5, y: 5} == {x: 16, y: 27};
assert -p == {x: -11, y: -22};
}