Skip to content

Commit ae1ceb1

Browse files
make better async fn kind errors
1 parent ee9c7c9 commit ae1ceb1

File tree

7 files changed

+147
-37
lines changed

7 files changed

+147
-37
lines changed

compiler/rustc_trait_selection/messages.ftl

+7-5
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur
88
*[other] arguments
99
}
1010
11-
trait_selection_closure_fn_mut_label = closure is `FnMut` because it mutates the variable `{$place}` here
11+
trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment
1212
13-
trait_selection_closure_fn_once_label = closure is `FnOnce` because it moves the variable `{$place}` out of its environment
13+
trait_selection_closure_fn_mut_label = closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here
1414
15-
trait_selection_closure_kind_mismatch = expected a closure that implements the `{$expected}` trait, but this closure only implements `{$found}`
16-
.label = this closure implements `{$found}`, not `{$expected}`
15+
trait_selection_closure_fn_once_label = closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment
1716
18-
trait_selection_closure_kind_requirement = the requirement to implement `{$expected}` derives from here
17+
trait_selection_closure_kind_mismatch = expected a closure that implements the `{$trait_prefix}{$expected}` trait, but this closure only implements `{$trait_prefix}{$found}`
18+
.label = this closure implements `{$trait_prefix}{$found}`, not `{$trait_prefix}{$expected}`
19+
20+
trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here
1921
2022
trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries}
2123

compiler/rustc_trait_selection/src/errors.rs

+10
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ pub struct ClosureKindMismatch {
135135
#[label(trait_selection_closure_kind_requirement)]
136136
pub cause_span: Span,
137137

138+
pub trait_prefix: &'static str,
139+
138140
#[subdiagnostic]
139141
pub fn_once_label: Option<ClosureFnOnceLabel>,
140142

@@ -157,3 +159,11 @@ pub struct ClosureFnMutLabel {
157159
pub span: Span,
158160
pub place: String,
159161
}
162+
163+
#[derive(Diagnostic)]
164+
#[diag(trait_selection_async_closure_not_fn)]
165+
pub(crate) struct AsyncClosureNotFn {
166+
#[primary_span]
167+
pub span: Span,
168+
pub kind: &'static str,
169+
}

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+91-25
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
44
use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
5-
use crate::errors::{ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch};
5+
use crate::errors::{
6+
AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
7+
};
68
use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
79
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
810
use crate::infer::InferCtxtExt as _;
@@ -959,34 +961,95 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
959961
fn emit_specialized_closure_kind_error(
960962
&self,
961963
obligation: &PredicateObligation<'tcx>,
962-
trait_ref: ty::PolyTraitRef<'tcx>,
964+
mut trait_ref: ty::PolyTraitRef<'tcx>,
963965
) -> Option<ErrorGuaranteed> {
964-
let self_ty = trait_ref.self_ty().skip_binder();
965-
if let ty::Closure(closure_def_id, closure_args) = *self_ty.kind()
966-
&& let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id())
967-
&& let Some(found_kind) = self.closure_kind(self_ty)
966+
if Some(trait_ref.def_id()) == self.tcx.lang_items().async_fn_kind_helper()
967+
&& let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind()
968+
&& let Some(expected_kind) =
969+
trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
968970
&& !found_kind.extends(expected_kind)
969-
&& let sig = closure_args.as_closure().sig()
970-
&& self.can_sub(
971-
obligation.param_env,
972-
trait_ref,
973-
sig.map_bound(|sig| {
974-
ty::TraitRef::new(
975-
self.tcx,
976-
trait_ref.def_id(),
977-
[trait_ref.self_ty().skip_binder(), sig.inputs()[0]],
978-
)
979-
}),
980-
)
981971
{
982-
let mut err =
983-
self.report_closure_error(&obligation, closure_def_id, found_kind, expected_kind);
984-
self.note_obligation_cause(&mut err, &obligation);
985-
self.point_at_returns_when_relevant(&mut err, &obligation);
986-
Some(err.emit())
987-
} else {
988-
None
972+
if let Some((_, Some(parent))) = obligation.cause.code().parent() {
973+
trait_ref = parent.to_poly_trait_ref();
974+
} else if let &ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
975+
obligation.cause.code()
976+
&& let Some(typeck_results) = &self.typeck_results
977+
&& let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) =
978+
*typeck_results.node_type(arg_hir_id).kind()
979+
{
980+
let mut err = self.report_closure_error(
981+
&obligation,
982+
closure_def_id,
983+
found_kind,
984+
expected_kind,
985+
"async ",
986+
);
987+
self.note_obligation_cause(&mut err, &obligation);
988+
self.point_at_returns_when_relevant(&mut err, &obligation);
989+
return Some(err.emit());
990+
}
989991
}
992+
993+
let self_ty = trait_ref.self_ty().skip_binder();
994+
995+
'outer: {
996+
if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) {
997+
let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
998+
ty::Closure(def_id, args) => {
999+
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
1000+
}
1001+
ty::CoroutineClosure(def_id, args) => (
1002+
def_id,
1003+
args.as_coroutine_closure()
1004+
.coroutine_closure_sig()
1005+
.map_bound(|sig| sig.tupled_inputs_ty),
1006+
Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
1007+
),
1008+
_ => break 'outer,
1009+
};
1010+
1011+
let expected_args = trait_ref.map_bound(|trait_ref| trait_ref.args.type_at(1));
1012+
1013+
// Verify that the arguments are compatible.
1014+
if self.enter_forall(found_args, |found_args| {
1015+
self.enter_forall(expected_args, |expected_args| {
1016+
!self.can_sub(obligation.param_env, expected_args, found_args)
1017+
})
1018+
}) {
1019+
break 'outer;
1020+
}
1021+
1022+
if let Some(found_kind) = self.closure_kind(self_ty)
1023+
&& !found_kind.extends(expected_kind)
1024+
{
1025+
let mut err = self.report_closure_error(
1026+
&obligation,
1027+
closure_def_id,
1028+
found_kind,
1029+
expected_kind,
1030+
"",
1031+
);
1032+
self.note_obligation_cause(&mut err, &obligation);
1033+
self.point_at_returns_when_relevant(&mut err, &obligation);
1034+
return Some(err.emit());
1035+
}
1036+
1037+
if let Some(by_ref_captures) = by_ref_captures
1038+
&& let ty::FnPtr(sig) = by_ref_captures.kind()
1039+
&& !sig.skip_binder().output().is_unit()
1040+
{
1041+
let mut err = self.tcx.dcx().create_err(AsyncClosureNotFn {
1042+
span: self.tcx.def_span(closure_def_id),
1043+
kind: expected_kind.as_str(),
1044+
});
1045+
self.note_obligation_cause(&mut err, &obligation);
1046+
self.point_at_returns_when_relevant(&mut err, &obligation);
1047+
return Some(err.emit());
1048+
}
1049+
}
1050+
}
1051+
1052+
None
9901053
}
9911054

