Skip to content

Commit 08af64e

Browse files
Regular closures now built-in impls for AsyncFn*
1 parent 0dd4078 commit 08af64e

File tree

7 files changed

+318
-114
lines changed

7 files changed

+318
-114
lines changed

compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,78 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
394394
))
395395
}
396396

397-
ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) => Err(NoSolution),
397+
ty::FnDef(..) | ty::FnPtr(..) => {
398+
let bound_sig = self_ty.fn_sig(tcx);
399+
let sig = bound_sig.skip_binder();
400+
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
401+
// `FnDef` and `FnPtr` only implement `AsyncFn*` when their
402+
// return type implements `Future`.
403+
let nested = vec![
404+
bound_sig
405+
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
406+
.to_predicate(tcx),
407+
];
408+
let future_output_def_id = tcx
409+
.associated_items(future_trait_def_id)
410+
.filter_by_name_unhygienic(sym::Output)
411+
.next()
412+
.unwrap()
413+
.def_id;
414+
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
415+
Ok((
416+
bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)),
417+
nested,
418+
))
419+
}
420+
ty::Closure(_, args) => {
421+
let args = args.as_closure();
422+
let bound_sig = args.sig();
423+
let sig = bound_sig.skip_binder();
424+
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
425+
// `Closure`s only implement `AsyncFn*` when their return type
426+
// implements `Future`.
427+
let mut nested = vec![
428+
bound_sig
429+
.rebind(ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]))
430+
.to_predicate(tcx),
431+
];
432+
433+
// Additionally, we need to check that the closure kind
434+
// is still compatible.
435+
let kind_ty = args.kind_ty();
436+
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
437+
if !closure_kind.extends(goal_kind) {
438+
return Err(NoSolution);
439+
}
440+
} else {
441+
let async_fn_kind_trait_def_id =
442+
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
443+
// When we don't know the closure kind (and therefore also the closure's upvars,
444+
// which are computed at the same time), we must delay the computation of the
445+
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
446+
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
447+
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
448+
// will project to the right upvars for the generator, appending the inputs and
449+
// coroutine upvars respecting the closure kind.
450+
nested.push(
451+
ty::TraitRef::new(
452+
tcx,
453+
async_fn_kind_trait_def_id,
454+
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
455+
)
456+
.to_predicate(tcx),
457+
);
458+
}
459+
460+
let future_output_def_id = tcx
461+
.associated_items(future_trait_def_id)
462+
.filter_by_name_unhygienic(sym::Output)
463+
.next()
464+
.unwrap()
465+
.def_id;
466+
let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]);
467+
Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested))
468+
}
398469

399470
ty::Bool
400471
| ty::Char

compiler/rustc_trait_selection/src/traits/project.rs

+146-75
Original file line numberDiff line numberDiff line change
@@ -2450,99 +2450,170 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
24502450
) -> Progress<'tcx> {
24512451
let tcx = selcx.tcx();
24522452
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
2453-
let ty::CoroutineClosure(def_id, args) = *self_ty.kind() else {
2454-
unreachable!(
2455-
"expected coroutine-closure self type for coroutine-closure candidate, found {self_ty}"
2456-
)
2457-
};
2458-
let args = args.as_coroutine_closure();
2459-
let kind_ty = args.kind_ty();
2460-
let sig = args.coroutine_closure_sig().skip_binder();
24612453

