Use extract_callable_info, generalize it to async closure
This commit is contained in:
parent
78bbc648c5
commit
cbacb6d931
7 changed files with 121 additions and 139 deletions
|
@ -31,6 +31,7 @@ use rustc_span::symbol::{Ident, kw, sym};
|
|||
use rustc_span::{
|
||||
DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span, Symbol, edit_distance,
|
||||
};
|
||||
use rustc_trait_selection::error_reporting::traits::DefIdOrName;
|
||||
use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
|
@ -45,50 +46,6 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
|
|||
use crate::{Expectation, FnCtxt};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
||||
let tcx = self.tcx;
|
||||
match ty.kind() {
|
||||
// Not all of these (e.g., unsafe fns) implement `FnOnce`,
|
||||
// so we look for these beforehand.
|
||||
// FIXME(async_closures): These don't impl `FnOnce` by default.
|
||||
ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(..) => true,
|
||||
// If it's not a simple function, look for things which implement `FnOnce`.
|
||||
_ => {
|
||||
let Some(fn_once) = tcx.lang_items().fn_once_trait() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// This conditional prevents us from asking to call errors and unresolved types.
|
||||
// It might seem that we can use `predicate_must_hold_modulo_regions`,
|
||||
// but since a Dummy binder is used to fill in the FnOnce trait's arguments,
|
||||
// type resolution always gives a "maybe" here.
|
||||
if self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
|
||||
info!("check deref {:?} error", ty);
|
||||
matches!(ty.kind(), ty::Error(_) | ty::Infer(_))
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.autoderef(span, ty).silence_errors().any(|(ty, _)| {
|
||||
info!("check deref {:?} impl FnOnce", ty);
|
||||
self.probe(|_| {
|
||||
let trait_ref =
|
||||
ty::TraitRef::new(tcx, fn_once, [ty, self.next_ty_var(span)]);
|
||||
let poly_trait_ref = ty::Binder::dummy(trait_ref);
|
||||
let obligation = Obligation::misc(
|
||||
tcx,
|
||||
span,
|
||||
self.body_id,
|
||||
self.param_env,
|
||||
poly_trait_ref,
|
||||
);
|
||||
self.predicate_may_hold(&obligation)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
|
||||
self.autoderef(span, ty)
|
||||
.silence_errors()
|
||||
|
@ -2365,12 +2322,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let is_accessible = field.vis.is_accessible_from(scope, tcx);
|
||||
|
||||
if is_accessible {
|
||||
if self.is_fn_ty(field_ty, span) {
|
||||
if let Some((what, _, _)) = self.extract_callable_info(field_ty) {
|
||||
let what = match what {
|
||||
DefIdOrName::DefId(def_id) => self.tcx.def_descr(def_id),
|
||||
DefIdOrName::Name(what) => what,
|
||||
};
|
||||
let expr_span = expr.span.to(item_name.span);
|
||||
err.multipart_suggestion(
|
||||
format!(
|
||||
"to call the function stored in `{item_name}`, \
|
||||
surround the field access with parentheses",
|
||||
"to call the {what} stored in `{item_name}`, \
|
||||
surround the field access with parentheses",
|
||||
),
|
||||
vec![
|
||||
(expr_span.shrink_to_lo(), '('.to_string()),
|
||||
|
|
|
@ -1075,93 +1075,110 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
|
||||
// Autoderef is useful here because sometimes we box callables, etc.
|
||||
let Some((def_id_or_name, output, inputs)) =
|
||||
(self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
|
||||
match *found.kind() {
|
||||
ty::FnPtr(sig_tys, _) => Some((
|
||||
DefIdOrName::Name("function pointer"),
|
||||
sig_tys.output(),
|
||||
sig_tys.inputs(),
|
||||
)),
|
||||
ty::FnDef(def_id, _) => {
|
||||
let fn_sig = found.fn_sig(self.tcx);
|
||||
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
|
||||
}
|
||||
ty::Closure(def_id, args) => {
|
||||
let fn_sig = args.as_closure().sig();
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
fn_sig.output(),
|
||||
fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),
|
||||
))
|
||||
}
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => {
|
||||
self.tcx
|
||||
.item_super_predicates(def_id)
|
||||
.instantiate(self.tcx, args)
|
||||
.iter()
|
||||
.find_map(|pred| {
|
||||
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
|
||||
// args tuple will always be args[1]
|
||||
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
pred.kind().rebind(proj.term.expect_type()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
ty::Dynamic(data, _, ty::Dyn) => {
|
||||
data.iter().find_map(|pred| {
|
||||
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
|
||||
(self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() {
|
||||
ty::FnPtr(sig_tys, _) => Some((
|
||||
DefIdOrName::Name("function pointer"),
|
||||
sig_tys.output(),
|
||||
sig_tys.inputs(),
|
||||
)),
|
||||
ty::FnDef(def_id, _) => {
|
||||
let fn_sig = found.fn_sig(self.tcx);
|
||||
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
|
||||
}
|
||||
ty::Closure(def_id, args) => {
|
||||
let fn_sig = args.as_closure().sig();
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
fn_sig.output(),
|
||||
fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),
|
||||
))
|
||||
}
|
||||
ty::CoroutineClosure(def_id, args) => {
|
||||
let sig_parts = args.as_coroutine_closure().coroutine_closure_sig();
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
sig_parts.map_bound(|sig| {
|
||||
sig.to_coroutine(
|
||||
self.tcx,
|
||||
args.as_coroutine_closure().parent_args(),
|
||||
// Just use infer vars here, since we don't really care
|
||||
// what these types are, just that we're returning a coroutine.
|
||||
self.next_ty_var(DUMMY_SP),
|
||||
self.tcx.coroutine_for_closure(def_id),
|
||||
self.next_ty_var(DUMMY_SP),
|
||||
)
|
||||
}),
|
||||
sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),
|
||||
))
|
||||
}
|
||||
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
|
||||
.tcx
|
||||
.item_super_predicates(def_id)
|
||||
.instantiate(self.tcx, args)
|
||||
.iter()
|
||||
.find_map(|pred| {
|
||||
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& self
|
||||
.tcx
|
||||
.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
|
||||
// args tuple will always be args[1]
|
||||
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::DefId(def_id),
|
||||
pred.kind().rebind(proj.term.expect_type()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| {
|
||||
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
|
||||
&& self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)
|
||||
// for existential projection, args are shifted over by 1
|
||||
&& let ty::Tuple(args) = proj.args.type_at(0).kind()
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::Name("trait object"),
|
||||
pred.rebind(proj.term.expect_type()),
|
||||
pred.rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
{
|
||||
Some((
|
||||
DefIdOrName::Name("trait object"),
|
||||
pred.rebind(proj.term.expect_type()),
|
||||
pred.rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ty::Param(param) => {
|
||||
let generics = self.tcx.generics_of(body_id);
|
||||
let name = if generics.count() > param.index as usize
|
||||
&& let def = generics.param_at(param.index as usize, self.tcx)
|
||||
&& matches!(def.kind, ty::GenericParamDefKind::Type { .. })
|
||||
&& def.name == param.name
|
||||
}),
|
||||
ty::Param(param) => {
|
||||
let generics = self.tcx.generics_of(body_id);
|
||||
let name = if generics.count() > param.index as usize
|
||||
&& let def = generics.param_at(param.index as usize, self.tcx)
|
||||
&& matches!(def.kind, ty::GenericParamDefKind::Type { .. })
|
||||
&& def.name == param.name
|
||||
{
|
||||
DefIdOrName::DefId(def.def_id)
|
||||
} else {
|
||||
DefIdOrName::Name("type parameter")
|
||||
};
|
||||
param_env.caller_bounds().iter().find_map(|pred| {
|
||||
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& self
|
||||
.tcx
|
||||
.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
|
||||
&& proj.projection_term.self_ty() == found
|
||||
// args tuple will always be args[1]
|
||||
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
|
||||
{
|
||||
DefIdOrName::DefId(def.def_id)
|
||||
Some((
|
||||
name,
|
||||
pred.kind().rebind(proj.term.expect_type()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
DefIdOrName::Name("type parameter")
|
||||
};
|
||||
param_env.caller_bounds().iter().find_map(|pred| {
|
||||
if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()
|
||||
&& self.tcx.is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput)
|
||||
&& proj.projection_term.self_ty() == found
|
||||
// args tuple will always be args[1]
|
||||
&& let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()
|
||||
{
|
||||
Some((
|
||||
name,
|
||||
pred.kind().rebind(proj.term.expect_type()),
|
||||
pred.kind().rebind(args.as_slice()),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
else {
|
||||
return None;
|
||||
|
|
|
@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnMut() -> u32 {
|
|||
LL | o.closure();
|
||||
| ^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `closure`, surround the field access with parentheses
|
||||
help: to call the closure stored in `closure`, surround the field access with parentheses
|
||||
|
|
||||
LL | (o.closure)();
|
||||
| + +
|
||||
|
|
|
@ -7,7 +7,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 {
|
|||
LL | o_closure.closure();
|
||||
| ^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `closure`, surround the field access with parentheses
|
||||
help: to call the closure stored in `closure`, surround the field access with parentheses
|
||||
|
|
||||
LL | (o_closure.closure)();
|
||||
| + +
|
||||
|
@ -46,7 +46,7 @@ LL | struct BoxedObj {
|
|||
LL | boxed_fn.boxed_closure();
|
||||
| ^^^^^^^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `boxed_closure`, surround the field access with parentheses
|
||||
help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses
|
||||
|
|
||||
LL | (boxed_fn.boxed_closure)();
|
||||
| + +
|
||||
|
@ -60,7 +60,7 @@ LL | struct BoxedObj {
|
|||
LL | boxed_closure.boxed_closure();
|
||||
| ^^^^^^^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `boxed_closure`, surround the field access with parentheses
|
||||
help: to call the trait object stored in `boxed_closure`, surround the field access with parentheses
|
||||
|
|
||||
LL | (boxed_closure.boxed_closure)();
|
||||
| + +
|
||||
|
@ -99,7 +99,7 @@ LL | struct Obj<F> where F: FnOnce() -> u32 {
|
|||
LL | check_expression().closure();
|
||||
| ^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `closure`, surround the field access with parentheses
|
||||
help: to call the trait object stored in `closure`, surround the field access with parentheses
|
||||
|
|
||||
LL | (check_expression().closure)();
|
||||
| + +
|
||||
|
@ -113,7 +113,7 @@ LL | struct FuncContainer {
|
|||
LL | (*self.container).f1(1);
|
||||
| ^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `f1`, surround the field access with parentheses
|
||||
help: to call the function pointer stored in `f1`, surround the field access with parentheses
|
||||
|
|
||||
LL | ((*self.container).f1)(1);
|
||||
| + +
|
||||
|
@ -127,7 +127,7 @@ LL | struct FuncContainer {
|
|||
LL | (*self.container).f2(1);
|
||||
| ^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `f2`, surround the field access with parentheses
|
||||
help: to call the function pointer stored in `f2`, surround the field access with parentheses
|
||||
|
|
||||
LL | ((*self.container).f2)(1);
|
||||
| + +
|
||||
|
@ -141,7 +141,7 @@ LL | struct FuncContainer {
|
|||
LL | (*self.container).f3(1);
|
||||
| ^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `f3`, surround the field access with parentheses
|
||||
help: to call the function pointer stored in `f3`, surround the field access with parentheses
|
||||
|
|
||||
LL | ((*self.container).f3)(1);
|
||||
| + +
|
||||
|
|
|
@ -7,7 +7,7 @@ LL | struct Example {
|
|||
LL | demo.example(1);
|
||||
| ^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `example`, surround the field access with parentheses
|
||||
help: to call the trait object stored in `example`, surround the field access with parentheses
|
||||
|
|
||||
LL | (demo.example)(1);
|
||||
| + +
|
||||
|
|
|
@ -4,7 +4,7 @@ error[E0599]: no method named `closure` found for reference `&Obj<{closure@$DIR/
|
|||
LL | p.closure();
|
||||
| ^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `closure`, surround the field access with parentheses
|
||||
help: to call the closure stored in `closure`, surround the field access with parentheses
|
||||
|
|
||||
LL | (p.closure)();
|
||||
| + +
|
||||
|
@ -19,7 +19,7 @@ error[E0599]: no method named `fn_ptr` found for reference `&&Obj<{closure@$DIR/
|
|||
LL | q.fn_ptr();
|
||||
| ^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `fn_ptr`, surround the field access with parentheses
|
||||
help: to call the function pointer stored in `fn_ptr`, surround the field access with parentheses
|
||||
|
|
||||
LL | (q.fn_ptr)();
|
||||
| + +
|
||||
|
@ -30,7 +30,7 @@ error[E0599]: no method named `c_fn_ptr` found for reference `&D` in the current
|
|||
LL | s.c_fn_ptr();
|
||||
| ^^^^^^^^ field, not a method
|
||||
|
|
||||
help: to call the function stored in `c_fn_ptr`, surround the field access with parentheses
|
||||
help: to call the function pointer stored in `c_fn_ptr`, surround the field access with parentheses
|
||||
|
|
||||
LL | (s.c_fn_ptr)();
|
||||
| + +
|
||||
|
|
|
@ -31,6 +31,10 @@ note: required by a bound in `bar`
|
|||
|
|
||||
LL | fn bar(f: impl Future<Output=()>) {}
|
||||
| ^^^^^^^^^^^^^^^^^ required by this bound in `bar`
|
||||
help: use parentheses to call this closure
|
||||
|
|
||||
LL | bar(async_closure());
|
||||
| ++
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue