From ec1c424293c04258da127ed4dfc8d4289834efe6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 7 Aug 2024 12:36:41 -0400 Subject: [PATCH] Don't implement AsyncFn for FnDef/FnPtr that wouldnt implement Fn --- .../src/solve/assembly/structural_traits.rs | 60 ++++++++++++------- .../src/traits/select/candidate_assembly.rs | 16 ++++- .../fn-exception-target-features.rs | 17 ++++++ .../fn-exception-target-features.stderr | 17 ++++++ .../async-closures/fn-exception.rs | 21 +++++++ .../async-closures/fn-exception.stderr | 31 ++++++++++ 6 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 tests/ui/async-await/async-closures/fn-exception-target-features.rs create mode 100644 tests/ui/async-await/async-closures/fn-exception-target-features.stderr create mode 100644 tests/ui/async-await/async-closures/fn-exception.rs create mode 100644 tests/ui/async-await/async-closures/fn-exception.stderr diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index e69d8d84d7d89..00837f7cdd87d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -458,28 +458,23 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable { - let bound_sig = self_ty.fn_sig(cx); - let sig = bound_sig.skip_binder(); - let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); - // `FnDef` and `FnPtr` only implement `AsyncFn*` when their - // return type implements `Future`. - let nested = vec![ - bound_sig - .rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])) - .upcast(cx), - ]; - let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); - let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); - Ok(( - bound_sig.rebind(AsyncCallableRelevantTypes { - tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), - output_coroutine_ty: sig.output(), - coroutine_return_ty: future_output_ty, - }), - nested, - )) + ty::FnDef(def_id, _) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() && !cx.has_target_features(def_id) { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } + } + ty::FnPtr(..) => { + let sig = self_ty.fn_sig(cx); + if sig.skip_binder().is_fn_trait_compatible() { + fn_item_to_async_callable(cx, sig) + } else { + Err(NoSolution) + } } + ty::Closure(_, args) => { let args = args.as_closure(); let bound_sig = args.sig(); @@ -563,6 +558,29 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable( + cx: I, + bound_sig: ty::Binder>, +) -> Result<(ty::Binder>, Vec), NoSolution> { + let sig = bound_sig.skip_binder(); + let future_trait_def_id = cx.require_lang_item(TraitSolverLangItem::Future); + // `FnDef` and `FnPtr` only implement `AsyncFn*` when their + // return type implements `Future`. + let nested = vec![ + bound_sig.rebind(ty::TraitRef::new(cx, future_trait_def_id, [sig.output()])).upcast(cx), + ]; + let future_output_def_id = cx.require_lang_item(TraitSolverLangItem::FutureOutput); + let future_output_ty = Ty::new_projection(cx, future_output_def_id, [sig.output()]); + Ok(( + bound_sig.rebind(AsyncCallableRelevantTypes { + tupled_inputs_ty: Ty::new_tup(cx, sig.inputs().as_slice()), + output_coroutine_ty: sig.output(), + coroutine_return_ty: future_output_ty, + }), + nested, + )) +} + /// 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( diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 2085d3da44341..9de62031311b7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -467,8 +467,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } candidates.vec.push(AsyncClosureCandidate); } - ty::FnDef(..) | ty::FnPtr(..) => { - candidates.vec.push(AsyncClosureCandidate); + // Provide an impl, but only for suitable `fn` pointers. + ty::FnPtr(sig) => { + if sig.is_fn_trait_compatible() { + candidates.vec.push(AsyncClosureCandidate); + } + } + // Provide an impl for suitable functions, rejecting `#[target_feature]` functions (RFC 2396). + ty::FnDef(def_id, _) => { + let tcx = self.tcx(); + if tcx.fn_sig(def_id).skip_binder().is_fn_trait_compatible() + && tcx.codegen_fn_attrs(def_id).target_features.is_empty() + { + candidates.vec.push(AsyncClosureCandidate); + } } _ => {} } diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.rs b/tests/ui/async-await/async-closures/fn-exception-target-features.rs new file mode 100644 index 0000000000000..de62fc8bf7e3c --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.rs @@ -0,0 +1,17 @@ +//@ edition: 2021 +//@ only-x86_64 + +#![feature(async_closure, target_feature_11)] +// `target_feature_11` just to test safe functions w/ target features. + +use std::pin::Pin; +use std::future::Future; + +#[target_feature(enable = "sse2")] +fn target_feature() -> Pin + 'static>> { todo!() } + +fn test(f: impl async Fn()) {} + +fn main() { + test(target_feature); //~ ERROR the trait bound +} diff --git a/tests/ui/async-await/async-closures/fn-exception-target-features.stderr b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr new file mode 100644 index 0000000000000..396167f407055 --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception-target-features.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `fn() -> Pin + 'static)>> {target_feature}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception-target-features.rs:16:10 + | +LL | test(target_feature); + | ---- ^^^^^^^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `fn() -> Pin + 'static)>> {target_feature}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception-target-features.rs:13:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/fn-exception.rs b/tests/ui/async-await/async-closures/fn-exception.rs new file mode 100644 index 0000000000000..0e06ebf48a4be --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception.rs @@ -0,0 +1,21 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +use std::pin::Pin; +use std::future::Future; + +unsafe extern "Rust" { + pub unsafe fn unsafety() -> Pin + 'static>>; +} + +unsafe extern "C" { + pub safe fn abi() -> Pin + 'static>>; +} + +fn test(f: impl async Fn()) {} + +fn main() { + test(unsafety); //~ ERROR the trait bound + test(abi); //~ ERROR the trait bound +} diff --git a/tests/ui/async-await/async-closures/fn-exception.stderr b/tests/ui/async-await/async-closures/fn-exception.stderr new file mode 100644 index 0000000000000..bacd079af0f2d --- /dev/null +++ b/tests/ui/async-await/async-closures/fn-exception.stderr @@ -0,0 +1,31 @@ +error[E0277]: the trait bound `unsafe fn() -> Pin + 'static)>> {unsafety}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception.rs:19:10 + | +LL | test(unsafety); + | ---- ^^^^^^^^ the trait `AsyncFn<()>` is not implemented for fn item `unsafe fn() -> Pin + 'static)>> {unsafety}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception.rs:16:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error[E0277]: the trait bound `extern "C" fn() -> Pin + 'static)>> {abi}: AsyncFn<()>` is not satisfied + --> $DIR/fn-exception.rs:20:10 + | +LL | test(abi); + | ---- ^^^ the trait `AsyncFn<()>` is not implemented for fn item `extern "C" fn() -> Pin + 'static)>> {abi}` + | | + | required by a bound introduced by this call + | +note: required by a bound in `test` + --> $DIR/fn-exception.rs:16:17 + | +LL | fn test(f: impl async Fn()) {} + | ^^^^^^^^^^ required by this bound in `test` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`.