9921055
fn fn_arg_obligation(
@@ -1493,6 +1556,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
14931556
closure_def_id: DefId,
14941557
found_kind: ty::ClosureKind,
14951558
kind: ty::ClosureKind,
1559+
trait_prefix: &'static str,
14961560
) -> DiagnosticBuilder<'tcx>;
14971561

14981562
fn report_cyclic_signature_error(
@@ -3376,6 +3440,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33763440
closure_def_id: DefId,
33773441
found_kind: ty::ClosureKind,
33783442
kind: ty::ClosureKind,
3443+
trait_prefix: &'static str,
33793444
) -> DiagnosticBuilder<'tcx> {
33803445
let closure_span = self.tcx.def_span(closure_def_id);
33813446

@@ -3384,6 +3449,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33843449
expected: kind,
33853450
found: found_kind,
33863451
cause_span: obligation.cause.span,
3452+
trait_prefix,
33873453
fn_once_label: None,
33883454
fn_mut_label: None,
33893455
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// edition:2021
2+
3+
// FIXME(async_closures): This needs a better error message!
4+
5+
#![feature(async_closure)]
6+
7+
fn main() {
8+
fn needs_fn<T>(_: impl FnMut() -> T) {}
9+
10+
let mut x = 1;
11+
needs_fn(async || {
12+
//~^ ERROR async closure does not implement `FnMut` because it captures state from its environment
13+
x += 1;
14+
});
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error: async closure does not implement `FnMut` because it captures state from its environment
2+
--> $DIR/not-fn.rs:11:14
3+
|
4+
LL | needs_fn(async || {
5+
| -------- ^^^^^^^^
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `needs_fn`
10+
--> $DIR/not-fn.rs:8:28
11+
|
12+
LL | fn needs_fn<T>(_: impl FnMut() -> T) {}
13+
| ^^^^^^^^^^^^ required by this bound in `needs_fn`
14+
15+
error: aborting due to 1 previous error
16+

tests/ui/async-await/async-closures/wrong-fn-kind.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ fn main() {
99

1010
let mut x = 1;
1111
needs_async_fn(async || {
12-
//~^ ERROR i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>
13-
// FIXME: Should say "closure is `async FnMut` but it needs `async Fn`" or sth.
12+
//~^ ERROR expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut`
1413
x += 1;
1514
});
1615
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
error[E0277]: the trait bound `i16: ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not satisfied
1+
error[E0525]: expected a closure that implements the `async Fn` trait, but this closure only implements `async FnMut`
22
--> $DIR/wrong-fn-kind.rs:11:20
33
|
44
LL | needs_async_fn(async || {
5-
| _____--------------_^
5+
| -------------- -^^^^^^^
6+
| | |
7+
| _____|______________this closure implements `async FnMut`, not `async Fn`
68
| | |
79
| | required by a bound introduced by this call
810
LL | |
9-
LL | | // FIXME: Should say "closure is `async FnMut` but it needs `async Fn`" or sth.
1011
LL | | x += 1;
12+
| | - closure is `async FnMut` because it mutates the variable `x` here
1113
LL | | });
12-
| |_____^ the trait `ops::async_function::internal_implementation_detail::AsyncFnKindHelper<i8>` is not implemented for `i16`
14+
| |_____- the requirement to implement `async Fn` derives from here
1315
|
1416
note: required by a bound in `needs_async_fn`
1517
--> $DIR/wrong-fn-kind.rs:8:31
@@ -19,4 +21,4 @@ LL | fn needs_async_fn(_: impl async Fn()) {}
1921

2022
error: aborting due to 1 previous error
2123

22-
For more information about this error, try `rustc --explain E0277`.
24+
For more information about this error, try `rustc --explain E0525`.

0 commit comments

Comments
 (0)