use super::check_expr;
use super::check_method_argument_types;
use super::err_args;
+use super::Expectation;
+use super::expected_types_for_fn_args;
use super::FnCtxt;
use super::LvaluePreference;
use super::method;
pub fn check_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
callee_expr: &ast::Expr,
- arg_exprs: &[P<ast::Expr>])
+ arg_exprs: &[P<ast::Expr>],
+ expected: Expectation<'tcx>)
{
check_expr(fcx, callee_expr);
let original_callee_ty = fcx.expr_ty(callee_expr);
match result {
None => {
// this will report an error since original_callee_ty is not a fn
- confirm_builtin_call(fcx, call_expr, original_callee_ty, arg_exprs);
+ confirm_builtin_call(fcx, call_expr, original_callee_ty, arg_exprs, expected);
}
Some(CallStep::Builtin) => {
- confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs);
+ confirm_builtin_call(fcx, call_expr, callee_ty, arg_exprs, expected);
}
Some(CallStep::Overloaded(method_callee)) => {
- confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee);
+ confirm_overloaded_call(fcx, call_expr, arg_exprs, method_callee, expected);
}
}
}
fn confirm_builtin_call<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
call_expr: &ast::Expr,
callee_ty: Ty<'tcx>,
- arg_exprs: &[P<ast::Expr>])
+ arg_exprs: &[P<ast::Expr>],
+ expected: Expectation<'tcx>)
{
let error_fn_sig;
fcx.normalize_associated_types_in(call_expr.span, &fn_sig);
// Call the generic checker.
+ let expected_arg_tys = expected_types_for_fn_args(fcx,
+ call_expr.span,
+ expected,
+ fn_sig.output,
+ fn_sig.inputs.as_slice());
check_argument_types(fcx,
call_expr.span,
fn_sig.inputs.as_slice(),
+ &expected_arg_tys[],
arg_exprs,
AutorefArgs::No,
fn_sig.variadic,
fn confirm_overloaded_call<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>,
call_expr: &ast::Expr,
arg_exprs: &[P<ast::Expr>],
- method_callee: ty::MethodCallee<'tcx>)
+ method_callee: ty::MethodCallee<'tcx>,
+ expected: Expectation<'tcx>)
{
let output_type = check_method_argument_types(fcx,
call_expr.span,
call_expr,
arg_exprs,
AutorefArgs::No,
- TupleArgumentsFlag::TupleArguments);
+ TupleArgumentsFlag::TupleArguments,
+ expected);
let method_call = ty::MethodCall::expr(call_expr.id);
fcx.inh.method_map.borrow_mut().insert(method_call, method_callee);
write_call(fcx, call_expr, output_type);
iterator_expr,
&[],
AutorefArgs::No,
- DontTupleArguments);
+ DontTupleArguments,
+ NoExpectation);
match method {
Some(method) => {
callee_expr: &ast::Expr,
args_no_rcvr: &[P<ast::Expr>],
autoref_args: AutorefArgs,
- tuple_arguments: TupleArgumentsFlag)
+ tuple_arguments: TupleArgumentsFlag,
+ expected: Expectation<'tcx>)
-> ty::FnOutput<'tcx> {
if ty::type_is_error(method_fn_ty) {
let err_inputs = err_args(fcx.tcx(), args_no_rcvr.len());
check_argument_types(fcx,
sp,
&err_inputs[],
+ &[],
args_no_rcvr,
autoref_args,
false,
match method_fn_ty.sty {
ty::ty_bare_fn(_, ref fty) => {
// HACK(eddyb) ignore self in the definition (see above).
+ let expected_arg_tys = expected_types_for_fn_args(fcx,
+ sp,
+ expected,
+ fty.sig.0.output,
+ &fty.sig.0.inputs[1..]);
check_argument_types(fcx,
sp,
&fty.sig.0.inputs[1..],
+ &expected_arg_tys[],
args_no_rcvr,
autoref_args,
fty.sig.0.variadic,
fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
sp: Span,
fn_inputs: &[Ty<'tcx>],
+ expected_arg_tys: &[Ty<'tcx>],
args: &[P<ast::Expr>],
autoref_args: AutorefArgs,
variadic: bool,
1
};
+ let mut expected_arg_tys = expected_arg_tys;
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]);
if arg_types.len() == 1 {""} else {"s"},
args.len(),
if args.len() == 1 {" was"} else {"s were"});
+ expected_arg_tys = &[][];
err_args(fcx.tcx(), args.len())
} else {
+ expected_arg_tys = match expected_arg_tys.get(0) {
+ Some(&ty) => match ty.sty {
+ ty::ty_tup(ref tys) => &**tys,
+ _ => &[]
+ },
+ None => &[]
+ };
(*arg_types).clone()
}
}
span_err!(tcx.sess, sp, E0059,
"cannot use call notation; the first type parameter \
for the function trait is neither a tuple nor unit");
+ expected_arg_tys = &[][];
err_args(fcx.tcx(), args.len())
}
}
} else if expected_arg_count == supplied_arg_count {
- fn_inputs.iter().map(|a| *a).collect()
+ fn_inputs.to_vec()
} else if variadic {
if supplied_arg_count >= expected_arg_count {
- fn_inputs.iter().map(|a| *a).collect()
+ fn_inputs.to_vec()
} else {
span_err!(tcx.sess, sp, E0060,
"this function takes at least {} parameter{} \
if expected_arg_count == 1 {""} else {"s"},
supplied_arg_count,
if supplied_arg_count == 1 {" was"} else {"s were"});
+ expected_arg_tys = &[][];
err_args(fcx.tcx(), supplied_arg_count)
}
} else {
if expected_arg_count == 1 {""} else {"s"},
supplied_arg_count,
if supplied_arg_count == 1 {" was"} else {"s were"});
+ expected_arg_tys = &[][];
err_args(fcx.tcx(), supplied_arg_count)
};
AutorefArgs::No => {}
}
- check_expr_coercable_to_type(fcx, &**arg, formal_ty);
+ // The special-cased logic below has three functions:
+ // 1. Provide as good of an expected type as possible.
+ let expected = expected_arg_tys.get(i).map(|&ty| {
+ Expectation::rvalue_hint(ty)
+ });
+
+ check_expr_with_unifier(fcx, &**arg,
+ expected.unwrap_or(ExpectHasType(formal_ty)),
+ NoPreference, || {
+ // 2. Coerce to the most detailed type that could be coerced
+ // to, which is `expected_ty` if `rvalue_hint` returns an
+ // `ExprHasType(expected_ty)`, or the `formal_ty` otherwise.
+ let coerce_ty = expected.and_then(|e| e.only_has_type(fcx));
+ demand::coerce(fcx, arg.span, coerce_ty.unwrap_or(formal_ty), &**arg);
+
+ // 3. Relate the expected type and the formal one,
+ // if the expected type was used for the coercion.
+ coerce_ty.map(|ty| demand::suptype(fcx, arg.span, formal_ty, ty));
+ });
}
}
}
TupleArguments,
}
+/// Unifies the return type with the expected type early, for more coercions
+/// and forward type information on the argument expressions.
+fn expected_types_for_fn_args<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+ call_span: Span,
+ expected_ret: Expectation<'tcx>,
+ formal_ret: ty::FnOutput<'tcx>,
+ formal_args: &[Ty<'tcx>])
+ -> Vec<Ty<'tcx>> {
+ let expected_args = expected_ret.only_has_type(fcx).and_then(|ret_ty| {
+ if let ty::FnConverging(formal_ret_ty) = formal_ret {
+ fcx.infcx().commit_regions_if_ok(|| {
+ // Attempt to apply a subtyping relationship between the formal
+ // return type (likely containing type variables if the function
+ // is polymorphic) and the expected return type.
+ // No argument expectations are produced if unification fails.
+ let origin = infer::Misc(call_span);
+ let ures = fcx.infcx().sub_types(false, origin, formal_ret_ty, ret_ty);
+ // FIXME(#15760) can't use try! here, FromError doesn't default
+ // to identity so the resulting type is not constrained.
+ if let Err(e) = ures {
+ return Err(e);
+ }
+
+ // Record all the argument types, with the substitutions
+ // produced from the above subtyping unification.
+ Ok(formal_args.iter().map(|ty| {
+ fcx.infcx().resolve_type_vars_if_possible(ty)
+ }).collect())
+ }).ok()
+ } else {
+ None
+ }
+ }).unwrap_or(vec![]);
+ debug!("expected_types_for_fn_args(formal={} -> {}, expected={} -> {})",
+ formal_args.repr(fcx.tcx()), formal_ret.repr(fcx.tcx()),
+ expected_args.repr(fcx.tcx()), expected_ret.repr(fcx.tcx()));
+ expected_args
+}
+
/// Invariant:
/// If an expression has any sub-expressions that result in a type error,
/// inspecting that expression's type with `ty::type_is_error` will return
expr.repr(fcx.tcx()), expected.repr(fcx.tcx()));
// Checks a method call.
- fn check_method_call(fcx: &FnCtxt,
- expr: &ast::Expr,
- method_name: ast::SpannedIdent,
- args: &[P<ast::Expr>],
- tps: &[P<ast::Ty>],
- lvalue_pref: LvaluePreference) {
+ fn check_method_call<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
+ expr: &ast::Expr,
+ method_name: ast::SpannedIdent,
+ args: &[P<ast::Expr>],
+ tps: &[P<ast::Ty>],
+ expected: Expectation<'tcx>,
+ lvalue_pref: LvaluePreference) {
let rcvr = &*args[0];
check_expr_with_lvalue_pref(fcx, &*rcvr, lvalue_pref);
expr,
&args[1..],
AutorefArgs::No,
- DontTupleArguments);
+ DontTupleArguments,
+ expected);
write_call(fcx, expr, ret_ty);
}
op_ex,
args,
autoref_args,
- DontTupleArguments) {
+ DontTupleArguments,
+ NoExpectation) {
ty::FnConverging(result_type) => result_type,
ty::FnDiverging => fcx.tcx().types.err
}
op_ex,
args,
autoref_args,
- DontTupleArguments);
+ DontTupleArguments,
+ NoExpectation);
fcx.tcx().types.err
}
}
fcx.write_ty(id, fcx.node_ty(b.id));
}
ast::ExprCall(ref callee, ref args) => {
- callee::check_call(fcx, expr, &**callee, &args[]);
+ callee::check_call(fcx, expr, &**callee, &args[], expected);
}
ast::ExprMethodCall(ident, ref tps, ref args) => {
- check_method_call(fcx, expr, ident, &args[], &tps[], lvalue_pref);
+ check_method_call(fcx, expr, ident, &args[], &tps[], expected, lvalue_pref);
let arg_tys = args.iter().map(|a| fcx.expr_ty(&**a));
let args_err = arg_tys.fold(false,
|rest_err, a| {