forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add regression test for issue rust-lang#72470
This was fixed with the upgrade to LLVM 11 in rust-lang#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.
- Loading branch information
Showing
2 changed files
with
241 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
}); | ||
} |