about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-10 07:15:15 +0000
committerbors <bors@rust-lang.org>2024-02-10 07:15:15 +0000
commit757b8efed44eacbbc6baf4a393f77d23d5be90c8 (patch)
tree3d1898c476ccb02da378740f1440ebf590084fcb /tests
parent68125c72d389060fe9aaee8d87ebd834f417c9fc (diff)
parent540be28f6c2571e7be3ab3936b62635fa0d3caf3 (diff)
downloadrust-757b8efed44eacbbc6baf4a393f77d23d5be90c8.tar.gz
rust-757b8efed44eacbbc6baf4a393f77d23d5be90c8.zip
Auto merge of #120712 - compiler-errors:async-closures-harmonize, r=oli-obk
Harmonize `AsyncFn` implementations, make async closures conditionally impl `Fn*` traits

This PR implements several changes to the built-in and libcore-provided implementations of `Fn*` and `AsyncFn*` to address two problems:
1. async closures do not implement the `Fn*` family traits, leading to breakage: https://crater-reports.s3.amazonaws.com/pr-120361/index.html
2. *references* to async closures do not implement `AsyncFn*`, as a consequence of the existing blanket impls of the shape `AsyncFn for F where F: Fn, F::Output: Future`.

In order to fix (1.), we implement `Fn` traits appropriately for async closures. It turns out that async closures can:
* always implement `FnOnce`, meaning that they're drop-in compatible with `FnOnce`-bound combinators like `Option::map`.
* conditionally implement `Fn`/`FnMut` if they have no captures, which means that existing usages of async closures should *probably* work without breakage (crater checking this: https://github.com/rust-lang/rust/pull/120712#issuecomment-1930587805).

In order to fix (2.), we make all of the built-in callables implement `AsyncFn*` via built-in impls, and instead adjust the blanket impls for `AsyncFn*` provided by libcore to match the blanket impls for `Fn*`.
Diffstat (limited to 'tests')
-rw-r--r--tests/ui/async-await/async-closures/is-not-fn.rs5
-rw-r--r--tests/ui/async-await/async-closures/is-not-fn.stderr10
-rw-r--r--tests/ui/async-await/async-closures/once.rs22
-rw-r--r--tests/ui/async-await/async-closures/refd.rs18
-rw-r--r--tests/ui/async-await/async-fn/dyn-pos.stderr24
-rw-r--r--tests/ui/async-await/async-fn/simple.rs2
-rw-r--r--tests/ui/did_you_mean/bad-assoc-ty.stderr9
-rw-r--r--tests/ui/generic-associated-types/gat-in-trait-path.base.stderr2
-rw-r--r--tests/ui/generic-associated-types/issue-79422.base.stderr4
-rw-r--r--tests/ui/wf/wf-unsafe-trait-obj-match.stderr4
10 files changed, 77 insertions, 23 deletions
diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs
index 94c8e8563bd..40b0febbf06 100644
--- a/tests/ui/async-await/async-closures/is-not-fn.rs
+++ b/tests/ui/async-await/async-closures/is-not-fn.rs
@@ -5,8 +5,5 @@
 fn main() {
     fn needs_fn(x: impl FnOnce()) {}
     needs_fn(async || {});
-    //~^ ERROR expected a `FnOnce()` closure, found `{coroutine-closure@
-    // FIXME(async_closures): This should explain in more detail how async fns don't
-    // implement the regular `Fn` traits. Or maybe we should just fix it and make them
-    // when there are no upvars or whatever.
+    //~^ ERROR expected `{coroutine-closure@is-not-fn.rs:7:14}` to be a closure that returns `()`
 }
diff --git a/tests/ui/async-await/async-closures/is-not-fn.stderr b/tests/ui/async-await/async-closures/is-not-fn.stderr
index 12da4b1fc6f..6169cee85fd 100644
--- a/tests/ui/async-await/async-closures/is-not-fn.stderr
+++ b/tests/ui/async-await/async-closures/is-not-fn.stderr
@@ -1,13 +1,13 @@
-error[E0277]: expected a `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
+error[E0271]: expected `{coroutine-closure@is-not-fn.rs:7:14}` to be a closure that returns `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:7:23: 7:25}`
   --> $DIR/is-not-fn.rs:7:14
    |
 LL |     needs_fn(async || {});
-   |     -------- ^^^^^^^^^^^ expected an `FnOnce()` closure, found `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
+   |     -------- ^^^^^^^^^^^ expected `()`, found `async` closure body
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the trait `FnOnce<()>` is not implemented for `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}`
-   = note: wrap the `{coroutine-closure@$DIR/is-not-fn.rs:7:14: 7:22}` in a closure with no arguments: `|| { /* code */ }`
+   = note:         expected unit type `()`
+           found `async` closure body `{async closure body@$DIR/is-not-fn.rs:7:23: 7:25}`
 note: required by a bound in `needs_fn`
   --> $DIR/is-not-fn.rs:6:25
    |