24622454
let goal_kind =
24632455
tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
24642456
let env_region = match goal_kind {
24652457
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => obligation.predicate.args.region_at(2),
24662458
ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
24672459
};
2468-
24692460
let item_name = tcx.item_name(obligation.predicate.def_id);
2470-
let term = match item_name {
2471-
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
2472-
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
2473-
if !closure_kind.extends(goal_kind) {
2474-
bug!("we should not be confirming if the closure kind is not met");
2461+
2462+
let poly_cache_entry = match *self_ty.kind() {
2463+
ty::CoroutineClosure(def_id, args) => {
2464+
let args = args.as_coroutine_closure();
2465+
let kind_ty = args.kind_ty();
2466+
let sig = args.coroutine_closure_sig().skip_binder();
2467+
2468+
let term = match item_name {
2469+
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => {
2470+
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
2471+
if !closure_kind.extends(goal_kind) {
2472+
bug!("we should not be confirming if the closure kind is not met");
2473+
}
2474+
sig.to_coroutine_given_kind_and_upvars(
2475+
tcx,
2476+
args.parent_args(),
2477+
tcx.coroutine_for_closure(def_id),
2478+
goal_kind,
2479+
env_region,
2480+
args.tupled_upvars_ty(),
2481+
args.coroutine_captures_by_ref_ty(),
2482+
)
2483+
} else {
2484+
let async_fn_kind_trait_def_id =
2485+
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
2486+
let upvars_projection_def_id = tcx
2487+
.associated_items(async_fn_kind_trait_def_id)
2488+
.filter_by_name_unhygienic(sym::Upvars)
2489+
.next()
2490+
.unwrap()
2491+
.def_id;
2492+
// When we don't know the closure kind (and therefore also the closure's upvars,
2493+
// which are computed at the same time), we must delay the computation of the
2494+
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
2495+
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
2496+
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
2497+
// will project to the right upvars for the generator, appending the inputs and
2498+
// coroutine upvars respecting the closure kind.
2499+
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
2500+
let tupled_upvars_ty = Ty::new_projection(
2501+
tcx,
2502+
upvars_projection_def_id,
2503+
[
2504+
ty::GenericArg::from(kind_ty),
2505+
Ty::from_closure_kind(tcx, goal_kind).into(),
2506+
env_region.into(),
2507+
sig.tupled_inputs_ty.into(),
2508+
args.tupled_upvars_ty().into(),
2509+
args.coroutine_captures_by_ref_ty().into(),
2510+
],
2511+
);
2512+
sig.to_coroutine(
2513+
tcx,
2514+
args.parent_args(),
2515+
Ty::from_closure_kind(tcx, goal_kind),
2516+
tcx.coroutine_for_closure(def_id),
2517+
tupled_upvars_ty,
2518+
)
2519+
}
24752520
}
2476-
sig.to_coroutine_given_kind_and_upvars(
2521+
sym::Output => sig.return_ty,
2522+
name => bug!("no such associated type: {name}"),
2523+
};
2524+
let projection_ty = match item_name {
2525+
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
24772526
tcx,
2478-
args.parent_args(),
2479-
tcx.coroutine_for_closure(def_id),
2480-
goal_kind,
2481-
env_region,
2482-
args.tupled_upvars_ty(),
2483-
args.coroutine_captures_by_ref_ty(),
2484-
)
2485-
} else {
2486-
let async_fn_kind_trait_def_id =
2487-
tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
2488-
let upvars_projection_def_id = tcx
2489-
.associated_items(async_fn_kind_trait_def_id)
2490-
.filter_by_name_unhygienic(sym::Upvars)
2491-
.next()
2492-
.unwrap()
2493-
.def_id;
2494-
// When we don't know the closure kind (and therefore also the closure's upvars,
2495-
// which are computed at the same time), we must delay the computation of the
2496-
// generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait
2497-
// goal functions similarly to the old `ClosureKind` predicate, and ensures that
2498-
// the goal kind <= the closure kind. As a projection `AsyncFnKindHelper::Upvars`
2499-
// will project to the right upvars for the generator, appending the inputs and
2500-
// coroutine upvars respecting the closure kind.
2501-
// N.B. No need to register a `AsyncFnKindHelper` goal here, it's already in `nested`.
2502-
let tupled_upvars_ty = Ty::new_projection(
2527+
obligation.predicate.def_id,
2528+
[self_ty, sig.tupled_inputs_ty],
2529+
),
2530+
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
25032531
tcx,
2504-
upvars_projection_def_id,
2532+
obligation.predicate.def_id,
2533+
[ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
2534+
),
2535+
name => bug!("no such associated type: {name}"),
2536+
};
2537+
2538+
args.coroutine_closure_sig()
2539+
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
2540+
}
2541+
ty::FnDef(..) | ty::FnPtr(..) => {
2542+
let bound_sig = self_ty.fn_sig(tcx);
2543+
let sig = bound_sig.skip_binder();
2544+
2545+
let term = match item_name {
2546+
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
2547+
sym::Output => {
2548+
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
2549+
let future_output_def_id = tcx
2550+
.associated_items(future_trait_def_id)
2551+
.filter_by_name_unhygienic(sym::Output)
2552+
.next()
2553+
.unwrap()
2554+
.def_id;
2555+
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
2556+
}
2557+
name => bug!("no such associated type: {name}"),
2558+
};
2559+
let projection_ty = match item_name {
2560+
sym::CallOnceFuture | sym::Output => ty::AliasTy::new(
2561+
tcx,
2562+
obligation.predicate.def_id,
2563+
[self_ty, Ty::new_tup(tcx, sig.inputs())],
2564+
),
2565+
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
2566+
tcx,
2567+
obligation.predicate.def_id,
25052568
[
2506-
ty::GenericArg::from(kind_ty),
2507-
Ty::from_closure_kind(tcx, goal_kind).into(),
2569+
ty::GenericArg::from(self_ty),
2570+
Ty::new_tup(tcx, sig.inputs()).into(),
25082571
env_region.into(),
2509-
sig.tupled_inputs_ty.into(),
2510-
args.tupled_upvars_ty().into(),
2511-
args.coroutine_captures_by_ref_ty().into(),
25122572
],
2513-
);
2514-
sig.to_coroutine(
2515-
tcx,
2516-
args.parent_args(),
2517-
Ty::from_closure_kind(tcx, goal_kind),
2518-
tcx.coroutine_for_closure(def_id),
2519-
tupled_upvars_ty,
2520-
)
2521-
}
2573+
),
2574+
name => bug!("no such associated type: {name}"),
2575+
};
2576+
2577+
bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
25222578
}
2523-
sym::Output => sig.return_ty,
2524-
name => bug!("no such associated type: {name}"),
2525-
};
2526-
let projection_ty = match item_name {
2527-
sym::CallOnceFuture | sym::Output => {
2528-
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.tupled_inputs_ty])
2579+
ty::Closure(_, args) => {
2580+
let args = args.as_closure();
2581+
let bound_sig = args.sig();
2582+
let sig = bound_sig.skip_binder();
2583+
2584+
let term = match item_name {
2585+
sym::CallOnceFuture | sym::CallMutFuture | sym::CallFuture => sig.output(),
2586+
sym::Output => {
2587+
let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None);
2588+
let future_output_def_id = tcx
2589+
.associated_items(future_trait_def_id)
2590+
.filter_by_name_unhygienic(sym::Output)
2591+
.next()
2592+
.unwrap()
2593+
.def_id;
2594+
Ty::new_projection(tcx, future_output_def_id, [sig.output()])
2595+
}
2596+
name => bug!("no such associated type: {name}"),
2597+
};
2598+
let projection_ty = match item_name {
2599+
sym::CallOnceFuture | sym::Output => {
2600+
ty::AliasTy::new(tcx, obligation.predicate.def_id, [self_ty, sig.inputs()[0]])
2601+
}
2602+
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
2603+
tcx,
2604+
obligation.predicate.def_id,
2605+
[ty::GenericArg::from(self_ty), sig.inputs()[0].into(), env_region.into()],
2606+
),
2607+
name => bug!("no such associated type: {name}"),
2608+
};
2609+
2610+
bound_sig.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() })
25292611
}
2530-
sym::CallMutFuture | sym::CallFuture => ty::AliasTy::new(
2531-
tcx,
2532-
obligation.predicate.def_id,
2533-
[ty::GenericArg::from(self_ty), sig.tupled_inputs_ty.into(), env_region.into()],
2534-
),
2535-
name => bug!("no such associated type: {name}"),
2612+
_ => bug!("expected callable type for AsyncFn candidate"),
25362613
};
25372614

