Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Harmonize AsyncFn implementations, make async closures conditionally impl Fn* traits #120712

Merged
merged 7 commits into from
Feb 10, 2024

Conversation

compiler-errors
Copy link
Member

@compiler-errors compiler-errors commented Feb 6, 2024

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:

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*.

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Feb 6, 2024
@compiler-errors compiler-errors changed the title Async closures harmonize Harmonize AsyncFn implementations, make async closures conditionally impl Fn* traits Feb 6, 2024
@compiler-errors
Copy link
Member Author

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Feb 6, 2024
@bors
Copy link
Contributor

bors commented Feb 6, 2024

⌛ Trying commit e988944 with merge 788c8f4...

bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 6, 2024
…nize, r=<try>

Harmonize `AsyncFn` implementations, make async closures conditionally impl `Fn*` traits

TODO: description
TODO: tests (specifically: check that `&async ||` impls `AsyncFn`, flesh out the `Fn` tests, add an always-fnonce test maybe using `Option::map`)

r? `@ghost`
@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Contributor

bors commented Feb 6, 2024

☀️ Try build successful - checks-actions
Build commit: 788c8f4 (788c8f48d1a159cf4f84e1ac1b41dd209ac66964)

@rust-timer

This comment has been minimized.

@compiler-errors
Copy link
Member Author

@craterbot
Copy link
Collaborator

👌 Experiment pr-120712 created and queued.
🤖 Automatically detected try build 788c8f4
🔍 You can check out the queue and this experiment's details.

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-crater Status: Waiting on a crater run to be completed. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Feb 6, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (788c8f4): comparison URL.

Overall result: ✅ improvements - no action needed

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf.

@bors rollup=never
@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.5% [-0.8%, -0.2%] 7
Improvements ✅
(secondary)
-0.6% [-0.7%, -0.5%] 2
All ❌✅ (primary) -0.5% [-0.8%, -0.2%] 7

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-1.6% [-2.2%, -0.7%] 6
Improvements ✅
(secondary)
-3.0% [-3.9%, -2.4%] 8
All ❌✅ (primary) -1.6% [-2.2%, -0.7%] 6

Cycles

This benchmark run did not return any relevant results for this metric.

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 662.774s -> 664.823s (0.31%)
Artifact size: 308.21 MiB -> 308.33 MiB (0.04%)

@rust-cloud-vms rust-cloud-vms bot force-pushed the async-closures-harmonize branch from e988944 to b093e0c Compare February 6, 2024 20:52
@rust-log-analyzer

This comment has been minimized.

@compiler-errors
Copy link
Member Author

Hey CI, wtf is wrong with you?

@craterbot
Copy link
Collaborator

🚧 Experiment pr-120712 is now running

ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot
Copy link
Collaborator

🎉 Experiment pr-120712 is completed!
📊 0 regressed and 9 fixed (52 total)
📰 Open the full report.

⚠️ If you notice any spurious failure please add them to the blacklist!
ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more

@craterbot craterbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-crater Status: Waiting on a crater run to be completed. labels Feb 8, 2024
@rust-log-analyzer
Copy link
Collaborator

The job aarch64-gnu failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
[796/3236] Building CXX object lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/Mem2Reg.cpp.o
[797/3236] Building CXX object lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/MemoryTaggingSupport.cpp.o
[798/3236] Building CXX object lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/MisExpect.cpp.o
[799/3236] Building CXX object lib/Transforms/Utils/CMakeFiles/LLVMTransformUtils.dir/ModuleUtils.cpp.o
##[error]The runner has received a shutdown signal. This can happen when the runner service is stopped, or a manually started runner is canceled.
##[group]Clock drift check
  local time: Sat Feb 10 00:01:48 UTC 2024
  network time: Sat, 10 Feb 2024 00:01:49 GMT
##[endgroup]
---
[862/3236] Building CXX object lib/Transforms/InstCombine/CMakeFiles/LLVMInstCombine.dir/InstCombineSelect.cpp.o
[863/3236] Building CXX object lib/Transforms/InstCombine/CMakeFiles/LLVMInstCombine.dir/InstCombineShifts.cpp.o
[864/3236] Building CXX object lib/Transforms/InstCombine/CMakeFiles/LLVMInstCombine.dir/InstCombineSimplifyDemanded.cpp.o
[865/3236] Building CXX object lib/Transforms/Scalar/CMakeFiles/LLVMScalarOpts.dir/AnnotationRemarks.cpp.o
Session terminated, killing shell... ...killed.
##[error]The operation was canceled.

@bors
Copy link
Contributor

bors commented Feb 10, 2024

⌛ Testing commit 540be28 with merge 16f9cfa...

