about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2012-11-30 11:18:25 -0800
committerPatrick Walton <pcwalton@mimiga.net>2012-11-30 20:41:46 -0800
commitf34833abfce74cf178c0589a4b7cf5fba9d2a2db (patch)
tree08943a296d704c10303fbd6b33b3d7e8fb704ac6
parent54ae377ec26ed47bbb627bdcb58bb10658cf03c4 (diff)
downloadrust-f34833abfce74cf178c0589a4b7cf5fba9d2a2db.tar.gz
rust-f34833abfce74cf178c0589a4b7cf5fba9d2a2db.zip
librustc: Make `a.b()` always a method call. r=nmatsakis
-rw-r--r--src/libcore/io.rs2
-rw-r--r--src/librustc/middle/borrowck/check_loans.rs3
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs32
-rw-r--r--src/librustc/middle/kind.rs12
-rw-r--r--src/librustc/middle/liveness.rs58
-rw-r--r--src/librustc/middle/mem_categorization.rs3
-rw-r--r--src/librustc/middle/region.rs2
-rw-r--r--src/librustc/middle/resolve.rs9
-rw-r--r--src/librustc/middle/trans/callee.rs32
-rw-r--r--src/librustc/middle/trans/expr.rs7
-rw-r--r--src/librustc/middle/trans/reachable.rs12
-rw-r--r--src/librustc/middle/trans/type_use.rs52
-rw-r--r--src/librustc/middle/ty.rs1
-rw-r--r--src/librustc/middle/typeck/check/mod.rs102
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs11
-rw-r--r--src/librustc/middle/typeck/check/vtable.rs2
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs5
-rw-r--r--src/librustc/util/ppaux.rs3
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/fold.rs7
-rw-r--r--src/libsyntax/parse/classify.rs10
-rw-r--r--src/libsyntax/parse/parser.rs48
-rw-r--r--src/libsyntax/print/pprust.rs89
-rw-r--r--src/libsyntax/visit.rs5
24 files changed, 413 insertions, 97 deletions
diff --git a/src/libcore/io.rs b/src/libcore/io.rs
index aa2eccfb343..1bebf817f9b 100644
--- a/src/libcore/io.rs
+++ b/src/libcore/io.rs
@@ -1047,7 +1047,7 @@ pub mod fsync {
             option::None => (),
             option::Some(level) => {
               // 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);
             }
           }
         }
