Skip to content

Commit 1458b35

Browse files
Rollup merge of rust-lang#137910 - compiler-errors:async-fn-goal-error, r=oli-obk
Improve error message for `AsyncFn` trait failure for RPIT Use a `WellFormedDerived` obligation cause to make sure we can turn an `AsyncFnKindHelper` trait goal into its parent `AsyncFn*` goal, then fix the logic for reporting `AsyncFn*` kind mismatches. Best reviewed without whitespace. Fixes rust-lang#137905 r? oli-obk
2 parents 38b4820 + e213f4b commit 1458b35

File tree

5 files changed

+101
-54
lines changed

5 files changed

+101
-54
lines changed

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

+63-52
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
829829
&& let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) =
830830
*typeck_results.node_type(arg_hir_id).kind()
831831
{
832-
// Otherwise, extract the closure kind from the obligation.
832+
// Otherwise, extract the closure kind from the obligation,
833+
// but only if we actually have an argument to deduce the
834+
// closure type from...
833835
let mut err = self.report_closure_error(
834836
&obligation,
835837
closure_def_id,
@@ -844,63 +846,72 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
844846

845847
let self_ty = trait_pred.self_ty().skip_binder();
846848

847-
if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) {
848-
let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
849-
ty::Closure(def_id, args) => {
850-
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
851-
}
852-
ty::CoroutineClosure(def_id, args) => (
853-
def_id,
854-
args.as_coroutine_closure()
855-
.coroutine_closure_sig()
856-
.map_bound(|sig| sig.tupled_inputs_ty),
857-
Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
858-
),
859-
_ => return None,
849+
let (expected_kind, trait_prefix) =
850+
if let Some(expected_kind) = self.tcx.fn_trait_kind_from_def_id(trait_pred.def_id()) {
851+
(expected_kind, "")
852+
} else if let Some(expected_kind) =
853+
self.tcx.async_fn_trait_kind_from_def_id(trait_pred.def_id())
854+
{
855+
(expected_kind, "Async")
856+
} else {
857+
return None;
860858
};
861859

862-
let expected_args =
863-
trait_pred.map_bound(|trait_pred| trait_pred.trait_ref.args.type_at(1));
864-
865-
// Verify that the arguments are compatible. If the signature is
866-
// mismatched, then we have a totally different error to report.
867-
if self.enter_forall(found_args, |found_args| {
868-
self.enter_forall(expected_args, |expected_args| {
869-
!self.can_eq(obligation.param_env, expected_args, found_args)
870-
})
871-
}) {
872-
return None;
860+
let (closure_def_id, found_args, by_ref_captures) = match *self_ty.kind() {
861+
ty::Closure(def_id, args) => {
862+
(def_id, args.as_closure().sig().map_bound(|sig| sig.inputs()[0]), None)
873863
}
864+
ty::CoroutineClosure(def_id, args) => (
865+
def_id,
866+
args.as_coroutine_closure()
867+
.coroutine_closure_sig()
868+
.map_bound(|sig| sig.tupled_inputs_ty),
869+
Some(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
870+
),
871+
_ => return None,
872+
};
874873

875-
if let Some(found_kind) = self.closure_kind(self_ty)
876-
&& !found_kind.extends(expected_kind)
877-
{
878-
let mut err = self.report_closure_error(
879-
&obligation,
880-
closure_def_id,
881-
found_kind,
882-
expected_kind,
883-
"",
884-
);
885-
self.note_obligation_cause(&mut err, &obligation);
886-
return Some(err.emit());
887-
}
874+
let expected_args = trait_pred.map_bound(|trait_pred| trait_pred.trait_ref.args.type_at(1));
888875

889-
// If the closure has captures, then perhaps the reason that the trait
890-
// is unimplemented is because async closures don't implement `Fn`/`FnMut`
891-
// if they have captures.
892-
if let Some(by_ref_captures) = by_ref_captures
893-
&& let ty::FnPtr(sig_tys, _) = by_ref_captures.kind()
894-
&& !sig_tys.skip_binder().output().is_unit()
895-
{
896-
let mut err = self.dcx().create_err(AsyncClosureNotFn {
897-
span: self.tcx.def_span(closure_def_id),
898-
kind: expected_kind.as_str(),
899-
});
900-
self.note_obligation_cause(&mut err, &obligation);
901-
return Some(err.emit());
902-
}
876+
// Verify that the arguments are compatible. If the signature is
877+
// mismatched, then we have a totally different error to report.
878+
if self.enter_forall(found_args, |found_args| {
879+
self.enter_forall(expected_args, |expected_args| {
880+
!self.can_eq(obligation.param_env, expected_args, found_args)
881+
})
882+
}) {
883+
return None;
903884
}
885+
886+
if let Some(found_kind) = self.closure_kind(self_ty)
887+
&& !found_kind.extends(expected_kind)
888+
{
889+
let mut err = self.report_closure_error(
890+
&obligation,
891+
closure_def_id,
892+
found_kind,
893+
expected_kind,
894+
trait_prefix,
895+
);
896+
self.note_obligation_cause(&mut err, &obligation);
897+
return Some(err.emit());
898+
}
899+
900+
// If the closure has captures, then perhaps the reason that the trait
901+
// is unimplemented is because async closures don't implement `Fn`/`FnMut`
902+
// if they have captures.
903+
if let Some(by_ref_captures) = by_ref_captures
904+
&& let ty::FnPtr(sig_tys, _) = by_ref_captures.kind()
905+
&& !sig_tys.skip_binder().output().is_unit()
906+
{
907+
let mut err = self.dcx().create_err(AsyncClosureNotFn {
908+
span: self.tcx.def_span(closure_def_id),
909+
kind: expected_kind.as_str(),
910+
});
911+
self.note_obligation_cause(&mut err, &obligation);
912+
return Some(err.emit());
913+
}
914+
904915
None
905916
}
906917

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -3191,7 +3191,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
31913191
false
31923192
};
31933193

3194-
if !is_upvar_tys_infer_tuple {
3194+
let is_builtin_async_fn_trait =
3195+
tcx.async_fn_trait_kind_from_def_id(data.parent_trait_pred.def_id()).is_some();
3196+
3197+
if !is_upvar_tys_infer_tuple && !is_builtin_async_fn_trait {
31953198
let ty_str = tcx.short_string(ty, err.long_ty_path());
31963199
let msg = format!("required because it appears within the type `{ty_str}`");
31973200
match ty.kind() {

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
983983
return Err(SelectionError::Unimplemented);
984984
}
985985
} else {
986-
nested.push(obligation.with(
986+
nested.push(Obligation::new(
987987
self.tcx(),
988+
obligation.derived_cause(ObligationCauseCode::BuiltinDerived),
989+
obligation.param_env,
988990
ty::TraitRef::new(
989991
self.tcx(),
990992
self.tcx().require_lang_item(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//@ edition: 2024
2+
3+
// Make sure the error message is understandable when an `AsyncFn` goal is not satisfied
4+
// (due to closure kind), and that goal originates from an RPIT.
5+
6+
fn repro(foo: impl Into<bool>) -> impl AsyncFn() {
7+
let inner_fn = async move || {
8+
//~^ ERROR expected a closure that implements the `AsyncFn` trait
9+
let _ = foo.into();
10+
};
11+
inner_fn
12+
}
13+
14+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0525]: expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnOnce`
2+
--> $DIR/kind-due-to-rpit.rs:7:20
3+
|
4+
LL | fn repro(foo: impl Into<bool>) -> impl AsyncFn() {
5+
| -------------- the requirement to implement `AsyncFn` derives from here
6+
LL | let inner_fn = async move || {
7+
| ^^^^^^^^^^^^^ this closure implements `AsyncFnOnce`, not `AsyncFn`
8+
LL |
9+
LL | let _ = foo.into();
10+
| --- closure is `AsyncFnOnce` because it moves the variable `foo` out of its environment
11+
LL | };
12+
LL | inner_fn
13+
| -------- return type was inferred to be `{async closure@$DIR/kind-due-to-rpit.rs:7:20: 7:33}` here
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0525`.

0 commit comments

Comments
 (0)