2538-
confirm_param_env_candidate(
2539-
selcx,
2540-
obligation,
2541-
args.coroutine_closure_sig()
2542-
.rebind(ty::ProjectionPredicate { projection_ty, term: term.into() }),
2543-
true,
2544-
)
2545-
.with_addl_obligations(nested)
2615+
confirm_param_env_candidate(selcx, obligation, poly_cache_entry, true)
2616+
.with_addl_obligations(nested)
25462617
}
25472618

25482619
fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(

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

+12-2
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
361361
}
362362
candidates.vec.push(AsyncClosureCandidate);
363363
}
364-
ty::Infer(ty::TyVar(_)) => {
365-
candidates.ambiguous = true;
364+
// Closures and fn pointers implement `AsyncFn*` if their return types
365+
// implement `Future`, which is checked later.
366+
ty::Closure(_, args) => {
367+
if let Some(closure_kind) = args.as_closure().kind_ty().to_opt_closure_kind()
368+
&& !closure_kind.extends(goal_kind)
369+
{
370+
return;
371+
}
372+
candidates.vec.push(AsyncClosureCandidate);
373+
}
374+
ty::FnDef(..) | ty::FnPtr(..) => {
375+
candidates.vec.push(AsyncClosureCandidate);
366376
}
367377
_ => {}
368378
}

0 commit comments

Comments
 (0)