blob: dfd0b7c2945c4b487c544c77b625070a10164401 [file] [log] [blame]
use super::method::probe::ProbeScope;
use super::method::MethodCallee;
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
use crate::errors;
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{self, CtorKind, Namespace, Res};
use rustc_hir::def_id::DefId;
use rustc_hir_analysis::autoderef::Autoderef;
use rustc_infer::{
infer,
traits::{self, Obligation},
};
use rustc_infer::{infer::type_variable::TypeVariableOrigin, traits::ObligationCause};
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
};
use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_target::spec::abi;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use std::{iter, slice};
/// Checks 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,
receiver: Option<Span>,
expr_span: Span,
trait_id: DefId,
body_id: DefId,
) -> Result<(), ErrorGuaranteed> {
if tcx.lang_items().drop_trait() == Some(trait_id)
&& tcx.lang_items().fallback_surface_drop_fn() != Some(body_id)
{
let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) {
errors::ExplicitDestructorCallSugg::Snippet {
lo: expr_span.shrink_to_lo(),
hi: receiver.shrink_to_hi().to(expr_span.shrink_to_hi()),
}
} else {
errors::ExplicitDestructorCallSugg::Empty(span)
};
return Err(tcx.dcx().emit_err(errors::ExplicitDestructorCall { span, sugg }));
}
tcx.ensure().coherent_trait(trait_id)
}
#[derive(Debug)]
enum CallStep<'tcx> {
Builtin(Ty<'tcx>),
DeferredClosure(LocalDefId, ty::FnSig<'tcx>),
/// E.g., enum variant constructors.
Overloaded(MethodCallee<'tcx>),
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn check_call(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let original_callee_ty = match &callee_expr.kind {
hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self
.check_expr_with_expectation_and_args(
callee_expr,
Expectation::NoExpectation,
arg_exprs,
Some(call_expr),
),
_ => self.check_expr(callee_expr),
};
let expr_ty = self.structurally_resolve_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, arg_exprs, &autoderef);
}
self.register_predicates(autoderef.into_obligations());
let output = match result {
None => {
// this will report an error since original_callee_ty is not a fn
self.confirm_builtin_call(
call_expr,
callee_expr,
original_callee_ty,
arg_exprs,
expected,
)
}
Some(CallStep::Builtin(callee_ty)) => {
self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected)
}
Some(CallStep::DeferredClosure(def_id, fn_sig)) => {
self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, 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.into(), call_expr.span, traits::WellFormed(None));
output
}
#[instrument(level = "debug", skip(self, call_expr, callee_expr, arg_exprs, autoderef), ret)]
fn try_overloaded_call_step(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
autoderef: &Autoderef<'a, 'tcx>,
) -> Option<CallStep<'tcx>> {
let adjusted_ty =
self.structurally_resolve_type(autoderef.span(), autoderef.final_ty(false));
// If the callee is a bare function or a closure, then we're all set.
match *adjusted_ty.kind() {
ty::FnDef(..) | ty::FnPtr(_) => {
let adjustments = self.adjust_steps(autoderef);
self.apply_adjustments(callee_expr, adjustments);
return Some(CallStep::Builtin(adjusted_ty));
}
// 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.
ty::Closure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => {
let def_id = def_id.expect_local();
let closure_sig = args.as_closure().sig();
let closure_sig = self.instantiate_binder_with_fresh_vars(
call_expr.span,
infer::FnCall,
closure_sig,
);
let adjustments = self.adjust_steps(autoderef);
self.record_deferred_call_resolution(
def_id,
DeferredCallResolution {
call_expr,
callee_expr,
closure_ty: adjusted_ty,
adjustments,
fn_sig: closure_sig,
},
);
return Some(CallStep::DeferredClosure(def_id, closure_sig));
}
// When calling a `CoroutineClosure` that is local to the body, we will
// not know what its `closure_kind` is yet. Instead, just fill in the
// signature with an infer var for the `tupled_upvars_ty` of the coroutine,
// and record a deferred call resolution which will constrain that var
// as part of `AsyncFn*` trait confirmation.
ty::CoroutineClosure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => {
let def_id = def_id.expect_local();
let closure_args = args.as_coroutine_closure();
let coroutine_closure_sig = self.instantiate_binder_with_fresh_vars(
call_expr.span,
infer::FnCall,
closure_args.coroutine_closure_sig(),
);
let tupled_upvars_ty = self
.next_ty_var(TypeVariableOrigin { param_def_id: None, span: callee_expr.span });
// We may actually receive a coroutine back whose kind is different
// from the closure that this dispatched from. This is because when
// we have no captures, we automatically implement `FnOnce`. This
// impl forces the closure kind to `FnOnce` i.e. `u8`.
let kind_ty = self
.next_ty_var(TypeVariableOrigin { param_def_id: None, span: callee_expr.span });
let call_sig = self.tcx.mk_fn_sig(
[coroutine_closure_sig.tupled_inputs_ty],
coroutine_closure_sig.to_coroutine(
self.tcx,
closure_args.parent_args(),
kind_ty,
self.tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
),
coroutine_closure_sig.c_variadic,
coroutine_closure_sig.unsafety,
coroutine_closure_sig.abi,
);
let adjustments = self.adjust_steps(autoderef);
self.record_deferred_call_resolution(
def_id,
DeferredCallResolution {
call_expr,
callee_expr,
closure_ty: adjusted_ty,
adjustments,
fn_sig: call_sig,
},
);
return Some(CallStep::DeferredClosure(def_id, call_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 `f: &mut FnMut()`, if there is a call `f()`, we would
// normally translate to `FnMut::call_mut(&mut f, ())`, but
// that winds up potentially requiring the user to mark their
// variable as `mut` which feels unnecessary and unexpected.
//
// fn foo(f: &mut impl FnMut()) { f() }
// ^ without this hack `f` would have to be declared as mutable
//
// The simplest fix by far is to just ignore this case and deref again,
// so we wind up with `FnMut::call_mut(&mut *f, ())`.
ty::Ref(..) if autoderef.step_count() == 0 => {
return None;
}
ty::Error(_) => {
return None;
}
_ => {}
}
// Now, we look for the implementation of a Fn trait on the object's type.
// We first do it with the explicit instruction to look for an impl of
// `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding
// to the number of call parameters.
// If that fails (or_else branch), we try again without specifying the
// shape of the tuple (hence the None). This allows to detect an Fn trait
// is implemented, and use this information for diagnostic.
self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
.or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
.map(|(autoref, method)| {
let mut adjustments = self.adjust_steps(autoderef);
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>,
opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>,
) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> {
// HACK(async_closures): For async closures, prefer `AsyncFn*`
// over `Fn*`, since all async closures implement `FnOnce`, but
// choosing that over `AsyncFn`/`AsyncFnMut` would be more restrictive.
// For other callables, just prefer `Fn*` for perf reasons.
//
// The order of trait choices here is not that big of a deal,
// since it just guides inference (and our choice of autoref).
// Though in the future, I'd like typeck to choose:
// `Fn > AsyncFn > FnMut > AsyncFnMut > FnOnce > AsyncFnOnce`
// ...or *ideally*, we just have `LendingFn`/`LendingFnMut`, which
// would naturally unify these two trait hierarchies in the most
// general way.
let call_trait_choices = if self.shallow_resolve(adjusted_ty).is_coroutine_closure() {
[
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
(self.tcx.lang_items().fn_trait(), sym::call, true),
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
]
} else {
[
(self.tcx.lang_items().fn_trait(), sym::call, true),
(self.tcx.lang_items().fn_mut_trait(), sym::call_mut, true),
(self.tcx.lang_items().fn_once_trait(), sym::call_once, false),
(self.tcx.lang_items().async_fn_trait(), sym::async_call, true),
(self.tcx.lang_items().async_fn_mut_trait(), sym::async_call_mut, true),
(self.tcx.lang_items().async_fn_once_trait(), sym::async_call_once, false),
]
};
// Try the options that are least restrictive on the caller first.
for (opt_trait_def_id, method_name, borrow) in call_trait_choices {
let Some(trait_def_id) = opt_trait_def_id else { continue };
let opt_input_type = opt_arg_exprs.map(|arg_exprs| {
Ty::new_tup_from_iter(
self.tcx,
arg_exprs.iter().map(|e| {
self.next_ty_var(TypeVariableOrigin { param_def_id: None, span: e.span })
}),
)
});
if let Some(ok) = self.lookup_method_in_trait(
self.misc(call_expr.span),
Ident::with_dummy_span(method_name),
trait_def_id,
adjusted_ty,
opt_input_type.as_ref().map(slice::from_ref),
) {
let method = self.register_infer_ok_obligations(ok);
let mut autoref = None;
if borrow {
// Check for &self vs &mut self in the method signature. Since this is either
// the Fn or FnMut trait, it should be one of those.
let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() else {
bug!("Expected `FnMut`/`Fn` to take receiver by-ref/by-mut")
};
// For initial two-phase borrow
// deployment, conservatively omit
// overloaded function call ops.
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::No);
autoref = Some(Adjustment {
kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
target: method.sig.inputs()[0],
});
}
return Some((autoref, method));
}
}
None
}
/// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the
/// likely intention is to call the closure, suggest `(||{})()`. (#55851)
fn identify_bad_closure_def_and_call(
&self,
err: &mut Diag<'_>,
hir_id: hir::HirId,
callee_node: &hir::ExprKind<'_>,
callee_span: Span,
) {
let hir::ExprKind::Block(..) = callee_node else {
// Only calls on blocks suggested here.
return;
};
let hir = self.tcx.hir();
let fn_decl_span = if let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
..
}) = self.tcx.parent_hir_node(hir_id)
{
fn_decl_span
} else if let Some((
_,
hir::Node::Expr(&hir::Expr {
hir_id: parent_hir_id,
kind:
hir::ExprKind::Closure(&hir::Closure {
kind:
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Closure,
)),
..
}),
..
}),
)) = hir.parent_iter(hir_id).nth(3)
{
// Actually need to unwrap one more layer of HIR to get to
// the _real_ closure...
if let hir::Node::Expr(hir::Expr {
kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }),
..
}) = self.tcx.parent_hir_node(parent_hir_id)
{
fn_decl_span
} else {
return;
}
} else {
return;
};
let start = fn_decl_span.shrink_to_lo();
let end = callee_span.shrink_to_hi();
err.multipart_suggestion(
"if you meant to create this closure and immediately call it, surround the \
closure with parentheses",
vec![(start, "(".to_string()), (end, ")".to_string())],
Applicability::MaybeIncorrect,
);
}
/// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the
/// likely intention is to create an array containing tuples.
fn maybe_suggest_bad_array_definition(
&self,
err: &mut Diag<'_>,
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
) -> bool {
let parent_node = self.tcx.parent_hir_node(call_expr.hir_id);
if let (
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }),
hir::ExprKind::Tup(exp),
hir::ExprKind::Call(_, args),
) = (parent_node, &callee_expr.kind, &call_expr.kind)
&& args.len() == exp.len()
{
let start = callee_expr.span.shrink_to_hi();
err.span_suggestion(
start,
"consider separating array elements with a comma",
",",
Applicability::MaybeIncorrect,
);
return true;
}
false
}
fn confirm_builtin_call(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
let (fn_sig, def_id) = match *callee_ty.kind() {
ty::FnDef(def_id, args) => {
self.enforce_context_effects(call_expr.span, def_id, args);
let fn_sig = self.tcx.fn_sig(def_id).instantiate(self.tcx, args);
// Unit testing: function items annotated with
// `#[rustc_evaluate_where_clauses]` trigger special output
// to let us test the trait evaluation system.
// Untranslatable diagnostics are okay for rustc internals
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
let predicates = self.tcx.predicates_of(def_id);
let predicates = predicates.instantiate(self.tcx, args);
for (predicate, predicate_span) in predicates {
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy_with_span(callee_expr.span),
self.param_env,
predicate,
);
let result = self.evaluate_obligation(&obligation);
self.dcx()
.struct_span_err(
callee_expr.span,
format!("evaluate({predicate:?}) = {result:?}"),
)
.with_span_label(predicate_span, "predicate")
.emit();
}
}
(fn_sig, Some(def_id))
}
// FIXME(effects): these arms should error because we can't enforce them
ty::FnPtr(sig) => (sig, None),
_ => {
for arg in arg_exprs {
self.check_expr(arg);
}
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
&& let [segment] = path.segments
{
self.dcx().try_steal_modify_and_emit_err(
segment.ident.span,
StashKey::CallIntoMethod,
|err| {
// Try suggesting `foo(a)` -> `a.foo()` if possible.
self.suggest_call_as_method(
err, segment, arg_exprs, call_expr, expected,
);
},
);
}
let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
return Ty::new_error(self.tcx, err);
}
};
// 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.instantiate_binder_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig);
let fn_sig = self.normalize(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,
fn_sig.inputs(),
expected_arg_tys,
arg_exprs,
fn_sig.c_variadic,
TupleArgumentsFlag::DontTupleArguments,
def_id,
);
if fn_sig.abi == abi::Abi::RustCall {
let sp = arg_exprs.last().map_or(call_expr.span, |expr| expr.span);
if let Some(ty) = fn_sig.inputs().last().copied() {
self.register_bound(
ty,
self.tcx.require_lang_item(hir::LangItem::Tuple, Some(sp)),
traits::ObligationCause::new(sp, self.body_id, traits::RustCall),
);
self.require_type_is_sized(ty, sp, traits::RustCall);
} else {
self.dcx().emit_err(errors::RustCallIncorrectArgs { span: sp });
}
}
if let Some(def_id) = def_id
&& self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
&& self.tcx.is_intrinsic(def_id, sym::const_eval_select)
{
let fn_sig = self.resolve_vars_if_possible(fn_sig);
for idx in 0..=1 {
let arg_ty = fn_sig.inputs()[idx + 1];
let span = arg_exprs.get(idx + 1).map_or(call_expr.span, |arg| arg.span);
// Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
// the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
// in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
//
// This check is here because there is currently no way to express a trait bound for `FnDef` types only.
if let ty::FnDef(def_id, _args) = *arg_ty.kind() {
let fn_once_def_id =
self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span));
let fn_once_output_def_id =
self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span));
if self.tcx.has_host_param(fn_once_def_id) {
let const_param: ty::GenericArg<'tcx> =
([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into();
self.register_predicate(traits::Obligation::new(
self.tcx,
self.misc(span),
self.param_env,
ty::TraitRef::new(
self.tcx,
fn_once_def_id,
[arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
),
));
self.register_predicate(traits::Obligation::new(
self.tcx,
self.misc(span),
self.param_env,
ty::ProjectionPredicate {
projection_ty: ty::AliasTy::new(
self.tcx,
fn_once_output_def_id,
[arg_ty.into(), fn_sig.inputs()[0].into(), const_param],
),
term: fn_sig.output().into(),
},
));
self.select_obligations_where_possible(|_| {});
} else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
self.dcx().emit_err(errors::ConstSelectMustBeConst { span });
}
} else {
self.dcx().emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty });
}
}
}
fn_sig.output()
}
/// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)`
/// and suggesting the fix if the method probe is successful.
fn suggest_call_as_method(
&self,
diag: &mut Diag<'_>,
segment: &'tcx hir::PathSegment<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
call_expr: &'tcx hir::Expr<'tcx>,
expected: Expectation<'tcx>,
) {
if let [callee_expr, rest @ ..] = arg_exprs {
let Some(callee_ty) = self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)
else {
return;
};
// First, do a probe with `IsSuggestion(true)` to avoid emitting
// any strange errors. If it's successful, then we'll do a true
// method lookup.
let Ok(pick) = self.lookup_probe_for_diagnostic(
segment.ident,
callee_ty,
call_expr,
// We didn't record the in scope traits during late resolution
// so we need to probe AllTraits unfortunately
ProbeScope::AllTraits,
expected.only_has_type(self),
) else {
return;
};
let pick = self.confirm_method(
call_expr.span,
callee_expr,
call_expr,
callee_ty,
&pick,
segment,
);
if pick.illegal_sized_bound.is_some() {
return;
}
let Some(callee_expr_span) = callee_expr.span.find_ancestor_inside(call_expr.span)
else {
return;
};
let up_to_rcvr_span = segment.ident.span.until(callee_expr_span);
let rest_span = callee_expr_span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
let rest_snippet = if let Some(first) = rest.first() {
self.tcx
.sess
.source_map()
.span_to_snippet(first.span.to(call_expr.span.shrink_to_hi()))
} else {
Ok(")".to_string())
};
if let Ok(rest_snippet) = rest_snippet {
let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX {
vec![
(up_to_rcvr_span, "".to_string()),
(rest_span, format!(".{}({rest_snippet}", segment.ident)),
]
} else {
vec![
(up_to_rcvr_span, "(".to_string()),
(rest_span, format!(").{}({rest_snippet}", segment.ident)),
]
};
let self_ty = self.resolve_vars_if_possible(pick.callee.sig.inputs()[0]);
diag.multipart_suggestion(
format!(
"use the `.` operator to call the method `{}{}` on `{self_ty}`",
self.tcx
.associated_item(pick.callee.def_id)
.trait_container(self.tcx)
.map_or_else(
|| String::new(),
|trait_def_id| self.tcx.def_path_str(trait_def_id) + "::"
),
segment.ident
),
sugg,
Applicability::MaybeIncorrect,
);
}
}
}
fn report_invalid_callee(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
) -> ErrorGuaranteed {
// Callee probe fails when APIT references errors, so suppress those
// errors here.
if let Some((_, _, args)) = self.extract_callable_info(callee_ty)
&& let Err(err) = args.error_reported()
{
return err;
}
let mut unit_variant = None;
if let hir::ExprKind::Path(qpath) = &callee_expr.kind
&& let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _)
= self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
// Only suggest removing parens if there are no arguments
&& arg_exprs.is_empty()
&& call_expr.span.contains(callee_expr.span)
{
let descr = match kind {
def::CtorOf::Struct => "struct",
def::CtorOf::Variant => "enum variant",
};
let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi());
unit_variant =
Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(&self.tcx, qpath)));
}
let callee_ty = self.resolve_vars_if_possible(callee_ty);
let mut err = self.dcx().create_err(errors::InvalidCallee {
span: callee_expr.span,
ty: match &unit_variant {
Some((_, kind, path)) => format!("{kind} `{path}`"),
None => format!("`{callee_ty}`"),
},
});
if callee_ty.references_error() {
err.downgrade_to_delayed_bug();
}
self.identify_bad_closure_def_and_call(
&mut err,
call_expr.hir_id,
&callee_expr.kind,
callee_expr.span,
);
if let Some((removal_span, kind, path)) = &unit_variant {
err.span_suggestion_verbose(
*removal_span,
format!(
"`{path}` is a unit {kind}, and does not take parentheses to be constructed",
),
"",
Applicability::MachineApplicable,
);
}
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = callee_expr.kind
&& let Res::Local(_) = path.res
&& let [segment] = &path.segments
{
for id in self.tcx.hir().items() {
if let Some(node) = self.tcx.hir().get_if_local(id.owner_id.into())
&& let hir::Node::Item(item) = node
&& let hir::ItemKind::Fn(..) = item.kind
&& item.ident.name == segment.ident.name
{
err.span_label(
self.tcx.def_span(id.owner_id),
"this function of the same name is available here, but it's shadowed by \
the local binding",
);
}
}
}
let mut inner_callee_path = None;
let def = match callee_expr.kind {
hir::ExprKind::Path(ref qpath) => {
self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id)
}
hir::ExprKind::Call(inner_callee, _) => {
if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind {
inner_callee_path = Some(inner_qpath);
self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id)
} else {
Res::Err
}
}
_ => Res::Err,
};
if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
// 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 (#51055, #106515).
let call_is_multiline = self
.tcx
.sess
.source_map()
.is_multiline(call_expr.span.with_lo(callee_expr.span.hi()))
&& call_expr.span.eq_ctxt(callee_expr.span);
if call_is_multiline {
err.span_suggestion(
callee_expr.span.shrink_to_hi(),
"consider using a semicolon here to finish the statement",
";",
Applicability::MaybeIncorrect,
);
}
if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty)
&& !self.type_is_sized_modulo_regions(self.param_env, output_ty)
{
let descr = match maybe_def {
DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id),
DefIdOrName::Name(name) => name,
};
err.span_label(
callee_expr.span,
format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called")
);
if let DefIdOrName::DefId(def_id) = maybe_def
&& let Some(def_span) = self.tcx.hir().span_if_local(def_id)
{
err.span_label(def_span, "the callable type is defined here");
}
} else {
err.span_label(call_expr.span, "call expression requires function");
}
}
if let Some(span) = self.tcx.hir().res_span(def) {
let callee_ty = callee_ty.to_string();
let label = match (unit_variant, inner_callee_path) {
(Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")),
(_, Some(hir::QPath::Resolved(_, path))) => self
.tcx
.sess
.source_map()
.span_to_snippet(path.span)
.ok()
.map(|p| format!("`{p}` defined here returns `{callee_ty}`")),
_ => {
match def {
// Emit a different diagnostic for local variables, as they are not
// type definitions themselves, but rather variables *of* that type.
Res::Local(hir_id) => Some(format!(
"`{}` has type `{}`",
self.tcx.hir().name(hir_id),
callee_ty
)),
Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => {
Some(format!("`{}` defined here", self.tcx.def_path_str(def_id),))
}
_ => Some(format!("`{callee_ty}` defined here")),
}
}
};
if let Some(label) = label {
err.span_label(span, label);
}
}
err.emit()
}
fn confirm_deferred_closure_call(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
closure_def_id: LocalDefId,
fn_sig: ty::FnSig<'tcx>,
) -> Ty<'tcx> {
// `fn_sig` is the *signature* of the closure 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(),
fn_sig.inputs(),
);
self.check_argument_types(
call_expr.span,
call_expr,
fn_sig.inputs(),
expected_arg_tys,
arg_exprs,
fn_sig.c_variadic,
TupleArgumentsFlag::TupleArguments,
Some(closure_def_id.to_def_id()),
);
fn_sig.output()
}
#[tracing::instrument(level = "debug", skip(self, span))]
pub(super) fn enforce_context_effects(
&self,
span: Span,
callee_did: DefId,
callee_args: GenericArgsRef<'tcx>,
) {
let tcx = self.tcx;
// fast-reject if callee doesn't have the host effect param (non-const)
let generics = tcx.generics_of(callee_did);
let Some(host_effect_index) = generics.host_effect_index else { return };
let effect = tcx.expected_host_effect_param_for_body(self.body_id);
trace!(?effect, ?generics, ?callee_args);
let param = callee_args.const_at(host_effect_index);
let cause = self.misc(span);
// We know the type of `effect` to be `bool`, there will be no opaque type inference.
match self.at(&cause, self.param_env).eq(infer::DefineOpaqueTypes::Yes, effect, param) {
Ok(infer::InferOk { obligations, value: () }) => {
self.register_predicates(obligations);
}
Err(e) => {
// FIXME(effects): better diagnostic
self.err_ctxt().report_mismatched_consts(&cause, effect, param, e).emit();
}
}
}
fn confirm_overloaded_call(
&self,
call_expr: &'tcx hir::Expr<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
method_callee: MethodCallee<'tcx>,
) -> Ty<'tcx> {
let output_type = self.check_method_argument_types(
call_expr.span,
call_expr,
Ok(method_callee),
arg_exprs,
TupleArgumentsFlag::TupleArguments,
expected,
);
self.write_method_call_and_enforce_effects(call_expr.hir_id, call_expr.span, method_callee);
output_type
}
}
#[derive(Debug)]
pub struct DeferredCallResolution<'tcx> {
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
closure_ty: Ty<'tcx>,
adjustments: Vec<Adjustment<'tcx>>,
fn_sig: ty::FnSig<'tcx>,
}
impl<'a, 'tcx> DeferredCallResolution<'tcx> {
pub fn resolve(self, fcx: &FnCtxt<'a, '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_ty).is_some());
// We may now know enough to figure out fn vs fnmut etc.
match fcx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) {
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
iter::zip(method_sig.inputs().iter().skip(1), 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_and_enforce_effects(
self.call_expr.hir_id,
self.call_expr.span,
method_callee,
);
}
None => {
span_bug!(
self.call_expr.span,
"Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`",
self.closure_ty
)
}
}
}
}