supress niches in coroutines
This commit is contained in:
parent
a971212545
commit
a281f93d3d
@ -1001,7 +1001,13 @@ fn coroutine_layout<'tcx>(
|
||||
},
|
||||
fields: outer_fields,
|
||||
abi,
|
||||
largest_niche: prefix.largest_niche,
|
||||
// Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to
|
||||
// self-referentiality), getting the discriminant can cause aliasing violations.
|
||||
// `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that
|
||||
// would do the same for us here.
|
||||
// See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
|
||||
// FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
|
||||
largest_niche: None,
|
||||
size,
|
||||
align,
|
||||
max_repr_align: None,
|
||||
|
66
src/tools/miri/tests/pass/async-niche-aliasing.rs
Normal file
66
src/tools/miri/tests/pass/async-niche-aliasing.rs
Normal file
@ -0,0 +1,66 @@
|
||||
//@revisions: stack tree
|
||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
task::{Context, Poll, Wake},
|
||||
mem::MaybeUninit,
|
||||
};
|
||||
|
||||
struct ThingAdder<'a> {
|
||||
// Using `MaybeUninit` to ensure there are no niches here.
|
||||
thing: MaybeUninit<&'a mut String>,
|
||||
}
|
||||
|
||||
impl Future for ThingAdder<'_> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
unsafe {
|
||||
**self.get_unchecked_mut().thing.assume_init_mut() += ", world";
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut thing = "hello".to_owned();
|
||||
// This future has (at least) two fields, a String (`thing`) and a ThingAdder pointing to that string.
|
||||
let fut = async move { ThingAdder { thing: MaybeUninit::new(&mut thing) }.await };
|
||||
|
||||
let mut fut = MaybeDone::Future(fut);
|
||||
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
|
||||
|
||||
let waker = Arc::new(DummyWaker).into();
|
||||
let mut ctx = Context::from_waker(&waker);
|
||||
// This ends up reading the discriminant of the `MaybeDone`. If that is stored inside the
|
||||
// `thing: String` as a niche optimization, that causes aliasing conflicts with the reference
|
||||
// stored in `ThingAdder`.
|
||||
assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending);
|
||||
assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending);
|
||||
}
|
||||
|
||||
struct DummyWaker;
|
||||
|
||||
impl Wake for DummyWaker {
|
||||
fn wake(self: Arc<Self>) {}
|
||||
}
|
||||
|
||||
pub enum MaybeDone<F: Future> {
|
||||
Future(F),
|
||||
Done,
|
||||
}
|
||||
impl<F: Future<Output = ()>> Future for MaybeDone<F> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
unsafe {
|
||||
match *self.as_mut().get_unchecked_mut() {
|
||||
MaybeDone::Future(ref mut f) => Pin::new_unchecked(f).poll(cx),
|
||||
MaybeDone::Done => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user