about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2020-10-02 16:30:36 -0400
committerAaron Hill <aa1ronham@gmail.com>2020-10-04 07:54:02 -0400
commitf23e9a1adf9fff618b9a40cb6e5e570c18e2a226 (patch)
tree585e86d410ead35fda8f488b8c0604fd1626b82a /src
parent4c83eec00883e10e7b859d039dcc0e937a0706d5 (diff)
Add regression test for issue #72470
This was fixed with the upgrade to LLVM 11 in #73526.
It seems extremely unlikey that this exact issue will ever reoccur,
since slight modifications to the code caused the crash to stop
happening. However, it can't hurt to have a test for it.
Diffstat (limited to 'src')
-rw-r--r--src/test/ui/auxiliary/issue-72470-lib.rs175
-rw-r--r--src/test/ui/issue-72470-llvm-dominate.rs66
2 files changed, 241 insertions, 0 deletions
diff --git a/src/test/ui/auxiliary/issue-72470-lib.rs b/src/test/ui/auxiliary/issue-72470-lib.rs
new file mode 100644
index 00000000000..8383eba8912
--- /dev/null
+++ b/src/test/ui/auxiliary/issue-72470-lib.rs
@@ -0,0 +1,175 @@
+// compile-flags: -C opt-level=3
+// edition:2018
+
+use std::future::Future;
+use std::marker::PhantomData;
+use std::pin::Pin;
+use std::sync::atomic::AtomicUsize;
+use std::sync::Arc;
+use std::task::Poll::{Pending, Ready};
+use std::task::Waker;
+use std::task::{Context, Poll};
+use std::{
+    ptr,
+    task::{RawWaker, RawWakerVTable},
+};
+
+/// Future for the [`poll_fn`] function.
+pub struct PollFn<F> {
+    f: F,
+}
+
+impl<F> Unpin for PollFn<F> {}
+
+/// Creates a new future wrapping around a function returning [`Poll`].
+pub fn poll_fn<T, F>(f: F) -> PollFn<F>
+where
+    F: FnMut(&mut Context<'_>) -> Poll<T>,
+{
+    PollFn { f }
+}
+
+impl<T, F> Future for PollFn<F>
+where
+    F: FnMut(&mut Context<'_>) -> Poll<T>,
+{
+    type Output = T;
+
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
+        (&mut self.f)(cx)
+    }
+}
+pub fn run<F: Future>(future: F) -> F::Output {
+    BasicScheduler.block_on(future)
+}
+
+pub(crate) struct BasicScheduler;
+
+impl BasicScheduler {
+    pub(crate) fn block_on<F>(&mut self, mut future: F) -> F::Output
+    where
+        F: Future,
+    {
+        let waker = unsafe { Waker::from_raw(raw_waker()) };
+        let mut cx = std::task::Context::from_waker(&waker);
+
+        let mut future = unsafe { Pin::new_unchecked(&mut future) };
+
+        loop {
+            if let Ready(v) = future.as_mut().poll(&mut cx) {
+                return v;
+            }
+        }
+    }
+}
+
+// ===== impl Spawner =====
+
+fn raw_waker() -> RawWaker {
+    RawWaker::new(ptr::null(), waker_vtable())
+}
+
+fn waker_vtable() -> &'static RawWakerVTable {
+    &RawWakerVTable::new(
+        clone_arc_raw,
+        wake_arc_raw,
+        wake_by_ref_arc_raw,
+        drop_arc_raw,
+    )
+}
+
+unsafe fn clone_arc_raw(_: *const ()) -> RawWaker {
+    raw_waker()
+}
+
+unsafe fn wake_arc_raw(_: *const ()) {}
+
+unsafe fn wake_by_ref_arc_raw(_: *const ()) {}
+
+unsafe fn drop_arc_raw(_: *const ()) {}
+
+struct AtomicWaker {}
+
+impl AtomicWaker {
+    /// Create an `AtomicWaker`
+    fn new() -> AtomicWaker {
+        AtomicWaker {}
+    }
+
+    fn register_by_ref(&self, _waker: &Waker) {}
+}
+
+#[allow(dead_code)]
+struct Tx<T> {
+    inner: Arc<Chan<T>>,
+}
+
+struct Rx<T> {
+    inner: Arc<Chan<T>>,
+}
+
+#[allow(dead_code)]
+struct Chan<T> {
+    tx: PhantomData<T>,
+    semaphore: Sema,
+    rx_waker: AtomicWaker,
+    rx_closed: bool,
+}
+
+fn channel<T>() -> (Tx<T>, Rx<T>) {
+    let chan = Arc::new(Chan {
+        tx: PhantomData,
+        semaphore: Sema(AtomicUsize::new(0)),
+        rx_waker: AtomicWaker::new(),
+        rx_closed: false,
+    });
+
+    (
+        Tx {
+            inner: chan.clone(),
+        },
+        Rx { inner: chan },
+    )
+}
+
+// ===== impl Rx =====
+
+impl<T> Rx<T> {
+    /// Receive the next value
+    fn recv(&mut self, cx: &mut Context<'_>) -> Poll<Option<T>> {
+        self.inner.rx_waker.register_by_ref(cx.waker());
+
+        if self.inner.rx_closed && self.inner.semaphore.is_idle() {
+            Ready(None)
+        } else {
+            Pending
+        }
+    }
+}
+
+struct Sema(AtomicUsize);
+
+impl Sema {
+    fn is_idle(&self) -> bool {
+        false
+    }
+}
+
+pub struct UnboundedReceiver<T> {
+    chan: Rx<T>,
+}
+
+pub fn unbounded_channel<T>() -> UnboundedReceiver<T> {
+    let (tx, rx) = channel();
+
+    drop(tx);
+    let rx = UnboundedReceiver { chan: rx };
+
+    rx
+}
+
+impl<T> UnboundedReceiver<T> {
+    pub async fn recv(&mut self) -> Option<T> {
+        poll_fn(|cx| self.chan.recv(cx)).await
+    }
+}
diff --git a/src/test/ui/issue-72470-llvm-dominate.rs b/src/test/ui/issue-72470-llvm-dominate.rs
new file mode 100644
index 00000000000..feea4683dad
--- /dev/null
+++ b/src/test/ui/issue-72470-llvm-dominate.rs
@@ -0,0 +1,66 @@
+// compile-flags: -C opt-level=3
+// aux-build: issue-72470-lib.rs
+// edition:2018
+// check-pass
+
+// Regression test for issue #72470, using the minimization
+// in https://github.com/jonas-schievink/llvm-error
+
+extern crate issue_72470_lib;
+
+use std::future::Future;
+use std::pin::Pin;
+use std::sync::Mutex;
+use std::task::Poll::{Pending, Ready};
+
+#[allow(dead_code)]
+enum Msg {
+    A(Vec<()>),
+    B,
+}
+
+#[allow(dead_code)]
+enum Out {
+    _0(Option<Msg>),
+    Disabled,
+}
+
+#[allow(unused_must_use)]
+fn main() {
+    let mut rx = issue_72470_lib::unbounded_channel::<Msg>();
+    let entity = Mutex::new(());
+    issue_72470_lib::run(async move {
+        {
+            let output = {
+                let mut fut = rx.recv();
+                issue_72470_lib::poll_fn(|cx| {
+                    loop {
+                        let fut = unsafe { Pin::new_unchecked(&mut fut) };
+                        let out = match fut.poll(cx) {
+                            Ready(out) => out,
+                            Pending => {
+                                break;
+                            }
+                        };
+                        #[allow(unused_variables)]
+                        match &out {
+                            Some(_msg) => {}
+                            _ => break,
+                        }
+                        return Ready(Out::_0(out));
+                    }
+                    Ready(Out::_0(None))
+                })
+                .await
+            };
+            match output {
+                Out::_0(Some(_msg)) => {
+                    entity.lock();
+                }
+                Out::_0(None) => unreachable!(),
+                _ => unreachable!(),
+            }
+        }
+        entity.lock();
+    });
+}