|
| 1 | +use std::future::Future; |
| 2 | + |
| 3 | +// This test: |
| 4 | +// - Compares addresses of non-Copy data before and after moving it |
| 5 | +// - Writes to the pointer after it has moved across the await point |
| 6 | +async fn data_moved_async() { |
| 7 | + // Vec<T> is not Copy |
| 8 | + let mut x: Vec<u8> = vec![2]; |
| 9 | + let raw_pointer = &mut x as *mut Vec<u8>; |
| 10 | + helper_async(x, raw_pointer).await; |
| 11 | + unsafe { |
| 12 | + assert_eq!(*raw_pointer, vec![3]); |
| 13 | + // Drop to prevent leak. |
| 14 | + std::ptr::drop_in_place(raw_pointer); |
| 15 | + } |
| 16 | +} |
| 17 | + |
| 18 | +async fn helper_async(mut data: Vec<u8>, raw_pointer: *mut Vec<u8>) { |
| 19 | + let raw_pointer2 = &mut data as *mut Vec<u8>; |
| 20 | + // Addresses are different because the generator upvar for `data` is a copy. |
| 21 | + // To be more precise, there is a `move` happening in the MIR of the |
| 22 | + // generator closure, which copies the pointer. |
| 23 | + // |
| 24 | + // When copy propagation is enabled for generator upvars, the pointer addresses |
| 25 | + // here should be equal. |
| 26 | + assert_ne!(raw_pointer, raw_pointer2); |
| 27 | + unsafe { |
| 28 | + std::ptr::write(raw_pointer, vec![3]); |
| 29 | + } |
| 30 | +} |
| 31 | + |
| 32 | +// Same thing as above, but non-async. |
| 33 | +fn data_moved() { |
| 34 | + let mut x: Vec<u8> = vec![2]; |
| 35 | + let raw_pointer = &mut x as *mut Vec<u8>; |
| 36 | + helper(x, raw_pointer); |
| 37 | + unsafe { |
| 38 | + assert_eq!(*raw_pointer, vec![3]); |
| 39 | + std::ptr::drop_in_place(raw_pointer); |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +#[inline(always)] |
| 44 | +fn helper(mut data: Vec<u8>, raw_pointer: *mut Vec<u8>) { |
| 45 | + let raw_pointer2 = &mut data as *mut Vec<u8>; |
| 46 | + assert_ne!(raw_pointer, raw_pointer2); |
| 47 | + unsafe { |
| 48 | + std::ptr::write(raw_pointer, vec![3]); |
| 49 | + } |
| 50 | +} |
| 51 | + |
| 52 | +fn run_fut<T>(fut: impl Future<Output = T>) -> T { |
| 53 | + use std::sync::Arc; |
| 54 | + use std::task::{Context, Poll, Wake, Waker}; |
| 55 | + |
| 56 | + struct MyWaker; |
| 57 | + impl Wake for MyWaker { |
| 58 | + fn wake(self: Arc<Self>) { |
| 59 | + unimplemented!() |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + let waker = Waker::from(Arc::new(MyWaker)); |
| 64 | + let mut context = Context::from_waker(&waker); |
| 65 | + |
| 66 | + let mut pinned = Box::pin(fut); |
| 67 | + loop { |
| 68 | + match pinned.as_mut().poll(&mut context) { |
| 69 | + Poll::Pending => continue, |
| 70 | + Poll::Ready(v) => return v, |
| 71 | + } |
| 72 | + } |
| 73 | +} |
| 74 | + |
| 75 | +fn main() { |
| 76 | + run_fut(data_moved_async()); |
| 77 | + data_moved(); |
| 78 | +} |
0 commit comments