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<u8>, raw_pointer: *mut Vec<u8>) {
        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 coroutine 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<T> is not Copy
    let mut x: Vec<u8> = 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<u8>, raw_pointer: *mut Vec<u8>) {
        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<u8> = 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<T>(fut: impl Future<Output = T>) -> T {
    use std::task::{Context, Poll, Waker};

    let mut context = Context::from_waker(Waker::noop());

    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();
}
