blob: 7a71cf57a2f2784f3184feee8bdfaaed90d69441 [file] [log] [blame]
// Copyright 2014 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.
use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
use super::autoderef::Autoderef;
use super::method::MethodCallee;
use hir::def::Def;
use hir::def_id::{DefId, LOCAL_CRATE};
use rustc::{infer, traits};
use rustc::ty::{self, TyCtxt, TypeFoldable, Ty};
use rustc::ty::adjustment::{Adjustment, Adjust, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc_target::spec::abi;
use syntax::ast::Ident;
use syntax_pos::Span;
use errors::Applicability;
use rustc::hir;
/// Check that it is legal to call methods of the trait corresponding
/// to `trait_id` (this only cares about the trait, not the specific
/// method that is called)
pub fn check_legal_trait_for_method_call(tcx: TyCtxt, span: Span, trait_id: DefId) {
if tcx.lang_items().drop_trait() == Some(trait_id) {
struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method")
.span_label(span, "explicit destructor calls not allowed")
.emit();
}
}
enum CallStep<'tcx> {
Builtin(Ty<'tcx>),
DeferredClosure(ty::FnSig<'tcx>),
/// e.g. enum variant constructors
Overloaded(MethodCallee<'tcx>),
}
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn check_call(&self,
call_expr: &'gcx hir::Expr,
callee_expr: &'gcx hir::Expr,
arg_exprs: &'gcx [hir::Expr],
expected: Expectation<'tcx>)
-> Ty<'tcx> {
let original_callee_ty = self.check_expr(callee_expr);
let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty);
let mut autoderef = self.autoderef(callee_expr.span, expr_ty);
let mut result = None;
while result.is_none() && autoderef.next().is_some() {
result = self.try_overloaded_call_step(call_expr, callee_expr, &autoderef);
}
autoderef.finalize();
let output = match result {
None => {
// this will report an error since original_callee_ty is not a fn
self.confirm_builtin_call(call_expr, original_callee_ty, arg_exprs, expected)
}
Some(CallStep::Builtin(callee_ty)) => {
self.confirm_builtin_call(call_expr, callee_ty, arg_exprs, expected)
}
Some(CallStep::DeferredClosure(fn_sig)) => {
self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, fn_sig)
}
Some(CallStep::Overloaded(method_callee)) => {
self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee)
}
};
// we must check that return type of called functions is WF:
self.register_wf_obligation(output, call_expr.span, traits::MiscObligation);
output
}
fn try_overloaded_call_step(&self,
call_expr: &'gcx hir::Expr,
callee_expr: &'gcx hir::Expr,
autoderef: &Autoderef<'a, 'gcx, 'tcx>)
-> Option<CallStep<'tcx>> {
let adjusted_ty = autoderef.unambiguous_final_ty();
debug!("try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})",
call_expr,
adjusted_ty);
// If the callee is a bare function or a closure, then we're all set.
match adjusted_ty.sty {
ty::FnDef(..) | ty::FnPtr(_) => {
let adjustments = autoderef.adjust_steps(Needs::None);
self.apply_adjustments(callee_expr, adjustments);
return Some(CallStep::Builtin(adjusted_ty));
}
ty::Closure(def_id, substs) => {
assert_eq!(def_id.krate, LOCAL_CRATE);
// Check whether this is a call to a closure where we
// haven't yet decided on whether the closure is fn vs
// fnmut vs fnonce. If so, we have to defer further processing.
if self.closure_kind(def_id, substs).is_none() {
let closure_ty = self.closure_sig(def_id, substs);
let fn_sig = self.replace_bound_vars_with_fresh_vars(
call_expr.span,
infer::FnCall,
&closure_ty
).0;
let adjustments = autoderef.adjust_steps(Needs::None);
self.record_deferred_call_resolution(def_id, DeferredCallResolution {
call_expr,
callee_expr,
adjusted_ty,
adjustments,
fn_sig,
closure_def_id: def_id,
closure_substs: substs,
});
return Some(CallStep::DeferredClosure(fn_sig));
}
}
// Hack: we know that there are traits implementing Fn for &F
// where F:Fn and so forth. In the particular case of types
// like `x: &mut FnMut()`, if there is a call `x()`, we would
// normally translate to `FnMut::call_mut(&mut x, ())`, but
// that winds up requiring `mut x: &mut FnMut()`. A little
// over the top. The simplest fix by far is to just ignore
// this case and deref again, so we wind up with
// `FnMut::call_mut(&mut *x, ())`.
ty::Ref(..) if autoderef.step_count() == 0 => {
return None;
}
_ => {}
}
self.try_overloaded_call_traits(call_expr, adjusted_ty).map(|(autoref, method)| {
let mut adjustments = autoderef.adjust_steps(Needs::None);
adjustments.extend(autoref);
self.apply_adjustments(callee_expr, adjustments);
CallStep::Overloaded(method)
})
}
fn try_overloaded_call_traits(&self,
call_expr: &hir::Expr,
adjusted_ty: Ty<'tcx>)
-> Option<(Option<Adjustment<'tcx>>,
MethodCallee<'tcx>)> {
// Try the options that are least restrictive on the caller first.
for &(opt_trait_def_id, method_name, borrow) in
&[(self.tcx.lang_items().fn_trait(), Ident::from_str("call"), true),
(self.tcx.lang_items().fn_mut_trait(), Ident::from_str("call_mut"), true),
(self.tcx.lang_items().fn_once_trait(), Ident::from_str("call_once"), false)] {
let trait_def_id = match opt_trait_def_id {
Some(def_id) => def_id,
None => continue,
};
if let Some(ok) = self.lookup_method_in_trait(call_expr.span,
method_name,
trait_def_id,
adjusted_ty,
None) {
let method = self.register_infer_ok_obligations(ok);
let mut autoref = None;
if borrow {
if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].sty {
let mutbl = match mutbl {
hir::MutImmutable => AutoBorrowMutability::Immutable,
hir::MutMutable => AutoBorrowMutability::Mutable {
// For initial two-phase borrow
// deployment, conservatively omit
// overloaded function call ops.
allow_two_phase_borrow: AllowTwoPhase::No,
}
};
autoref = Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
target: method.sig.inputs()[0]
});
}
}
return Some((autoref, method));
}
}
None
}
fn confirm_builtin_call(&self,
call_expr: &hir::Expr,
callee_ty: Ty<'tcx>,
arg_exprs: &'gcx [hir::Expr],
expected: Expectation<'tcx>)
-> Ty<'tcx> {
let (fn_sig, def_span) = match callee_ty.sty {
ty::FnDef(def_id, _) => {
(callee_ty.fn_sig(self.tcx), self.tcx.hir.span_if_local(def_id))
}
ty::FnPtr(sig) => (sig, None),
ref t => {
let mut unit_variant = None;
if let &ty::Adt(adt_def, ..) = t {
if adt_def.is_enum() {
if let hir::ExprKind::Call(ref expr, _) = call_expr.node {
unit_variant = Some(self.tcx.hir.node_to_pretty_string(expr.id))
}
}
}
if let hir::ExprKind::Call(ref callee, _) = call_expr.node {
let mut err = type_error_struct!(
self.tcx.sess,
callee.span,
callee_ty,
E0618,
"expected function, found {}",
match unit_variant {
Some(ref path) => format!("enum variant `{}`", path),
None => format!("`{}`", callee_ty),
});
if let Some(ref path) = unit_variant {
err.span_suggestion_with_applicability(
call_expr.span,
&format!("`{}` is a unit variant, you need to write it \
without the parenthesis", path),
path.to_string(),
Applicability::MachineApplicable
);
}
let mut inner_callee_path = None;
let def = match callee.node {
hir::ExprKind::Path(ref qpath) => {
self.tables.borrow().qpath_def(qpath, callee.hir_id)
},
hir::ExprKind::Call(ref inner_callee, _) => {
// If the call spans more than one line and the callee kind is
// itself another `ExprCall`, that's a clue that we might just be
// missing a semicolon (Issue #51055)
let call_is_multiline = self.tcx.sess.source_map()
.is_multiline(call_expr.span);
if call_is_multiline {
let span = self.tcx.sess.source_map().next_point(callee.span);
err.span_suggestion_with_applicability(
span,
"try adding a semicolon",
";".to_owned(),
Applicability::MaybeIncorrect
);
}
if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.node {
inner_callee_path = Some(inner_qpath);
self.tables.borrow().qpath_def(inner_qpath, inner_callee.hir_id)
} else {
Def::Err
}
},
_ => {
Def::Err
}
};
err.span_label(call_expr.span, "call expression requires function");
let def_span = match def {
Def::Err => None,
Def::Local(id) | Def::Upvar(id, ..) => {
Some(self.tcx.hir.span(id))
}
_ => self.tcx.hir.span_if_local(def.def_id())
};
if let Some(span) = def_span {
let label = match (unit_variant, inner_callee_path) {
(Some(path), _) => format!("`{}` defined here", path),
(_, Some(hir::QPath::Resolved(_, path))) => format!(
"`{}` defined here returns `{}`", path, callee_ty.to_string()
),
_ => format!("`{}` defined here", callee_ty.to_string()),
};
err.span_label(span, label);
}
err.emit();
} else {
bug!("call_expr.node should be an ExprKind::Call, got {:?}", call_expr.node);
}
// This is the "default" function signature, used in case of error.
// In that case, we check each argument against "error" in order to
// set up all the node type bindings.
(ty::Binder::bind(self.tcx.mk_fn_sig(
self.err_args(arg_exprs.len()).into_iter(),
self.tcx.types.err,
false,
hir::Unsafety::Normal,
abi::Abi::Rust
)), None)
}
};
// Replace any late-bound regions that appear in the function
// signature with region variables. We also have to
// renormalize the associated types at this point, since they
// previously appeared within a `Binder<>` and hence would not
// have been normalized before.
let fn_sig =
self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, &fn_sig)
.0;
let fn_sig = self.normalize_associated_types_in(call_expr.span, &fn_sig);
// Call the generic checker.
let expected_arg_tys =
self.expected_inputs_for_expected_output(call_expr.span,
expected,
fn_sig.output(),
fn_sig.inputs());
self.check_argument_types(call_expr.span,
call_expr.span,
fn_sig.inputs(),
&expected_arg_tys[..],
arg_exprs,
fn_sig.variadic,
TupleArgumentsFlag::DontTupleArguments,
def_span);
fn_sig.output()
}
fn confirm_deferred_closure_call(&self,
call_expr: &hir::Expr,
arg_exprs: &'gcx [hir::Expr],
expected: Expectation<'tcx>,
fn_sig: ty::FnSig<'tcx>)
-> Ty<'tcx> {
// `fn_sig` is the *signature* of the cosure being called. We
// don't know the full details yet (`Fn` vs `FnMut` etc), but we
// do know the types expected for each argument and the return
// type.
let expected_arg_tys = self.expected_inputs_for_expected_output(call_expr.span,
expected,
fn_sig.output().clone(),
fn_sig.inputs());
self.check_argument_types(call_expr.span,
call_expr.span,
fn_sig.inputs(),
&expected_arg_tys,
arg_exprs,
fn_sig.variadic,
TupleArgumentsFlag::TupleArguments,
None);
fn_sig.output()
}
fn confirm_overloaded_call(&self,
call_expr: &hir::Expr,
arg_exprs: &'gcx [hir::Expr],
expected: Expectation<'tcx>,
method_callee: MethodCallee<'tcx>)
-> Ty<'tcx> {
let output_type = self.check_method_argument_types(call_expr.span,
call_expr.span,
Ok(method_callee),
arg_exprs,
TupleArgumentsFlag::TupleArguments,
expected);
self.write_method_call(call_expr.hir_id, method_callee);
output_type
}
}
#[derive(Debug)]
pub struct DeferredCallResolution<'gcx: 'tcx, 'tcx> {
call_expr: &'gcx hir::Expr,
callee_expr: &'gcx hir::Expr,
adjusted_ty: Ty<'tcx>,
adjustments: Vec<Adjustment<'tcx>>,
fn_sig: ty::FnSig<'tcx>,
closure_def_id: DefId,
closure_substs: ty::ClosureSubsts<'tcx>,
}
impl<'a, 'gcx, 'tcx> DeferredCallResolution<'gcx, 'tcx> {
pub fn resolve(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) {
debug!("DeferredCallResolution::resolve() {:?}", self);
// we should not be invoked until the closure kind has been
// determined by upvar inference
assert!(fcx.closure_kind(self.closure_def_id, self.closure_substs).is_some());
// We may now know enough to figure out fn vs fnmut etc.
match fcx.try_overloaded_call_traits(self.call_expr,
self.adjusted_ty) {
Some((autoref, method_callee)) => {
// One problem is that when we get here, we are going
// to have a newly instantiated function signature
// from the call trait. This has to be reconciled with
// the older function signature we had before. In
// principle we *should* be able to fn_sigs(), but we
// can't because of the annoying need for a TypeTrace.
// (This always bites me, should find a way to
// refactor it.)
let method_sig = method_callee.sig;
debug!("attempt_resolution: method_callee={:?}", method_callee);
for (method_arg_ty, self_arg_ty) in
method_sig.inputs().iter().skip(1).zip(self.fn_sig.inputs()) {
fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty);
}
fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output());
let mut adjustments = self.adjustments;
adjustments.extend(autoref);
fcx.apply_adjustments(self.callee_expr, adjustments);
fcx.write_method_call(self.call_expr.hir_id,
method_callee);
}
None => {
span_bug!(self.call_expr.span,
"failed to find an overloaded call trait for closure call");
}
}
}
}