]> git.lizzy.rs Git - rust.git/commitdiff
librustc: Implement overloading for the call operator behind a feature
authorPatrick Walton <pcwalton@mimiga.net>
Sun, 1 Jun 2014 23:35:01 +0000 (16:35 -0700)
committerPatrick Walton <pcwalton@mimiga.net>
Mon, 9 Jun 2014 19:39:17 +0000 (12:39 -0700)
gate.

This is part of unboxed closures.

12 files changed:
src/libcore/ops.rs
src/librustc/front/feature_gate.rs
src/librustc/middle/expr_use_visitor.rs
src/librustc/middle/lang_items.rs
src/librustc/middle/liveness.rs
src/librustc/middle/trans/expr.rs
src/librustc/middle/typeck/check/mod.rs
src/librustc/middle/typeck/check/regionck.rs
src/test/compile-fail/overloaded-calls-bad.rs [new file with mode: 0644]
src/test/compile-fail/overloaded-calls-nontuple.rs [new file with mode: 0644]
src/test/run-pass/overloaded-calls-simple.rs [new file with mode: 0644]
src/test/run-pass/overloaded-calls-zero-args.rs [new file with mode: 0644]

index 08e033f961f45d3583aa59d5f299641b4f6e48fd..af1df973a3e655c2fc61da948e7a48ccc3b5410c 100644 (file)
@@ -726,6 +726,27 @@ pub trait DerefMut<Result>: Deref<Result> {
     fn deref_mut<'a>(&'a mut self) -> &'a mut Result;
 }
 
+/// A version of the call operator that takes an immutable receiver.
+#[lang="fn"]
+pub trait Fn<Args,Result> {
+    /// This is called when the call operator is used.
+    fn call(&self, args: Args) -> Result;
+}
+
+/// A version of the call operator that takes a mutable receiver.
+#[lang="fn_mut"]
+pub trait FnMut<Args,Result> {
+    /// This is called when the call operator is used.
+    fn call_mut(&mut self, args: Args) -> Result;
+}
+
+/// A version of the call operator that takes a by-value receiver.
+#[lang="fn_once"]
+pub trait FnOnce<Args,Result> {
+    /// This is called when the call operator is used.
+    fn call_once(self, args: Args) -> Result;
+}
+
 #[cfg(test)]
 mod bench {
     extern crate test;
index 25f0dc808c849f62ca1da0391bef7f2e78a04c29..5edee5ea2ef735bc17ff1828e17f8bc9c74983ee 100644 (file)
@@ -56,6 +56,7 @@
     ("quote", Active),
     ("linkage", Active),
     ("struct_inherit", Active),
+    ("overloaded_calls", Active),
 
     ("quad_precision_float", Active),
 
@@ -86,6 +87,7 @@ pub struct Features {
     pub default_type_params: Cell<bool>,
     pub quad_precision_float: Cell<bool>,
     pub issue_5723_bootstrap: Cell<bool>,
+    pub overloaded_calls: Cell<bool>,
 }
 
 impl Features {
@@ -94,6 +96,7 @@ pub fn new() -> Features {
             default_type_params: Cell::new(false),
             quad_precision_float: Cell::new(false),
             issue_5723_bootstrap: Cell::new(false),
+            overloaded_calls: Cell::new(false),
         }
     }
 }
@@ -376,4 +379,5 @@ pub fn check_crate(sess: &Session, krate: &ast::Crate) {
     sess.features.default_type_params.set(cx.has_feature("default_type_params"));
     sess.features.quad_precision_float.set(cx.has_feature("quad_precision_float"));
     sess.features.issue_5723_bootstrap.set(cx.has_feature("issue_5723_bootstrap"));
+    sess.features.overloaded_calls.set(cx.has_feature("overloaded_calls"));
 }
index c44ea0ae78b10a9765fbc466a8a1b70672f041ce..540dfdab19e990b6362f1926bf4e6e07954ae41f 100644 (file)
@@ -19,6 +19,7 @@
 use middle::freevars;
 use middle::pat_util;
 use middle::ty;
+use middle::typeck::MethodCall;
 use middle::typeck;
 use syntax::ast;
 use syntax::codemap::{Span};
@@ -427,10 +428,20 @@ fn walk_callee(&mut self, call: &ast::Expr, callee: &ast::Expr) {
                 }
             }
             _ => {
-                self.tcx().sess.span_bug(
-                    callee.span,
-                    format!("unxpected callee type {}",
-                            callee_ty.repr(self.tcx())).as_slice());
+                match self.tcx()
+                          .method_map
+                          .borrow()
+                          .find(&MethodCall::expr(call.id)) {
+                    Some(_) => {
+                        // FIXME(#14774, pcwalton): Implement this.
+                    }
+                    None => {
+                        self.tcx().sess.span_bug(
+                            callee.span,
+                            format!("unxpected callee type {}",
+                                    callee_ty.repr(self.tcx())).as_slice());
+                    }
+                }
             }
         }
     }
