Skip to content

Commit a281f93

Browse files
committed
supress niches in coroutines
1 parent a971212 commit a281f93

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

compiler/rustc_ty_utils/src/layout.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,13 @@ fn coroutine_layout<'tcx>(
10011001
},
10021002
fields: outer_fields,
10031003
abi,
1004-
largest_niche: prefix.largest_niche,
1004+
// Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to
1005+
// self-referentiality), getting the discriminant can cause aliasing violations.
1006+
// `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that
1007+
// would do the same for us here.
1008+
// See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
1009+
// FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
1010+
largest_niche: None,
10051011
size,
10061012
align,
10071013
max_repr_align: None,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//@revisions: stack tree
2+
//@[tree]compile-flags: -Zmiri-tree-borrows
3+
4+
use std::{
5+
future::Future,
6+
pin::Pin,
7+
sync::Arc,
8+
task::{Context, Poll, Wake},
9+
mem::MaybeUninit,
10+
};
11+
12+
struct ThingAdder<'a> {
13+
// Using `MaybeUninit` to ensure there are no niches here.
14+
thing: MaybeUninit<&'a mut String>,
15+
}
16+
17+
impl Future for ThingAdder<'_> {
18+
type Output = ();
19+
20+
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
21+
unsafe {
22+
**self.get_unchecked_mut().thing.assume_init_mut() += ", world";
23+
}
24+
Poll::Pending
25+
}
26+
}
27+
28+
fn main() {
29+
let mut thing = "hello".to_owned();
30+
// This future has (at least) two fields, a String (`thing`) and a ThingAdder pointing to that string.
31+
let fut = async move { ThingAdder { thing: MaybeUninit::new(&mut thing) }.await };
32+
33+
let mut fut = MaybeDone::Future(fut);
34+
let mut fut = unsafe { Pin::new_unchecked(&mut fut) };
35+
36+
let waker = Arc::new(DummyWaker).into();
37+
let mut ctx = Context::from_waker(&waker);
38+
// This ends up reading the discriminant of the `MaybeDone`. If that is stored inside the
39+
// `thing: String` as a niche optimization, that causes aliasing conflicts with the reference
40+
// stored in `ThingAdder`.
41+
assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending);
42+
assert_eq!(fut.as_mut().poll(&mut ctx), Poll::Pending);
43+
}
44+
45+
struct DummyWaker;
46+
47+
impl Wake for DummyWaker {
48+
fn wake(self: Arc<Self>) {}
49+
}
50+
51+
pub enum MaybeDone<F: Future> {
52+
Future(F),
53+
Done,
54+
}
55+
impl<F: Future<Output = ()>> Future for MaybeDone<F> {
56+
type Output = ();
57+
58+
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
59+
unsafe {
60+
match *self.as_mut().get_unchecked_mut() {
61+
MaybeDone::Future(ref mut f) => Pin::new_unchecked(f).poll(cx),
62+
MaybeDone::Done => unreachable!(),
63+
}
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)