//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows

use std::future::*;
use std::marker::PhantomPinned;
use std::pin::*;
use std::ptr;
use std::task::*;

struct Delay {
    delay: usize,
}

impl Delay {
    fn new(delay: usize) -> Self {
        Delay { delay }
    }
}

impl Future for Delay {
    type Output = ();
    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
        if self.delay > 0 {
            self.delay -= 1;
            Poll::Pending
        } else {
            Poll::Ready(())
        }
    }
}

async fn do_stuff() {
    (&mut Delay::new(1)).await;
}

// Same thing implemented by hand
struct DoStuff {
    state: usize,
    delay: Delay,
    delay_ref: *mut Delay,
    _marker: PhantomPinned,
}

impl DoStuff {
    fn new() -> Self {
        DoStuff {
            state: 0,
            delay: Delay::new(1),
            delay_ref: ptr::null_mut(),
            _marker: PhantomPinned,
        }
    }
}

impl Future for DoStuff {
    type Output = ();
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        unsafe {
            let this = self.get_unchecked_mut();
            match this.state {
                0 => {
                    // Set up self-ref.
                    this.delay_ref = &mut this.delay;
                    // Move to next state.
                    this.state = 1;
                    Poll::Pending
                }
                1 => {
                    let delay = &mut *this.delay_ref;
                    Pin::new_unchecked(delay).poll(cx)
                }
                _ => unreachable!(),
            }
        }
    }
}

fn run_fut<T>(fut: impl Future<Output = T>) -> T {
    let mut context = Context::from_waker(Waker::noop());

    let mut pinned = pin!(fut);
    loop {
        match pinned.as_mut().poll(&mut context) {
            Poll::Pending => continue,
            Poll::Ready(v) => return v,
        }
    }
}

fn self_referential_box() {
    let cx = &mut Context::from_waker(Waker::noop());

    async fn my_fut() -> i32 {
        let val = 10;
        let val_ref = &val;

        let _ = Delay::new(1).await;

        *val_ref
    }

    fn box_poll<F: Future>(
        mut f: Pin<Box<F>>,
        cx: &mut Context<'_>,
    ) -> (Pin<Box<F>>, Poll<F::Output>) {
        let p = f.as_mut().poll(cx);
        (f, p)
    }

    let my_fut = Box::pin(my_fut());
    let (my_fut, p1) = box_poll(my_fut, cx);
    assert!(p1.is_pending());
    let (my_fut, p2) = box_poll(my_fut, cx);
    assert!(p2.is_ready());
    drop(my_fut);
}

fn main() {
    run_fut(do_stuff());
    run_fut(DoStuff::new());
    self_referential_box();
}
