Skip to content

Commit 189e64c

Browse files
committed
Async drop - fix for StorageLive/StorageDead codegen for pinned async drop future
1 parent 7c10378 commit 189e64c

File tree

9 files changed

+265
-9
lines changed

9 files changed

+265
-9
lines changed

compiler/rustc_mir_transform/src/coroutine/drop.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ fn build_poll_switch<'tcx>(
132132
body: &mut Body<'tcx>,
133133
poll_enum: Ty<'tcx>,
134134
poll_unit_place: &Place<'tcx>,
135+
fut_pin_place: &Place<'tcx>,
135136
ready_block: BasicBlock,
136137
yield_block: BasicBlock,
137138
) -> BasicBlock {
@@ -162,9 +163,11 @@ fn build_poll_switch<'tcx>(
162163
Rvalue::Discriminant(*poll_unit_place),
163164
))),
164165
};
166+
let storage_dead =
167+
Statement { source_info, kind: StatementKind::StorageDead(fut_pin_place.local) };
165168
let unreachable_block = insert_term_block(body, TerminatorKind::Unreachable);
166169
body.basic_blocks_mut().push(BasicBlockData {
167-
statements: [discr_assign].to_vec(),
170+
statements: [storage_dead, discr_assign].to_vec(),
168171
terminator: Some(Terminator {
169172
source_info,
170173
kind: TerminatorKind::SwitchInt {
@@ -332,10 +335,17 @@ pub(super) fn expand_async_drops<'tcx>(
332335
kind: StatementKind::Assign(Box::new((context_ref_place, arg))),
333336
});
334337
let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield
335-
let switch_block =
336-
build_poll_switch(tcx, body, poll_enum, &poll_unit_place, target, yield_block);
337338
let (pin_bb, fut_pin_place) =
338339
build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue);
340+
let switch_block = build_poll_switch(
341+
tcx,
342+
body,
343+
poll_enum,
344+
&poll_unit_place,
345+
&fut_pin_place,
346+
target,
347+
yield_block,
348+
);
339349
let call_bb = build_poll_call(
340350
tcx,
341351
body,
@@ -357,16 +367,17 @@ pub(super) fn expand_async_drops<'tcx>(
357367
body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span)),
358368
);
359369
let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield
370+
let (pin_bb2, fut_pin_place2) =
371+
build_pin_fut(tcx, body, fut_place, UnwindAction::Continue);
360372
let drop_switch_block = build_poll_switch(
361373
tcx,
362374
body,
363375
poll_enum,
364376
&poll_unit_place,
377+
&fut_pin_place2,
365378
drop.unwrap(),
366379
drop_yield_block,
367380
);
368-
let (pin_bb2, fut_pin_place2) =
369-
build_pin_fut(tcx, body, fut_place, UnwindAction::Continue);
370381
let drop_call_bb = build_poll_call(
371382
tcx,
372383
body,

compiler/rustc_mir_transform/src/elaborate_drop.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,20 @@ where
390390
Location { block: self.succ, statement_index: 0 },
391391
StatementKind::StorageDead(fut.local),
392392
);
393+
// StorageDead(fut) in unwind block (at the begin)
394+
if let Unwind::To(block) = unwind {
395+
self.elaborator.patch().add_statement(
396+
Location { block, statement_index: 0 },
397+
StatementKind::StorageDead(fut.local),
398+
);
399+
}
400+
// StorageDead(fut) in dropline block (at the begin)
401+
if let Some(block) = dropline {
402+
self.elaborator.patch().add_statement(
403+
Location { block, statement_index: 0 },
404+
StatementKind::StorageDead(fut.local),
405+
);
406+
}
393407

