diff options
| author | Marijn Haverbeke <marijnh@gmail.com> | 2012-01-26 12:26:14 +0100 |
|---|---|---|
| committer | Marijn Haverbeke <marijnh@gmail.com> | 2012-01-26 14:25:06 +0100 |
| commit | 87b064b249657c8e65079d01beb77409f69d49cd (patch) | |
| tree | cecb525f7ddf7312521d1a65e93ea8531f23426e /src/comp | |
| parent | 1792d9ec96d680cb3ec257bfef84baffea352d80 (diff) | |
| download | rust-87b064b249657c8e65079d01beb77409f69d49cd.tar.gz rust-87b064b249657c8e65079d01beb77409f69d49cd.zip | |
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
Diffstat (limited to 'src/comp')
| -rw-r--r-- | src/comp/driver/driver.rs | 4 | ||||
| -rw-r--r-- | src/comp/middle/check_const.rs | 15 | ||||
| -rw-r--r-- | src/comp/middle/resolve.rs | 6 | ||||
| -rw-r--r-- | src/comp/middle/trans.rs | 90 | ||||
| -rw-r--r-- | src/comp/middle/trans_impl.rs | 37 | ||||
| -rw-r--r-- | src/comp/middle/typeck.rs | 231 | ||||
| -rw-r--r-- | src/comp/syntax/ast_util.rs | 5 | ||||
| -rw-r--r-- | src/comp/syntax/parse/parser.rs | 4 |
8 files changed, 251 insertions, 141 deletions
diff --git a/src/comp/driver/driver.rs b/src/comp/driver/driver.rs index e7eafae197a..41130766035 100644 --- a/src/comp/driver/driver.rs +++ b/src/comp/driver/driver.rs @@ -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)}; } diff --git a/src/comp/middle/check_const.rs b/src/comp/middle/check_const.rs index 78eec533e3f..eec014a47f7 100644 --- a/src/comp/middle/check_const.rs +++ b/src/comp/middle/check_const.rs @@ -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"); diff --git a/src/comp/middle/resolve.rs b/src/comp/middle/resolve.rs index 0781f2ef1a1..8c3375796f8 100644 --- a/src/comp/middle/resolve.rs +++ b/src/comp/middle/resolve.rs @@ -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); diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index d3a81412457..045cbfdc28d 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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); } } } diff --git a/src/comp/middle/trans_impl.rs b/src/comp/middle/trans_impl.rs index ff4a8db24a7..b454ca729bb 100644 --- a/src/comp/middle/trans_impl.rs +++ b/src/comp/middle/trans_impl.rs @@ -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} { diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index 2327d79008a..6ae92ee6538 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -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,18 +1764,76 @@ 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; @@ -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)); } } diff --git a/src/comp/syntax/ast_util.rs b/src/comp/syntax/ast_util.rs index cb0545c07b7..65c117cfa6b 100644 --- a/src/comp/syntax/ast_util.rs +++ b/src/comp/syntax/ast_util.rs @@ -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; diff --git a/src/comp/syntax/parse/parser.rs b/src/comp/syntax/parse/parser.rs index a66b223175c..1e892eb43b1 100644 --- a/src/comp/syntax/parse/parser.rs +++ b/src/comp/syntax/parse/parser.rs @@ -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 { |
