From fc17e9193c02fd9bfce75370777ac527f5282e82 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 20 Aug 2024 16:54:36 +0200 Subject: [PATCH] supress niches in coroutines --- tests/pass/async-niche-aliasing.rs | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 tests/pass/async-niche-aliasing.rs diff --git a/tests/pass/async-niche-aliasing.rs b/tests/pass/async-niche-aliasing.rs new file mode 100644 index 0000000000..7f19afb33e --- /dev/null +++ b/tests/pass/async-niche-aliasing.rs @@ -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 { + 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!(), + } + } + } +}