Skip to content

Add tests for moving data across await point #2784

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 18, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions tests/pass/move-data-across-await-point.rs
Original file line number Diff line number Diff line change
@@ -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<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 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<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::sync::Arc;
use std::task::{Context, Poll, Wake, Waker};

struct MyWaker;
impl Wake for MyWaker {
fn wake(self: Arc<Self>) {
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();
}