librustc: Implement overloading for the call operator behind a feature
gate. This is part of unboxed closures.
This commit is contained in:
parent
e55f64f997
commit
966c7346ca
@ -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;
|
||||
|
@ -56,6 +56,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
|
||||
("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 @@ impl 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"));
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ use middle::def;
|
||||
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 @@ impl<'d,'t,TYPER:mc::Typer> ExprUseVisitor<'d,'t,TYPER> {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,6 +240,10 @@ lets_do_this! {
|
||||
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;
|
||||
|
||||
|
@ -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 @@ impl<'a> Liveness<'a> {
|
||||
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)
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ use lib;
|
||||
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::{AutoBorrowObj, AutoDerefRef, AutoAddEnv, AutoObject, AutoUnsafe
|
||||
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,
|
||||
|
@ -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_expr_with_unifier(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_expr_with_unifier(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 check_expr_with_unifier(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_expr_with_unifier(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) => {
|
||||
|
@ -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, ());
|
||||
}
|
||||
|
35
src/test/compile-fail/overloaded-calls-bad.rs
Normal file
35
src/test/compile-fail/overloaded-calls-bad.rs
Normal file
@ -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
|
||||
}
|
33
src/test/compile-fail/overloaded-calls-nontuple.rs
Normal file
33
src/test/compile-fail/overloaded-calls-nontuple.rs
Normal file
@ -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
|
||||
}
|
||||
|
70
src/test/run-pass/overloaded-calls-simple.rs
Normal file
70
src/test/run-pass/overloaded-calls-simple.rs
Normal file
@ -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);
|
||||
}
|
||||
|
35
src/test/run-pass/overloaded-calls-zero-args.rs
Normal file
35
src/test/run-pass/overloaded-calls-zero-args.rs
Normal file
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user