diff --git a/src/librustc/middle/borrowck/check_loans.rs b/src/librustc/middle/borrowck/check_loans.rs
index a9deb51f1c7..7cf5ad32b3c 100644
--- a/src/librustc/middle/borrowck/check_loans.rs
+++ b/src/librustc/middle/borrowck/check_loans.rs
@@ -646,6 +646,9 @@ fn check_loans_in_expr(expr: @ast::expr,
       ast::expr_call(f, 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_binary(_, _, rval)
       if self.bccx.method_map.contains_key(expr.id) => {
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index 25c43019e47..c0f9cb3d5e2 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -125,6 +125,38 @@ fn req_loans_in_expr(ex: @ast::expr,
         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) => {
         let cmt = self.bccx.cat_expr(ex_v);
         for arms.each |arm| {
diff --git a/src/librustc/middle/kind.rs b/src/librustc/middle/kind.rs
index c0887150f24..b4099325fb1 100644
--- a/src/librustc/middle/kind.rs
+++ b/src/librustc/middle/kind.rs
@@ -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, _, _) => {
         // If this is a method call with a by-val argument, we need
         // to check the copy
diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs
index 86136d60d54..985f62e86a6 100644
--- a/src/librustc/middle/liveness.rs
+++ b/src/librustc/middle/liveness.rs
@@ -550,8 +550,8 @@ fn visit_expr(expr: @expr, &&self: @IrMaps, vt: vt<@IrMaps>) {
 
       // otherwise, live nodes are not required:
       expr_index(*) | expr_field(*) | expr_vstore(*) |
-      expr_vec(*) | expr_rec(*) | expr_call(*) | expr_tup(*) |
-      expr_log(*) | expr_binary(*) |
+      expr_vec(*) | expr_rec(*) | expr_call(*) | expr_method_call(*) |
+      expr_tup(*) | expr_log(*) | expr_binary(*) |
       expr_assert(*) | expr_addr_of(*) | expr_copy(*) |
       expr_loop_body(*) | expr_do_body(*) | expr_cast(*) |
       expr_unary(*) | expr_fail(*) |
@@ -1237,6 +1237,17 @@ impl Liveness {
             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) => {
             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);
 }
 
+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>) {
     match expr.node {
       expr_path(_) => {
@@ -1531,23 +1562,14 @@ fn check_expr(expr: @expr, &&self: @Liveness, vt: vt<@Liveness>) {
 
       expr_call(f, args, _) => {
         let targs = ty::ty_fn_args(ty::expr_ty(self.tcx, f));
-        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())));
-                    }
-                }
-            }
-        }
+        check_call(args, targs, self);
+        visit::visit_expr(expr, self, vt);
+      }
 
+      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);
       }
 
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index fd5735e78de..e42d18a06c9 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -530,7 +530,8 @@ impl &mem_categorization_ctxt {
           ast::expr_swap(*) | ast::expr_assign(*) |
           ast::expr_assign_op(*) | ast::expr_fn(*) | ast::expr_fn_block(*) |
           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_vstore(*) | ast::expr_vec(*) | ast::expr_tup(*) |
           ast::expr_if(*) | ast::expr_log(*) |
diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs
index fbbef2a3658..06011d928fd 100644
--- a/src/librustc/middle/region.rs
+++ b/src/librustc/middle/region.rs
@@ -258,7 +258,7 @@ fn resolve_expr(expr: @ast::expr, cx: ctxt, visitor: visit::vt<ctxt>) {
       // FIXME #3387
       // ast::expr_index(*) | ast::expr_binary(*) |
       // ast::expr_unary(*) |
-      ast::expr_call(*) => {
+      ast::expr_call(*) | ast::expr_method_call(*) => {
         debug!("node %d: %s", expr.id, pprust::expr_to_str(expr,
                                                            cx.sess.intr()));
         new_cx.parent = Some(expr.id);
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 8d04a2beb02..56515b548cf 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -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_typaram_binder, def_static_method};
 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_fn_block, expr_index, expr_path};
+use syntax::ast::{expr_binary, expr_break, expr_cast, expr_field, expr_fn};
+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_upvar, def_use, def_variant, div, eq};
 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_path, expr_struct, expr_unary, fn_decl};
 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);
                 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, _, _) => {
                 self.add_fixed_trait_for_expr(expr.id,
                                               self.lang_items.add_trait);
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 796b4263535..b3b4238034a 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -292,6 +292,38 @@ fn trans_call(in_cx: block,
         |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)
     -> block
 {
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index 36696594e42..2cd68bd0d23 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -592,6 +592,13 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
             return callee::trans_call(
                 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) => {
             // if not overloaded, would be RvalueDatumExpr
             return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest,
diff --git a/src/librustc/middle/trans/reachable.rs b/src/librustc/middle/trans/reachable.rs
index 7b03460bb7f..a3df1c630f5 100644
--- a/src/librustc/middle/trans/reachable.rs
+++ b/src/librustc/middle/trans/reachable.rs
@@ -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);
diff --git a/src/librustc/middle/trans/type_use.rs b/src/librustc/middle/trans/type_use.rs
index 14744c4cc9e..93c79e24ce8 100644
--- a/src/librustc/middle/trans/type_use.rs
+++ b/src/librustc/middle/trans/type_use.rs
@@ -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));
 }
 
