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:
parent
1792d9ec96
commit
87b064b249
10 changed files with 269 additions and 142 deletions
|
@ -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)}; }
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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} {
|
||||
|
|
|
@ -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,45 +1613,30 @@ 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
|
||||
}
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_fatal(f.span,
|
||||
"mismatched types: \
|
||||
expected function or native \
|
||||
function but found "
|
||||
+ ty_to_str(fcx.ccx.tcx, fty))
|
||||
}
|
||||
};
|
||||
let arg_tys = alt sty {
|
||||
ty::ty_fn({inputs: arg_tys, _}) { arg_tys }
|
||||
_ {
|
||||
fcx.ccx.tcx.sess.span_fatal(sp, "mismatched types: \
|
||||
expected function or native \
|
||||
function but found "
|
||||
+ ty_to_str(fcx.ccx.tcx, fty))
|
||||
}
|
||||
};
|
||||
|
||||
// Check that the correct number of arguments were supplied.
|
||||
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)};
|
||||
|
@ -1665,33 +1650,27 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
|
|||
// of arguments when we typecheck the functions. This isn't really the
|
||||
// right way to do this.
|
||||
let check_args = fn@(check_blocks: bool) -> bool {
|
||||
let i = 0u;
|
||||
let bot = false;
|
||||
for a_opt: option::t<@ast::expr> in args {
|
||||
alt a_opt {
|
||||
some(a) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
none { }
|
||||
let i = 0u;
|
||||
let bot = false;
|
||||
for a_opt in args {
|
||||
alt a_opt {
|
||||
some(a) {
|
||||
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);
|
||||
}
|
||||
i += 1u;
|
||||
}
|
||||
none { }
|
||||
}
|
||||
ret bot;
|
||||
};
|
||||
bot |= check_args(false);
|
||||
bot |= check_args(true);
|
||||
|
||||
ret bot;
|
||||
i += 1u;
|
||||
}
|
||||
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) + "`");
|
||||
ty::type_is_fp(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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"; }
|
||||
|
|
17
src/test/run-pass/operator-overloading.rs
Normal file
17
src/test/run-pass/operator-overloading.rs
Normal 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};
|
||||
}
|
Loading…
Add table
Reference in a new issue