@@ -16,4 +16,4 @@ LL |     fn needs_fn(x: impl FnOnce()) {}
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0271`.
diff --git a/tests/ui/async-await/async-closures/once.rs b/tests/ui/async-await/async-closures/once.rs
new file mode 100644
index 00000000000..a1c56c5de6a
--- /dev/null
+++ b/tests/ui/async-await/async-closures/once.rs
@@ -0,0 +1,22 @@
+// aux-build:block-on.rs
+// edition:2021
+// build-pass
+
+#![feature(async_closure)]
+
+use std::future::Future;
+
+extern crate block_on;
+
+struct NoCopy;
+
+fn main() {
+    block_on::block_on(async {
+        async fn call_once<F: Future>(x: impl Fn(&'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/refd.rs b/tests/ui/async-await/async-closures/refd.rs
new file mode 100644
index 00000000000..7c61ff2d9bd
--- /dev/null
+++ b/tests/ui/async-await/async-closures/refd.rs
@@ -0,0 +1,18 @@
+// aux-build:block-on.rs
+// edition:2021
+// build-pass
+
+// check that `&{async-closure}` implements `AsyncFn`.
+
+#![feature(async_closure)]
+
+extern crate block_on;
+
+struct NoCopy;
+
+fn main() {
+    block_on::block_on(async {
+        async fn call_once(x: impl async Fn()) { x().await }
+        call_once(&async || {}).await
+    });
+}
diff --git a/tests/ui/async-await/async-fn/dyn-pos.stderr b/tests/ui/async-await/async-fn/dyn-pos.stderr
index c9323526516..488c5d06938 100644
--- a/tests/ui/async-await/async-fn/dyn-pos.stderr
+++ b/tests/ui/async-await/async-fn/dyn-pos.stderr
@@ -8,6 +8,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead:
+             &F
+             std::boxed::Box<F, A>
 
 error[E0038]: the trait `AsyncFnMut` cannot be made into an object
   --> $DIR/dyn-pos.rs:5:16
@@ -19,6 +22,10 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead:
+             &F
+             &mut F
+             std::boxed::Box<F, A>
 
 error[E0038]: the trait `AsyncFn` cannot be made into an object
   --> $DIR/dyn-pos.rs:5:16
@@ -30,6 +37,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead:
+             &F
+             std::boxed::Box<F, A>
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0038]: the trait `AsyncFnMut` cannot be made into an object
@@ -42,6 +52,10 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead:
+             &F
+             &mut F
+             std::boxed::Box<F, A>
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0038]: the trait `AsyncFn` cannot be made into an object
@@ -54,6 +68,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead:
+             &F
+             std::boxed::Box<F, A>
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0038]: the trait `AsyncFnMut` cannot be made into an object
@@ -66,6 +83,10 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFnMut` for this new enum and using it instead:
+             &F
+             &mut F
+             std::boxed::Box<F, A>
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error[E0038]: the trait `AsyncFn` cannot be made into an object
@@ -81,6 +102,9 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all
   ::: $SRC_DIR/core/src/ops/async_function.rs:LL:COL
    |
    = note: the trait cannot be made into an object because it contains the generic associated type `CallMutFuture`
