From 966c7346cae89cf47017bfbf27e383d0b0351d82 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 1 Jun 2014 16:35:01 -0700 Subject: [PATCH] librustc: Implement overloading for the call operator behind a feature gate. This is part of unboxed closures. --- src/libcore/ops.rs | 21 + src/librustc/front/feature_gate.rs | 4 + src/librustc/middle/expr_use_visitor.rs | 19 +- src/librustc/middle/lang_items.rs | 4 + src/librustc/middle/liveness.rs | 13 +- src/librustc/middle/trans/expr.rs | 87 ++- src/librustc/middle/typeck/check/mod.rs | 598 +++++++++++------- src/librustc/middle/typeck/check/regionck.rs | 16 +- src/test/compile-fail/overloaded-calls-bad.rs | 35 + .../compile-fail/overloaded-calls-nontuple.rs | 33 + src/test/run-pass/overloaded-calls-simple.rs | 70 ++ .../run-pass/overloaded-calls-zero-args.rs | 35 + 12 files changed, 701 insertions(+), 234 deletions(-) create mode 100644 src/test/compile-fail/overloaded-calls-bad.rs create mode 100644 src/test/compile-fail/overloaded-calls-nontuple.rs create mode 100644 src/test/run-pass/overloaded-calls-simple.rs create mode 100644 src/test/run-pass/overloaded-calls-zero-args.rs diff --git a/src/libcore/ops.rs b/src/libcore/ops.rs index 08e033f961f..af1df973a3e 100644 --- a/src/libcore/ops.rs +++ b/src/libcore/ops.rs @@ -726,6 +726,27 @@ pub trait DerefMut: Deref { 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 { + /// 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 { + /// 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 { + /// This is called when the call operator is used. + fn call_once(self, args: Args) -> Result; +} + #[cfg(test)] mod bench { extern crate test; diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index 25f0dc808c8..5edee5ea2ef 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -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, pub quad_precision_float: Cell, pub issue_5723_bootstrap: Cell, + pub overloaded_calls: Cell, } 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")); } diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index c44ea0ae78b..540dfdab19e 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -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()); + } + } } } } diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 748c29cd92c..20e4188a8f6 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -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; diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 8b1de130053..278d5d8187b 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -105,6 +105,7 @@ 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) } diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 9f90de61cfe..b21877e4fa0 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -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, + args: &[@ast::Expr], + dest: Option) + -> &'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, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index d25fc9cc5bc..1463cf9602d 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -1353,6 +1353,61 @@ pub fn autoderef(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, @@ -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::>()); + + // 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 { + 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::>()); - - // 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 { - 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) => { diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index e488a946d4a..d3ddbcf5b38 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -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 index 00000000000..3c03c874757 --- /dev/null +++ b/src/test/compile-fail/overloaded-calls-bad.rs @@ -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 or the MIT license +// , 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 index 00000000000..9bbc4ab3ba3 --- /dev/null +++ b/src/test/compile-fail/overloaded-calls-nontuple.rs @@ -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 or the MIT license +// , 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 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 index 00000000000..33120defedd --- /dev/null +++ b/src/test/run-pass/overloaded-calls-simple.rs @@ -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 or the MIT license +// , 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 index 00000000000..f8f7df6b49b --- /dev/null +++ b/src/test/run-pass/overloaded-calls-zero-args.rs @@ -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 or the MIT license +// , 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); +} + + -- 2.44.0