diff --git a/tests/pass/move-data-across-await-point.rs b/tests/pass/move-data-across-await-point.rs new file mode 100644 index 0000000000..489fae66ff --- /dev/null +++ b/tests/pass/move-data-across-await-point.rs @@ -0,0 +1,81 @@ +use std::future::Future; +use std::ptr; + +// This test: +// - Compares addresses of non-Copy data before and after moving it +// - Writes to the pointer after it has moved across the await point +// +// This is only meant to assert current behavior, not guarantee that this is +// how it should work in the future. In fact, upcoming changes to rustc +// *should* break these tests. +// See: https://github.com/rust-lang/rust/issues/62958 +async fn data_moved_async() { + async fn helper(mut data: Vec, raw_pointer: *mut Vec) { + let raw_pointer2 = ptr::addr_of_mut!(data); + // `raw_pointer` points to the original location where the Vec was stored in the caller. + // `data` is where that Vec (to be precise, its ptr+capacity+len on-stack data) + // got moved to. Those will usually not be the same since the Vec got moved twice + // (into the function call, and then into the generator upvar). + assert_ne!(raw_pointer, raw_pointer2); + unsafe { + // This writes into the `x` in `data_moved_async`, re-initializing it. + std::ptr::write(raw_pointer, vec![3]); + } + } + // Vec is not Copy + let mut x: Vec = vec![2]; + let raw_pointer = ptr::addr_of_mut!(x); + helper(x, raw_pointer).await; + unsafe { + assert_eq!(*raw_pointer, vec![3]); + // Drop to prevent leak. + std::ptr::drop_in_place(raw_pointer); + } +} + +// Same thing as above, but non-async. +fn data_moved() { + fn helper(mut data: Vec, raw_pointer: *mut Vec) { + let raw_pointer2 = ptr::addr_of_mut!(data); + assert_ne!(raw_pointer, raw_pointer2); + unsafe { + std::ptr::write(raw_pointer, vec![3]); + } + } + + let mut x: Vec = vec![2]; + let raw_pointer = ptr::addr_of_mut!(x); + helper(x, raw_pointer); + unsafe { + assert_eq!(*raw_pointer, vec![3]); + std::ptr::drop_in_place(raw_pointer); + } +} + +fn run_fut(fut: impl Future) -> T { + use std::sync::Arc; + use std::task::{Context, Poll, Wake, Waker}; + + struct MyWaker; + impl Wake for MyWaker { + fn wake(self: Arc) { + unimplemented!() + } + } + + let waker = Waker::from(Arc::new(MyWaker)); + let mut context = Context::from_waker(&waker); + + let mut pinned = Box::pin(fut); + loop { + match pinned.as_mut().poll(&mut context) { + Poll::Pending => continue, + Poll::Ready(v) => return v, + } + } +} + +fn main() { + run_fut(data_moved_async()); + data_moved(); +}