+   = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `AsyncFn` for this new enum and using it instead:
+             &F
+             std::boxed::Box<F, A>
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/async-await/async-fn/simple.rs b/tests/ui/async-await/async-fn/simple.rs
index 99a5d56a309..172ede7098a 100644
--- a/tests/ui/async-await/async-fn/simple.rs
+++ b/tests/ui/async-await/async-fn/simple.rs
@@ -1,5 +1,5 @@
 // edition: 2021
-// check-pass
+// build-pass
 
 #![feature(async_fn_traits)]
 
diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr
index eed01267224..3c474d19d1d 100644
--- a/tests/ui/did_you_mean/bad-assoc-ty.stderr
+++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr
@@ -191,14 +191,7 @@ error[E0223]: ambiguous associated type
   --> $DIR/bad-assoc-ty.rs:33:10
    |
 LL | type H = Fn(u8) -> (u8)::Output;
-   |          ^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: use fully-qualified syntax
-   |
-LL | type H = <(dyn Fn(u8) -> u8 + 'static) as AsyncFnOnce>::Output;
-   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | type H = <(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output;
-   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+   |          ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output`
 
 error[E0223]: ambiguous associated type
   --> $DIR/bad-assoc-ty.rs:39:19
diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr
index bd3728cec8c..e05c83ebc76 100644
--- a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr
+++ b/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr
@@ -13,8 +13,8 @@ LL |     type A<'a> where Self: 'a;
    |          ^ ...because it contains the generic associated type `A`
    = help: consider moving `A` to another trait
    = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Foo` for this new enum and using it instead:
-             Fooy
              Fooer<T>
+             Fooy
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/generic-associated-types/issue-79422.base.stderr b/tests/ui/generic-associated-types/issue-79422.base.stderr
index bcc6382cf7c..7f58f825702 100644
--- a/tests/ui/generic-associated-types/issue-79422.base.stderr
+++ b/tests/ui/generic-associated-types/issue-79422.base.stderr
@@ -29,8 +29,8 @@ LL |     type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
    |          ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
    = help: consider moving `VRefCont` to another trait
    = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead:
-             std::collections::BTreeMap<K, V>
              Source
+             std::collections::BTreeMap<K, V>
 
 error[E0038]: the trait `MapLike` cannot be made into an object
   --> $DIR/issue-79422.rs:44:13
@@ -47,8 +47,8 @@ LL |     type VRefCont<'a>: RefCont<'a, V> where Self: 'a;
    |          ^^^^^^^^ ...because it contains the generic associated type `VRefCont`
    = help: consider moving `VRefCont` to another trait
    = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `MapLike` for this new enum and using it instead:
-             std::collections::BTreeMap<K, V>
              Source
+             std::collections::BTreeMap<K, V>
    = note: required for the cast from `Box<BTreeMap<u8, u8>>` to `Box<dyn MapLike<u8, u8, VRefCont = (dyn RefCont<'_, u8> + 'static)>>`
 
 error: aborting due to 3 previous errors
diff --git a/tests/ui/wf/wf-unsafe-trait-obj-match.stderr b/tests/ui/wf/wf-unsafe-trait-obj-match.stderr
index a0279774abe..3b53f55ffdc 100644
--- a/tests/ui/wf/wf-unsafe-trait-obj-match.stderr
+++ b/tests/ui/wf/wf-unsafe-trait-obj-match.stderr
@@ -30,8 +30,8 @@ LL | trait Trait: Sized {}
    |       |
    |       this trait cannot be made into an object...
    = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead:
-             S
              R
+             S
    = note: required for the cast from `&S` to `&dyn Trait`
 
 error[E0038]: the trait `Trait` cannot be made into an object
@@ -52,8 +52,8 @@ LL | trait Trait: Sized {}
    |       |
    |       this trait cannot be made into an object...
    = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead:
-             S
              R
+             S
    = note: required for the cast from `&R` to `&dyn Trait`
 
 error: aborting due to 3 previous errors