//@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows use std::future::Future; use std::mem::MaybeUninit; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll, Wake}; 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 { 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) {} } pub enum MaybeDone { Future(F), Done, } impl> Future for MaybeDone { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { unsafe { match *self.as_mut().get_unchecked_mut() { MaybeDone::Future(ref mut f) => Pin::new_unchecked(f).poll(cx), MaybeDone::Done => unreachable!(), } } } }