bors added a commit to rust-lang-ci/rust that referenced this pull request Feb 10, 2024
…nize, 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: rust-lang#120712 (comment)).

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*`.
@rust-log-analyzer
Copy link
Collaborator

The job x86_64-apple-1 failed! Check out the build log: (web) (plain)

Click to see the possible cause of the failure (guessed by this bot)
error: linking with `cc` failed: exit status: 1

  |

command did not execute successfully: cd "/Users/runner/work/rust/rust/compiler/rustc_codegen_cranelift" && env -u MAKEFLAGS -u MFLAGS AR_x86_64_apple_darwin="ar" CARGO_BUILD_INCREMENTAL="false" CARGO_INCREMENTAL="0" CARGO_PROFILE_RELEASE_DEBUG="0" CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS="false" CARGO_PROFILE_RELEASE_OVERFLOW_CHECKS="false" CARGO_PROFILE_RELEASE_STRIP="false" CARGO_TARGET_DIR="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-codegen" CC_x86_64_apple_darwin="sccache /Users/runner/work/rust/rust/clang+llvm-14.0.5-x86_64-apple-darwin/bin/clang" CFG_COMPILER_BUILD_TRIPLE="x86_64-apple-darwin" CFG_COMPILER_HOST_TRIPLE="x86_64-apple-darwin" CFG_DEFAULT_CODEGEN_BACKEND="llvm" CFG_LIBDIR_RELATIVE="lib" CFG_RELEASE="1.78.0-nightly" CFG_RELEASE_CHANNEL="nightly" CFG_VERSION="1.78.0-nightly (16f9cfa79 2024-02-10)" CFG_VER_DATE="2024-02-10" CFG_VER_HASH="16f9cfa79c177a19197b519df9be08a36dce828b" CFLAGS_x86_64_apple_darwin="-ffunction-sections -fdata-sections -fPIC --target=x86_64-apple-darwin" CXXFLAGS_x86_64_apple_darwin="-ffunction-sections -fdata-sections -fPIC --target=x86_64-apple-darwin -stdlib=libc++" CXX_x86_64_apple_darwin="sccache /Users/runner/work/rust/rust/clang+llvm-14.0.5-x86_64-apple-darwin/bin/clang++" LIBC_CHECK_CFG="1" LIBRARY_PATH="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/ci-llvm/lib" LLVM_CONFIG="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/ci-llvm/bin/llvm-config" LLVM_NDEBUG="1" LLVM_RUSTLLVM="1" RANLIB_x86_64_apple_darwin="ar s" REAL_LIBRARY_PATH_VAR="DYLD_LIBRARY_PATH" RUSTBUILD_NATIVE_DIR="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/native" RUSTC="/Users/runner/work/rust/rust/build/bootstrap/debug/rustc" RUSTC_BOOTSTRAP="1" RUSTC_BREAK_ON_ICE="1" RUSTC_ERROR_METADATA_DST="/Users/runner/work/rust/rust/build/tmp/extended-error-metadata" RUSTC_FORCE_UNSTABLE="1" RUSTC_HOST_FLAGS="-Zunstable-options --check-cfg=cfg(bootstrap)" RUSTC_INSTALL_BINDIR="bin" RUSTC_LIBDIR="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1/lib" RUSTC_LINT_FLAGS="-Wrust_2018_idioms -Wunused_lifetimes -Dwarnings" RUSTC_PRINT_STEP_TIMINGS="1" RUSTC_REAL="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1/bin/rustc" RUSTC_SNAPSHOT="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1/bin/rustc" RUSTC_SNAPSHOT_LIBDIR="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1/lib" RUSTC_STAGE="1" RUSTC_SYSROOT="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1" RUSTC_VERBOSE="0" RUSTC_VERIFY_LLVM_IR="1" RUSTC_WRAPPER="/Users/runner/work/rust/rust/build/bootstrap/debug/rustc" RUSTDOC="/Users/runner/work/rust/rust/build/bootstrap/debug/rustdoc" RUSTDOCFLAGS="-Csymbol-mangling-version=v0 -Zunstable-options --check-cfg=cfg(bootstrap) --check-cfg=cfg(parallel_compiler) -Dwarnings -Wrustdoc::invalid_codeblock_attributes --crate-version 1.78.0-nightly\t(16f9cfa79\t2024-02-10) --cfg=parallel_compiler" RUSTDOC_LIBDIR="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1/lib" RUSTDOC_REAL="/path/to/nowhere/rustdoc/not/required" RUSTFLAGS="-Csymbol-mangling-version=v0 -Zunstable-options --check-cfg=cfg(bootstrap) --check-cfg=cfg(parallel_compiler) -Zmacro-backtrace -Csplit-debuginfo=unpacked -Zosx-rpath-install-name -Clink-args=-Wl,-rpath,@loader_path/../lib --cfg=parallel_compiler" RUST_TEST_THREADS="4" TERM="xterm" WINAPI_NO_BUNDLED_LIBRARIES="1" __CARGO_DEFAULT_LIB_METADATA="nightlycodegen" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage0/bin/cargo" "run" "--target" "x86_64-apple-darwin" "--release" "-Zcheck-cfg" "-Zbinary-dep-depinfo" "-j" "4" "--locked" "--color" "always" "--manifest-path" "/Users/runner/work/rust/rust/compiler/rustc_codegen_cranelift/build_system/Cargo.toml" "--" "test" "--download-dir" "/Users/runner/work/rust/rust/build/cg_clif_download" "--out-dir" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif" "--no-unstable-features" "--use-backend" "cranelift" "--sysroot" "llvm" "--skip-test" "testsuite.extended_sysroot"
  = note: env -u IPHONEOS_DEPLOYMENT_TARGET -u TVOS_DEPLOYMENT_TARGET LC_ALL="C" PATH="/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/bin:/usr/local/lib/ruby/gems/3.0.0/bin:/usr/local/opt/ruby@3.0/bin:/usr/local/opt/pipx_bin:/Users/runner/.cargo/bin:/usr/local/opt/curl/bin:/usr/local/bin:/usr/local/sbin:/Users/runner/bin:/Users/runner/.yarn/bin:/Users/runner/Library/Android/sdk/tools:/Users/runner/Library/Android/sdk/platform-tools:/Library/Frameworks/Python.framework/Versions/Current/bin:/Library/Frameworks/Mono.framework/Versions/Current/Commands:/usr/bin:/bin:/usr/sbin:/sbin:/Users/runner/.dotnet/tools" VSLANG="1033" ZERO_AR_DATE="1" "cc" "-arch" "x86_64" "-m64" "/var/folders/r0/ztvld9wd66bfpv_g6h3ksl000000gn/T/rustc66QIy2/symbols.o" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example/issue-72793.issue_72793.bc5a872a9ae27c1-cgu.0.rcgu.o" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example/issue-72793.allocator_shim.rcgu.o" "-L" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example" "-L" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libstd-a7ffcf8feb5de0df.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libpanic_abort-f72dbe40e56cf4e9.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libobject-93683961e4fe014c.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libmemchr-608f39623da2acc6.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libaddr2line-2b17e3e98a369b70.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libgimli-98e18d571f0d3c98.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/librustc_demangle-105905fe6fc6bf57.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libstd_detect-c72f1c866e7081dc.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libhashbrown-e82114027f985b88.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_alloc-f99fcd695211a1c9.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libminiz_oxide-9bba2c1110da01a8.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libadler-f86ef4dff75dd50c.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libunwind-0b11117ce25a1560.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libcfg_if-3fce4a1c07607df0.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/liblibc-e38fe75b4b440008.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/liballoc-2b3dd1868cffbb25.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/librustc_std_workspace_core-0171fe66a8ec9014.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libcore-939f78aa140a5d6b.rlib" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib/libcompiler_builtins-83f97429b7f69221.rlib" "-lSystem" "-lc" "-lm" "-L" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/lib/rustlib/x86_64-apple-darwin/lib" "-o" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example/issue-72793" "-Wl,-dead_strip" "-nodefaultlibs" "-Wl,-rpath,@loader_path/../lib" "-undefined" "dynamic_lookup"
expected success, got: exit status: 1
  = note: ld: invalid r_symbolnum=14 in '/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example/issue-72793.issue_72793.bc5a872a9ae27c1-cgu.0.rcgu.o'

          clang: error: linker command failed with exit code 1 (use -v to see invocation)
          


stderr ----
stderr ----
error: aborting due to 1 previous error



"/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/dist/rustc-clif" "--cap-lints=allow" "-Csymbol-mangling-version=v0" "-Zunstable-options" "--check-cfg=cfg(bootstrap)" "--check-cfg=cfg(parallel_compiler)" "-Zmacro-backtrace" "-Csplit-debuginfo=unpacked" "-Zosx-rpath-install-name" "-Clink-args=-Wl,-rpath,@loader_path/../lib" "--cfg=parallel_compiler" "-Clink-arg=-undefined" "-Clink-arg=dynamic_lookup" "-L" "crate=/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example" "--out-dir" "/Users/runner/work/rust/rust/build/x86_64-apple-darwin/stage1-tools/cg_clif/build/example" "-Cdebuginfo=2" "--target" "x86_64-apple-darwin" "-Cpanic=abort" "-Zunstable-options" "--check-cfg=cfg(no_unstable_features)" "--check-cfg=cfg(jit)" "example/issue-72793.rs" "--cfg" "no_unstable_features" exited with status ExitStatus(unix_wait_status(256))
Build completed unsuccessfully in 0:41:48
  local time: Sat Feb 10 04:11:04 UTC 2024
  network time: Sat, 10 Feb 2024 04:11:05 GMT
##[error]Process completed with exit code 1.

@bors
Copy link
Contributor

bors commented Feb 10, 2024

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Feb 10, 2024
@compiler-errors
Copy link
Member Author

Linker error on clif looks unrelated?

@bors retry

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 10, 2024
@bors
Copy link
Contributor

bors commented Feb 10, 2024

⌛ Testing commit 540be28 with merge 757b8ef...

@bors
Copy link
Contributor

bors commented Feb 10, 2024

☀️ Test successful - checks-actions
Approved by: oli-obk
Pushing 757b8ef to master...

@bors bors added the merged-by-bors This PR was explicitly merged by bors. label Feb 10, 2024
@bors bors merged commit 757b8ef into rust-lang:master Feb 10, 2024
12 checks passed
@rustbot rustbot added this to the 1.78.0 milestone Feb 10, 2024
@rust-timer
Copy link
Collaborator

Finished benchmarking commit (757b8ef): comparison URL.

Overall result: ✅ improvements - no action needed

@rustbot label: -perf-regression

Instruction count

This is a highly reliable metric that was used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.5% [-0.8%, -0.2%] 5
Improvements ✅
(secondary)
-0.6% [-0.7%, -0.5%] 2
All ❌✅ (primary) -0.5% [-0.8%, -0.2%] 5

Max RSS (memory usage)

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-1.0% [-1.0%, -1.0%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -1.0% [-1.0%, -1.0%] 1

Cycles

Results

This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
0.9% [0.9%, 0.9%] 1
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

This benchmark run did not return any relevant results for this metric.

Bootstrap: 667.844s -> 666.007s (-0.28%)
Artifact size: 308.06 MiB -> 308.04 MiB (-0.01%)

github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Feb 11, 2024
…li-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: rust-lang/rust#120712 (comment)).

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*`.
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Feb 26, 2024
…-check, r=oli-obk