index 748c29cd92c42d56ffc1d2dfbcba3a1d9d46f040..20e4188a8f6c5cf01386610e71d9b435b0b4a968 100644 (file)
@@ -240,6 +240,10 @@ pub fn collect_language_items(krate: &ast::Crate,
     DerefTraitLangItem,              "deref",                   deref_trait;
     DerefMutTraitLangItem,           "deref_mut",               deref_mut_trait;
 
+    FnTraitLangItem,                 "fn",                      fn_trait;
+    FnMutTraitLangItem,              "fn_mut",                  fn_mut_trait;
+    FnOnceTraitLangItem,             "fn_once",                 fn_once_trait;
+
     EqTraitLangItem,                 "eq",                      eq_trait;
     OrdTraitLangItem,                "ord",                     ord_trait;
 
index 8b1de130053ee15f944f3640a9a87769df2784b8..278d5d8187b72440c3dd6c4482737fc81db8a6d9 100644 (file)
 use middle::def::*;
 use middle::freevars;
 use middle::lint::{UnusedVariable, DeadAssignment};
+use middle::mem_categorization::Typer;
 use middle::pat_util;
 use middle::ty;
 use util::nodemap::NodeMap;
@@ -1146,9 +1147,15 @@ fn propagate_through_expr(&mut self, expr: &Expr, succ: LiveNode)
           ExprCall(f, ref args) => {
             // calling a fn with bot return type means that the fn
             // will fail, and hence the successors can be ignored
-            let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, f));
-            let succ = if ty::type_is_bot(t_ret) {self.s.exit_ln}
-                       else {succ};
+            let is_bot = !self.ir.tcx.is_method_call(expr.id) && {
+                let t_ret = ty::ty_fn_ret(ty::expr_ty(self.ir.tcx, f));
+                ty::type_is_bot(t_ret)
+            };
+            let succ = if is_bot {
+                self.s.exit_ln
+            } else {
+                succ
+            };
             let succ = self.propagate_through_exprs(args.as_slice(), succ);
             self.propagate_through_expr(f, succ)
           }
index 9f90de61cfeb6478bca49d3b66add136ac4dbc80..b21877e4fa030f56c6348e14d6714da110889c7f 100644 (file)
@@ -39,6 +39,7 @@
 use metadata::csearch;
 use middle::def;
 use middle::lang_items::MallocFnLangItem;
+use middle::mem_categorization::Typer;
 use middle::trans::_match;
 use middle::trans::adt;
 use middle::trans::asm;
@@ -65,6 +66,7 @@
 use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef};
 use middle::ty;
 use middle::typeck::MethodCall;
+use middle::typeck;
 use util::common::indenter;
 use util::ppaux::Repr;
 use util::nodemap::NodeMap;
