librustc: Make a.b()
always a method call. r=nmatsakis
This commit is contained in:
parent
54ae377ec2
commit
f34833abfc
24 changed files with 413 additions and 97 deletions
|
@ -1047,7 +1047,7 @@ pub mod fsync {
|
||||||
option::None => (),
|
option::None => (),
|
||||||
option::Some(level) => {
|
option::Some(level) => {
|
||||||
// fail hard if not succesful
|
// fail hard if not succesful
|
||||||
assert(self.arg.fsync_fn(self.arg.val, level) != -1);
|
assert((self.arg.fsync_fn)(self.arg.val, level) != -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -646,6 +646,9 @@ fn check_loans_in_expr(expr: @ast::expr,
|
||||||
ast::expr_call(f, args, _) => {
|
ast::expr_call(f, args, _) => {
|
||||||
self.check_call(expr, Some(f), f.id, f.span, args);
|
self.check_call(expr, Some(f), f.id, f.span, args);
|
||||||
}
|
}
|
||||||
|
ast::expr_method_call(_, _, _, args, _) => {
|
||||||
|
self.check_call(expr, None, expr.callee_id, expr.span, args);
|
||||||
|
}
|
||||||
ast::expr_index(_, rval) |
|
ast::expr_index(_, rval) |
|
||||||
ast::expr_binary(_, _, rval)
|
ast::expr_binary(_, _, rval)
|
||||||
if self.bccx.method_map.contains_key(expr.id) => {
|
if self.bccx.method_map.contains_key(expr.id) => {
|
||||||
|
|
|
@ -125,6 +125,38 @@ fn req_loans_in_expr(ex: @ast::expr,
|
||||||
visit::visit_expr(ex, self, vt);
|
visit::visit_expr(ex, self, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_method_call(rcvr, _, _, args, _) => {
|
||||||
|
let arg_tys = ty::ty_fn_args(ty::node_id_to_type(self.tcx(),
|
||||||
|
ex.callee_id));
|
||||||
|
let scope_r = ty::re_scope(ex.id);
|
||||||
|
for vec::each2(args, arg_tys) |arg, arg_ty| {
|
||||||
|
match ty::resolved_mode(self.tcx(), arg_ty.mode) {
|
||||||
|
ast::by_ref => {
|
||||||
|
let arg_cmt = self.bccx.cat_expr(*arg);
|
||||||
|
self.guarantee_valid(arg_cmt, m_imm, scope_r);
|
||||||
|
}
|
||||||
|
ast::by_val | ast::by_move | ast::by_copy => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.bccx.method_map.find(ex.id) {
|
||||||
|
Some(method_map_entry) => {
|
||||||
|
match method_map_entry.explicit_self {
|
||||||
|
ast::sty_by_ref => {
|
||||||
|
let rcvr_cmt = self.bccx.cat_expr(rcvr);
|
||||||
|
self.guarantee_valid(rcvr_cmt, m_imm, scope_r);
|
||||||
|
}
|
||||||
|
_ => {} // Nothing to do.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.tcx().sess.span_bug(ex.span, ~"no method map entry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit::visit_expr(ex, self, vt);
|
||||||
|
}
|
||||||
|
|
||||||
ast::expr_match(ex_v, arms) => {
|
ast::expr_match(ex_v, arms) => {
|
||||||
let cmt = self.bccx.cat_expr(ex_v);
|
let cmt = self.bccx.cat_expr(ex_v);
|
||||||
for arms.each |arm| {
|
for arms.each |arm| {
|
||||||
|
|
|
@ -339,6 +339,18 @@ fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expr_method_call(_, _, _, args, _) => {
|
||||||
|
for ty::ty_fn_args(ty::node_id_to_type(cx.tcx, e.callee_id)).eachi
|
||||||
|
|i, arg_t| {
|
||||||
|
match ty::arg_mode(cx.tcx, *arg_t) {
|
||||||
|
by_copy => maybe_copy(cx, args[i],
|
||||||
|
Some(("function arguments must be copyable",
|
||||||
|
"try changing the function to take a \
|
||||||
|
reference instead"))),
|
||||||
|
by_ref | by_val | by_move => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
expr_field(lhs, _, _) => {
|
expr_field(lhs, _, _) => {
|
||||||
// If this is a method call with a by-val argument, we need
|
// If this is a method call with a by-val argument, we need
|
||||||
// to check the copy
|
// to check the copy
|
||||||
|
|
|
@ -550,8 +550,8 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
|
||||||
|
|
||||||
// otherwise, live nodes are not required:
|
// otherwise, live nodes are not required:
|
||||||
expr_index(*) | expr_field(*) | expr_vstore(*) |
|
expr_index(*) | expr_field(*) | expr_vstore(*) |
|
||||||
expr_vec(*) | expr_rec(*) | expr_call(*) | expr_tup(*) |
|
expr_vec(*) | expr_rec(*) | expr_call(*) | expr_method_call(*) |
|
||||||
expr_log(*) | expr_binary(*) |
|
expr_tup(*) | expr_log(*) | expr_binary(*) |
|
||||||
expr_assert(*) | expr_addr_of(*) | expr_copy(*) |
|
expr_assert(*) | expr_addr_of(*) | expr_copy(*) |
|
||||||
expr_loop_body(*) | expr_do_body(*) | expr_cast(*) |
|
expr_loop_body(*) | expr_do_body(*) | expr_cast(*) |
|
||||||
expr_unary(*) | expr_fail(*) |
|
expr_unary(*) | expr_fail(*) |
|
||||||
|
@ -1237,6 +1237,17 @@ impl Liveness {
|
||||||
self.propagate_through_expr(f, succ)
|
self.propagate_through_expr(f, succ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_method_call(rcvr, _, _, args, _) => {
|
||||||
|
// calling a method with bot return type means that the method
|
||||||
|
// will fail, and hence the successors can be ignored
|
||||||
|
let t_ret = ty::ty_fn_ret(ty::node_id_to_type(self.tcx,
|
||||||
|
expr.callee_id));
|
||||||
|
let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln}
|
||||||
|
else {succ};
|
||||||
|
let succ = self.propagate_through_exprs(args, succ);
|
||||||
|
self.propagate_through_expr(rcvr, succ)
|
||||||
|
}
|
||||||
|
|
||||||
expr_tup(exprs) => {
|
expr_tup(exprs) => {
|
||||||
self.propagate_through_exprs(exprs, succ)
|
self.propagate_through_exprs(exprs, succ)
|
||||||
}
|
}
|
||||||
|
@ -1486,6 +1497,26 @@ fn check_arm(arm: arm, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
visit::visit_arm(arm, self, vt);
|
visit::visit_arm(arm, self, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_call(args: &[@expr],
|
||||||
|
targs: &[ty::arg],
|
||||||
|
&&self: @Liveness) {
|
||||||
|
for vec::each2(args, targs) |arg_expr, arg_ty| {
|
||||||
|
match ty::resolved_mode(self.tcx, arg_ty.mode) {
|
||||||
|
by_val | by_copy | by_ref => {}
|
||||||
|
by_move => {
|
||||||
|
if ty::expr_is_lval(self.tcx, self.ir.method_map, *arg_expr) {
|
||||||
|
// Probably a bad error message (what's an rvalue?)
|
||||||
|
// but I can't think of anything better
|
||||||
|
self.tcx.sess.span_err(arg_expr.span,
|
||||||
|
fmt!("move mode argument must be an rvalue: try (move \
|
||||||
|
%s) instead",
|
||||||
|
expr_to_str(*arg_expr, self.tcx.sess.intr())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
expr_path(_) => {
|
expr_path(_) => {
|
||||||
|
@ -1531,23 +1562,14 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
|
||||||
|
|
||||||
expr_call(f, args, _) => {
|
expr_call(f, args, _) => {
|
||||||
let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f));
|
let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f));
|
||||||
for vec::each2(args, targs) |arg_expr, arg_ty| {
|
check_call(args, targs, self);
|
||||||
match ty::resolved_mode(self.tcx, arg_ty.mode) {
|
visit::visit_expr(expr, self, vt);
|
||||||
by_val | by_copy | by_ref => {}
|
}
|
||||||
by_move => {
|
|
||||||
if ty::expr_is_lval(self.tcx, self.ir.method_map,
|
|
||||||
*arg_expr) {
|
|
||||||
// Probably a bad error message (what's an rvalue?)
|
|
||||||
// but I can't think of anything better
|
|
||||||
self.tcx.sess.span_err(arg_expr.span,
|
|
||||||
fmt!("Move mode argument must be an rvalue: try \
|
|
||||||
(move %s) instead", expr_to_str(*arg_expr,
|
|
||||||
self.tcx.sess.intr())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
expr_method_call(_, _, _, args, _) => {
|
||||||
|
let targs = ty::ty_fn_args(ty::node_id_to_type(self.tcx,
|
||||||
|
expr.callee_id));
|
||||||
|
check_call(args, targs, self);
|
||||||
visit::visit_expr(expr, self, vt);
|
visit::visit_expr(expr, self, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -530,7 +530,8 @@ impl &mem_categorization_ctxt {
|
||||||
ast::expr_swap(*) | ast::expr_assign(*) |
|
ast::expr_swap(*) | ast::expr_assign(*) |
|
||||||
ast::expr_assign_op(*) | ast::expr_fn(*) | ast::expr_fn_block(*) |
|
ast::expr_assign_op(*) | ast::expr_fn(*) | ast::expr_fn_block(*) |
|
||||||
ast::expr_assert(*) | ast::expr_ret(*) |
|
ast::expr_assert(*) | ast::expr_ret(*) |
|
||||||
ast::expr_loop_body(*) | ast::expr_do_body(*) | ast::expr_unary(*) |
|
ast::expr_loop_body(*) | ast::expr_do_body(*) |
|
||||||
|
ast::expr_unary(*) | ast::expr_method_call(*) |
|
||||||
ast::expr_copy(*) | ast::expr_cast(*) | ast::expr_fail(*) |
|
ast::expr_copy(*) | ast::expr_cast(*) | ast::expr_fail(*) |
|
||||||
ast::expr_vstore(*) | ast::expr_vec(*) | ast::expr_tup(*) |
|
ast::expr_vstore(*) | ast::expr_vec(*) | ast::expr_tup(*) |
|
||||||
ast::expr_if(*) | ast::expr_log(*) |
|
ast::expr_if(*) | ast::expr_log(*) |
|
||||||
|
|
|
@ -258,7 +258,7 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
|
||||||
// FIXME #3387
|
// FIXME #3387
|
||||||
// ast::expr_index(*) | ast::expr_binary(*) |
|
// ast::expr_index(*) | ast::expr_binary(*) |
|
||||||
// ast::expr_unary(*) |
|
// ast::expr_unary(*) |
|
||||||
ast::expr_call(*) => {
|
ast::expr_call(*) | ast::expr_method_call(*) => {
|
||||||
debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
|
debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
|
||||||
cx.sess.intr()));
|
cx.sess.intr()));
|
||||||
new_cx.parent = Some(expr.id);
|
new_cx.parent = Some(expr.id);
|
||||||
|
|
|
@ -16,12 +16,11 @@ use syntax::ast::{def_foreign_mod, def_id, def_label, def_local, def_mod};
|
||||||
use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
|
use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
|
||||||
use syntax::ast::{def_typaram_binder, def_static_method};
|
use syntax::ast::{def_typaram_binder, def_static_method};
|
||||||
use syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
|
use syntax::ast::{def_upvar, def_use, def_variant, expr, expr_assign_op};
|
||||||
use syntax::ast::{expr_binary, expr_cast, expr_field, expr_fn};
|
use syntax::ast::{expr_binary, expr_break, expr_cast, expr_field, expr_fn};
|
||||||
use syntax::ast::{expr_fn_block, expr_index, expr_path};
|
use syntax::ast::{expr_fn_block, expr_index, expr_method_call, expr_path};
|
||||||
use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
|
use syntax::ast::{def_prim_ty, def_region, def_self, def_ty, def_ty_param};
|
||||||
use syntax::ast::{def_upvar, def_use, def_variant, div, eq};
|
use syntax::ast::{def_upvar, def_use, def_variant, div, eq};
|
||||||
use syntax::ast::{enum_variant_kind, expr, expr_again, expr_assign_op};
|
use syntax::ast::{enum_variant_kind, expr, expr_again, expr_assign_op};
|
||||||
use syntax::ast::{expr_binary, expr_break, expr_cast, expr_field, expr_fn};
|
|
||||||
use syntax::ast::{expr_fn_block, expr_index, expr_loop};
|
use syntax::ast::{expr_fn_block, expr_index, expr_loop};
|
||||||
use syntax::ast::{expr_path, expr_struct, expr_unary, fn_decl};
|
use syntax::ast::{expr_path, expr_struct, expr_unary, fn_decl};
|
||||||
use syntax::ast::{foreign_item, foreign_item_const, foreign_item_fn, ge};
|
use syntax::ast::{foreign_item, foreign_item_const, foreign_item_fn, ge};
|
||||||
|
@ -4971,6 +4970,10 @@ impl Resolver {
|
||||||
let traits = self.search_for_traits_containing_method(ident);
|
let traits = self.search_for_traits_containing_method(ident);
|
||||||
self.trait_map.insert(expr.id, traits);
|
self.trait_map.insert(expr.id, traits);
|
||||||
}
|
}
|
||||||
|
expr_method_call(_, ident, _, _, _) => {
|
||||||
|
let traits = self.search_for_traits_containing_method(ident);
|
||||||
|
self.trait_map.insert(expr.id, traits);
|
||||||
|
}
|
||||||
expr_binary(add, _, _) | expr_assign_op(add, _, _) => {
|
expr_binary(add, _, _) | expr_assign_op(add, _, _) => {
|
||||||
self.add_fixed_trait_for_expr(expr.id,
|
self.add_fixed_trait_for_expr(expr.id,
|
||||||
self.lang_items.add_trait);
|
self.lang_items.add_trait);
|
||||||
|
|
|
@ -292,6 +292,38 @@ fn trans_call(in_cx: block,
|
||||||
|cx| trans(cx, f), args, dest, DontAutorefArg)
|
|cx| trans(cx, f), args, dest, DontAutorefArg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trans_method_call(in_cx: block,
|
||||||
|
call_ex: @ast::expr,
|
||||||
|
rcvr: @ast::expr,
|
||||||
|
args: CallArgs,
|
||||||
|
dest: expr::Dest)
|
||||||
|
-> block {
|
||||||
|
let _icx = in_cx.insn_ctxt("trans_method_call");
|
||||||
|
trans_call_inner(
|
||||||
|
in_cx,
|
||||||
|
call_ex.info(),
|
||||||
|
node_id_type(in_cx, call_ex.callee_id),
|
||||||
|
expr_ty(in_cx, call_ex),
|
||||||
|
|cx| {
|
||||||
|
match cx.ccx().maps.method_map.find(call_ex.id) {
|
||||||
|
Some(origin) => {
|
||||||
|
meth::trans_method_callee(cx,
|
||||||
|
call_ex.callee_id,
|
||||||
|
rcvr,
|
||||||
|
origin)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
cx.tcx().sess.span_bug(call_ex.span,
|
||||||
|
~"method call expr wasn't in \
|
||||||
|
method map")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
dest,
|
||||||
|
DontAutorefArg)
|
||||||
|
}
|
||||||
|
|
||||||
fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: expr::Dest)
|
fn trans_rtcall(bcx: block, name: ~str, args: ~[ValueRef], dest: expr::Dest)
|
||||||
-> block
|
-> block
|
||||||
{
|
{
|
||||||
|
|
|
@ -592,6 +592,13 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
|
||||||
return callee::trans_call(
|
return callee::trans_call(
|
||||||
bcx, expr, f, callee::ArgExprs(args), expr.id, dest);
|
bcx, expr, f, callee::ArgExprs(args), expr.id, dest);
|
||||||
}
|
}
|
||||||
|
ast::expr_method_call(rcvr, _, _, args, _) => {
|
||||||
|
return callee::trans_method_call(bcx,
|
||||||
|
expr,
|
||||||
|
rcvr,
|
||||||
|
callee::ArgExprs(args),
|
||||||
|
dest);
|
||||||
|
}
|
||||||
ast::expr_binary(_, lhs, rhs) => {
|
ast::expr_binary(_, lhs, rhs) => {
|
||||||
// if not overloaded, would be RvalueDatumExpr
|
// if not overloaded, would be RvalueDatumExpr
|
||||||
return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest,
|
return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest,
|
||||||
|
|
|
@ -170,6 +170,18 @@ fn traverse_inline_body(cx: ctx, body: blk) {
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expr_method_call(*) => {
|
||||||
|
match cx.method_map.find(e.id) {
|
||||||
|
Some({origin: typeck::method_static(did), _}) => {
|
||||||
|
traverse_def_id(cx, did);
|
||||||
|
}
|
||||||
|
Some(_) => {}
|
||||||
|
None => {
|
||||||
|
cx.tcx.sess.span_bug(e.span, ~"expr_method_call not in \
|
||||||
|
method map");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
visit::visit_expr(e, cx, v);
|
visit::visit_expr(e, cx, v);
|
||||||
|
|
|
@ -173,6 +173,25 @@ fn node_type_needs(cx: ctx, use_: uint, id: node_id) {
|
||||||
type_needs(cx, use_, ty::node_id_to_type(cx.ccx.tcx, id));
|
type_needs(cx, use_, ty::node_id_to_type(cx.ccx.tcx, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mark_for_method_call(cx: ctx, e_id: node_id) {
|
||||||
|
do option::iter(&cx.ccx.maps.method_map.find(e_id)) |mth| {
|
||||||
|
match mth.origin {
|
||||||
|
typeck::method_static(did) => {
|
||||||
|
do cx.ccx.tcx.node_type_substs.find(e_id).iter |ts| {
|
||||||
|
let type_uses = type_uses_for(cx.ccx, did, ts.len());
|
||||||
|
for vec::each2(type_uses, *ts) |uses, subst| {
|
||||||
|
type_needs(cx, *uses, *subst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeck::method_param({param_num: param, _}) => {
|
||||||
|
cx.uses[param] |= use_tydesc;
|
||||||
|
}
|
||||||
|
typeck::method_trait(*) | typeck::method_self(*) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn mark_for_expr(cx: ctx, e: @expr) {
|
fn mark_for_expr(cx: ctx, e: @expr) {
|
||||||
match e.node {
|
match e.node {
|
||||||
expr_vstore(_, _) |
|
expr_vstore(_, _) |
|
||||||
|
@ -231,23 +250,7 @@ fn mark_for_expr(cx: ctx, e: @expr) {
|
||||||
// the chosen field.
|
// the chosen field.
|
||||||
let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
|
let base_ty = ty::node_id_to_type(cx.ccx.tcx, base.id);
|
||||||
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
|
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
|
||||||
|
mark_for_method_call(cx, e.id);
|
||||||
do option::iter(&cx.ccx.maps.method_map.find(e.id)) |mth| {
|
|
||||||
match mth.origin {
|
|
||||||
typeck::method_static(did) => {
|
|
||||||
do cx.ccx.tcx.node_type_substs.find(e.id).iter |ts| {
|
|
||||||
let type_uses = type_uses_for(cx.ccx, did, ts.len());
|
|
||||||
for vec::each2(type_uses, *ts) |uses, subst| {
|
|
||||||
type_needs(cx, *uses, *subst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typeck::method_param({param_num: param, _}) => {
|
|
||||||
cx.uses[param] |= use_tydesc;
|
|
||||||
}
|
|
||||||
typeck::method_trait(*) | typeck::method_self(*) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
expr_log(_, _, val) => {
|
expr_log(_, _, val) => {
|
||||||
node_type_needs(cx, use_tydesc, val.id);
|
node_type_needs(cx, use_tydesc, val.id);
|
||||||
|
@ -264,6 +267,21 @@ fn mark_for_expr(cx: ctx, e: @expr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
expr_method_call(rcvr, _, _, _, _) => {
|
||||||
|
let base_ty = ty::node_id_to_type(cx.ccx.tcx, rcvr.id);
|
||||||
|
type_needs(cx, use_repr, ty::type_autoderef(cx.ccx.tcx, base_ty));
|
||||||
|
|
||||||
|
for ty::ty_fn_args(ty::node_id_to_type(cx.ccx.tcx,
|
||||||
|
e.callee_id)).each |a| {
|
||||||
|
match a.mode {
|
||||||
|
expl(by_move) | expl(by_copy) | expl(by_val) => {
|
||||||
|
type_needs(cx, use_repr, a.ty);
|
||||||
|
}
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mark_for_method_call(cx, e.id);
|
||||||
|
}
|
||||||
expr_paren(e) => mark_for_expr(cx, e),
|
expr_paren(e) => mark_for_expr(cx, e),
|
||||||
expr_match(*) | expr_block(_) | expr_if(*) |
|
expr_match(*) | expr_block(_) | expr_if(*) |
|
||||||
expr_while(*) | expr_fail(_) | expr_break(_) | expr_again(_) |
|
expr_while(*) | expr_fail(_) | expr_break(_) | expr_again(_) |
|
||||||
|
|
|
@ -3053,6 +3053,7 @@ fn expr_kind(tcx: ctxt,
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::expr_call(*) |
|
ast::expr_call(*) |
|
||||||
|
ast::expr_method_call(*) |
|
||||||
ast::expr_rec(*) |
|
ast::expr_rec(*) |
|
||||||
ast::expr_struct(*) |
|
ast::expr_struct(*) |
|
||||||
ast::expr_tup(*) |
|
ast::expr_tup(*) |
|
||||||
|
|
|
@ -1101,24 +1101,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
return bot;
|
return bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A generic function for doing all of the checking for call expressions
|
// A generic function for doing all of the checking for call or
|
||||||
fn check_call(fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id,
|
// method expressions
|
||||||
f: @ast::expr, args: ~[@ast::expr]) -> bool {
|
fn check_call_or_method(fcx: @fn_ctxt,
|
||||||
|
sp: span,
|
||||||
// Index expressions need to be handled seperately, to inform
|
call_expr_id: ast::node_id,
|
||||||
// them that they appear in call position.
|
fn_ty: ty::t,
|
||||||
let mut bot = match f.node {
|
expr: @ast::expr,
|
||||||
ast::expr_field(base, field, tys) => {
|
args: ~[@ast::expr],
|
||||||
check_field(fcx, f, true, base, field, tys)
|
bot: bool)
|
||||||
}
|
-> bool {
|
||||||
_ => check_expr(fcx, f, None)
|
let mut bot = bot;
|
||||||
};
|
|
||||||
let fn_ty = fcx.expr_ty(f);
|
|
||||||
|
|
||||||
// Call the generic checker.
|
// Call the generic checker.
|
||||||
let fty = {
|
let fty = {
|
||||||
let r = check_call_inner(fcx, sp, call_expr_id,
|
let r = check_call_inner(fcx, sp, call_expr_id,
|
||||||
fn_ty, f, args, DontDerefArgs);
|
fn_ty, expr, args, DontDerefArgs);
|
||||||
bot |= r.bot;
|
bot |= r.bot;
|
||||||
r.fty
|
r.fty
|
||||||
};
|
};
|
||||||
|
@ -1139,6 +1137,79 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A generic function for doing all of the checking for call expressions
|
||||||
|
fn check_call(fcx: @fn_ctxt, sp: span, call_expr_id: ast::node_id,
|
||||||
|
f: @ast::expr, args: ~[@ast::expr]) -> bool {
|
||||||
|
|
||||||
|
// Index expressions need to be handled separately, to inform them
|
||||||
|
// that they appear in call position.
|
||||||
|
let mut bot = match f.node {
|
||||||
|
ast::expr_field(base, field, tys) => {
|
||||||
|
check_field(fcx, f, true, base, field, tys)
|
||||||
|
}
|
||||||
|
_ => check_expr(fcx, f, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
check_call_or_method(fcx,
|
||||||
|
sp,
|
||||||
|
call_expr_id,
|
||||||
|
fcx.expr_ty(f),
|
||||||
|
f,
|
||||||
|
args,
|
||||||
|
bot)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks a method call.
|
||||||
|
fn check_method_call(fcx: @fn_ctxt,
|
||||||
|
expr: @ast::expr,
|
||||||
|
rcvr: @ast::expr,
|
||||||
|
method_name: ast::ident,
|
||||||
|
args: ~[@ast::expr],
|
||||||
|
tps: ~[@ast::Ty])
|
||||||
|
-> bool {
|
||||||
|
let bot = check_expr(fcx, rcvr, None);
|
||||||
|
let expr_t = structurally_resolved_type(fcx,
|
||||||
|
expr.span,
|
||||||
|
fcx.expr_ty(rcvr));
|
||||||
|
|
||||||
|
let tps = tps.map(|ast_ty| fcx.to_ty(*ast_ty));
|
||||||
|
match method::lookup(fcx,
|
||||||
|
expr,
|
||||||
|
rcvr,
|
||||||
|
expr.callee_id,
|
||||||
|
method_name,
|
||||||
|
expr_t,
|
||||||
|
tps,
|
||||||
|
DontDerefArgs) {
|
||||||
|
Some(entry) => {
|
||||||
|
fcx.ccx.method_map.insert(expr.id, entry);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
fcx.type_error_message(expr.span,
|
||||||
|
|actual| {
|
||||||
|
fmt!("type `%s` does not implement any method in scope \
|
||||||
|
named `%s`",
|
||||||
|
actual,
|
||||||
|
fcx.ccx.tcx.sess.str_of(method_name))
|
||||||
|
},
|
||||||
|
expr_t,
|
||||||
|
None);
|
||||||
|
|
||||||
|
// Add error type for the result
|
||||||
|
fcx.write_ty(expr.id, ty::mk_err(fcx.ccx.tcx));
|
||||||
|
fcx.write_ty(expr.callee_id, ty::mk_err(fcx.ccx.tcx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_call_or_method(fcx,
|
||||||
|
expr.span,
|
||||||
|
expr.id,
|
||||||
|
fcx.node_ty(expr.callee_id),
|
||||||
|
expr,
|
||||||
|
args,
|
||||||
|
bot)
|
||||||
|
}
|
||||||
|
|
||||||
// A generic function for checking for or for-each loops
|
// A generic function for checking for or for-each loops
|
||||||
fn check_for(fcx: @fn_ctxt, local: @ast::local,
|
fn check_for(fcx: @fn_ctxt, local: @ast::local,
|
||||||
element_ty: ty::t, body: ast::blk,
|
element_ty: ty::t, body: ast::blk,
|
||||||
|
@ -2092,6 +2163,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
|
||||||
ast::expr_call(f, args, _) => {
|
ast::expr_call(f, args, _) => {
|
||||||
bot = check_call(fcx, expr.span, expr.id, f, args);
|
bot = check_call(fcx, expr.span, expr.id, f, args);
|
||||||
}
|
}
|
||||||
|
ast::expr_method_call(rcvr, ident, tps, args, _) => {
|
||||||
|
bot = check_method_call(fcx, expr, rcvr, ident, args, tps);
|
||||||
|
}
|
||||||
ast::expr_cast(e, t) => {
|
ast::expr_cast(e, t) => {
|
||||||
bot = check_expr(fcx, e, None);
|
bot = check_expr(fcx, e, None);
|
||||||
let t_1 = fcx.to_ty(t);
|
let t_1 = fcx.to_ty(t);
|
||||||
|
|
|
@ -199,6 +199,17 @@ fn visit_expr(expr: @ast::expr, &&rcx: @rcx, v: rvt) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_method_call(rcvr, _, _, args, _) => {
|
||||||
|
// Check for a.b() where b is a method. Ensure that
|
||||||
|
// any types in the callee are valid for the entire
|
||||||
|
// method call.
|
||||||
|
|
||||||
|
constrain_auto_ref(rcx, rcvr);
|
||||||
|
for args.each |arg| {
|
||||||
|
constrain_auto_ref(rcx, *arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ast::expr_cast(source, _) => {
|
ast::expr_cast(source, _) => {
|
||||||
// Determine if we are casting `source` to an trait
|
// Determine if we are casting `source` to an trait
|
||||||
// instance. If so, we have to be sure that the type of
|
// instance. If so, we have to be sure that the type of
|
||||||
|
|
|
@ -521,7 +521,7 @@ fn early_resolve_expr(ex: @ast::expr, &&fcx: @fn_ctxt, is_early: bool) {
|
||||||
// Must resolve bounds on methods with bounded params
|
// Must resolve bounds on methods with bounded params
|
||||||
ast::expr_field(*) | ast::expr_binary(*) |
|
ast::expr_field(*) | ast::expr_binary(*) |
|
||||||
ast::expr_unary(*) | ast::expr_assign_op(*) |
|
ast::expr_unary(*) | ast::expr_assign_op(*) |
|
||||||
ast::expr_index(*) => {
|
ast::expr_index(*) | ast::expr_method_call(*) => {
|
||||||
match ty::method_call_bounds(cx.tcx, cx.method_map, ex.id) {
|
match ty::method_call_bounds(cx.tcx, cx.method_map, ex.id) {
|
||||||
Some(bounds) => {
|
Some(bounds) => {
|
||||||
if has_trait_bounds(*bounds) {
|
if has_trait_bounds(*bounds) {
|
||||||
|
|
|
@ -158,6 +158,11 @@ fn visit_expr(e: @ast::expr, wbcx: wb_ctxt, v: wb_vt) {
|
||||||
maybe_resolve_type_vars_for_node(wbcx, e.span, e.callee_id);
|
maybe_resolve_type_vars_for_node(wbcx, e.span, e.callee_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast::expr_method_call(*) => {
|
||||||
|
// We must always have written in a callee ID type for these.
|
||||||
|
resolve_type_vars_for_node(wbcx, e.span, e.callee_id);
|
||||||
|
}
|
||||||
|
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
visit::visit_expr(e, wbcx, v);
|
visit::visit_expr(e, wbcx, v);
|
||||||
|
|
|
@ -61,6 +61,9 @@ fn explain_region_and_span(cx: ctxt, region: ty::Region)
|
||||||
Some(ast_map::node_expr(expr)) => {
|
Some(ast_map::node_expr(expr)) => {
|
||||||
match expr.node {
|
match expr.node {
|
||||||
ast::expr_call(*) => explain_span(cx, ~"call", expr.span),
|
ast::expr_call(*) => explain_span(cx, ~"call", expr.span),
|
||||||
|
ast::expr_method_call(*) => {
|
||||||
|
explain_span(cx, ~"method call", expr.span)
|
||||||
|
},
|
||||||
ast::expr_match(*) => explain_span(cx, ~"match", expr.span),
|
ast::expr_match(*) => explain_span(cx, ~"match", expr.span),
|
||||||
_ => explain_span(cx, ~"expression", expr.span)
|
_ => explain_span(cx, ~"expression", expr.span)
|
||||||
}
|
}
|
||||||
|
|
|
@ -645,7 +645,7 @@ impl blk_check_mode : cmp::Eq {
|
||||||
#[auto_serialize]
|
#[auto_serialize]
|
||||||
#[auto_deserialize]
|
#[auto_deserialize]
|
||||||
type expr = {id: node_id, callee_id: node_id, node: expr_, span: span};
|
type expr = {id: node_id, callee_id: node_id, node: expr_, span: span};
|
||||||
// Extra node ID is only used for index, assign_op, unary, binary
|
// Extra node ID is only used for index, assign_op, unary, binary, method call
|
||||||
|
|
||||||
#[auto_serialize]
|
#[auto_serialize]
|
||||||
#[auto_deserialize]
|
#[auto_deserialize]
|
||||||
|
@ -659,6 +659,7 @@ enum expr_ {
|
||||||
expr_vec(~[@expr], mutability),
|
expr_vec(~[@expr], mutability),
|
||||||
expr_rec(~[field], Option<@expr>),
|
expr_rec(~[field], Option<@expr>),
|
||||||
expr_call(@expr, ~[@expr], bool), // True iff last argument is a block
|
expr_call(@expr, ~[@expr], bool), // True iff last argument is a block
|
||||||
|
expr_method_call(@expr, ident, ~[@Ty], ~[@expr], bool), // Ditto
|
||||||
expr_tup(~[@expr]),
|
expr_tup(~[@expr]),
|
||||||
expr_binary(binop, @expr, @expr),
|
expr_binary(binop, @expr, @expr),
|
||||||
expr_unary(unop, @expr),
|
expr_unary(unop, @expr),
|
||||||
|
|
|
@ -409,6 +409,13 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
|
||||||
fld.map_exprs(|x| fld.fold_expr(x), args),
|
fld.map_exprs(|x| fld.fold_expr(x), args),
|
||||||
blk)
|
blk)
|
||||||
}
|
}
|
||||||
|
expr_method_call(f, i, tps, args, blk) => {
|
||||||
|
expr_method_call(fld.fold_expr(f),
|
||||||
|
fld.fold_ident(i),
|
||||||
|
vec::map(tps, |x| fld.fold_ty(*x)),
|
||||||
|
fld.map_exprs(|x| fld.fold_expr(x), args),
|
||||||
|
blk)
|
||||||
|
}
|
||||||
expr_binary(binop, lhs, rhs) => {
|
expr_binary(binop, lhs, rhs) => {
|
||||||
expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs))
|
expr_binary(binop, fld.fold_expr(lhs), fld.fold_expr(rhs))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,13 @@ use ast_util::operator_prec;
|
||||||
|
|
||||||
fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
|
fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
|
||||||
match e.node {
|
match e.node {
|
||||||
ast::expr_if(*) | ast::expr_match(*) | ast::expr_block(_)
|
ast::expr_if(*)
|
||||||
| ast::expr_while(*) | ast::expr_loop(*)
|
| ast::expr_match(*)
|
||||||
| ast::expr_call(_, _, true) => false,
|
| ast::expr_block(_)
|
||||||
|
| ast::expr_while(*)
|
||||||
|
| ast::expr_loop(*)
|
||||||
|
| ast::expr_call(_, _, true)
|
||||||
|
| ast::expr_method_call(_, _, _, _, true) => false,
|
||||||
_ => true
|
_ => true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,10 @@ use ast::{_mod, add, arg, arm, attribute,
|
||||||
expr_call, expr_cast, expr_copy, expr_do_body, expr_fail,
|
expr_call, expr_cast, expr_copy, expr_do_body, expr_fail,
|
||||||
expr_field, expr_fn, expr_fn_block, expr_if, expr_index,
|
expr_field, expr_fn, expr_fn_block, expr_if, expr_index,
|
||||||
expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac,
|
expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac,
|
||||||
expr_paren, expr_path, expr_rec, expr_repeat, expr_ret,
|
expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat,
|
||||||
expr_swap, expr_struct, expr_tup, expr_unary, expr_unary_move,
|
expr_ret, expr_swap, expr_struct, expr_tup, expr_unary,
|
||||||
expr_vec, expr_vstore, expr_vstore_mut_box, expr_while,
|
expr_unary_move, expr_vec, expr_vstore, expr_vstore_mut_box,
|
||||||
extern_fn, field, fn_decl,
|
expr_while, extern_fn, field, fn_decl,
|
||||||
foreign_item, foreign_item_const, foreign_item_fn, foreign_mod,
|
foreign_item, foreign_item_const, foreign_item_fn, foreign_mod,
|
||||||
ident, impure_fn, infer, inherited,
|
ident, impure_fn, infer, inherited,
|
||||||
item, item_, item_class, item_const, item_enum, item_fn,
|
item, item_, item_class, item_const, item_enum, item_fn,
|
||||||
|
@ -1212,8 +1212,26 @@ impl Parser {
|
||||||
self.expect(token::LT);
|
self.expect(token::LT);
|
||||||
self.parse_seq_to_gt(Some(token::COMMA),
|
self.parse_seq_to_gt(Some(token::COMMA),
|
||||||
|p| p.parse_ty(false))
|
|p| p.parse_ty(false))
|
||||||
} else { ~[] };
|
} else {
|
||||||
e = self.mk_expr(lo, hi, expr_field(e, i, tys));
|
~[]
|
||||||
|
};
|
||||||
|
|
||||||
|
// expr.f() method call
|
||||||
|
match copy self.token {
|
||||||
|
token::LPAREN if self.permits_call() => {
|
||||||
|
let es = self.parse_unspanned_seq(
|
||||||
|
token::LPAREN, token::RPAREN,
|
||||||
|
seq_sep_trailing_disallowed(token::COMMA),
|
||||||
|
|p| p.parse_expr());
|
||||||
|
hi = self.span.hi;
|
||||||
|
|
||||||
|
let nd = expr_method_call(e, i, tys, es, false);
|
||||||
|
e = self.mk_expr(lo, hi, move nd);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
e = self.mk_expr(lo, hi, expr_field(e, i, tys));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => self.unexpected()
|
_ => self.unexpected()
|
||||||
}
|
}
|
||||||
|
@ -1674,7 +1692,23 @@ impl Parser {
|
||||||
@{node: expr_call(f, args, true),
|
@{node: expr_call(f, args, true),
|
||||||
.. *e}
|
.. *e}
|
||||||
}
|
}
|
||||||
expr_path(*) | expr_field(*) | expr_call(*) | expr_paren(*) => {
|
expr_method_call(f, i, tps, args, false) => {
|
||||||
|
let block = self.parse_lambda_block_expr();
|
||||||
|
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
|
||||||
|
ctor(block));
|
||||||
|
let args = vec::append(args, ~[last_arg]);
|
||||||
|
@{node: expr_method_call(f, i, tps, args, true),
|
||||||
|
.. *e}
|
||||||
|
}
|
||||||
|
expr_field(f, i, tps) => {
|
||||||
|
let block = self.parse_lambda_block_expr();
|
||||||
|
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
|
||||||
|
ctor(block));
|
||||||
|
@{node: expr_method_call(f, i, tps, ~[last_arg], true),
|
||||||
|
.. *e}
|
||||||
|
}
|
||||||
|
expr_path(*) | expr_call(*) | expr_method_call(*) |
|
||||||
|
expr_paren(*) => {
|
||||||
let block = self.parse_lambda_block_expr();
|
let block = self.parse_lambda_block_expr();
|
||||||
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
|
let last_arg = self.mk_expr(block.span.lo, block.span.hi,
|
||||||
ctor(block));
|
ctor(block));
|
||||||
|
|
|
@ -1041,6 +1041,50 @@ fn print_expr_vstore(s: ps, t: ast::expr_vstore) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_call_pre(s: ps,
|
||||||
|
has_block: bool,
|
||||||
|
base_args: &mut ~[@ast::expr])
|
||||||
|
-> Option<@ast::expr> {
|
||||||
|
if has_block {
|
||||||
|
let blk_arg = base_args.pop();
|
||||||
|
match blk_arg.node {
|
||||||
|
ast::expr_loop_body(_) => { head(s, ~"for"); }
|
||||||
|
ast::expr_do_body(_) => { head(s, ~"do"); }
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
Some(blk_arg)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_call_post(s: ps,
|
||||||
|
has_block: bool,
|
||||||
|
blk: &Option<@ast::expr>,
|
||||||
|
base_args: &mut ~[@ast::expr]) {
|
||||||
|
if !has_block || base_args.is_not_empty() {
|
||||||
|
popen(s);
|
||||||
|
commasep_exprs(s, inconsistent, *base_args);
|
||||||
|
pclose(s);
|
||||||
|
}
|
||||||
|
if has_block {
|
||||||
|
nbsp(s);
|
||||||
|
match blk.get().node {
|
||||||
|
// need to handle closures specifically
|
||||||
|
ast::expr_do_body(e) | ast::expr_loop_body(e) => {
|
||||||
|
end(s); // we close our head box; closure
|
||||||
|
// will create it's own.
|
||||||
|
print_expr(s, e);
|
||||||
|
end(s); // close outer box, as closures don't
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// not sure if this can happen.
|
||||||
|
print_expr(s, blk.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn print_expr(s: ps, &&expr: @ast::expr) {
|
fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||||
fn print_field(s: ps, field: ast::field) {
|
fn print_field(s: ps, field: ast::field) {
|
||||||
ibox(s, indent_unit);
|
ibox(s, indent_unit);
|
||||||
|
@ -1135,38 +1179,23 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
|
||||||
pclose(s);
|
pclose(s);
|
||||||
}
|
}
|
||||||
ast::expr_call(func, args, has_block) => {
|
ast::expr_call(func, args, has_block) => {
|
||||||
let mut base_args = args;
|
let mut base_args = copy args;
|
||||||
let blk = if has_block {
|
let blk = print_call_pre(s, has_block, &mut base_args);
|
||||||
let blk_arg = base_args.pop();
|
|
||||||
match blk_arg.node {
|
|
||||||
ast::expr_loop_body(_) => { head(s, ~"for"); }
|
|
||||||
ast::expr_do_body(_) => { head(s, ~"do"); }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
Some(blk_arg)
|
|
||||||
} else { None };
|
|
||||||
print_expr(s, func);
|
print_expr(s, func);
|
||||||
if !has_block || base_args.is_not_empty() {
|
print_call_post(s, has_block, &blk, &mut base_args);
|
||||||
popen(s);
|
}
|
||||||
commasep_exprs(s, inconsistent, base_args);
|
ast::expr_method_call(func, ident, tys, args, has_block) => {
|
||||||
pclose(s);
|
let mut base_args = copy args;
|
||||||
}
|
let blk = print_call_pre(s, has_block, &mut base_args);
|
||||||
if has_block {
|
print_expr(s, func);
|
||||||
nbsp(s);
|
word(s.s, ~".");
|
||||||
match blk.get().node {
|
print_ident(s, ident);
|
||||||
// need to handle closures specifically
|
if vec::len(tys) > 0u {
|
||||||
ast::expr_do_body(e) | ast::expr_loop_body(e) => {
|
word(s.s, ~"::<");
|
||||||
end(s); // we close our head box; closure
|
commasep(s, inconsistent, tys, print_type);
|
||||||
// will create it's own.
|
word(s.s, ~">");
|
||||||
print_expr(s, e);
|
|
||||||
end(s); // close outer box, as closures don't
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// not sure if this can happen.
|
|
||||||
print_expr(s, blk.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
print_call_post(s, has_block, &blk, &mut base_args);
|
||||||
}
|
}
|
||||||
ast::expr_binary(op, lhs, rhs) => {
|
ast::expr_binary(op, lhs, rhs) => {
|
||||||
print_expr(s, lhs);
|
print_expr(s, lhs);
|
||||||
|
|
|
@ -408,6 +408,11 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
|
||||||
visit_exprs(args, e, v);
|
visit_exprs(args, e, v);
|
||||||
(v.visit_expr)(callee, e, v);
|
(v.visit_expr)(callee, e, v);
|
||||||
}
|
}
|
||||||
|
expr_method_call(callee, _, tys, args, _) => {
|
||||||
|
visit_exprs(args, e, v);
|
||||||
|
for tys.each |tp| { (v.visit_ty)(*tp, e, v); }
|
||||||
|
(v.visit_expr)(callee, e, v);
|
||||||
|
}
|
||||||
expr_binary(_, a, b) => {
|
expr_binary(_, a, b) => {
|
||||||
(v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v);
|
(v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue