Auto merge of #123402 - workingjubilee:rollup-0j5ihn6, r=workingjubilee
Rollup of 4 pull requests Successful merges: - #122411 ( Provide cabi_realloc on wasm32-wasip2 by default ) - #123349 (Fix capture analysis for by-move closure bodies) - #123359 (Link against libc++abi and libunwind as well when building LLVM wrappers on AIX) - #123388 (use a consistent style for links) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
c7491b9733
@ -391,6 +391,12 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// libc++abi and libunwind have to be specified explicitly on AIX.
|
||||||
|
if target.contains("aix") {
|
||||||
|
println!("cargo:rustc-link-lib=c++abi");
|
||||||
|
println!("cargo:rustc-link-lib=unwind");
|
||||||
|
}
|
||||||
|
|
||||||
// Libstdc++ depends on pthread which Rust doesn't link on MinGW
|
// Libstdc++ depends on pthread which Rust doesn't link on MinGW
|
||||||
// since nothing else requires it.
|
// since nothing else requires it.
|
||||||
if target.ends_with("windows-gnu") {
|
if target.ends_with("windows-gnu") {
|
||||||
|
@ -1,7 +1,66 @@
|
|||||||
//! A MIR pass which duplicates a coroutine's body and removes any derefs which
|
//! This pass constructs a second coroutine body sufficient for return from
|
||||||
//! would be present for upvars that are taken by-ref. The result of which will
|
//! `FnOnce`/`AsyncFnOnce` implementations for coroutine-closures (e.g. async closures).
|
||||||
//! be a coroutine body that takes all of its upvars by-move, and which we stash
|
//!
|
||||||
//! into the `CoroutineInfo` for all coroutines returned by coroutine-closures.
|
//! Consider an async closure like:
|
||||||
|
//! ```rust
|
||||||
|
//! #![feature(async_closure)]
|
||||||
|
//!
|
||||||
|
//! let x = vec![1, 2, 3];
|
||||||
|
//!
|
||||||
|
//! let closure = async move || {
|
||||||
|
//! println!("{x:#?}");
|
||||||
|
//! };
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! This desugars to something like:
|
||||||
|
//! ```rust,ignore (invalid-borrowck)
|
||||||
|
//! let x = vec![1, 2, 3];
|
||||||
|
//!
|
||||||
|
//! let closure = move || {
|
||||||
|
//! async {
|
||||||
|
//! println!("{x:#?}");
|
||||||
|
//! }
|
||||||
|
//! };
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Important to note here is that while the outer closure *moves* `x: Vec<i32>`
|
||||||
|
//! into its upvars, the inner `async` coroutine simply captures a ref of `x`.
|
||||||
|
//! This is the "magic" of async closures -- the futures that they return are
|
||||||
|
//! allowed to borrow from their parent closure's upvars.
|
||||||
|
//!
|
||||||
|
//! However, what happens when we call `closure` with `AsyncFnOnce` (or `FnOnce`,
|
||||||
|
//! since all async closures implement that too)? Well, recall the signature:
|
||||||
|
//! ```
|
||||||
|
//! use std::future::Future;
|
||||||
|
//! pub trait AsyncFnOnce<Args>
|
||||||
|
//! {
|
||||||
|
//! type CallOnceFuture: Future<Output = Self::Output>;
|
||||||
|
//! type Output;
|
||||||
|
//! fn async_call_once(
|
||||||
|
//! self,
|
||||||
|
//! args: Args
|
||||||
|
//! ) -> Self::CallOnceFuture;
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! This signature *consumes* the async closure (`self`) and returns a `CallOnceFuture`.
|
||||||
|
//! How do we deal with the fact that the coroutine is supposed to take a reference
|
||||||
|
//! to the captured `x` from the parent closure, when that parent closure has been
|
||||||
|
//! destroyed?
|
||||||
|
//!
|
||||||
|
//! This is the second piece of magic of async closures. We can simply create a
|
||||||
|
//! *second* `async` coroutine body where that `x` that was previously captured
|
||||||
|
//! by reference is now captured by value. This means that we consume the outer
|
||||||
|
//! closure and return a new coroutine that will hold onto all of these captures,
|
||||||
|
//! and drop them when it is finished (i.e. after it has been `.await`ed).
|
||||||
|
//!
|
||||||
|
//! We do this with the analysis below, which detects the captures that come from
|
||||||
|
//! borrowing from the outer closure, and we simply peel off a `deref` projection
|
||||||
|
//! from them. This second body is stored alongside the first body, and optimized
|
||||||
|
//! with it in lockstep. When we need to resolve a body for `FnOnce` or `AsyncFnOnce`,
|
||||||
|
//! we use this "by move" body instead.
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
use rustc_data_structures::unord::UnordSet;
|
use rustc_data_structures::unord::UnordSet;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
@ -14,6 +73,8 @@
|
|||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for ByMoveBody {
|
impl<'tcx> MirPass<'tcx> for ByMoveBody {
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
|
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
|
||||||
|
// We only need to generate by-move coroutine bodies for coroutines that come
|
||||||
|
// from coroutine-closures.
|
||||||
let Some(coroutine_def_id) = body.source.def_id().as_local() else {
|
let Some(coroutine_def_id) = body.source.def_id().as_local() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@ -22,44 +83,70 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
|
|||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Also, let's skip processing any bodies with errors, since there's no guarantee
|
||||||
|
// the MIR body will be constructed well.
|
||||||
let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
|
let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
|
||||||
if coroutine_ty.references_error() {
|
if coroutine_ty.references_error() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!("{body:#?}") };
|
|
||||||
|
|
||||||
let coroutine_kind = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap();
|
let ty::Coroutine(_, coroutine_args) = *coroutine_ty.kind() else { bug!("{body:#?}") };
|
||||||
|
// We don't need to generate a by-move coroutine if the kind of the coroutine is
|
||||||
|
// already `FnOnce` -- that means that any upvars that the closure consumes have
|
||||||
|
// already been taken by-value.
|
||||||
|
let coroutine_kind = coroutine_args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap();
|
||||||
if coroutine_kind == ty::ClosureKind::FnOnce {
|
if coroutine_kind == ty::ClosureKind::FnOnce {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let parent_def_id = tcx.local_parent(coroutine_def_id);
|
||||||
|
let ty::CoroutineClosure(_, parent_args) =
|
||||||
|
*tcx.type_of(parent_def_id).instantiate_identity().kind()
|
||||||
|
else {
|
||||||
|
bug!();
|
||||||
|
};
|
||||||
|
let parent_closure_args = parent_args.as_coroutine_closure();
|
||||||
|
let num_args = parent_closure_args
|
||||||
|
.coroutine_closure_sig()
|
||||||
|
.skip_binder()
|
||||||
|
.tupled_inputs_ty
|
||||||
|
.tuple_fields()
|
||||||
|
.len();
|
||||||
|
|
||||||
let mut by_ref_fields = UnordSet::default();
|
let mut by_ref_fields = UnordSet::default();
|
||||||
let by_move_upvars = Ty::new_tup_from_iter(
|
for (idx, (coroutine_capture, parent_capture)) in tcx
|
||||||
tcx,
|
.closure_captures(coroutine_def_id)
|
||||||
tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| {
|
.iter()
|
||||||
if capture.is_by_ref() {
|
// By construction we capture all the args first.
|
||||||
by_ref_fields.insert(FieldIdx::from_usize(idx));
|
.skip(num_args)
|
||||||
}
|
.zip_eq(tcx.closure_captures(parent_def_id))
|
||||||
capture.place.ty()
|
.enumerate()
|
||||||
}),
|
{
|
||||||
);
|
// This upvar is captured by-move from the parent closure, but by-ref
|
||||||
let by_move_coroutine_ty = Ty::new_coroutine(
|
// from the inner async block. That means that it's being borrowed from
|
||||||
tcx,
|
// the outer closure body -- we need to change the coroutine to take the
|
||||||
coroutine_def_id.to_def_id(),
|
// upvar by value.
|
||||||
ty::CoroutineArgs::new(
|
if coroutine_capture.is_by_ref() && !parent_capture.is_by_ref() {
|
||||||
|
by_ref_fields.insert(FieldIdx::from_usize(num_args + idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we're actually talking about the same capture.
|
||||||
|
// FIXME(async_closures): We could look at the `hir::Upvar` instead?
|
||||||
|
assert_eq!(coroutine_capture.place.ty(), parent_capture.place.ty());
|
||||||
|
}
|
||||||
|
|
||||||
|
let by_move_coroutine_ty = tcx
|
||||||
|
.instantiate_bound_regions_with_erased(parent_closure_args.coroutine_closure_sig())
|
||||||
|
.to_coroutine_given_kind_and_upvars(
|
||||||
tcx,
|
tcx,
|
||||||
ty::CoroutineArgsParts {
|
parent_closure_args.parent_args(),
|
||||||
parent_args: args.as_coroutine().parent_args(),
|
coroutine_def_id.to_def_id(),
|
||||||
kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
|
ty::ClosureKind::FnOnce,
|
||||||
resume_ty: args.as_coroutine().resume_ty(),
|
tcx.lifetimes.re_erased,
|
||||||
yield_ty: args.as_coroutine().yield_ty(),
|
parent_closure_args.tupled_upvars_ty(),
|
||||||
return_ty: args.as_coroutine().return_ty(),
|
parent_closure_args.coroutine_captures_by_ref_ty(),
|
||||||
witness: args.as_coroutine().witness(),
|
);
|
||||||
tupled_upvars_ty: by_move_upvars,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.args,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut by_move_body = body.clone();
|
let mut by_move_body = body.clone();
|
||||||
MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body);
|
MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body);
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Creates a new file and write bytes to it (you can also use [`write()`]):
|
/// Creates a new file and write bytes to it (you can also use [`write`]):
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use std::fs::File;
|
/// use std::fs::File;
|
||||||
@ -2018,7 +2018,7 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
|
|||||||
/// the length of the `to` file as reported by `metadata`.
|
/// the length of the `to` file as reported by `metadata`.
|
||||||
///
|
///
|
||||||
/// If you want to copy the contents of one file to another and you’re
|
/// If you want to copy the contents of one file to another and you’re
|
||||||
/// working with [`File`]s, see the [`io::copy()`] function.
|
/// working with [`File`]s, see the [`io::copy`](io::copy()) function.
|
||||||
///
|
///
|
||||||
/// # Platform-specific behavior
|
/// # Platform-specific behavior
|
||||||
///
|
///
|
||||||
|
@ -37,12 +37,12 @@
|
|||||||
} else if #[cfg(target_os = "hermit")] {
|
} else if #[cfg(target_os = "hermit")] {
|
||||||
mod hermit;
|
mod hermit;
|
||||||
pub use self::hermit::*;
|
pub use self::hermit::*;
|
||||||
} else if #[cfg(target_os = "wasi")] {
|
|
||||||
mod wasi;
|
|
||||||
pub use self::wasi::*;
|
|
||||||
} else if #[cfg(all(target_os = "wasi", target_env = "p2"))] {
|
} else if #[cfg(all(target_os = "wasi", target_env = "p2"))] {
|
||||||
mod wasip2;
|
mod wasip2;
|
||||||
pub use self::wasip2::*;
|
pub use self::wasip2::*;
|
||||||
|
} else if #[cfg(target_os = "wasi")] {
|
||||||
|
mod wasi;
|
||||||
|
pub use self::wasi::*;
|
||||||
} else if #[cfg(target_family = "wasm")] {
|
} else if #[cfg(target_family = "wasm")] {
|
||||||
mod wasm;
|
mod wasm;
|
||||||
pub use self::wasm::*;
|
pub use self::wasm::*;
|
||||||
|
65
library/std/src/sys/pal/wasip2/cabi_realloc.rs
Normal file
65
library/std/src/sys/pal/wasip2/cabi_realloc.rs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//! This module contains a canonical definition of the `cabi_realloc` function
|
||||||
|
//! for the component model.
|
||||||
|
//!
|
||||||
|
//! The component model's canonical ABI for representing datatypes in memory
|
||||||
|
//! makes use of this function when transferring lists and strings, for example.
|
||||||
|
//! This function behaves like C's `realloc` but also takes alignment into
|
||||||
|
//! account.
|
||||||
|
//!
|
||||||
|
//! Components are notably not required to export this function, but nearly
|
||||||
|
//! all components end up doing so currently. This definition in the standard
|
||||||
|
//! library removes the need for all compilations to define this themselves.
|
||||||
|
//!
|
||||||
|
//! More information about the canonical ABI can be found at
|
||||||
|
//! <https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md>
|
||||||
|
//!
|
||||||
|
//! Note that the name of this function is not standardized in the canonical ABI
|
||||||
|
//! at this time. Instead it's a convention of the "componentization process"
|
||||||
|
//! where a core wasm module is converted to a component to use this name.
|
||||||
|
//! Additionally this is not the only possible definition of this function, so
|
||||||
|
//! this is defined as a "weak" symbol. This means that other definitions are
|
||||||
|
//! allowed to overwrite it if they are present in a compilation.
|
||||||
|
|
||||||
|
use crate::alloc::{self, Layout};
|
||||||
|
use crate::ptr;
|
||||||
|
|
||||||
|
#[used]
|
||||||
|
static FORCE_CODEGEN_OF_CABI_REALLOC: unsafe extern "C" fn(
|
||||||
|
*mut u8,
|
||||||
|
usize,
|
||||||
|
usize,
|
||||||
|
usize,
|
||||||
|
) -> *mut u8 = cabi_realloc;
|
||||||
|
|
||||||
|
#[linkage = "weak"]
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn cabi_realloc(
|
||||||
|
old_ptr: *mut u8,
|
||||||
|
old_len: usize,
|
||||||
|
align: usize,
|
||||||
|
new_len: usize,
|
||||||
|
) -> *mut u8 {
|
||||||
|
let layout;
|
||||||
|
let ptr = if old_len == 0 {
|
||||||
|
if new_len == 0 {
|
||||||
|
return ptr::without_provenance_mut(align);
|
||||||
|
}
|
||||||
|
layout = Layout::from_size_align_unchecked(new_len, align);
|
||||||
|
alloc::alloc(layout)
|
||||||
|
} else {
|
||||||
|
debug_assert_ne!(new_len, 0, "non-zero old_len requires non-zero new_len!");
|
||||||
|
layout = Layout::from_size_align_unchecked(old_len, align);
|
||||||
|
alloc::realloc(old_ptr, layout, new_len)
|
||||||
|
};
|
||||||
|
if ptr.is_null() {
|
||||||
|
// Print a nice message in debug mode, but in release mode don't
|
||||||
|
// pull in so many dependencies related to printing so just emit an
|
||||||
|
// `unreachable` instruction.
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
alloc::handle_alloc_error(layout);
|
||||||
|
} else {
|
||||||
|
super::abort_internal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
@ -10,8 +10,6 @@
|
|||||||
pub mod alloc;
|
pub mod alloc;
|
||||||
#[path = "../wasi/args.rs"]
|
#[path = "../wasi/args.rs"]
|
||||||
pub mod args;
|
pub mod args;
|
||||||
#[path = "../unix/cmath.rs"]
|
|
||||||
pub mod cmath;
|
|
||||||
#[path = "../wasi/env.rs"]
|
#[path = "../wasi/env.rs"]
|
||||||
pub mod env;
|
pub mod env;
|
||||||
#[path = "../wasi/fd.rs"]
|
#[path = "../wasi/fd.rs"]
|
||||||
@ -28,10 +26,6 @@
|
|||||||
pub mod net;
|
pub mod net;
|
||||||
#[path = "../wasi/os.rs"]
|
#[path = "../wasi/os.rs"]
|
||||||
pub mod os;
|
pub mod os;
|
||||||
#[path = "../unix/os_str.rs"]
|
|
||||||
pub mod os_str;
|
|
||||||
#[path = "../unix/path.rs"]
|
|
||||||
pub mod path;
|
|
||||||
#[path = "../unsupported/pipe.rs"]
|
#[path = "../unsupported/pipe.rs"]
|
||||||
pub mod pipe;
|
pub mod pipe;
|
||||||
#[path = "../unsupported/process.rs"]
|
#[path = "../unsupported/process.rs"]
|
||||||
@ -72,3 +66,5 @@
|
|||||||
use helpers::err2io;
|
use helpers::err2io;
|
||||||
pub use helpers::hashmap_random_keys;
|
pub use helpers::hashmap_random_keys;
|
||||||
pub use helpers::is_interrupted;
|
pub use helpers::is_interrupted;
|
||||||
|
|
||||||
|
mod cabi_realloc;
|
||||||
|
91
src/tools/miri/tests/pass/async-closure-captures.rs
Normal file
91
src/tools/miri/tests/pass/async-closure-captures.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Same as rustc's `tests/ui/async-await/async-closures/captures.rs`, keep in sync
|
||||||
|
|
||||||
|
#![feature(async_closure, noop_waker)]
|
||||||
|
|
||||||
|
use std::future::Future;
|
||||||
|
use std::pin::pin;
|
||||||
|
use std::task::*;
|
||||||
|
|
||||||
|
pub fn block_on<T>(fut: impl Future<Output = T>) -> T {
|
||||||
|
let mut fut = pin!(fut);
|
||||||
|
let ctx = &mut Context::from_waker(Waker::noop());
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match fut.as_mut().poll(ctx) {
|
||||||
|
Poll::Pending => {}
|
||||||
|
Poll::Ready(t) => break t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
block_on(async_main());
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn call<T>(f: &impl async Fn() -> T) -> T {
|
||||||
|
f().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn call_once<T>(f: impl async FnOnce() -> T) -> T {
|
||||||
|
f().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
struct Hello(i32);
|
||||||
|
|
||||||
|
async fn async_main() {
|
||||||
|
// Capture something by-ref
|
||||||
|
{
|
||||||
|
let x = Hello(0);
|
||||||
|
let c = async || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
|
||||||
|
let x = &Hello(1);
|
||||||
|
let c = async || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture something and consume it (force to `AsyncFnOnce`)
|
||||||
|
{
|
||||||
|
let x = Hello(2);
|
||||||
|
let c = async || {
|
||||||
|
println!("{x:?}");
|
||||||
|
drop(x);
|
||||||
|
};
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture something with `move`, don't consume it
|
||||||
|
{
|
||||||
|
let x = Hello(3);
|
||||||
|
let c = async move || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
|
||||||
|
let x = &Hello(4);
|
||||||
|
let c = async move || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture something with `move`, also consume it (so `AsyncFnOnce`)
|
||||||
|
{
|
||||||
|
let x = Hello(5);
|
||||||
|
let c = async move || {
|
||||||
|
println!("{x:?}");
|
||||||
|
drop(x);
|
||||||
|
};
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
}
|
10
src/tools/miri/tests/pass/async-closure-captures.stdout
Normal file
10
src/tools/miri/tests/pass/async-closure-captures.stdout
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Hello(0)
|
||||||
|
Hello(0)
|
||||||
|
Hello(1)
|
||||||
|
Hello(1)
|
||||||
|
Hello(2)
|
||||||
|
Hello(3)
|
||||||
|
Hello(3)
|
||||||
|
Hello(4)
|
||||||
|
Hello(4)
|
||||||
|
Hello(5)
|
82
tests/ui/async-await/async-closures/captures.rs
Normal file
82
tests/ui/async-await/async-closures/captures.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//@ aux-build:block-on.rs
|
||||||
|
//@ edition:2021
|
||||||
|
//@ run-pass
|
||||||
|
//@ check-run-results
|
||||||
|
|
||||||
|
// Same as miri's `tests/pass/async-closure-captures.rs`, keep in sync
|
||||||
|
|
||||||
|
#![feature(async_closure)]
|
||||||
|
|
||||||
|
extern crate block_on;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
block_on::block_on(async_main());
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn call<T>(f: &impl async Fn() -> T) -> T {
|
||||||
|
f().await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn call_once<T>(f: impl async FnOnce() -> T) -> T {
|
||||||
|
f().await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(unused)]
|
||||||
|
struct Hello(i32);
|
||||||
|
|
||||||
|
async fn async_main() {
|
||||||
|
// Capture something by-ref
|
||||||
|
{
|
||||||
|
let x = Hello(0);
|
||||||
|
let c = async || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
|
||||||
|
let x = &Hello(1);
|
||||||
|
let c = async || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture something and consume it (force to `AsyncFnOnce`)
|
||||||
|
{
|
||||||
|
let x = Hello(2);
|
||||||
|
let c = async || {
|
||||||
|
println!("{x:?}");
|
||||||
|
drop(x);
|
||||||
|
};
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture something with `move`, don't consume it
|
||||||
|
{
|
||||||
|
let x = Hello(3);
|
||||||
|
let c = async move || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
|
||||||
|
let x = &Hello(4);
|
||||||
|
let c = async move || {
|
||||||
|
println!("{x:?}");
|
||||||
|
};
|
||||||
|
call(&c).await;
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Capture something with `move`, also consume it (so `AsyncFnOnce`)
|
||||||
|
{
|
||||||
|
let x = Hello(5);
|
||||||
|
let c = async move || {
|
||||||
|
println!("{x:?}");
|
||||||
|
drop(x);
|
||||||
|
};
|
||||||
|
call_once(c).await;
|
||||||
|
}
|
||||||
|
}
|
10
tests/ui/async-await/async-closures/captures.run.stdout
Normal file
10
tests/ui/async-await/async-closures/captures.run.stdout
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Hello(0)
|
||||||
|
Hello(0)
|
||||||
|
Hello(1)
|
||||||
|
Hello(1)
|
||||||
|
Hello(2)
|
||||||
|
Hello(3)
|
||||||
|
Hello(3)
|
||||||
|
Hello(4)
|
||||||
|
Hello(4)
|
||||||
|
Hello(5)
|
Loading…
Reference in New Issue
Block a user