make better async fn kind errors
This commit is contained in:
parent
fa9f77ff35
commit
acb201af54
7 changed files with 154 additions and 37 deletions
|
@ -8,14 +8,16 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur
|
||||||
*[other] arguments
|
*[other] arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
trait_selection_closure_fn_mut_label = closure is `FnMut` because it mutates the variable `{$place}` here
|
trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment
|
||||||
|
|
||||||
trait_selection_closure_fn_once_label = closure is `FnOnce` because it moves the variable `{$place}` out of its environment
|
trait_selection_closure_fn_mut_label = closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here
|
||||||
|
|
||||||
trait_selection_closure_kind_mismatch = expected a closure that implements the `{$expected}` trait, but this closure only implements `{$found}`
|
trait_selection_closure_fn_once_label = closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment
|
||||||
.label = this closure implements `{$found}`, not `{$expected}`
|
|
||||||
|
|
||||||
trait_selection_closure_kind_requirement = the requirement to implement `{$expected}` derives from here
|
trait_selection_closure_kind_mismatch = expected a closure that implements the `{$trait_prefix}{$expected}` trait, but this closure only implements `{$trait_prefix}{$found}`
|
||||||
|
.label = this closure implements `{$trait_prefix}{$found}`, not `{$trait_prefix}{$expected}`
|
||||||
|
|
||||||
|
trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here
|
||||||
|
|
||||||
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
|
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,8 @@ pub struct ClosureKindMismatch {
|
||||||
#[label(trait_selection_closure_kind_requirement)]
|
#[label(trait_selection_closure_kind_requirement)]
|
||||||
pub cause_span: Span,
|
pub cause_span: Span,
|
||||||
|
|
||||||
|
pub trait_prefix: &'static str,
|
||||||
|
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
pub fn_once_label: Option<ClosureFnOnceLabel>,
|
pub fn_once_label: Option<ClosureFnOnceLabel>,
|
||||||
|
|
||||||
|
@ -157,3 +159,11 @@ pub struct ClosureFnMutLabel {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub place: String,
|
pub place: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(trait_selection_async_closure_not_fn)]
|
||||||
|
pub(crate) struct AsyncClosureNotFn {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
pub kind: &'static str,
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
|
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
|
||||||
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
|
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
|
||||||
use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch};
|
use crate::errors::{
|
||||||
|
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
|
||||||
|
};
|
||||||
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
|
||||||
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use crate::infer::InferCtxtExt as _;
|
use crate::infer::InferCtxtExt as _;
|
||||||
|
@ -959,34 +961,102 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
fn emit_specialized_closure_kind_error(
|
fn emit_specialized_closure_kind_error(
|
||||||
&self,
|
&self,
|
||||||
obligation: &PredicateObligation<'tcx>,
|
obligation: &PredicateObligation<'tcx>,
|
||||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
mut trait_ref: ty::PolyTraitRef<'tcx>,
|
||||||
) -> Option<ErrorGuaranteed> {
|
) -> Option<ErrorGuaranteed> {
|
||||||
let self_ty = trait_ref.self_ty().skip_binder();
|
// If `AsyncFnKindHelper` is not implemented, that means that the closure kind
|
||||||
if let ty::Closure(closure_def_id, closure_args) = *self_ty.kind()
|
// doesn't extend the goal kind. This is worth reporting, but we can only do so
|
||||||
&& let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id())
|
// if we actually know which closure this goal comes from, so look at the cause
|
||||||
&& let Some(found_kind) = self.closure_kind(self_ty)
|
// to see if we can extract that information.
|
||||||
|
if Some(trait_ref.def_id()) == self.tcx.lang_items().async_fn_kind_helper()
|
||||||
|
&& let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind()
|
||||||
|
&& let Some(expected_kind) =
|
||||||
|
trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
|
||||||
&& !found_kind.extends(expected_kind)
|
&& !found_kind.extends(expected_kind)
|
||||||
&& let sig = closure_args.as_closure().sig()
|
|
||||||
&& self.can_sub(
|
|
||||||
obligation.param_env,
|
|
||||||
trait_ref,
|
|
||||||
sig.map_bound(|sig| {
|
|
||||||
ty::TraitRef::new(
|
|
||||||
self.tcx,
|
|
||||||
trait_ref.def_id(),
|
|
||||||
[trait_ref.self_ty().skip_binder(), sig.inputs()[0]],
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
let mut err =
|
if let Some((_, Some(parent))) = obligation.cause.code().parent() {
|
||||||
self.report_closure_error(&obligation, closure_def_id, found_kind, expected_kind);
|
// If we have a derived obligation, then the parent will be a `AsyncFn*` goal.
|
||||||
self.note_obligation_cause(&mut err, &obligation);
|
trait_ref = parent.to_poly_trait_ref();
|
||||||
self.point_at_returns_when_relevant(&mut err, &obligation);
|
} else if let &ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
|
||||||
Some(err.emit())
|
obligation.cause.code()
|
||||||
} else {
|
&& let Some(typeck_results) = &self.typeck_results
|
||||||
None
|
&& let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) =
|
||||||
|
*typeck_results.node_type(arg_hir_id).kind()
|
||||||
|
{
|
||||||
|
// Otherwise, extract the closure kind from the obligation.
|
||||||
|
let mut err = self.report_closure_error(
|
||||||
|
&obligation,
|
||||||
|
closure_def_id,
|
||||||
|
found_kind,
|
||||||
|
expected_kind,
|
||||||
|
"async ",
|
||||||
|
);
|
||||||
|
self.note_obligation_cause(&mut err, &obligation);
|
||||||
|
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||||
|
return Some(err.emit());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let self_ty = trait_ref.self_ty().skip_binder();
|
||||||
|
|
||||||
|
if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) {
|
||||||
|
let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
|
||||||
|
ty::Closure(def_id, args) => {
|
||||||
|
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
|
||||||
|
}
|
||||||
|
ty::CoroutineClosure(def_id, args) => (
|
||||||
|
def_id,
|
||||||
|
args.as_coroutine_closure()
|
||||||
|
.coroutine_closure_sig()
|
||||||
|
.map_bound(|sig| sig.tupled_inputs_ty),
|
||||||
|
Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
|
||||||
|
),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_args = trait_ref.map_bound(|trait_ref| trait_ref.args.type_at(1));
|
||||||
|
|
||||||
|
// Verify that the arguments are compatible. If the signature is
|
||||||
|
// mismatched, then we have a totally different error to report.
|
||||||
|
if self.enter_forall(found_args, |found_args| {
|
||||||
|
self.enter_forall(expected_args, |expected_args| {
|
||||||
|
!self.can_sub(obligation.param_env, expected_args, found_args)
|
||||||
|
})
|
||||||
|
}) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(found_kind) = self.closure_kind(self_ty)
|
||||||
|
&& !found_kind.extends(expected_kind)
|
||||||
|
{
|
||||||
|
let mut err = self.report_closure_error(
|
||||||
|
&obligation,
|
||||||
|
closure_def_id,
|
||||||
|
found_kind,
|
||||||
|
expected_kind,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
self.note_obligation_cause(&mut err, &obligation);
|
||||||
|
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||||
|
return Some(err.emit());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the closure has captures, then perhaps the reason that the trait
|
||||||
|
// is unimplemented is because async closures don't implement `Fn`/`FnMut`
|
||||||
|
// if they have captures.
|
||||||
|
if let Some(by_ref_captures) = by_ref_captures
|
||||||
|
&& let ty::FnPtr(sig) = by_ref_captures.kind()
|
||||||
|
&& !sig.skip_binder().output().is_unit()
|
||||||
|
{
|
||||||
|
let mut err = self.tcx.dcx().create_err(AsyncClosureNotFn {
|
||||||
|
span: self.tcx.def_span(closure_def_id),
|
||||||
|
kind: expected_kind.as_str(),
|
||||||
|
});
|
||||||
|
self.note_obligation_cause(&mut err, &obligation);
|
||||||
|
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||||
|
return Some(err.emit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fn_arg_obligation(
|
fn fn_arg_obligation(
|
||||||
|
@ -1493,6 +1563,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
|
||||||
closure_def_id: DefId,
|
closure_def_id: DefId,
|
||||||
found_kind: ty::ClosureKind,
|
found_kind: ty::ClosureKind,
|
||||||
kind: ty::ClosureKind,
|
kind: ty::ClosureKind,
|
||||||
|
trait_prefix: &'static str,
|
||||||
) -> DiagnosticBuilder<'tcx>;
|
) -> DiagnosticBuilder<'tcx>;
|
||||||
|
|
||||||
fn report_cyclic_signature_error(
|
fn report_cyclic_signature_error(
|
||||||
|
@ -3376,6 +3447,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
closure_def_id: DefId,
|
closure_def_id: DefId,
|
||||||
found_kind: ty::ClosureKind,
|
found_kind: ty::ClosureKind,
|
||||||
kind: ty::ClosureKind,
|
kind: ty::ClosureKind,
|
||||||
|
trait_prefix: &'static str,
|
||||||
) -> DiagnosticBuilder<'tcx> {
|
) -> DiagnosticBuilder<'tcx> {
|
||||||
let closure_span = self.tcx.def_span(closure_def_id);
|
let closure_span = self.tcx.def_span(closure_def_id);
|
||||||
|
|
||||||
|
@ -3384,6 +3456,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||||
expected: kind,
|
expected: kind,
|
||||||
found: found_kind,
|
found: found_kind,
|
||||||
cause_span: obligation.cause.span,
|
cause_span: obligation.cause.span,
|
||||||
|
trait_prefix,
|
||||||
fn_once_label: None,
|
fn_once_label: None,
|
||||||
fn_mut_label: None,
|
fn_mut_label: None,
|
||||||
};
|
};
|
||||||
|
|
15
tests/ui/async-await/async-closures/not-fn.rs
Normal file
15
tests/ui/async-await/async-closures/not-fn.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// edition:2021
|
||||||
|
|
||||||
|
// FIXME(async_closures): This needs a better error message!
|
||||||
|
|
||||||
|
#![feature(async_closure)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
fn needs_fn<T>(_: impl FnMut() -> T) {}
|
||||||
|
|
||||||
|
let mut x = 1;
|
||||||
|
needs_fn(async || {
|
||||||
|
//~^ ERROR async closure does not implement `FnMut` because it captures state from its environment
|
||||||
|
x += 1;
|
||||||
|
});
|
||||||
|
}
|
16
tests/ui/async-await/async-closures/not-fn.stderr
Normal file
16
tests/ui/async-await/async-closures/not-fn.stderr
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
error: async closure does not implement `FnMut` because it captures state from its environment
|
||||||
|
--> $DIR/not-fn.rs:11:14
|
||||||
|
|
|
||||||
|
LL | needs_fn(async || {
|
||||||
|
| -------- ^^^^^^^^
|
||||||
|
| |
|
||||||
|
| required by a bound introduced by this call
|
||||||
|
|
|
||||||
|
note: required by a bound in `needs_fn`
|
||||||
|
--> $DIR/not-fn.rs:8:28
|
||||||
|
|
|
||||||
|
LL | fn needs_fn<T>(_: impl FnMut() -> T) {}
|
||||||
|
| ^^^^^^^^^^^^ required by this bound in `needs_fn`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
|
@ -9,8 +9,7 @@ fn main() {
|
||||||
|
|
||||||
let mut x = 1;
|
let mut x = 1;
|
||||||
needs_async_fn(async || {
|
needs_async_fn(async || {
|
||||||
//~^ ERROR i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>
|
//~^ ERROR expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut`
|
||||||
// FIXME: Should say "closure is `async FnMut` but it needs `async Fn`" or sth.
|
|
||||||
x += 1;
|
x += 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
error[E0277]: the trait bound `i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not satisfied
|
error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut`
|
||||||
--> $DIR/wrong-fn-kind.rs:11:20
|
--> $DIR/wrong-fn-kind.rs:11:20
|
||||||
|
|
|
|
||||||
LL | needs_async_fn(async || {
|
LL | needs_async_fn(async || {
|
||||||
| _____--------------_^
|
| -------------- -^^^^^^^
|
||||||
|
| | |
|
||||||
|
| _____|______________this closure implements `async FnMut`, not `async Fn`
|
||||||
| | |
|
| | |
|
||||||
| | required by a bound introduced by this call
|
| | required by a bound introduced by this call
|
||||||
LL | |
|
LL | |
|
||||||
LL | | // FIXME: Should say "closure is `async FnMut` but it needs `async Fn`" or sth.
|
|
||||||
LL | | x += 1;
|
LL | | x += 1;
|
||||||
|
| | - closure is `async FnMut` because it mutates the variable `x` here
|
||||||
LL | | });
|
LL | | });
|
||||||
| |_____^ the trait `ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not implemented for `i16`
|
| |_____- the requirement to implement `async Fn` derives from here
|
||||||
|
|
|
|
||||||
note: required by a bound in `needs_async_fn`
|
note: required by a bound in `needs_async_fn`
|
||||||
--> $DIR/wrong-fn-kind.rs:8:31
|
--> $DIR/wrong-fn-kind.rs:8:31
|
||||||
|
@ -19,4 +21,4 @@ LL | fn needs_async_fn(_: impl async Fn()) {}
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0277`.
|
For more information about this error, try `rustc --explain E0525`.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue