diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 75613a2c55555..733e2f93b2535 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -37,7 +37,11 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<' } ty::InstanceDef::FnPtrShim(def_id, ty) => { let trait_ = tcx.trait_of_item(def_id).unwrap(); - let adjustment = match tcx.fn_trait_kind_from_def_id(trait_) { + // Supports `Fn` or `async Fn` traits. + let adjustment = match tcx + .fn_trait_kind_from_def_id(trait_) + .or_else(|| tcx.async_fn_trait_kind_from_def_id(trait_)) + { Some(ty::ClosureKind::FnOnce) => Adjustment::Identity, Some(ty::ClosureKind::Fn) => Adjustment::Deref { source: DerefSource::ImmRef }, Some(ty::ClosureKind::FnMut) => Adjustment::Deref { source: DerefSource::MutRef }, diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index 3b902dd80f59b..af533d8db7149 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -281,7 +281,58 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } // Coroutine-closures don't implement `Fn` traits the normal way. - ty::CoroutineClosure(..) => Err(NoSolution), + // Instead, they always implement `FnOnce`, but only implement + // `FnMut`/`Fn` if they capture no upvars, since those may borrow + // from the closure. + ty::CoroutineClosure(def_id, args) => { + let args = args.as_coroutine_closure(); + let kind_ty = args.kind_ty(); + let sig = args.coroutine_closure_sig().skip_binder(); + + let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() { + if !closure_kind.extends(goal_kind) { + return Err(NoSolution); + } + + // If `Fn`/`FnMut`, we only implement this goal if we + // have no captures. + let no_borrows = match args.tupled_upvars_ty().kind() { + ty::Tuple(tys) => tys.is_empty(), + ty::Error(_) => false, + _ => bug!("tuple_fields called on non-tuple"), + }; + if closure_kind != ty::ClosureKind::FnOnce && !no_borrows { + return Err(NoSolution); + } + + coroutine_closure_to_certain_coroutine( + tcx, + goal_kind, + // No captures by ref, so this doesn't matter. + tcx.lifetimes.re_static, + def_id, + args, + sig, + ) + } else { + // Closure kind is not yet determined, so we return ambiguity unless + // the expected kind is `FnOnce` as that is always implemented. + if goal_kind != ty::ClosureKind::FnOnce { + return Ok(None); + } + + coroutine_closure_to_ambiguous_coroutine( + tcx, + goal_kind, // No captures by ref, so this doesn't matter. + tcx.lifetimes.re_static, + def_id, + args, + sig, + ) + }; + + Ok(Some(args.coroutine_closure_sig().rebind((sig.tupled_inputs_ty, coroutine_ty)))) + } ty::Bool | ty::Char @@ -313,6 +364,19 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>( } } +/// Relevant types for an async callable, including its inputs, output, +/// and the return type you get from awaiting the output. +#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)] +pub(in crate::solve) struct AsyncCallableRelevantTypes<'tcx> { + pub tupled_inputs_ty: Ty<'tcx>, + /// Type returned by calling the closure + /// i.e. `f()`. + pub output_coroutine_ty: Ty<'tcx>, + /// Type returned by `await`ing the output + /// i.e. `f().await`. + pub coroutine_return_ty: Ty<'tcx>, +} + // Returns a binder of the tupled inputs types, output type, and coroutine type // from a builtin coroutine-closure type. If we don't yet know the closure kind of // the coroutine-closure, emit an additional trait predicate for `AsyncFnKindHelper` @@ -323,8 +387,10 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc self_ty: Ty<'tcx>, goal_kind: ty::ClosureKind, env_region: ty::Region<'tcx>, -) -> Result<(ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>)>, Vec>), NoSolution> -{ +) -> Result< + (ty::Binder<'tcx, AsyncCallableRelevantTypes<'tcx>>, Vec>), + NoSolution, +> { match *self_ty.kind() { ty::CoroutineClosure(def_id, args) => { let args = args.as_coroutine_closure(); @@ -335,24 +401,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc if !closure_kind.extends(goal_kind) { return Err(NoSolution); } - sig.to_coroutine_given_kind_and_upvars( - tcx, - args.parent_args(), - tcx.coroutine_for_closure(def_id), - goal_kind, - env_region, - args.tupled_upvars_ty(), - args.coroutine_captures_by_ref_ty(), + + coroutine_closure_to_certain_coroutine( + tcx, goal_kind, env_region, def_id, args, sig, ) } else { - let async_fn_kind_trait_def_id = - tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); - let upvars_projection_def_id = tcx - .associated_items(async_fn_kind_trait_def_id) - .filter_by_name_unhygienic(sym::Upvars) - .next() - .unwrap() - .def_id; // When we don't know the closure kind (and therefore also the closure's upvars, // which are computed at the same time), we must delay the computation of the // generator's upvars. We do this using the `AsyncFnKindHelper`, which as a trait @@ -363,38 +416,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc nested.push( ty::TraitRef::new( tcx, - async_fn_kind_trait_def_id, + tcx.require_lang_item(LangItem::AsyncFnKindHelper, None), [kind_ty, Ty::from_closure_kind(tcx, goal_kind)], ) .to_predicate(tcx), ); - let tupled_upvars_ty = Ty::new_projection( - tcx, - upvars_projection_def_id, - [ - ty::GenericArg::from(kind_ty), - Ty::from_closure_kind(tcx, goal_kind).into(), - env_region.into(), - sig.tupled_inputs_ty.into(), - args.tupled_upvars_ty().into(), - args.coroutine_captures_by_ref_ty().into(), - ], - ); - sig.to_coroutine( - tcx, - args.parent_args(), - Ty::from_closure_kind(tcx, goal_kind), - tcx.coroutine_for_closure(def_id), - tupled_upvars_ty, + + coroutine_closure_to_ambiguous_coroutine( + tcx, goal_kind, env_region, def_id, args, sig, ) }; Ok(( - args.coroutine_closure_sig().rebind(( - sig.tupled_inputs_ty, - sig.return_ty, - coroutine_ty, - )), + args.coroutine_closure_sig().rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: sig.tupled_inputs_ty, + output_coroutine_ty: coroutine_ty, + coroutine_return_ty: sig.return_ty, + }), nested, )) } @@ -418,7 +456,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc .def_id; let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); Ok(( - bound_sig.rebind((Ty::new_tup(tcx, sig.inputs()), sig.output(), future_output_ty)), + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: Ty::new_tup(tcx, sig.inputs()), + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), nested, )) } @@ -469,7 +511,14 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc .unwrap() .def_id; let future_output_ty = Ty::new_projection(tcx, future_output_def_id, [sig.output()]); - Ok((bound_sig.rebind((sig.inputs()[0], sig.output(), future_output_ty)), nested)) + Ok(( + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: sig.inputs()[0], + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), + nested, + )) } ty::Bool @@ -502,6 +551,68 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc } } +/// Given a coroutine-closure, project to its returned coroutine when we are *certain* +/// that the closure's kind is compatible with the goal. +fn coroutine_closure_to_certain_coroutine<'tcx>( + tcx: TyCtxt<'tcx>, + goal_kind: ty::ClosureKind, + goal_region: ty::Region<'tcx>, + def_id: DefId, + args: ty::CoroutineClosureArgs<'tcx>, + sig: ty::CoroutineClosureSignature<'tcx>, +) -> Ty<'tcx> { + sig.to_coroutine_given_kind_and_upvars( + tcx, + args.parent_args(), + tcx.coroutine_for_closure(def_id), + goal_kind, + goal_region, + args.tupled_upvars_ty(), + args.coroutine_captures_by_ref_ty(), + ) +} + +/// Given a coroutine-closure, project to its returned coroutine when we are *not certain* +/// that the closure's kind is compatible with the goal, and therefore also don't know +/// yet what the closure's upvars are. +/// +/// Note that we do not also push a `AsyncFnKindHelper` goal here. +fn coroutine_closure_to_ambiguous_coroutine<'tcx>( + tcx: TyCtxt<'tcx>, + goal_kind: ty::ClosureKind, + goal_region: ty::Region<'tcx>, + def_id: DefId, + args: ty::CoroutineClosureArgs<'tcx>, + sig: ty::CoroutineClosureSignature<'tcx>, +) -> Ty<'tcx> { + let async_fn_kind_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None); + let upvars_projection_def_id = tcx + .associated_items(async_fn_kind_trait_def_id) + .filter_by_name_unhygienic(sym::Upvars) + .next() + .unwrap() + .def_id; + let tupled_upvars_ty = Ty::new_projection( + tcx, + upvars_projection_def_id, + [ + ty::GenericArg::from(args.kind_ty()), + Ty::from_closure_kind(tcx, goal_kind).into(), + goal_region.into(), + sig.tupled_inputs_ty.into(), + args.tupled_upvars_ty().into(), + args.coroutine_captures_by_ref_ty().into(), + ], + ); + sig.to_coroutine( + tcx, + args.parent_args(), + Ty::from_closure_kind(tcx, goal_kind), + tcx.coroutine_for_closure(def_id), + tupled_upvars_ty, + ) +} + /// Assemble a list of predicates that would be present on a theoretical /// user impl for an object type. These predicates must be checked any time /// we assemble a built-in object candidate for an object type, since they diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs index aa8cc3667cd7b..3aba5c85abc3a 100644 --- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs @@ -1,5 +1,6 @@ use crate::traits::{check_args_compatible, specialization_graph}; +use super::assembly::structural_traits::AsyncCallableRelevantTypes; use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, GoalSource}; use rustc_hir::def::DefKind; @@ -392,46 +393,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> { goal_kind, env_region, )?; - let output_is_sized_pred = - tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| { - ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) - }); + let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( + |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_ty]) + }, + ); let pred = tupled_inputs_and_output_and_coroutine - .map_bound(|(inputs, output, coroutine)| { - let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) { - sym::CallOnceFuture => ( - ty::AliasTy::new( - tcx, - goal.predicate.def_id(), - [goal.predicate.self_ty(), inputs], + .map_bound( + |AsyncCallableRelevantTypes { + tupled_inputs_ty, + output_coroutine_ty, + coroutine_return_ty, + }| { + let (projection_ty, term) = match tcx.item_name(goal.predicate.def_id()) { + sym::CallOnceFuture => ( + ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ), + output_coroutine_ty.into(), ), - coroutine.into(), - ), - sym::CallMutFuture | sym::CallFuture => ( - ty::AliasTy::new( - tcx, - goal.predicate.def_id(), - [ - ty::GenericArg::from(goal.predicate.self_ty()), - inputs.into(), - env_region.into(), - ], + sym::CallMutFuture | sym::CallFuture => ( + ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [ + ty::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + env_region.into(), + ], + ), + output_coroutine_ty.into(), ), - coroutine.into(), - ), - sym::Output => ( - ty::AliasTy::new( - tcx, - goal.predicate.def_id(), - [ty::GenericArg::from(goal.predicate.self_ty()), inputs.into()], + sym::Output => ( + ty::AliasTy::new( + tcx, + goal.predicate.def_id(), + [ + ty::GenericArg::from(goal.predicate.self_ty()), + tupled_inputs_ty.into(), + ], + ), + coroutine_return_ty.into(), ), - output.into(), - ), - name => bug!("no such associated type: {name}"), - }; - ty::ProjectionPredicate { projection_ty, term } - }) + name => bug!("no such associated type: {name}"), + }; + ty::ProjectionPredicate { projection_ty, term } + }, + ) .to_predicate(tcx); // A built-in `AsyncFn` impl only holds if the output is sized. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 73bf66f66890f..eba6ba3f7b062 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -2,6 +2,7 @@ use crate::traits::supertrait_def_ids; +use super::assembly::structural_traits::AsyncCallableRelevantTypes; use super::assembly::{self, structural_traits, Candidate}; use super::{EvalCtxt, GoalSource, SolverMode}; use rustc_data_structures::fx::FxIndexSet; @@ -327,14 +328,19 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { // This region doesn't matter because we're throwing away the coroutine type tcx.lifetimes.re_static, )?; - let output_is_sized_pred = - tupled_inputs_and_output_and_coroutine.map_bound(|(_, output, _)| { - ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output]) - }); + let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound( + |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| { + ty::TraitRef::from_lang_item(tcx, LangItem::Sized, DUMMY_SP, [output_coroutine_ty]) + }, + ); let pred = tupled_inputs_and_output_and_coroutine - .map_bound(|(inputs, _, _)| { - ty::TraitRef::new(tcx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]) + .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| { + ty::TraitRef::new( + tcx, + goal.predicate.def_id(), + [goal.predicate.self_ty(), tupled_inputs_ty], + ) }) .to_predicate(tcx); // A built-in `AsyncFn` impl only holds if the output is sized. diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index d316149731e14..b8733bab27b21 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -923,14 +923,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { [self_ty, Ty::new_tup(tcx, sig.inputs())], ) }); + // We must additionally check that the return type impls `Future`. + + // FIXME(async_closures): Investigate this before stabilization. + // We instantiate this binder eagerly because the `confirm_future_candidate` + // method doesn't support higher-ranked futures, which the `AsyncFn` + // traits expressly allow the user to write. To fix this correctly, + // we'd need to instantiate trait bounds before we get to selection, + // like the new trait solver does. let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output()); nested.push(obligation.with( tcx, - sig.map_bound(|sig| { - ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]) - }), + ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]), )); + (trait_ref, Ty::from_closure_kind(tcx, ty::ClosureKind::Fn)) } ty::Closure(_, args) => { @@ -943,14 +951,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { [self_ty, sig.inputs()[0]], ) }); + // We must additionally check that the return type impls `Future`. + // See FIXME in last branch for why we instantiate the binder eagerly. let future_trait_def_id = tcx.require_lang_item(LangItem::Future, None); + let placeholder_output_ty = self.infcx.enter_forall_and_leak_universe(sig.output()); nested.push(obligation.with( tcx, - sig.map_bound(|sig| { - ty::TraitRef::new(tcx, future_trait_def_id, [sig.output()]) - }), + ty::TraitRef::new(tcx, future_trait_def_id, [placeholder_output_ty]), )); + (trait_ref, args.kind_ty()) } _ => bug!("expected callable type for AsyncFn candidate"), diff --git a/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs b/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs index 9c3b458cd3adf..be4364653159c 100644 --- a/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs +++ b/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs @@ -8,11 +8,15 @@ extern crate block_on; fn main() { block_on::block_on(async { - let x = async || {}; - async fn needs_async_fn_once(x: impl async FnOnce()) { x().await; } - needs_async_fn_once(x).await; + + needs_async_fn_once(async || {}).await; + + needs_async_fn_once(|| async {}).await; + + async fn foo() {} + needs_async_fn_once(foo).await; }); } diff --git a/tests/ui/async-await/async-closures/is-fn.rs b/tests/ui/async-await/async-closures/is-fn.rs new file mode 100644 index 0000000000000..64cc28e425f52 --- /dev/null +++ b/tests/ui/async-await/async-closures/is-fn.rs @@ -0,0 +1,24 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ build-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +#![feature(async_closure)] + +use std::future::Future; + +extern crate block_on; + +// Check that closures that don't capture any state may implement `Fn`. + +fn main() { + block_on::block_on(async { + async fn call_once(x: impl FnOnce(&'static str) -> F) -> F::Output { + x("hello, world").await + } + call_once(async |x: &'static str| { + println!("hello, {x}"); + }).await + }); +} diff --git a/tests/ui/async-await/async-closures/move-is-async-fn.rs b/tests/ui/async-await/async-closures/move-is-async-fn.rs index 0ccd137266d53..79e2298f6092b 100644 --- a/tests/ui/async-await/async-closures/move-is-async-fn.rs +++ b/tests/ui/async-await/async-closures/move-is-async-fn.rs @@ -2,7 +2,7 @@ //@ edition:2021 //@ build-pass -#![feature(async_closure)] +#![feature(async_closure, async_fn_traits)] extern crate block_on; @@ -15,7 +15,11 @@ fn main() { c().await; c().await; - fn is_static(_: T) {} - is_static(c); + fn is_static(_: &T) {} + is_static(&c); + + // Check that `<{async fn} as AsyncFnOnce>::CallOnceFuture` owns its captures. + fn call_once(f: F) -> F::CallOnceFuture { f() } + is_static(&call_once(c)); }); } diff --git a/tests/ui/async-await/async-closures/once.rs b/tests/ui/async-await/async-closures/once.rs index 55e4cedb267a9..761df3de44429 100644 --- a/tests/ui/async-await/async-closures/once.rs +++ b/tests/ui/async-await/async-closures/once.rs @@ -1,6 +1,8 @@ //@ aux-build:block-on.rs //@ edition:2021 //@ build-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver #![feature(async_closure)] @@ -8,11 +10,11 @@ use std::future::Future; extern crate block_on; -struct NoCopy; +// Check that async closures always implement `FnOnce` fn main() { block_on::block_on(async { - async fn call_once(x: impl Fn(&'static str) -> F) -> F::Output { + async fn call_once(x: impl FnOnce(&'static str) -> F) -> F::Output { x("hello, world").await } call_once(async |x: &'static str| { diff --git a/tests/ui/async-await/async-closures/refd.rs b/tests/ui/async-await/async-closures/refd.rs index 1d9bc1a601bdd..0b8d3d7aff554 100644 --- a/tests/ui/async-await/async-closures/refd.rs +++ b/tests/ui/async-await/async-closures/refd.rs @@ -2,8 +2,6 @@ //@ edition:2021 //@ build-pass -// check that `&{async-closure}` implements `AsyncFn`. - #![feature(async_closure)] extern crate block_on; @@ -13,6 +11,15 @@ struct NoCopy; fn main() { block_on::block_on(async { async fn call_once(x: impl async Fn()) { x().await } - call_once(&async || {}).await + + // check that `&{async-closure}` implements `async Fn`. + call_once(&async || {}).await; + + // check that `&{closure}` implements `async Fn`. + call_once(&|| async {}).await; + + // check that `&fndef` implements `async Fn`. + async fn foo() {} + call_once(&foo).await; }); } diff --git a/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs b/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs new file mode 100644 index 0000000000000..5680c05773765 --- /dev/null +++ b/tests/ui/async-await/async-fn/higher-ranked-async-fn.rs @@ -0,0 +1,31 @@ +//@ aux-build:block-on.rs +//@ edition:2018 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ build-pass (since it ICEs during mono) + +#![feature(async_closure)] + +extern crate block_on; + +use std::future::Future; + +async fn f(arg: &i32) {} + +async fn func(f: F) +where + F: async for<'a> Fn(&'a i32), +{ + let x: i32 = 0; + f(&x).await; +} + +fn main() { + block_on::block_on(async { + // Function + func(f).await; + + // Regular closure (doesn't capture) + func(|x: &i32| async {}); + }); +} diff --git a/tests/ui/async-await/async-fn/project.rs b/tests/ui/async-await/async-fn/project.rs new file mode 100644 index 0000000000000..302564bb9519f --- /dev/null +++ b/tests/ui/async-await/async-fn/project.rs @@ -0,0 +1,12 @@ +//@ edition:2018 +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ check-pass + +#![feature(async_closure, unboxed_closures, async_fn_traits)] + +fn project>(_: F) -> Option { None } + +fn main() { + let x: Option = project(|| async { 1i32 }); +}