Actually use the right closure kind when checking async Fn goals

Dumb copy-paste mistake on my part from rust-lang#120712. Sorry!

r? oli-obk

Fixes rust-lang#121599
rust-timer added a commit to rust-lang-ci/rust that referenced this pull request Feb 26, 2024
Rollup merge of rust-lang#121617 - compiler-errors:async-closure-kind-check, r=oli-obk

Actually use the right closure kind when checking async Fn goals

Dumb copy-paste mistake on my part from rust-lang#120712. Sorry!

r? oli-obk

Fixes rust-lang#121599
bors added a commit to rust-lang-ci/rust that referenced this pull request Dec 13, 2024
…i-obk

Stabilize async closures (RFC 3668)

# Async Closures Stabilization Report

This report proposes the stabilization of `#![feature(async_closure)]` ([RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html)). This is a long-awaited feature that increases the expressiveness of the Rust language and fills a pressing gap in the async ecosystem.

## Stabilization summary

* You can write async closures like `async || {}` which return futures that can borrow from their captures and can be higher-ranked in their argument lifetimes.
* You can express trait bounds for these async closures using the `AsyncFn` family of traits, analogous to the `Fn` family.

```rust
async fn takes_an_async_fn(f: impl AsyncFn(&str)) {
    futures::join(f("hello"), f("world")).await;
}

takes_an_async_fn(async |s| { other_fn(s).await }).await;
```