+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) {
     match e.node {
       expr_vstore(_, _) |
@@ -231,23 +250,7 @@ fn mark_for_expr(cx: ctx, e: @expr) {
         // the chosen field.
         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));
-
-        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(*) => (),
-            }
-        }
+        mark_for_method_call(cx, e.id);
       }
       expr_log(_, _, val) => {
         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_match(*) | expr_block(_) | expr_if(*) |
       expr_while(*) | expr_fail(_) | expr_break(_) | expr_again(_) |
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index cd70f7b1958..534f70ebed4 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -3053,6 +3053,7 @@ fn expr_kind(tcx: ctxt,
         }
 
         ast::expr_call(*) |
+        ast::expr_method_call(*) |
         ast::expr_rec(*) |
         ast::expr_struct(*) |
         ast::expr_tup(*) |
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index b9795c11ed5..2081a9ef017 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -1101,24 +1101,22 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
         return bot;
     }
 
-    // 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 seperately, 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)
-        };
-        let fn_ty = fcx.expr_ty(f);
+    // A generic function for doing all of the checking for call or
+    // method expressions
+    fn check_call_or_method(fcx: @fn_ctxt,
+                            sp: span,
+                            call_expr_id: ast::node_id,
+                            fn_ty: ty::t,
+                            expr: @ast::expr,
+                            args: ~[@ast::expr],
+                            bot: bool)
+                         -> bool {
+        let mut bot = bot;
 
         // Call the generic checker.
         let fty = {
             let r = check_call_inner(fcx, sp, call_expr_id,
-                                     fn_ty, f, args, DontDerefArgs);
+                                     fn_ty, expr, args, DontDerefArgs);
             bot |= r.bot;
             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
     fn check_for(fcx: @fn_ctxt, local: @ast::local,
                  element_ty: ty::t, body: ast::blk,
@@ -2092,6 +2163,9 @@ fn check_expr_with_unifier(fcx: @fn_ctxt,
       ast::expr_call(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) => {
         bot = check_expr(fcx, e, None);
         let t_1 = fcx.to_ty(t);
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index b70c85474d5..f45bef2eb5f 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -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, _) => {
             // Determine if we are casting `source` to an trait
             // instance.  If so, we have to be sure that the type of
diff --git a/src/librustc/middle/typeck/check/vtable.rs b/src/librustc/middle/typeck/check/vtable.rs
index 99999ca2ae1..7087b7c5b79 100644
--- a/src/librustc/middle/typeck/check/vtable.rs
+++ b/src/librustc/middle/typeck/check/vtable.rs
@@ -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
       ast::expr_field(*) | ast::expr_binary(*) |
       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) {
           Some(bounds) => {
             if has_trait_bounds(*bounds) {
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index 3ac94297191..ba2c9416f56 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -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);
       }
 
+      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);
diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs
index 57c8f79b78a..c0af9fd2ae8 100644
--- a/src/librustc/util/ppaux.rs
+++ b/src/librustc/util/ppaux.rs
@@ -61,6 +61,9 @@ fn explain_region_and_span(cx: ctxt, region: ty::Region)
           Some(ast_map::node_expr(expr)) => {
             match expr.node {
               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),
               _ => explain_span(cx, ~"expression", expr.span)
             }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 1ffe741d255..9dab40ba991 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -645,7 +645,7 @@ impl blk_check_mode : cmp::Eq {
 #[auto_serialize]
 #[auto_deserialize]
 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_deserialize]
@@ -659,6 +659,7 @@ enum expr_ {
     expr_vec(~[@expr], mutability),
     expr_rec(~[field], Option<@expr>),
     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_binary(binop, @expr, @expr),
     expr_unary(unop, @expr),
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 9a1f3e7f04e..03a9729477a 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -409,6 +409,13 @@ fn noop_fold_expr(e: expr_, fld: ast_fold) -> expr_ {
                       fld.map_exprs(|x| fld.fold_expr(x), args),
                       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, fld.fold_expr(lhs), fld.fold_expr(rhs))
           }
diff --git a/src/libsyntax/parse/classify.rs b/src/libsyntax/parse/classify.rs
index 0d370525d17..e0210feb9de 100644
--- a/src/libsyntax/parse/classify.rs
+++ b/src/libsyntax/parse/classify.rs
@@ -6,9 +6,13 @@ use ast_util::operator_prec;
 
 fn expr_requires_semi_to_be_stmt(e: @ast::expr) -> bool {
     match e.node {
-      ast::expr_if(*) | ast::expr_match(*) | ast::expr_block(_)
-      | ast::expr_while(*) | ast::expr_loop(*)
-      | ast::expr_call(_, _, true) => false,
+      ast::expr_if(*)
+      | ast::expr_match(*)
+      | ast::expr_block(_)
+      | ast::expr_while(*)
+      | ast::expr_loop(*)
+      | ast::expr_call(_, _, true)
+      | ast::expr_method_call(_, _, _, _, true) => false,
       _ => true
     }
 }
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index d8fd58b3d50..7cf279d0d81 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -34,10 +34,10 @@ use ast::{_mod, add, arg, arm, attribute,
              expr_call, expr_cast, expr_copy, expr_do_body, expr_fail,
              expr_field, expr_fn, expr_fn_block, expr_if, expr_index,
              expr_lit, expr_log, expr_loop, expr_loop_body, expr_mac,
-             expr_paren, expr_path, expr_rec, expr_repeat, expr_ret,
-             expr_swap, expr_struct, expr_tup, expr_unary, expr_unary_move,
-             expr_vec, expr_vstore, expr_vstore_mut_box, expr_while,
-             extern_fn, field, fn_decl,
+             expr_method_call, expr_paren, expr_path, expr_rec, expr_repeat,
+             expr_ret, expr_swap, expr_struct, expr_tup, expr_unary,
+             expr_unary_move, expr_vec, expr_vstore, expr_vstore_mut_box,
+             expr_while, extern_fn, field, fn_decl,
              foreign_item, foreign_item_const, foreign_item_fn, foreign_mod,
              ident, impure_fn, infer, inherited,
              item, item_, item_class, item_const, item_enum, item_fn,
@@ -1212,8 +1212,26 @@ impl Parser {
                         self.expect(token::LT);
                         self.parse_seq_to_gt(Some(token::COMMA),
                                              |p| p.parse_ty(false))
-                    } else { ~[] };
-                    e = self.mk_expr(lo, hi, expr_field(e, i, tys));
+                    } else {
+                        ~[]
+                    };
+
+                    // 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()
                 }
@@ -1674,7 +1692,23 @@ impl Parser {
             @{node: expr_call(f, args, true),
               .. *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 last_arg = self.mk_expr(block.span.lo, block.span.hi,
                                     ctor(block));
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 5ad3c051c59..e13c6da403f 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -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_field(s: ps, field: ast::field) {
         ibox(s, indent_unit);
@@ -1135,38 +1179,23 @@ fn print_expr(s: ps, &&expr: @ast::expr) {
         pclose(s);
       }
       ast::expr_call(func, args, has_block) => {
-        let mut base_args = args;
-        let blk = 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 };
+        let mut base_args = copy args;
+        let blk = print_call_pre(s, has_block, &mut base_args);
         print_expr(s, func);
-        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());
-              }
-            }
+        print_call_post(s, has_block, &blk, &mut base_args);
+      }
+      ast::expr_method_call(func, ident, tys, args, has_block) => {
+        let mut base_args = copy args;
+        let blk = print_call_pre(s, has_block, &mut base_args);
+        print_expr(s, func);
+        word(s.s, ~".");
+        print_ident(s, ident);
+        if vec::len(tys) > 0u {
+            word(s.s, ~"::<");
+            commasep(s, inconsistent, tys, print_type);
+            word(s.s, ~">");
         }
+        print_call_post(s, has_block, &blk, &mut base_args);
       }
       ast::expr_binary(op, lhs, rhs) => {
         print_expr(s, lhs);
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index fbb1bc91172..bac651ef36c 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -408,6 +408,11 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
         visit_exprs(args, 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) => {
         (v.visit_expr)(a, e, v); (v.visit_expr)(b, e, v);
       }