about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-08-07 12:36:41 -0400
committerMichael Goulet <michael@errs.io>2024-08-08 14:07:31 -0400
commitec1c424293c04258da127ed4dfc8d4289834efe6 (patch)
treee93b77620ed6b1b0adc919be489bf03eb16eb363
parent9bad7ba324099d124c77c5b06aebf68e11763f7b (diff)
downloadrust-ec1c424293c04258da127ed4dfc8d4289834efe6.tar.gz
rust-ec1c424293c04258da127ed4dfc8d4289834efe6.zip
Don't implement AsyncFn for FnDef/FnPtr that wouldnt implement Fn
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs16
-rw-r--r--tests/ui/async-await/async-closures/fn-exception-target-features.rs17
-rw-r--r--tests/ui/async-await/async-closures/fn-exception-target-features.stderr17
-rw-r--r--tests/ui/async-await/async-closures/fn-exception.rs21
-rw-r--r--tests/ui/async-await/async-closures/fn-exception.stderr31
6 files changed, 139 insertions, 23 deletions
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 e69d8d84d7d..00837f7cdd8 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<I:
             ))
         }
 
-        ty::FnDef(..) | ty::FnPtr(..) => {
-            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<I:
     }
 }
 
+fn fn_item_to_async_callable<I: Interner>(
+    cx: I,
+    bound_sig: ty::Binder<I, ty::FnSig<I>>,
+) -> Result<(ty::Binder<I, AsyncCallableRelevantTypes<I>>, Vec<I::Predicate>), 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<I: Interner>(
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 2085d3da443..9de62031311 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 00000000000..de62fc8bf7e
--- /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<Box<dyn Future<Output = ()> + '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 00000000000..396167f4070
--- /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<Box<(dyn Future<Output = ()> + '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<Box<(dyn Future<Output = ()> + '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 00000000000..0e06ebf48a4
--- /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<Box<dyn Future<Output = ()> + 'static>>;
+}
+
+unsafe extern "C" {
+    pub safe fn abi() -> Pin<Box<dyn Future<Output = ()> + '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 00000000000..bacd079af0f
--- /dev/null
+++ b/tests/ui/async-await/async-closures/fn-exception.stderr
@@ -0,0 +1,31 @@
+error[E0277]: the trait bound `unsafe fn() -> Pin<Box<(dyn Future<Output = ()> + '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<Box<(dyn Future<Output = ()> + '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<Box<(dyn Future<Output = ()> + '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<Box<(dyn Future<Output = ()> + '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`.