394408
// #1:pin_obj_bb >>> call Pin<ObjTy>::new_unchecked(&mut obj)
395409
self.elaborator.patch().patch_terminator(
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// MIR for `a::{closure#0}` 0 coroutine_drop_async
2+
3+
fn a::{closure#0}(_1: Pin<&mut {async fn body of a<T>()}>, _2: &mut Context<'_>) -> Poll<()> {
4+
debug _task_context => _19;
5+
debug x => ((*(_1.0: &mut {async fn body of a<T>()})).0: T);
6+
let mut _0: std::task::Poll<()>;
7+
let _3: T;
8+
let mut _4: impl std::future::Future<Output = ()>;
9+
let mut _5: &mut T;
10+
let mut _6: std::pin::Pin<&mut T>;
11+
let mut _7: &mut T;
12+
let mut _8: *mut T;
13+
let mut _9: ();
14+
let mut _10: std::task::Poll<()>;
15+
let mut _11: &mut std::task::Context<'_>;
16+
let mut _12: &mut impl std::future::Future<Output = ()>;
17+
let mut _13: std::pin::Pin<&mut impl std::future::Future<Output = ()>>;
18+
let mut _14: isize;
19+
let mut _15: &mut std::task::Context<'_>;
20+
let mut _16: &mut impl std::future::Future<Output = ()>;
21+
let mut _17: std::pin::Pin<&mut impl std::future::Future<Output = ()>>;
22+
let mut _18: isize;
23+
let mut _19: &mut std::task::Context<'_>;
24+
let mut _20: u32;
25+
scope 1 {
26+
debug x => (((*(_1.0: &mut {async fn body of a<T>()})) as variant#4).0: T);
27+
}
28+
29+
bb0: {
30+
_20 = discriminant((*(_1.0: &mut {async fn body of a<T>()})));
31+
switchInt(move _20) -> [0: bb9, 3: bb12, 4: bb13, otherwise: bb14];
32+
}
33+
34+
bb1: {
35+
nop;
36+
nop;
37+
goto -> bb2;
38+
}
39+
40+
bb2: {
41+
_0 = Poll::<()>::Ready(const ());
42+
return;
43+
}
44+
45+
bb3: {
46+
_0 = Poll::<()>::Pending;
47+
discriminant((*(_1.0: &mut {async fn body of a<T>()}))) = 4;
48+
return;
49+
}
50+
51+
bb4: {
52+
StorageLive(_17);
53+
_16 = &mut (((*(_1.0: &mut {async fn body of a<T>()})) as variant#4).1: impl std::future::Future<Output = ()>);
54+
_17 = Pin::<&mut impl Future<Output = ()>>::new_unchecked(move _16) -> [return: bb7, unwind unreachable];
55+
}
56+
57+
bb5: {
58+
unreachable;
59+
}
60+
61+
bb6: {
62+
StorageDead(_17);
63+
_18 = discriminant(_10);
64+
switchInt(move _18) -> [0: bb1, 1: bb3, otherwise: bb5];
65+
}
66+
67+
bb7: {
68+
_10 = <impl Future<Output = ()> as Future>::poll(move _17, move _15) -> [return: bb6, unwind unreachable];
69+
}
70+
71+
bb8: {
72+
_0 = Poll::<()>::Ready(const ());
73+
return;
74+
}
75+
76+
bb9: {
77+
goto -> bb11;
78+
}
79+
80+
bb10: {
81+
goto -> bb8;
82+
}
83+
84+
bb11: {
85+
drop(((*(_1.0: &mut {async fn body of a<T>()})).0: T)) -> [return: bb10, unwind unreachable];
86+
}
87+
88+
bb12: {
89+
goto -> bb4;
90+
}
91+
92+
bb13: {
93+
goto -> bb4;
94+
}
95+
96+
bb14: {
97+
_0 = Poll::<()>::Ready(const ());
98+
return;
99+
}
100+
}

tests/mir-opt/async_drop_live_dead.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//@ edition:2024
2+
// skip-filecheck
3+
4+
#![feature(async_drop)]
5+
#![allow(incomplete_features)]
6+
7+
// EMIT_MIR async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.mir
8+
async fn a<T>(x: T) {}
9+
10+
fn main() {}
11+

tests/ui/async-await/async-drop/async-drop-initial.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn main() {
6262
test_async_drop(&j, 16).await;
6363
test_async_drop(
6464
AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 },
65-
if cfg!(panic = "unwind") { 168 } else { 136 },
65+
136,
6666
).await;
6767
test_async_drop(ManuallyDrop::new(AsyncInt(9)), 16).await;
6868

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
//@ known-bug: #140429
1+
// ex-ice: #140429
22
//@ compile-flags: -Zlint-mir --crate-type lib
33
//@ edition:2024
4+
//@ check-pass
45

56
#![feature(async_drop)]
7+
#![allow(incomplete_features)]
8+
69
async fn a<T>(x: T) {}
Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
//@ known-bug: #140531
2-
//@compile-flags: -Zlint-mir --crate-type lib
1+
// ex-ice: #140531
2+
//@ compile-flags: -Zlint-mir --crate-type lib
33
//@ edition:2024
4+
//@ check-pass
5+
46
#![feature(async_drop)]
7+
#![allow(incomplete_features)]
8+
59
async fn call_once(f: impl AsyncFnOnce()) {
610
let fut = Box::pin(f());
711
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// ex-ice: #141761
2+
//@ compile-flags: -Zlint-mir --crate-type lib
3+
//@ edition:2024
4+
//@ check-pass
5+
6+
#![feature(async_drop)]
7+
#![allow(incomplete_features)]
8+
9+
type BoxFuture<T> = std::pin::Pin<Box<dyn Future<Output = T>>>;
10+
fn main() {}
11+
async fn f() {
12+
run("").await
13+
}
14+
struct InMemoryStorage;
15+
struct User<'dep> {
16+
dep: &'dep str,
17+
}
18+
impl<'a> StorageRequest<InMemoryStorage> for SaveUser<'a> {
19+
fn execute(&self) -> BoxFuture<Result<(), String>> {
20+
todo!()
21+
}
22+
}
23+
trait Storage {
24+
type Error;
25+
}
26+
impl Storage for InMemoryStorage {
27+
type Error = String;
28+
}
29+
trait StorageRequestReturnType {
30+
type Output;
31+
}
32+
trait StorageRequest<S: Storage>: StorageRequestReturnType {
33+
fn execute(&self) -> BoxFuture<Result<<Self>::Output, S::Error>>;
34+
}
35+
struct SaveUser<'a> {
36+
name: &'a str,
37+
}
38+
impl<'a> StorageRequestReturnType for SaveUser<'a> {
39+
type Output = ();
40+
}
41+
impl<'dep> User<'dep> {
42+
async fn save<S>(self)
43+
where
44+
S: Storage,
45+
for<'a> SaveUser<'a>: StorageRequest<S>,
46+
{
47+
SaveUser { name: "" }.execute().await;
48+
}
49+
}
50+
async fn run<S>(dep: &str)
51+
where
52+
S: Storage,
53+
for<'a> SaveUser<'a>: StorageRequest<S>,
54+
{
55+
User { dep }.save().await
56+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// ex-ice: #141409
2+
//@ compile-flags: -Zmir-enable-passes=+Inline -Zvalidate-mir -Zlint-mir --crate-type lib
3+
//@ edition:2024
4+
//@ check-pass
5+
6+
#![feature(async_drop)]
7+
#![allow(incomplete_features)]
8+
#![allow(non_snake_case)]
9+
10+
use std::mem::ManuallyDrop;
11+
use std::{
12+
future::{async_drop_in_place, Future},
13+
pin::{pin, Pin},
14+
sync::{mpsc, Arc},
15+
task::{Context, Poll, Wake, Waker},
16+
};
17+
fn main() {
18+
block_on(bar(0))
19+
}
20+
async fn baz(ident_base: usize) {}
21+
async fn bar(ident_base: usize) {
22+
baz(1).await
23+
}
24+
fn block_on<F>(fut_unpin: F) -> F::Output
25+
where
26+
F: Future,
27+
{
28+
let fut_pin = pin!(ManuallyDrop::new(fut_unpin));
29+
let mut fut = unsafe { Pin::map_unchecked_mut(fut_pin, |x| &mut **x) };
30+
let (waker, rx) = simple_waker();
31+
let mut context = Context::from_waker(&waker);
32+
let rv = loop {
33+
match fut.as_mut().poll(&mut context) {
34+
Poll::Ready(out) => break out,
35+
PollPending => (),
36+
}
37+
};
38+
let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) };
39+
let drop_fut = pin!(drop_fut_unpin);
40+
loop {
41+
match drop_fut.poll(&mut context) {
42+
Poll => break,
43+
}
44+
}
45+
rv
46+
}
47+
fn simple_waker() -> (Waker, mpsc::Receiver<()>) {
48+
struct SimpleWaker {
49+
tx: mpsc::Sender<()>,
50+
}
51+
impl Wake for SimpleWaker {
52+
fn wake(self: Arc<Self>) {}
53+
}
54+
let (tx, rx) = mpsc::channel();
55+
(Waker::from(Arc::new(SimpleWaker { tx })), rx)
56+
}
57+

0 commit comments

Comments
 (0)