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..3ff8dfd572 --- /dev/null +++ b/tests/pass/move-data-across-await-point.rs @@ -0,0 +1,78 @@ +use std::future::Future; + +// 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 +async fn data_moved_async() { + // Vec is not Copy + let mut x: Vec = vec![2]; + let raw_pointer = &mut x as *mut Vec; + helper_async(x, raw_pointer).await; + unsafe { + assert_eq!(*raw_pointer, vec![3]); + // Drop to prevent leak. + std::ptr::drop_in_place(raw_pointer); + } +} + +async fn helper_async(mut data: Vec, raw_pointer: *mut Vec) { + let raw_pointer2 = &mut data as *mut Vec; + // Addresses are different because the generator upvar for `data` is a copy. + // To be more precise, there is a `move` happening in the MIR of the + // generator closure, which copies the pointer. + // + // When copy propagation is enabled for generator upvars, the pointer addresses + // here should be equal. + assert_ne!(raw_pointer, raw_pointer2); + unsafe { + std::ptr::write(raw_pointer, vec![3]); + } +} + +// Same thing as above, but non-async. +fn data_moved() { + let mut x: Vec = vec![2]; + let raw_pointer = &mut x as *mut Vec; + helper(x, raw_pointer); + unsafe { + assert_eq!(*raw_pointer, vec![3]); + std::ptr::drop_in_place(raw_pointer); + } +} + +#[inline(always)] +fn helper(mut data: Vec, raw_pointer: *mut Vec) { + let raw_pointer2 = &mut data as *mut Vec; + assert_ne!(raw_pointer, raw_pointer2); + unsafe { + std::ptr::write(raw_pointer, vec![3]); + } +} + +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(); +}