@@ -713,7 +715,20 @@ fn trans_rvalue_dps_unadjusted<'a>(bcx: &'a Block<'a>,
             closure::trans_expr_fn(bcx, store, decl, body, expr.id, dest)
         }
         ast::ExprCall(f, ref args) => {
-            callee::trans_call(bcx, expr, f, callee::ArgExprs(args.as_slice()), dest)
+            if bcx.tcx().is_method_call(expr.id) {
+                let callee_datum = unpack_datum!(bcx, trans(bcx, f));
+                trans_overloaded_call(bcx,
+                                      expr,
+                                      callee_datum,
+                                      args.as_slice(),
+                                      Some(dest))
+            } else {
+                callee::trans_call(bcx,
+                                   expr,
+                                   f,
+                                   callee::ArgExprs(args.as_slice()),
+                                   dest)
+            }
         }
         ast::ExprMethodCall(_, _, ref args) => {
             callee::trans_method_call(bcx,
@@ -1461,6 +1476,76 @@ fn trans_overloaded_op<'a, 'b>(
                              dest)
 }
 
+fn trans_overloaded_call<'a>(
+                         mut bcx: &'a Block<'a>,
+                         expr: &ast::Expr,
+                         callee: Datum<Expr>,
+                         args: &[@ast::Expr],
+                         dest: Option<Dest>)
+                         -> &'a Block<'a> {
+    // Evaluate and tuple the arguments.
+    let tuple_type = ty::mk_tup(bcx.tcx(),
+                                args.iter()
+                                    .map(|e| expr_ty(bcx, *e))
+                                    .collect());
+    let repr = adt::represent_type(bcx.ccx(), tuple_type);
+    let numbered_fields: Vec<(uint, @ast::Expr)> =
+        args.iter().enumerate().map(|(i, arg)| (i, *arg)).collect();
+    let argument_scope = bcx.fcx.push_custom_cleanup_scope();
+    let tuple_datum =
+        unpack_datum!(bcx,
+                      lvalue_scratch_datum(bcx,
+                                           tuple_type,
+                                           "tupled_arguments",
+                                           false,
+                                           cleanup::CustomScope(
+                                               argument_scope),
+                                           (),
+                                           |(), bcx, addr| {
+            trans_adt(bcx,
+                      &*repr,
+                      0,
+                      numbered_fields.as_slice(),
+                      None,
+                      SaveIn(addr))
+        }));
+
+    let method_call = typeck::MethodCall::expr(expr.id);
+    let method_type = bcx.tcx()
+                         .method_map
+                         .borrow()
+                         .get(&method_call)
+                         .ty;
+    let callee_rvalue = unpack_datum!(bcx,
+                                      callee.to_rvalue_datum(bcx, "callee"));
+    let tuple_datum = tuple_datum.to_expr_datum();
+    let tuple_rvalue = unpack_datum!(bcx,
+                                     tuple_datum.to_rvalue_datum(bcx,
+                                                                 "tuple"));
+    let argument_values = [
+        callee_rvalue.add_clean(bcx.fcx,
+                                cleanup::CustomScope(argument_scope)),
+        tuple_rvalue.add_clean(bcx.fcx, cleanup::CustomScope(argument_scope))
+    ];
+    unpack_result!(bcx,
+                   callee::trans_call_inner(bcx,
+                                            Some(expr_info(expr)),
+                                            monomorphize_type(bcx,
+                                                              method_type),
+                                            |bcx, arg_cleanup_scope| {
+                                                meth::trans_method_callee(
+                                                    bcx,
+                                                    method_call,
+                                                    None,
+                                                    arg_cleanup_scope)
+                                            },
+                                            callee::ArgVals(argument_values),
+                                            dest));
+
+    bcx.fcx.pop_custom_cleanup_scope(argument_scope);
+    bcx
+}
+
 fn int_cast(bcx: &Block,
             lldsttype: Type,
             llsrctype: Type,
index d25fc9cc5bcdb298abd9cd49517d9d4d487739ae..1463cf9602dcae137a104a643e90ea67757697b4 100644 (file)
@@ -1353,6 +1353,61 @@ pub fn autoderef<T>(fcx: &FnCtxt, sp: Span, base_ty: ty::t,
     (ty::mk_err(), 0, None)
 }
 
+/// Attempts to resolve a call expression as an overloaded call.
+fn try_overloaded_call(fcx: &FnCtxt,
+                       call_expression: &ast::Expr,
+                       callee: @ast::Expr,
+                       callee_type: ty::t,
+                       args: &[@ast::Expr])
+                       -> bool {
+    // Try `FnOnce`, then `FnMut`, then `Fn`.
+    for &(maybe_function_trait, method_name) in [
+        (fcx.tcx().lang_items.fn_once_trait(), token::intern("call_once")),
+        (fcx.tcx().lang_items.fn_mut_trait(), token::intern("call_mut")),
+        (fcx.tcx().lang_items.fn_trait(), token::intern("call"))
+    ].iter() {
+        let function_trait = match maybe_function_trait {
+            None => continue,
+            Some(function_trait) => function_trait,
+        };
+        let method_callee = match method::lookup_in_trait(
+                fcx,
+                call_expression.span,
+                Some(&*callee),
+                method_name,
+                function_trait,
+                callee_type,
+                [],
+                DontAutoderefReceiver,
+                IgnoreStaticMethods) {
+            None => continue,
+            Some(method_callee) => method_callee,
+        };
+        let method_call = MethodCall::expr(call_expression.id);
+        let output_type = check_method_argument_types(fcx,
+                                                      call_expression.span,
+                                                      method_callee.ty,
+                                                      call_expression,
+                                                      args,
+                                                      DontDerefArgs,
+                                                      TupleArguments);
+        fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
+        write_call(fcx, call_expression, output_type);
+
+        if !fcx.tcx().sess.features.overloaded_calls.get() {
+            fcx.tcx().sess.span_err(call_expression.span,
+                                    "overloaded calls are experimental");
+            fcx.tcx().sess.span_note(call_expression.span,
+                                     "add `#[feature(overloaded_calls)]` to \
+                                      the crate attributes to enable");
+        }
+
+        return true
+    }
+
+    false
+}
+
 fn try_overloaded_deref(fcx: &FnCtxt,
                         span: Span,
                         method_call: Option<MethodCall>,
@@ -1395,6 +1450,261 @@ fn try_overloaded_deref(fcx: &FnCtxt,
     }
 }
 
+fn check_method_argument_types(fcx: &FnCtxt,
+                               sp: Span,
+                               method_fn_ty: ty::t,
+                               callee_expr: &ast::Expr,
+                               args: &[@ast::Expr],
+                               deref_args: DerefArgs,
+                               tuple_arguments: TupleArgumentsFlag)
+                               -> ty::t {
+    // HACK(eddyb) ignore provided self (it has special typeck rules).
+    let args = if tuple_arguments == DontTupleArguments {
+        args.slice_from(1)
+    } else {
+        args
+    };
+    if ty::type_is_error(method_fn_ty) {
+        let err_inputs = err_args(args.len());
+        check_argument_types(fcx,
+                             sp,
+                             err_inputs.as_slice(),
+                             callee_expr,
+                             args,
+                             deref_args,
+                             false,
+                             tuple_arguments);
+        method_fn_ty
+    } else {
+        match ty::get(method_fn_ty).sty {
+            ty::ty_bare_fn(ref fty) => {
+                // HACK(eddyb) ignore self in the definition (see above).
+                check_argument_types(fcx,
+                                     sp,
+                                     fty.sig.inputs.slice_from(1),
+                                     callee_expr,
+                                     args,
+                                     deref_args,
+                                     fty.sig.variadic,
+                                     tuple_arguments);
+                fty.sig.output
+            }
+            _ => {
+                fcx.tcx().sess.span_bug(callee_expr.span,
+                                        "method without bare fn type");
+            }
+        }
+    }
+}
+
+fn check_argument_types(fcx: &FnCtxt,
+                        sp: Span,
+                        fn_inputs: &[ty::t],
+                        callee_expr: &ast::Expr,
+                        args: &[@ast::Expr],
+                        deref_args: DerefArgs,
+                        variadic: bool,
+                        tuple_arguments: TupleArgumentsFlag) {
+    /*!
+     *
+     * Generic function that factors out common logic from
+     * function calls, method calls and overloaded operators.
+     */
+
+    let tcx = fcx.ccx.tcx;
+
+    // Grab the argument types, supplying fresh type variables
+    // if the wrong number of arguments were supplied
+    let supplied_arg_count = if tuple_arguments == DontTupleArguments {
+        args.len()
+    } else {
+        1
+    };
+
+    let expected_arg_count = fn_inputs.len();
+    let formal_tys = if tuple_arguments == TupleArguments {
+        let tuple_type = structurally_resolved_type(fcx, sp, fn_inputs[0]);
+        match ty::get(tuple_type).sty {
+            ty::ty_tup(ref arg_types) => {
+                if arg_types.len() != args.len() {
+                    let msg = format!(
+                        "this function takes \
+                         {nexpected, plural, =1{# parameter} \
+                         other{# parameters}} \
+                         but {nsupplied, plural, =1{# parameter was} \
+                         other{# parameters were}} supplied",
+                         nexpected = arg_types.len(),
+                         nsupplied = args.len());
+                    tcx.sess.span_err(sp, msg.as_slice());
+                    err_args(args.len())
+                } else {
+                    (*arg_types).clone()
+                }
+            }
+            ty::ty_nil => {
+                if args.len() != 0 {
+                    let msg = format!(
+                        "this function takes 0 parameters \
+                         but {nsupplied, plural, =1{# parameter was} \
+                         other{# parameters were}} supplied",
+                         nsupplied = args.len());
+                    tcx.sess.span_err(sp, msg.as_slice());
+                }
+                Vec::new()
+            }
+            _ => {
+                tcx.sess
+                   .span_err(sp,
+                             "cannot use call notation; the first type \
+                              parameter for the function trait is neither a \
+                              tuple nor unit");
+                err_args(supplied_arg_count)
+            }
+        }
+    } else if expected_arg_count == supplied_arg_count {
+        fn_inputs.iter().map(|a| *a).collect()
+    } else if variadic {
+        if supplied_arg_count >= expected_arg_count {
+            fn_inputs.iter().map(|a| *a).collect()
+        } else {
+            let msg = format!(
+                "this function takes at least {nexpected, plural, =1{# parameter} \
+                                                               other{# parameters}} \
+                 but {nsupplied, plural, =1{# parameter was} \
+                                      other{# parameters were}} supplied",
+                 nexpected = expected_arg_count,
+                 nsupplied = supplied_arg_count);
+
+            tcx.sess.span_err(sp, msg.as_slice());
+
+            err_args(supplied_arg_count)
+        }
+    } else {
+        let msg = format!(
+            "this function takes {nexpected, plural, =1{# parameter} \
+                                                  other{# parameters}} \
+             but {nsupplied, plural, =1{# parameter was} \
+                                  other{# parameters were}} supplied",
+             nexpected = expected_arg_count,
+             nsupplied = supplied_arg_count);
+
+        tcx.sess.span_err(sp, msg.as_slice());
+
+        err_args(supplied_arg_count)
+    };
+
+    debug!("check_argument_types: formal_tys={:?}",
+           formal_tys.iter().map(|t| fcx.infcx().ty_to_str(*t)).collect::<Vec<String>>());
+
+    // Check the arguments.
+    // We do this in a pretty awful way: first we typecheck any arguments
+    // that are not anonymous functions, then we typecheck the anonymous
+    // functions. This is so that we have more information about the types
+    // of arguments when we typecheck the functions. This isn't really the
+    // right way to do this.
+    let xs = [false, true];
+    for check_blocks in xs.iter() {
+        let check_blocks = *check_blocks;
+        debug!("check_blocks={}", check_blocks);
+
+        // More awful hacks: before we check the blocks, try to do
+        // an "opportunistic" vtable resolution of any trait
+        // bounds on the call.
+        if check_blocks {
+            vtable::early_resolve_expr(callee_expr, fcx, true);
+        }
+
+        // For variadic functions, we don't have a declared type for all of
+        // the arguments hence we only do our usual type checking with
+        // the arguments who's types we do know.
+        let t = if variadic {
+            expected_arg_count
+        } else if tuple_arguments == TupleArguments {
+            args.len()
+        } else {
+            supplied_arg_count
+        };
+        for (i, arg) in args.iter().take(t).enumerate() {
+            let is_block = match arg.node {
+                ast::ExprFnBlock(..) |
+                ast::ExprProc(..) => true,
+                _ => false
+            };
+
+            if is_block == check_blocks {
+                debug!("checking the argument");
+                let mut formal_ty = *formal_tys.get(i);
+
+                match deref_args {
+                    DoDerefArgs => {
+                        match ty::get(formal_ty).sty {
+                            ty::ty_rptr(_, mt) => formal_ty = mt.ty,
+                            ty::ty_err => (),
+                            _ => {
+                                // So we hit this case when one implements the
+                                // operator traits but leaves an argument as
+                                // just T instead of &T. We'll catch it in the
+                                // mismatch impl/trait method phase no need to
+                                // ICE here.
+                                // See: #11450
+                                formal_ty = ty::mk_err();
+                            }
+                        }
+                    }
+                    DontDerefArgs => {}
+                }
+
+                check_expr_coercable_to_type(fcx, *arg, formal_ty);
+
+            }
+        }
+    }
+
+    // We also need to make sure we at least write the ty of the other
+    // arguments which we skipped above.
+    if variadic {
+        for arg in args.iter().skip(expected_arg_count) {
+            check_expr(fcx, *arg);
+
+            // There are a few types which get autopromoted when passed via varargs
+            // in C but we just error out instead and require explicit casts.
+            let arg_ty = structurally_resolved_type(fcx, arg.span, fcx.expr_ty(*arg));
+            match ty::get(arg_ty).sty {
+                ty::ty_float(ast::TyF32) => {
+                    fcx.type_error_message(arg.span,
+                                           |t| {
+                        format!("can't pass an {} to variadic \
+                                 function, cast to c_double", t)
+                    }, arg_ty, None);
+                }
+                ty::ty_int(ast::TyI8) | ty::ty_int(ast::TyI16) | ty::ty_bool => {
+                    fcx.type_error_message(arg.span, |t| {
+                        format!("can't pass {} to variadic \
+                                 function, cast to c_int",
+                                       t)
+                    }, arg_ty, None);
+                }
+                ty::ty_uint(ast::TyU8) | ty::ty_uint(ast::TyU16) => {
+                    fcx.type_error_message(arg.span, |t| {
+                        format!("can't pass {} to variadic \
+                                 function, cast to c_uint",
+                                       t)
+                    }, arg_ty, None);
+                }
+                _ => {}
+            }
+        }
+    }
+}
+
+fn err_args(len: uint) -> Vec<ty::t> {
+    Vec::from_fn(len, |_| ty::mk_err())
+}
+
+fn write_call(fcx: &FnCtxt, call_expr: &ast::Expr, output: ty::t) {
+    fcx.write_ty(call_expr.id, output);
+}
+
 // AST fragment checking
 pub fn check_lit(fcx: &FnCtxt, lit: &ast::Lit) -> ty::t {
     let tcx = fcx.ccx.tcx;
@@ -1521,6 +1831,28 @@ pub enum DerefArgs {
     DoDerefArgs
 }
 
+/// Controls whether the arguments are tupled. This is used for the call
+/// operator.
+///
+/// Tupling means that all call-side arguments are packed into a tuple and
+/// passed as a single parameter. For example, if tupling is enabled, this
+/// function:
+///
+///     fn f(x: (int, int))
+///
+/// Can be called as:
+///
+///     f(1, 2);
+///
+/// Instead of:
+///
+///     f((1, 2));
+#[deriving(Clone, Eq, PartialEq)]
+enum TupleArgumentsFlag {
+    DontTupleArguments,
+    TupleArguments,
+}
+
 // Given the provenance of a static method, returns the generics of the static
 // method's container.
 fn generics_of_static_method_container(type_context: &ty::ctxt,
@@ -1704,207 +2036,11 @@ fn check_expr_with_unifier(fcx: &FnCtxt,
                            unifier: ||) {
     debug!(">> typechecking");
 
-    fn check_method_argument_types(
-        fcx: &FnCtxt,
-        sp: Span,
-        method_fn_ty: ty::t,
-        callee_expr: &ast::Expr,
-        args: &[@ast::Expr],
-        deref_args: DerefArgs) -> ty::t {
-        // HACK(eddyb) ignore provided self (it has special typeck rules).
-        let args = args.slice_from(1);
-        if ty::type_is_error(method_fn_ty) {
-            let err_inputs = err_args(args.len());
-            check_argument_types(fcx, sp, err_inputs.as_slice(), callee_expr,
-                                 args, deref_args, false);
-            method_fn_ty
-        } else {
-            match ty::get(method_fn_ty).sty {
-                ty::ty_bare_fn(ref fty) => {
-                    // HACK(eddyb) ignore self in the definition (see above).
-                    check_argument_types(fcx, sp, fty.sig.inputs.slice_from(1),
-                                         callee_expr, args, deref_args,
-                                         fty.sig.variadic);
-                    fty.sig.output
-                }
-                _ => {
-                    fcx.tcx().sess.span_bug(callee_expr.span,
-                                            "method without bare fn type");
-                }
-            }
-        }
-    }
-
-    fn check_argument_types(fcx: &FnCtxt,
-                            sp: Span,
-                            fn_inputs: &[ty::t],
-                            callee_expr: &ast::Expr,
-                            args: &[@ast::Expr],
-                            deref_args: DerefArgs,
-                            variadic: bool) {
-        /*!
-         *
-         * Generic function that factors out common logic from
-         * function calls, method calls and overloaded operators.
-         */
-
-        let tcx = fcx.ccx.tcx;
-
-        // Grab the argument types, supplying fresh type variables
-        // if the wrong number of arguments were supplied
-        let supplied_arg_count = args.len();
-        let expected_arg_count = fn_inputs.len();
-        let formal_tys = if expected_arg_count == supplied_arg_count {
-            fn_inputs.iter().map(|a| *a).collect()
-        } else if variadic {
-            if supplied_arg_count >= expected_arg_count {
-                fn_inputs.iter().map(|a| *a).collect()
-            } else {
-                let msg = format!(
-                    "this function takes at least {nexpected, plural, =1{# parameter} \
-                                                                   other{# parameters}} \
-                     but {nsupplied, plural, =1{# parameter was} \
-                                          other{# parameters were}} supplied",
-                     nexpected = expected_arg_count,
-                     nsupplied = supplied_arg_count);
-
-                tcx.sess.span_err(sp, msg.as_slice());
-
-                err_args(supplied_arg_count)
-            }
-        } else {
-            let msg = format!(
-                "this function takes {nexpected, plural, =1{# parameter} \
-                                                      other{# parameters}} \
-                 but {nsupplied, plural, =1{# parameter was} \
-                                      other{# parameters were}} supplied",
-                 nexpected = expected_arg_count,
-                 nsupplied = supplied_arg_count);
-
-            tcx.sess.span_err(sp, msg.as_slice());
-
-            err_args(supplied_arg_count)
-        };
-
-        debug!("check_argument_types: formal_tys={:?}",
-               formal_tys.iter().map(|t| fcx.infcx().ty_to_str(*t)).collect::<Vec<String>>());
-
-        // Check the arguments.
-        // We do this in a pretty awful way: first we typecheck any arguments
-        // that are not anonymous functions, then we typecheck the anonymous
-        // functions. This is so that we have more information about the types
-        // of arguments when we typecheck the functions. This isn't really the
-        // right way to do this.
-        let xs = [false, true];
-        for check_blocks in xs.iter() {
-            let check_blocks = *check_blocks;
-            debug!("check_blocks={}", check_blocks);
-
-            // More awful hacks: before we check the blocks, try to do
-            // an "opportunistic" vtable resolution of any trait
-            // bounds on the call.
-            if check_blocks {
-                vtable::early_resolve_expr(callee_expr, fcx, true);
-            }
-
-            // For variadic functions, we don't have a declared type for all of
-            // the arguments hence we only do our usual type checking with
-            // the arguments who's types we do know.
-            let t = if variadic {
-                expected_arg_count
-            } else {
-                supplied_arg_count
-            };
-            for (i, arg) in args.iter().take(t).enumerate() {
-                let is_block = match arg.node {
-                    ast::ExprFnBlock(..) |
-                    ast::ExprProc(..) => true,
-                    _ => false
-                };
-
-                if is_block == check_blocks {
-                    debug!("checking the argument");
-                    let mut formal_ty = *formal_tys.get(i);
-
-                    match deref_args {
-                        DoDerefArgs => {
-                            match ty::get(formal_ty).sty {
-                                ty::ty_rptr(_, mt) => formal_ty = mt.ty,
-                                ty::ty_err => (),
-                                _ => {
-                                    // So we hit this case when one implements the
-                                    // operator traits but leaves an argument as
-                                    // just T instead of &T. We'll catch it in the
-                                    // mismatch impl/trait method phase no need to
-                                    // ICE here.
-                                    // See: #11450
-                                    formal_ty = ty::mk_err();
-                                }
-                            }
-                        }
-                        DontDerefArgs => {}
-                    }
-
-                    check_expr_coercable_to_type(fcx, *arg, formal_ty);
-
-                }
-            }
-        }
-
-        // We also need to make sure we at least write the ty of the other
-        // arguments which we skipped above.
-        if variadic {
-            for arg in args.iter().skip(expected_arg_count) {
-                check_expr(fcx, *arg);
-
-                // There are a few types which get autopromoted when passed via varargs
-                // in C but we just error out instead and require explicit casts.
-                let arg_ty = structurally_resolved_type(fcx, arg.span, fcx.expr_ty(*arg));
-                match ty::get(arg_ty).sty {
-                    ty::ty_float(ast::TyF32) => {
-                        fcx.type_error_message(arg.span,
-                                               |t| {
-                            format!("can't pass an {} to variadic \
-                                     function, cast to c_double", t)
-                        }, arg_ty, None);
-                    }
-                    ty::ty_int(ast::TyI8) | ty::ty_int(ast::TyI16) | ty::ty_bool => {
-                        fcx.type_error_message(arg.span, |t| {
-                            format!("can't pass {} to variadic \
-                                     function, cast to c_int",
-                                           t)
-                        }, arg_ty, None);
-                    }
-                    ty::ty_uint(ast::TyU8) | ty::ty_uint(ast::TyU16) => {
-                        fcx.type_error_message(arg.span, |t| {
-                            format!("can't pass {} to variadic \
-                                     function, cast to c_uint",
-                                           t)
-                        }, arg_ty, None);
-                    }
-                    _ => {}
-                }
-            }
-        }
-    }
-
-    fn err_args(len: uint) -> Vec<ty::t> {
-        Vec::from_fn(len, |_| ty::mk_err())
-    }
-
-    fn write_call(fcx: &FnCtxt, call_expr: &ast::Expr, output: ty::t) {
-        fcx.write_ty(call_expr.id, output);
-    }
-
     // A generic function for doing all of the checking for call expressions
     fn check_call(fcx: &FnCtxt,
                   call_expr: &ast::Expr,
                   f: &ast::Expr,
                   args: &[@ast::Expr]) {
-        // Index expressions need to be handled separately, to inform them
-        // that they appear in call position.
-        check_expr(fcx, f);
-
         // Store the type of `f` as the type of the callee
         let fn_ty = fcx.expr_ty(f);
 
@@ -1939,8 +2075,14 @@ fn check_call(fcx: &FnCtxt,
         });
 
         // Call the generic checker.
-        check_argument_types(fcx, call_expr.span, fn_sig.inputs.as_slice(), f,
-                             args, DontDerefArgs, fn_sig.variadic);
+        check_argument_types(fcx,
+                             call_expr.span,
+                             fn_sig.inputs.as_slice(),
+                             f,
+                             args,
+                             DontDerefArgs,
+                             fn_sig.variadic,
+                             DontTupleArguments);
 
         write_call(fcx, call_expr, fn_sig.output);
     }
@@ -2008,9 +2150,13 @@ fn check_method_call(fcx: &FnCtxt,
         };
 
         // Call the generic checker.
-        let ret_ty = check_method_argument_types(fcx, method_name.span,
-                                                 fn_ty, expr, args,
-                                                 DontDerefArgs);
+        let ret_ty = check_method_argument_types(fcx,
+                                                 method_name.span,
+                                                 fn_ty,
+                                                 expr,
+                                                 args,
+                                                 DontDerefArgs,
+                                                 DontTupleArguments);
 
         write_call(fcx, expr, ret_ty);
     }
@@ -2078,18 +2224,26 @@ fn lookup_op_method(fcx: &FnCtxt,
                 // HACK(eddyb) Fully qualified path to work around a resolve bug.
                 let method_call = ::middle::typeck::MethodCall::expr(op_ex.id);
                 fcx.inh.method_map.borrow_mut().insert(method_call, method);
-                check_method_argument_types(fcx, op_ex.span,
-                                            method_ty, op_ex,
-                                            args, DoDerefArgs)
+                check_method_argument_types(fcx,
+                                            op_ex.span,
+                                            method_ty,
+                                            op_ex,
+                                            args,
+                                            DoDerefArgs,
+                                            DontTupleArguments)
             }
             None => {
                 unbound_method();
                 // Check the args anyway
                 // so we get all the error messages
                 let expected_ty = ty::mk_err();
-                check_method_argument_types(fcx, op_ex.span,
-                                            expected_ty, op_ex,
-                                            args, DoDerefArgs);
+                check_method_argument_types(fcx,
+                                            op_ex.span,
+                                            expected_ty,
+                                            op_ex,
+                                            args,
+                                            DoDerefArgs,
+                                            DontTupleArguments);
                 ty::mk_err()
             }
         }
@@ -3045,19 +3199,25 @@ fn check_struct_enum_variant(fcx: &FnCtxt,
         fcx.write_ty(id, fcx.node_ty(b.id));
       }
       ast::ExprCall(f, ref args) => {
-          check_call(fcx, expr, f, args.as_slice());
+          // Index expressions need to be handled separately, to inform them
+          // that they appear in call position.
+          check_expr(fcx, f);
           let f_ty = fcx.expr_ty(f);
-          let (args_bot, args_err) = args.iter().fold((false, false),
-             |(rest_bot, rest_err), a| {
-                 // is this not working?
-                 let a_ty = fcx.expr_ty(*a);
-                 (rest_bot || ty::type_is_bot(a_ty),
-                  rest_err || ty::type_is_error(a_ty))});
-          if ty::type_is_error(f_ty) || args_err {
-              fcx.write_error(id);
-          }
-          else if ty::type_is_bot(f_ty) || args_bot {
-              fcx.write_bot(id);
+
+          if !try_overloaded_call(fcx, expr, f, f_ty, args.as_slice()) {
+              check_call(fcx, expr, f, args.as_slice());
+              let (args_bot, args_err) = args.iter().fold((false, false),
+                 |(rest_bot, rest_err), a| {
+                     // is this not working?
+                     let a_ty = fcx.expr_ty(*a);
+                     (rest_bot || ty::type_is_bot(a_ty),
+                      rest_err || ty::type_is_error(a_ty))});
+              if ty::type_is_error(f_ty) || args_err {
+                  fcx.write_error(id);
+              }
+              else if ty::type_is_bot(f_ty) || args_bot {
+                  fcx.write_bot(id);
+              }
           }
       }
       ast::ExprMethodCall(ident, ref tps, ref args) => {
index e488a946d4a14deae9248086f154691fd8d7c040..d3ddbcf5b38a412dd01627182cc8cd630be72f1b 100644 (file)
@@ -442,13 +442,15 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
 
     match expr.node {
         ast::ExprCall(callee, ref args) => {
-            constrain_callee(rcx, callee.id, expr, callee);
-            constrain_call(rcx,
-                           Some(callee.id),
-                           expr,
-                           None,
-                           args.as_slice(),
-                           false);
+            if !has_method_map {
+                constrain_callee(rcx, callee.id, expr, callee);
+                constrain_call(rcx,
+                               Some(callee.id),
+                               expr,
+                               None,
+                               args.as_slice(),
+                               false);
+            }
 
             visit::walk_expr(rcx, expr, ());
         }
diff --git a/src/test/compile-fail/overloaded-calls-bad.rs b/src/test/compile-fail/overloaded-calls-bad.rs
new file mode 100644 (file)
index 0000000..3c03c87
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(overloaded_calls)]
+
+use std::ops::FnMut;
+
+struct S {
+    x: int,
+    y: int,
+}
+
+impl FnMut<(int,),int> for S {
+    fn call_mut(&mut self, (z,): (int,)) -> int {
+        self.x * self.y * z
+    }
+}
+
+fn main() {
+    let mut s = S {
+        x: 3,
+        y: 3,
+    };
+    let ans = s("what");    //~ ERROR mismatched types
+    let ans = s();  //~ ERROR this function takes 1 parameter but 0 parameters were supplied
+    let ans = s("burma", "shave");
+    //~^ ERROR this function takes 1 parameter but 2 parameters were supplied
+}
diff --git a/src/test/compile-fail/overloaded-calls-nontuple.rs b/src/test/compile-fail/overloaded-calls-nontuple.rs
new file mode 100644 (file)
index 0000000..9bbc4ab
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(overloaded_calls)]
+
+use std::ops::FnMut;
+
+struct S {
+    x: int,
+    y: int,
+}
+
+impl FnMut<int,int> for S {
+    fn call_mut(&mut self, z: int) -> int {
+        self.x + self.y + z
+    }
+}
+
+fn main() {
+    let mut s = S {
+        x: 1,
+        y: 2,
+    };
+    drop(s(3))  //~ ERROR cannot use call notation
+}
+
diff --git a/src/test/run-pass/overloaded-calls-simple.rs b/src/test/run-pass/overloaded-calls-simple.rs
new file mode 100644 (file)
index 0000000..33120de
--- /dev/null
@@ -0,0 +1,70 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(overloaded_calls)]
+
+use std::ops::{Fn, FnMut, FnOnce};
+
+struct S1 {
+    x: int,
+    y: int,
+}
+
+impl FnMut<(int,),int> for S1 {
+    fn call_mut(&mut self, (z,): (int,)) -> int {
+        self.x * self.y * z
+    }
+}
+
+struct S2 {
+    x: int,
+    y: int,
+}
+
+impl Fn<(int,),int> for S2 {
+    fn call(&self, (z,): (int,)) -> int {
+        self.x * self.y * z
+    }
+}
+
+struct S3 {
+    x: int,
+    y: int,
+}
+
+impl FnOnce<(int,int),int> for S3 {
+    fn call_once(self, (z,zz): (int,int)) -> int {
+        self.x * self.y * z * zz
+    }
+}
+
+fn main() {
+    let mut s = S1 {
+        x: 3,
+        y: 3,
+    };
+    let ans = s(3);
+    assert_eq!(ans, 27);
+
+    let s = S2 {
+        x: 3,
+        y: 3,
+    };
+    let ans = s(3);
+    assert_eq!(ans, 27);
+
+    let s = S3 {
+        x: 3,
+        y: 3,
+    };
+    let ans = s(3, 1);
+    assert_eq!(ans, 27);
+}
+
diff --git a/src/test/run-pass/overloaded-calls-zero-args.rs b/src/test/run-pass/overloaded-calls-zero-args.rs
new file mode 100644 (file)
index 0000000..f8f7df6
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(overloaded_calls)]
+
+use std::ops::{FnMut};
+
+struct S {
+    x: int,
+    y: int,
+}
+
+impl FnMut<(),int> for S {
+    fn call_mut(&mut self, (): ()) -> int {
+        self.x * self.y
+    }
+}
+
+fn main() {
+    let mut s = S {
+        x: 3,
+        y: 3,
+    };
+    let ans = s();
+    assert_eq!(ans, 9);
+}
+
+