## Motivation

Without this feature, users hit two major obstacles when writing async code that uses closures and `Fn` trait bounds:

- The inability to express higher-ranked async function signatures.
- That closures cannot return futures that borrow from the closure captures.

That is, for the first, we cannot write:

```rust
// We cannot express higher-ranked async function signatures.
async fn f<Fut>(_: impl for<'a> Fn(&'a u8) -> Fut)
where
    Fut: Future<Output = ()>,
{ todo!() }

async fn main() {
    async fn g(_: &u8) { todo!() }
    f(g).await;
    //~^ ERROR mismatched types
    //~| ERROR one type is more general than the other
}
```

And for the second, we cannot write:

```rust
// Closures cannot return futures that borrow closure captures.
async fn f<Fut: Future<Output = ()>>(_: impl FnMut() -> Fut)
{ todo!() }

async fn main() {
    let mut xs = vec![];
    f(|| async {
        async fn g() -> u8 { todo!() }
        xs.push(g().await);
    });
    //~^ ERROR captured variable cannot escape `FnMut` closure body
}
```

Async closures provide a first-class solution to these problems.

For further background, please refer to the [motivation section](https://rust-lang.github.io/rfcs/3668-async-closures.html#motivation) of the RFC.

## Major design decisions since RFC

The RFC had left open the question of whether we would spell the bounds syntax for async closures...

```rust
// ...as this...
fn f() -> impl AsyncFn() -> u8 { todo!() }
// ...or as this:
fn f() -> impl async Fn() -> u8 { todo!() }
```

We've decided to spell this as `AsyncFn{,Mut,Once}`.

The `Fn` family of traits is special in many ways.  We had originally argued that, due to this specialness, that perhaps the `async Fn` syntax could be adopted without having to decide whether a general `async Trait` mechanism would ever be adopted.  However, concerns have been raised that we may not want to use `async Fn` syntax unless we would pursue more general trait modifiers.  Since there remain substantial open questions on those -- and we don't want to rush any design work there -- it makes sense to ship this needed feature using the `AsyncFn`-style bounds syntax.

Since we would, in no case, be shipping a generalized trait modifier system anytime soon, we'll be continuing to see `AsyncFoo` traits appear across the ecosystem regardless.  If we were to ever later ship some general mechanism, we could at that time manage the migration from `AsyncFn` to `async Fn`, just as we'd be enabling and managing the migration of many other traits.

Note that, as specified in RFC 3668, the details of the `AsyncFn*` traits are not exposed and they can only be named via the "parentheses sugar".  That is, we can write `T: AsyncFn() -> u8` but not `T: AsyncFn<Output = u8>`.

Unlike the `Fn` traits, we cannot project to the `Output` associated type of the `AsyncFn` traits.  That is, while we can write...

```rust
fn f<F: Fn() -> u8>(_: F::Output) {}
```

...we cannot write:

```rust
fn f<F: AsyncFn() -> u8>(_: F::Output) {}
//~^ ERROR
```

The choice of `AsyncFn{,Mut,Once}` bounds syntax obviates, for our purposes here, another question decided after that RFC, which was how to order bound modifiers such as `for<'a> async Fn()`.

Other than answering the open question in the RFC on syntax, nothing has changed about the design of this feature between RFC 3668 and this stabilization.

## What is stabilized

For those interested in the technical details, please see [the dev guide section](https://rustc-dev-guide.rust-lang.org/coroutine-closures.html) I authored.

#### Async closures

Other than in how they solve the problems described above, async closures act similarly to closures that return async blocks, and can have parts of their signatures specified:

```rust
// They can have arguments annotated with types:
let _ = async |_: u8| { todo!() };

// They can have their return types annotated:
let _ = async || -> u8 { todo!() };

// They can be higher-ranked:
let _ = async |_: &str| { todo!() };

// They can capture values by move:
let x = String::from("hello, world");
let _ = async move || do_something(&x).await };
```

When called, they return an anonymous future type corresponding to the (not-yet-executed) body of the closure. These can be awaited like any other future.

What distinguishes async closures is that, unlike closures that return async blocks, the futures returned from the async closure can capture state from the async closure. For example:

```rust
let vec: Vec<String> = vec![];

let closure = async || {
    vec.push(ready(String::from("")).await);
};
```

The async closure captures `vec` with some `&'closure mut Vec<String>` which lives until the closure is dropped. Every call to `closure()` returns a future which reborrows that mutable reference `&'call mut Vec<String>` which lives until the future is dropped (e.g. it is `await`ed).

As another example:

```rust
let string: String = "Hello, world".into();

let closure = async move || {
    ready(&string).await;
};
```

The closure is marked with `move`, which means it takes ownership of the string by *value*. The future that is returned by calling `closure()` returns a future which borrows a reference `&'call String` which lives until the future is dropped (e.g. it is `await`ed).

#### Async fn trait family

To support the lending capability of async closures, and to provide a first-class way to express higher-ranked async closures, we introduce the `AsyncFn*` family of traits. See the [corresponding section](https://rust-lang.github.io/rfcs/3668-async-closures.html#asyncfn) of the RFC.

We stabilize naming `AsyncFn*` via the "parenthesized sugar" syntax that normal `Fn*` traits can be named. The `AsyncFn*` trait can be used anywhere a `Fn*` trait bound is allowed, such as:

```rust
/// In return-position impl trait:
fn closure() -> impl AsyncFn() { async || {} }

/// In trait bounds:
trait Foo<F>: Sized
where
    F: AsyncFn()
{
    fn new(f: F) -> Self;
}

/// in GATs:
trait Gat {
    type AsyncHasher<T>: AsyncFn(T) -> i32;
}
```

Other than using them in trait bounds, the definitions of these traits are not directly observable, but certain aspects of their behavior can be indirectly observed such as the fact that:

* `AsyncFn::async_call` and `AsyncFnMut::async_call_mut` return a future which is *lending*, and therefore borrows the `&self` lifetime of the callee.

```rust
fn by_ref_call(c: impl AsyncFn()) {
    let fut = c();
    drop(c);
    //   ^ Cannot drop `c` since it is borrowed by `fut`.
}
```

* `AsyncFnOnce::async_call_once` returns a future that takes ownership of the callee.

```rust
fn by_ref_call(c: impl AsyncFnOnce()) {
    let fut = c();
    let _ = c();
    //      ^ Cannot call `c` since calling it takes ownership the callee.
}
```

* All currently-stable callable types (i.e., closures, function items, function pointers, and `dyn Fn*` trait objects) automatically implement `AsyncFn*() -> T` if they implement `Fn*() -> Fut` for some output type `Fut`, and `Fut` implements `Future<Output = T>`.
    * This is to make sure that `AsyncFn*()` trait bounds have maximum compatibility with existing callable types which return futures, such as async function items and closures which return boxed futures.
    * For now, this only works currently for *concrete* callable types -- for example, a argument-position impl trait like `impl Fn() -> impl Future<Output = ()>` does not implement `AsyncFn()`, due to the fact that a `AsyncFn`-if-`Fn` blanket impl does not exist in reality. This may be relaxed in the future. Users can work around this by wrapping their type in an async closure and calling it. I expect this to not matter much in practice, as users are encouraged to write `AsyncFn` bounds directly.

```rust
fn is_async_fn(_: impl AsyncFn(&str)) {}

async fn async_fn_item(s: &str) { todo!() }
is_async_fn(s);
// ^^^ This works.

fn generic(f: impl Fn() -> impl Future<Output = ()>) {
    is_async_fn(f);
    // ^^^ This does not work (yet).
}
```

#### The by-move future

When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped.

To work around around this limitation, we synthesize a separate future type for calling the async closure via `AsyncFnOnce`.

This future executes identically to the by-ref future returned from calling the async closure, except for the fact that it has a different set of captures, since we must *move* the captures from the parent async into the child future.

#### Interactions between async closures and the `Fn*` family of traits

Async closures always implement `FnOnce`, since they always can be called once. They may also implement `Fn` or `FnMut` if their body is compatible with the calling mode (i.e. if they do not mutate their captures, or they do not capture their captures, respectively) and if the future returned by the async closure is not *lending*.

```rust
let id = String::new();

let mapped: Vec</* impl Future */> =
    [/* elements */]
    .into_iter()
    // `Iterator::map` takes an `impl FnMut`
    .map(async |element| {
        do_something(&id, element).await;
    })
    .collect();
```

See [the dev guide](https://rustc-dev-guide.rust-lang.org/coroutine-closures.html#follow-up-when-do-async-closures-implement-the-regular-fn-traits) for a detailed explanation for the situations where this may not be possible due to the lending nature of async closures.

#### Other notable features of async closures shared with synchronous closures

* Async closures are `Copy` and/or `Clone` if their captures are `Copy`/`Clone`.
* Async closures do closure signature inference: If an async closure is passed to a function with a `AsyncFn` or `Fn` trait bound, we can eagerly infer the argument types of the closure. More details are provided in [the dev guide](https://rustc-dev-guide.rust-lang.org/coroutine-closures.html#closure-signature-inference).

#### Lints

This PR also stabilizes the `CLOSURE_RETURNING_ASYNC_BLOCK` lint as an `allow` lint. This lints on "old-style" async closures:

```rust
#![warn(closure_returning_async_block)]
let c = |x: &str| async {};
```

We should encourage users to use `async || {}` where possible. This lint remains `allow` and may be refined in the future because it has a few false positives (namely, see: "Where do we expect rewriting `|| async {}` into `async || {}` to fail?")

An alternative that could be made at the time of stabilization is to put this lint behind another gate, so we can decide to stabilize it later.

## What isn't stabilized (aka, potential future work)

#### `async Fn*()` bound syntax

We decided to stabilize async closures without the `async Fn*()` bound modifier syntax. The general direction of this syntax and how it fits is still being considered by T-lang (e.g. in [RFC 3710](rust-lang/rfcs#3710)).

#### Naming the futures returned by async closures

This stabilization PR does not provide a way of naming the futures returned by calling `AsyncFn*`.

Exposing a stable way to refer to these futures is important for building async-closure-aware combinators, and will be an important future step.

#### Return type notation-style bounds for async closures

The RFC described an RTN-like syntax for putting bounds on the future returned by an async closure:

```rust
async fn foo(x: F) -> Result<()>
where
    F: AsyncFn(&str) -> Result<()>,
    // The future from calling `F` is `Send` and `'static`.
    F(..): Send + 'static,
{}
```

This stabilization PR does not stabilize that syntax yet, which remains unimplemented (though will be soon).

#### `dyn AsyncFn*()`

`AsyncFn*` are not dyn-compatible yet. This will likely be implemented in the future along with the dyn-compatibility of async fn in trait, since the same issue (dealing with the future returned by a call) applies there.

## Tests

Tests exist for this feature in [`tests/ui/async-await/async-closures`](https://github.com/rust-lang/rust/tree/5b542866400ad4a294f468cfa7e059d95c27a079/tests/ui/async-await/async-closures).

<details>
    <summary>A selected set of tests:</summary>

* Lending behavior of async closures
    * `tests/ui/async-await/async-closures/mutate.rs`
    * `tests/ui/async-await/async-closures/captures.rs`
    * `tests/ui/async-await/async-closures/precise-captures.rs`
    * `tests/ui/async-await/async-closures/no-borrow-from-env.rs`
* Async closures may be higher-ranked
    * `tests/ui/async-await/async-closures/higher-ranked.rs`
    * `tests/ui/async-await/async-closures/higher-ranked-return.rs`
* Async closures may implement `Fn*` traits
    * `tests/ui/async-await/async-closures/is-fn.rs`
    * `tests/ui/async-await/async-closures/implements-fnmut.rs`
* Async closures may be cloned
    * `tests/ui/async-await/async-closures/clone-closure.rs`
* Ownership of the upvars when `AsyncFnOnce` is called
    * `tests/ui/async-await/async-closures/drop.rs`
    * `tests/ui/async-await/async-closures/move-is-async-fn.rs`
    * `tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs`
    * `tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs`
* Closure signature inference
    * `tests/ui/async-await/async-closures/signature-deduction.rs`
    * `tests/ui/async-await/async-closures/sig-from-bare-fn.rs`
    * `tests/ui/async-await/async-closures/signature-inference-from-two-part-bound.rs`

</details>

## Remaining bugs and open issues

* rust-lang#120694 tracks moving onto more general `LendingFn*` traits. No action needed, since it's not observable.
* rust-lang#124020 - Polymorphization ICE. Polymorphization needs to be heavily reworked. No action needed.
* rust-lang#127227 - Tracking reworking the way that rustdoc re-sugars bounds.
    * The part relevant to to `AsyncFn` is fixed by rust-lang#132697.

## Where do we expect rewriting `|| async {}` into `async || {}` to fail?

* Fn pointer coercions
    * Currently, it is not possible to coerce an async closure to an fn pointer like regular closures can be. This functionality may be implemented in the future.
```rust
let x: fn() -> _ = async || {};
```
* Argument capture
    * Like async functions, async closures always capture their input arguments. This is in contrast to something like `|t: T| async {}`, which doesn't capture `t` unless it is used in the async block. This may affect the `Send`-ness of the future or affect its outlives.
```rust
fn needs_send_future(_: impl Fn(NotSendArg) -> Fut)
where
    Fut: Future<Output = ()>,
{}

needs_send_future(async |_| {});
```

## History

#### Important feature history

- rust-lang#51580
- rust-lang#62292
- rust-lang#120361
- rust-lang#120712
- rust-lang#121857
- rust-lang#123660
- rust-lang#125259
- rust-lang#128506
- rust-lang#127482

## Acknowledgements

Thanks to `@oli-obk` for reviewing the bulk of the work for this feature. Thanks to `@nikomatsakis` for his design blog posts which generated interest for this feature, `@traviscross` for feedback and additions to this stabilization report. All errors are my own.

r? `@ghost`
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Dec 13, 2024
Stabilize async closures (RFC 3668)

# Async Closures Stabilization Report

This report proposes the stabilization of `#![feature(async_closure)]` ([RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html)). This is a long-awaited feature that increases the expressiveness of the Rust language and fills a pressing gap in the async ecosystem.

## Stabilization summary

* You can write async closures like `async || {}` which return futures that can borrow from their captures and can be higher-ranked in their argument lifetimes.
* You can express trait bounds for these async closures using the `AsyncFn` family of traits, analogous to the `Fn` family.

```rust
async fn takes_an_async_fn(f: impl AsyncFn(&str)) {
    futures::join(f("hello"), f("world")).await;
}

takes_an_async_fn(async |s| { other_fn(s).await }).await;
```

## Motivation

Without this feature, users hit two major obstacles when writing async code that uses closures and `Fn` trait bounds:

- The inability to express higher-ranked async function signatures.
- That closures cannot return futures that borrow from the closure captures.

That is, for the first, we cannot write:

```rust
// We cannot express higher-ranked async function signatures.
async fn f<Fut>(_: impl for<'a> Fn(&'a u8) -> Fut)
where
    Fut: Future<Output = ()>,
{ todo!() }

async fn main() {
    async fn g(_: &u8) { todo!() }
    f(g).await;
    //~^ ERROR mismatched types
    //~| ERROR one type is more general than the other
}
```

And for the second, we cannot write:

```rust
// Closures cannot return futures that borrow closure captures.
async fn f<Fut: Future<Output = ()>>(_: impl FnMut() -> Fut)
{ todo!() }

async fn main() {
    let mut xs = vec![];
    f(|| async {
        async fn g() -> u8 { todo!() }
        xs.push(g().await);
    });
    //~^ ERROR captured variable cannot escape `FnMut` closure body
}
```

Async closures provide a first-class solution to these problems.

For further background, please refer to the [motivation section](https://rust-lang.github.io/rfcs/3668-async-closures.html#motivation) of the RFC.

## Major design decisions since RFC

The RFC had left open the question of whether we would spell the bounds syntax for async closures...

```rust
// ...as this...
fn f() -> impl AsyncFn() -> u8 { todo!() }
// ...or as this:
fn f() -> impl async Fn() -> u8 { todo!() }
```

We've decided to spell this as `AsyncFn{,Mut,Once}`.

The `Fn` family of traits is special in many ways.  We had originally argued that, due to this specialness, that perhaps the `async Fn` syntax could be adopted without having to decide whether a general `async Trait` mechanism would ever be adopted.  However, concerns have been raised that we may not want to use `async Fn` syntax unless we would pursue more general trait modifiers.  Since there remain substantial open questions on those -- and we don't want to rush any design work there -- it makes sense to ship this needed feature using the `AsyncFn`-style bounds syntax.

Since we would, in no case, be shipping a generalized trait modifier system anytime soon, we'll be continuing to see `AsyncFoo` traits appear across the ecosystem regardless.  If we were to ever later ship some general mechanism, we could at that time manage the migration from `AsyncFn` to `async Fn`, just as we'd be enabling and managing the migration of many other traits.

Note that, as specified in RFC 3668, the details of the `AsyncFn*` traits are not exposed and they can only be named via the "parentheses sugar".  That is, we can write `T: AsyncFn() -> u8` but not `T: AsyncFn<Output = u8>`.

Unlike the `Fn` traits, we cannot project to the `Output` associated type of the `AsyncFn` traits.  That is, while we can write...

```rust
fn f<F: Fn() -> u8>(_: F::Output) {}
```

...we cannot write:

```rust
fn f<F: AsyncFn() -> u8>(_: F::Output) {}
//~^ ERROR
```

The choice of `AsyncFn{,Mut,Once}` bounds syntax obviates, for our purposes here, another question decided after that RFC, which was how to order bound modifiers such as `for<'a> async Fn()`.

Other than answering the open question in the RFC on syntax, nothing has changed about the design of this feature between RFC 3668 and this stabilization.

## What is stabilized

For those interested in the technical details, please see [the dev guide section](https://rustc-dev-guide.rust-lang.org/coroutine-closures.html) I authored.

#### Async closures

Other than in how they solve the problems described above, async closures act similarly to closures that return async blocks, and can have parts of their signatures specified:

```rust
// They can have arguments annotated with types:
let _ = async |_: u8| { todo!() };

// They can have their return types annotated:
let _ = async || -> u8 { todo!() };

// They can be higher-ranked:
let _ = async |_: &str| { todo!() };

// They can capture values by move:
let x = String::from("hello, world");
let _ = async move || do_something(&x).await };
```

When called, they return an anonymous future type corresponding to the (not-yet-executed) body of the closure. These can be awaited like any other future.

What distinguishes async closures is that, unlike closures that return async blocks, the futures returned from the async closure can capture state from the async closure. For example:

```rust
let vec: Vec<String> = vec![];

let closure = async || {
    vec.push(ready(String::from("")).await);
};
```

The async closure captures `vec` with some `&'closure mut Vec<String>` which lives until the closure is dropped. Every call to `closure()` returns a future which reborrows that mutable reference `&'call mut Vec<String>` which lives until the future is dropped (e.g. it is `await`ed).

As another example:

```rust
let string: String = "Hello, world".into();

let closure = async move || {
    ready(&string).await;
};
```

The closure is marked with `move`, which means it takes ownership of the string by *value*. The future that is returned by calling `closure()` returns a future which borrows a reference `&'call String` which lives until the future is dropped (e.g. it is `await`ed).

#### Async fn trait family

To support the lending capability of async closures, and to provide a first-class way to express higher-ranked async closures, we introduce the `AsyncFn*` family of traits. See the [corresponding section](https://rust-lang.github.io/rfcs/3668-async-closures.html#asyncfn) of the RFC.

We stabilize naming `AsyncFn*` via the "parenthesized sugar" syntax that normal `Fn*` traits can be named. The `AsyncFn*` trait can be used anywhere a `Fn*` trait bound is allowed, such as:

```rust
/// In return-position impl trait:
fn closure() -> impl AsyncFn() { async || {} }

/// In trait bounds:
trait Foo<F>: Sized
where
    F: AsyncFn()
{
    fn new(f: F) -> Self;
}

/// in GATs:
trait Gat {
    type AsyncHasher<T>: AsyncFn(T) -> i32;
}
```

Other than using them in trait bounds, the definitions of these traits are not directly observable, but certain aspects of their behavior can be indirectly observed such as the fact that:

* `AsyncFn::async_call` and `AsyncFnMut::async_call_mut` return a future which is *lending*, and therefore borrows the `&self` lifetime of the callee.

```rust
fn by_ref_call(c: impl AsyncFn()) {
    let fut = c();
    drop(c);
    //   ^ Cannot drop `c` since it is borrowed by `fut`.
}
```

* `AsyncFnOnce::async_call_once` returns a future that takes ownership of the callee.

```rust
fn by_ref_call(c: impl AsyncFnOnce()) {
    let fut = c();
    let _ = c();
    //      ^ Cannot call `c` since calling it takes ownership the callee.
}
```

* All currently-stable callable types (i.e., closures, function items, function pointers, and `dyn Fn*` trait objects) automatically implement `AsyncFn*() -> T` if they implement `Fn*() -> Fut` for some output type `Fut`, and `Fut` implements `Future<Output = T>`.
    * This is to make sure that `AsyncFn*()` trait bounds have maximum compatibility with existing callable types which return futures, such as async function items and closures which return boxed futures.
    * For now, this only works currently for *concrete* callable types -- for example, a argument-position impl trait like `impl Fn() -> impl Future<Output = ()>` does not implement `AsyncFn()`, due to the fact that a `AsyncFn`-if-`Fn` blanket impl does not exist in reality. This may be relaxed in the future. Users can work around this by wrapping their type in an async closure and calling it. I expect this to not matter much in practice, as users are encouraged to write `AsyncFn` bounds directly.

```rust
fn is_async_fn(_: impl AsyncFn(&str)) {}

async fn async_fn_item(s: &str) { todo!() }
is_async_fn(s);
// ^^^ This works.

fn generic(f: impl Fn() -> impl Future<Output = ()>) {
    is_async_fn(f);
    // ^^^ This does not work (yet).
}
```

#### The by-move future

When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped.

To work around around this limitation, we synthesize a separate future type for calling the async closure via `AsyncFnOnce`.

This future executes identically to the by-ref future returned from calling the async closure, except for the fact that it has a different set of captures, since we must *move* the captures from the parent async into the child future.

#### Interactions between async closures and the `Fn*` family of traits

Async closures always implement `FnOnce`, since they always can be called once. They may also implement `Fn` or `FnMut` if their body is compatible with the calling mode (i.e. if they do not mutate their captures, or they do not capture their captures, respectively) and if the future returned by the async closure is not *lending*.

```rust
let id = String::new();

let mapped: Vec</* impl Future */> =
    [/* elements */]
    .into_iter()
    // `Iterator::map` takes an `impl FnMut`
    .map(async |element| {
        do_something(&id, element).await;
    })
    .collect();
```

See [the dev guide](https://rustc-dev-guide.rust-lang.org/coroutine-closures.html#follow-up-when-do-async-closures-implement-the-regular-fn-traits) for a detailed explanation for the situations where this may not be possible due to the lending nature of async closures.

#### Other notable features of async closures shared with synchronous closures

* Async closures are `Copy` and/or `Clone` if their captures are `Copy`/`Clone`.
* Async closures do closure signature inference: If an async closure is passed to a function with a `AsyncFn` or `Fn` trait bound, we can eagerly infer the argument types of the closure. More details are provided in [the dev guide](https://rustc-dev-guide.rust-lang.org/coroutine-closures.html#closure-signature-inference).

#### Lints

This PR also stabilizes the `CLOSURE_RETURNING_ASYNC_BLOCK` lint as an `allow` lint. This lints on "old-style" async closures:

```rust
#![warn(closure_returning_async_block)]
let c = |x: &str| async {};
```

We should encourage users to use `async || {}` where possible. This lint remains `allow` and may be refined in the future because it has a few false positives (namely, see: "Where do we expect rewriting `|| async {}` into `async || {}` to fail?")

An alternative that could be made at the time of stabilization is to put this lint behind another gate, so we can decide to stabilize it later.

## What isn't stabilized (aka, potential future work)

#### `async Fn*()` bound syntax

We decided to stabilize async closures without the `async Fn*()` bound modifier syntax. The general direction of this syntax and how it fits is still being considered by T-lang (e.g. in [RFC 3710](rust-lang/rfcs#3710)).

#### Naming the futures returned by async closures

This stabilization PR does not provide a way of naming the futures returned by calling `AsyncFn*`.

Exposing a stable way to refer to these futures is important for building async-closure-aware combinators, and will be an important future step.

#### Return type notation-style bounds for async closures

The RFC described an RTN-like syntax for putting bounds on the future returned by an async closure:

```rust
async fn foo(x: F) -> Result<()>
where
    F: AsyncFn(&str) -> Result<()>,
    // The future from calling `F` is `Send` and `'static`.
    F(..): Send + 'static,
{}
```

This stabilization PR does not stabilize that syntax yet, which remains unimplemented (though will be soon).

#### `dyn AsyncFn*()`

`AsyncFn*` are not dyn-compatible yet. This will likely be implemented in the future along with the dyn-compatibility of async fn in trait, since the same issue (dealing with the future returned by a call) applies there.

## Tests

Tests exist for this feature in [`tests/ui/async-await/async-closures`](https://github.com/rust-lang/rust/tree/5b542866400ad4a294f468cfa7e059d95c27a079/tests/ui/async-await/async-closures).

<details>
    <summary>A selected set of tests:</summary>

* Lending behavior of async closures
    * `tests/ui/async-await/async-closures/mutate.rs`
    * `tests/ui/async-await/async-closures/captures.rs`
    * `tests/ui/async-await/async-closures/precise-captures.rs`
    * `tests/ui/async-await/async-closures/no-borrow-from-env.rs`
* Async closures may be higher-ranked
    * `tests/ui/async-await/async-closures/higher-ranked.rs`
    * `tests/ui/async-await/async-closures/higher-ranked-return.rs`
* Async closures may implement `Fn*` traits
    * `tests/ui/async-await/async-closures/is-fn.rs`
    * `tests/ui/async-await/async-closures/implements-fnmut.rs`
* Async closures may be cloned
    * `tests/ui/async-await/async-closures/clone-closure.rs`
* Ownership of the upvars when `AsyncFnOnce` is called
    * `tests/ui/async-await/async-closures/drop.rs`
    * `tests/ui/async-await/async-closures/move-is-async-fn.rs`
    * `tests/ui/async-await/async-closures/force-move-due-to-inferred-kind.rs`
    * `tests/ui/async-await/async-closures/force-move-due-to-actually-fnonce.rs`
* Closure signature inference
    * `tests/ui/async-await/async-closures/signature-deduction.rs`
    * `tests/ui/async-await/async-closures/sig-from-bare-fn.rs`
    * `tests/ui/async-await/async-closures/signature-inference-from-two-part-bound.rs`

</details>

## Remaining bugs and open issues

* rust-lang/rust#120694 tracks moving onto more general `LendingFn*` traits. No action needed, since it's not observable.
* rust-lang/rust#124020 - Polymorphization ICE. Polymorphization needs to be heavily reworked. No action needed.
* rust-lang/rust#127227 - Tracking reworking the way that rustdoc re-sugars bounds.
    * The part relevant to to `AsyncFn` is fixed by rust-lang/rust#132697.

## Where do we expect rewriting `|| async {}` into `async || {}` to fail?

* Fn pointer coercions
    * Currently, it is not possible to coerce an async closure to an fn pointer like regular closures can be. This functionality may be implemented in the future.
```rust
let x: fn() -> _ = async || {};
```
* Argument capture
    * Like async functions, async closures always capture their input arguments. This is in contrast to something like `|t: T| async {}`, which doesn't capture `t` unless it is used in the async block. This may affect the `Send`-ness of the future or affect its outlives.
```rust
fn needs_send_future(_: impl Fn(NotSendArg) -> Fut)
where
    Fut: Future<Output = ()>,
{}

needs_send_future(async |_| {});
```

## History

#### Important feature history

- rust-lang/rust#51580
- rust-lang/rust#62292
- rust-lang/rust#120361
- rust-lang/rust#120712
- rust-lang/rust#121857
- rust-lang/rust#123660
- rust-lang/rust#125259
- rust-lang/rust#128506
- rust-lang/rust#127482

## Acknowledgements

Thanks to `@oli-obk` for reviewing the bulk of the work for this feature. Thanks to `@nikomatsakis` for his design blog posts which generated interest for this feature, `@traviscross` for feedback and additions to this stabilization report. All errors are my own.

r? `@ghost`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
merged-by-bors This PR was explicitly merged by bors. S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants