Skip to content

Commit

Permalink
Some local unit tests to track progress and capture interesting cases…
Browse files Browse the repository at this point in the history
… as I identify them.

issue-62958-c.rs was reduced from the tracing-attributes proc-macro crate.

issue-62958-d.rs was reduced from the doc test attached to `AtomicPtr::from_mut_slice`.

issue-62958-e.rs covers some important operational characteristics.
  • Loading branch information
pnkfelix committed Aug 18, 2023
1 parent 6dbe78d commit f4a1d4b
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 0 deletions.
28 changes: 28 additions & 0 deletions tests/ui/mir/issue-62958-a.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// revisions: both_off just_prop both_on
// ignore-tidy-linelength
// run-pass
// [both_off] compile-flags: -Z mir-enable-passes=-UpvarToLocalProp,-InlineFutureIntoFuture
// [just_prop] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,-InlineFutureIntoFuture
// [both_on] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,+InlineFutureIntoFuture
// edition:2018

async fn wait() {}
#[allow(dropping_copy_types)]
async fn test(arg: [u8; 8192]) {
wait().await;
drop(arg);
}

#[cfg(both_off)]
fn main() {
let expected = 16000..=17000;
let actual = std::mem::size_of_val(&test([0; 8192]));
assert!(expected.contains(&actual));
}

#[cfg(any(just_prop, both_on))]
fn main() {
let expected = 8192..=9999;
let actual = std::mem::size_of_val(&test([0; 8192]));
assert!(expected.contains(&actual));
}
27 changes: 27 additions & 0 deletions tests/ui/mir/issue-62958-b.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// revisions: both_off just_prop both_on
// ignore-tidy-linelength
// run-pass
// [both_off] compile-flags: -Z mir-enable-passes=-UpvarToLocalProp,-InlineFutureIntoFuture
// [just_prop] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,-InlineFutureIntoFuture
// [both_on] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,+InlineFutureIntoFuture
// edition:2018

async fn test(_arg: [u8; 8192]) {}

async fn use_future(fut: impl std::future::Future<Output = ()>) {
fut.await
}

#[cfg(both_on)]
fn main() {
let expected = 8192..=9999;
let actual = std::mem::size_of_val(&use_future(use_future(use_future(test([0; 8192])))));
assert!(expected.contains(&actual), "expected: {:?} actual: {}", expected, actual);
}

#[cfg(any(both_off, just_prop))]
fn main() {
let expected = 16000..=8192*(2*2*2*2); // O(2^n) LOL
let actual = std::mem::size_of_val(&use_future(use_future(use_future(test([0; 8192])))));
assert!(expected.contains(&actual), "expected: {:?} actual: {}", expected, actual);
}
23 changes: 23 additions & 0 deletions tests/ui/mir/issue-62958-c.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// revisions: both_off just_prop both_on
// ignore-tidy-linelength
// build-pass
// [both_off] compile-flags: -Z mir-enable-passes=-UpvarToLocalProp,-InlineFutureIntoFuture
// [just_prop] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,-InlineFutureIntoFuture
// [both_on] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,+InlineFutureIntoFuture

#![crate_type="rlib"]

// FIXME: I should be able to expand the below into something that actually does
// some interesting work. The crucial thing is to enforce a rule that we never
// replace `_3` with `_1.0` on a place that holds a Deref.

pub struct G { p: () }
pub struct S { g: G }
pub struct R<'a> { s: &'a S, b: () }

pub fn gen_function(input: R<'_>) {
let R { s, b: _b } = input;
let S { g, .. } = s;
let G { p: _p, .. } = g;
loop { }
}
34 changes: 34 additions & 0 deletions tests/ui/mir/issue-62958-d.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// revisions: both_off just_prop both_on
// ignore-tidy-linelength
// run-pass
// [both_off] compile-flags: -Z mir-enable-passes=-UpvarToLocalProp,-InlineFutureIntoFuture
// [just_prop] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,-InlineFutureIntoFuture
// [both_on] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,+InlineFutureIntoFuture
// edition:2018

#![feature(atomic_from_mut)]

// FIXME: I should be able to reduce the below further now that I understand the
// nature of the problem. (Namely, the fact that the old upvar_to_local_prop
// code was ignoring locals in projections.)

use std::ptr::null_mut;
use std::sync::atomic::{AtomicPtr, Ordering};

fn main() {
let mut some_ptrs = [null_mut::<String>(); 10];
let a = &*AtomicPtr::from_mut_slice(&mut some_ptrs);
std::thread::scope(|s| {
for i in 0..a.len() {
s.spawn(move || {
let name = Box::new(format!("thread{i}"));
a[i].store(Box::into_raw(name), Ordering::Relaxed);
});
}
});
for p in some_ptrs {
assert!(!p.is_null());
let name = unsafe { Box::from_raw(p) };
println!("Hello, {name}!");
}
}
108 changes: 108 additions & 0 deletions tests/ui/mir/issue-62958-e.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// revisions: both_off both_on
// ignore-tidy-linelength
// run-pass
// [both_off] compile-flags: -Z mir-enable-passes=-UpvarToLocalProp,-InlineFutureIntoFuture
// [both_on] compile-flags: -Z mir-enable-passes=+UpvarToLocalProp,+InlineFutureIntoFuture
// edition:2018

use std::future::Future;

async fn wait() {}

fn test_shrinks_1(arg: [u8; 1000]) -> impl std::future::Future<Output=()> {
async move {
let mut local = arg;
local[2] = 3;
wait().await;
assert_eq!(local[2], 3);
}
}

fn test_noshrinks_1(mut arg: [u8; 1000]) -> impl std::future::Future<Output=()> {
async move {
let mut local = arg;
local[2] = 3;
let l2 = &mut arg;
l2[2] = 4;
wait().await;
assert_eq!(local[2], 3);
assert_eq!(l2[2], 4);
}
}

fn test_noshrinks_2(arg: [u8; 1000]) -> impl std::future::Future<Output=()> {
async move {
let mut local = arg;
local[2] = 1;
let l2 = arg;
wait().await;
assert_eq!(local[2], 1);
assert_eq!(l2[2], 0);
}
}

fn test_noshrinks_3(arg: [u8; 1000]) -> impl std::future::Future<Output=()> {
async move {
let bor = &arg[2];
let mut local = arg;
local[2] = 1;
wait().await;
assert_eq!(local[2], 1);
assert_eq!(*bor, 0);
}
}

#[cfg(both_on)]
fn check_shrinks(which: &str, fut: impl std::future::Future<Output=()>) {
let sz = std::mem::size_of_val(&fut);
println!("{which}: {sz}");
assert!((1000..=1500).contains(&sz));
run_fut(fut)
}

fn check_no_shrinks(which: &str, fut: impl std::future::Future<Output=()>) {
let sz = std::mem::size_of_val(&fut);
println!("{which}: {sz}");
assert!((2000..).contains(&sz));
run_fut(fut);
}

#[cfg(both_on)]
fn main() {
check_shrinks("s1", test_shrinks_1([0; 1000]));

check_no_shrinks("n1", test_noshrinks_1([0; 1000]));
check_no_shrinks("n2", test_noshrinks_2([0; 1000]));
check_no_shrinks("n3", test_noshrinks_3([0; 1000]));
}

#[cfg(both_off)]
fn main() {
check_no_shrinks("s1", test_shrinks_1([0; 1000]));
check_no_shrinks("n1", test_noshrinks_1([0; 1000]));
check_no_shrinks("n2", test_noshrinks_2([0; 1000]));
check_no_shrinks("n3", test_noshrinks_3([0; 1000]));
}

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,
}
}
}

0 comments on commit f4a1d4b

Please sign in to comment.