Skip to content

Commit d108915

Browse files
authored
Unrolled build for rust-lang#121119
Rollup merge of rust-lang#121119 - compiler-errors:async-fn-kind-errs, r=oli-obk Make `async Fn` trait kind errors better 1. Make it so that async closures with the wrong closurekind actually report a useful error 2. Explain why async closures can sometimes not implement `Fn`/`FnMut` (because they capture things) r? oli-obk
2 parents a447249 + acb201a commit d108915

File tree

7 files changed

+154
-37
lines changed

7 files changed

+154
-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

+98-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,102 @@ 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 `AsyncFnKindHelper` is not implemented, that means that the closure kind
967+
// doesn't extend the goal kind. This is worth reporting, but we can only do so
968+
// if we actually know which closure this goal comes from, so look at the cause
969+
// to see if we can extract that information.
970+
if Some(trait_ref.def_id()) == self.tcx.lang_items().async_fn_kind_helper()
971+
&& let Some(found_kind) = trait_ref.skip_binder().args.type_at(0).to_opt_closure_kind()
972+
&& let Some(expected_kind) =
973+
trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
968974
&& !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-
)
981975
{
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
976+
if let Some((_, Some(parent))) = obligation.cause.code().parent() {
977+
// If we have a derived obligation, then the parent will be a `AsyncFn*` goal.
978+
trait_ref = parent.to_poly_trait_ref();
979+
} else if let &ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } =
980+
obligation.cause.code()
981+
&& let Some(typeck_results) = &self.typeck_results
982+
&& let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) =
983+
*typeck_results.node_type(arg_hir_id).kind()
984+
{
985+
// Otherwise, extract the closure kind from the obligation.
986+
let mut err = self.report_closure_error(
987+
&obligation,
988+
closure_def_id,
989+
found_kind,
990+
expected_kind,
991+
"async ",
992+
);
993+
self.note_obligation_cause(&mut err, &obligation);
994+
self.point_at_returns_when_relevant(&mut err, &obligation);
995+
return Some(err.emit());
996+
}
997+
}
998+
999+
let self_ty = trait_ref.self_ty().skip_binder();
1000+
1001+
if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_ref.def_id()) {
1002+
let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
1003+
ty::Closure(def_id, args) => {
1004+
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
1005+
}
1006+
ty::CoroutineClosure(def_id, args) => (
1007+
def_id,
1008+
args.as_coroutine_closure()
1009+
.coroutine_closure_sig()
1010+
.map_bound(|sig| sig.tupled_inputs_ty),
1011+
Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
1012+
),
1013+
_ => return None,
1014+
};
1015+
1016+
let expected_args = trait_ref.map_bound(|trait_ref| trait_ref.args.type_at(1));
1017+
1018+
// Verify that the arguments are compatible. If the signature is
1019+
// mismatched, then we have a totally different error to report.
1020+
if self.enter_forall(found_args, |found_args| {
1021+
self.enter_forall(expected_args, |expected_args| {
1022+
!self.can_sub(obligation.param_env, expected_args, found_args)
1023+
})
1024+
}) {
1025+
return None;
1026+
}
1027+
1028+
if let Some(found_kind) = self.closure_kind(self_ty)
1029+
&& !found_kind.extends(expected_kind)
1030+
{
1031+
let mut err = self.report_closure_error(
1032+
&obligation,
1033+
closure_def_id,
1034+
found_kind,
1035+
expected_kind,
1036+
"",
1037+
);
1038+
self.note_obligation_cause(&mut err, &obligation);
1039+
self.point_at_returns_when_relevant(&mut err, &obligation);
1040+
return Some(err.emit());
1041+
}
1042+
1043+
// If the closure has captures, then perhaps the reason that the trait
1044+
// is unimplemented is because async closures don't implement `Fn`/`FnMut`
1045+
// if they have captures.
1046+
if let Some(by_ref_captures) = by_ref_captures
1047+
&& let ty::FnPtr(sig) = by_ref_captures.kind()
1048+
&& !sig.skip_binder().output().is_unit()
1049+
{
1050+
let mut err = self.tcx.dcx().create_err(AsyncClosureNotFn {
1051+
span: self.tcx.def_span(closure_def_id),
1052+
kind: expected_kind.as_str(),
1053+
});
1054+
self.note_obligation_cause(&mut err, &obligation);
1055+
self.point_at_returns_when_relevant(&mut err, &obligation);
1056+
return Some(err.emit());
1057+
}
9891058
}
1059+
None
9901060
}
9911061

9921062
fn fn_arg_obligation(
@@ -1493,6 +1563,7 @@ pub(super) trait InferCtxtPrivExt<'tcx> {
14931563
closure_def_id: DefId,
14941564
found_kind: ty::ClosureKind,
14951565
kind: ty::ClosureKind,
1566+
trait_prefix: &'static str,
14961567
) -> DiagnosticBuilder<'tcx>;
14971568

14981569
fn report_cyclic_signature_error(
@@ -3376,6 +3447,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33763447
closure_def_id: DefId,
33773448
found_kind: ty::ClosureKind,
33783449
kind: ty::ClosureKind,
3450+
trait_prefix: &'static str,
33793451
) -> DiagnosticBuilder<'tcx> {
33803452
let closure_span = self.tcx.def_span(closure_def_id);
33813453

@@ -3384,6 +3456,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
33843456
expected: kind,
33853457
found: found_kind,
33863458
cause_span: obligation.cause.span,
3459+
trait_prefix,
33873460
fn_once_label: None,
33883461
fn_mut_label: None,
33893462
};
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)