Skip to content

Commit 0e47a7e

Browse files
committed
Subpart8 for async drop (major2) - dropline in scopes for potentially async drops
1 parent 2199d76 commit 0e47a7e

File tree

1 file changed

+124
-20
lines changed
  • compiler/rustc_mir_build/src/build

1 file changed

+124
-20
lines changed

compiler/rustc_mir_build/src/build/scope.rs

+124-20
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
8989
use rustc_middle::middle::region;
9090
use rustc_middle::mir::*;
9191
use rustc_middle::thir::{ExprId, LintLevel};
92+
use rustc_middle::ty::{self, TyCtxt};
9293
use rustc_middle::{bug, span_bug};
9394
use rustc_session::lint::Level;
9495
use rustc_span::source_map::Spanned;
@@ -837,22 +838,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
837838
block.unit()
838839
}
839840

841+
fn is_async_drop_impl(
842+
tcx: TyCtxt<'tcx>,
843+
local_decls: &IndexVec<Local, LocalDecl<'tcx>>,
844+
param_env: ty::ParamEnv<'tcx>,
845+
local: Local,
846+
) -> bool {
847+
let ty = local_decls[local].ty;
848+
if ty.is_async_drop(tcx, param_env) || ty.is_coroutine() {
849+
return true;
850+
}
851+
ty.needs_async_drop(tcx, param_env)
852+
}
853+
fn is_async_drop(&self, local: Local) -> bool {
854+
Self::is_async_drop_impl(self.tcx, &self.local_decls, self.param_env, local)
855+
}
856+
840857
fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
841858
// If we are emitting a `drop` statement, we need to have the cached
842859
// diverge cleanup pads ready in case that drop panics.
843860
let needs_cleanup = self.scopes.scopes.last().is_some_and(|scope| scope.needs_cleanup());
844861
let is_coroutine = self.coroutine.is_some();
845862
let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX };
846863

864+
let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
865+
let has_async_drops = is_coroutine
866+
&& scope.drops.iter().any(|v| v.kind == DropKind::Value && self.is_async_drop(v.local));
867+
let dropline_to = if has_async_drops { Some(self.diverge_dropline()) } else { None };
847868
let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
848869
build_scope_drops(
849870
&mut self.cfg,
850871
&mut self.scopes.unwind_drops,
872+
&mut self.scopes.coroutine_drops,
851873
scope,
852874
block,
853875
unwind_to,
876+
dropline_to,
854877
is_coroutine && needs_cleanup,
855878
self.arg_count,
879+
|v: Local| Self::is_async_drop_impl(self.tcx, &self.local_decls, self.param_env, v),
856880
)
857881
.into_block()
858882
}
@@ -1234,22 +1258,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12341258
self.scopes.unwind_drops.add_entry_point(start, next_drop);
12351259
}
12361260

1237-
/// Sets up a path that performs all required cleanup for dropping a
1238-
/// coroutine, starting from the given block that ends in
1239-
/// [TerminatorKind::Yield].
1240-
///
1241-
/// This path terminates in CoroutineDrop.
1242-
pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) {
1261+
/// Returns the [DropIdx] for the innermost drop for dropline (coroutine drop path).
1262+
/// The `DropIdx` will be created if it doesn't already exist.
1263+
fn diverge_dropline(&mut self) -> DropIdx {
1264+
// It is okay to use dummy span because the getting scope index on the topmost scope
1265+
// must always succeed.
1266+
self.diverge_dropline_target(self.scopes.topmost(), DUMMY_SP)
1267+
}
1268+
1269+
/// Similar to diverge_cleanup_target, but for dropline (coroutine drop path)
1270+
fn diverge_dropline_target(&mut self, target_scope: region::Scope, span: Span) -> DropIdx {
12431271
debug_assert!(
1244-
matches!(
1245-
self.cfg.block_data(yield_block).terminator().kind,
1246-
TerminatorKind::Yield { .. }
1247-
),
1248-
"coroutine_drop_cleanup called on block with non-yield terminator."
1272+
self.coroutine.is_some(),
1273+
"diverge_dropline_target is valid only for coroutine"
12491274
);
1250-
let (uncached_scope, mut cached_drop) = self
1251-
.scopes
1252-
.scopes
1275+
let target = self.scopes.scope_index(target_scope, span);
1276+
let (uncached_scope, mut cached_drop) = self.scopes.scopes[..=target]
12531277
.iter()
12541278
.enumerate()
12551279
.rev()
@@ -1258,13 +1282,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12581282
})
12591283
.unwrap_or((0, ROOT_NODE));
12601284

1261-
for scope in &mut self.scopes.scopes[uncached_scope..] {
1285+
if uncached_scope > target {
1286+
return cached_drop;
1287+
}
1288+
1289+
for scope in &mut self.scopes.scopes[uncached_scope..=target] {
12621290
for drop in &scope.drops {
12631291
cached_drop = self.scopes.coroutine_drops.add_drop(*drop, cached_drop);
12641292
}
12651293
scope.cached_coroutine_drop_block = Some(cached_drop);
12661294
}
12671295

1296+
cached_drop
1297+
}
1298+
1299+
/// Sets up a path that performs all required cleanup for dropping a
1300+
/// coroutine, starting from the given block that ends in
1301+
/// [TerminatorKind::Yield].
1302+
///
1303+
/// This path terminates in CoroutineDrop.
1304+
pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) {
1305+
debug_assert!(
1306+
matches!(
1307+
self.cfg.block_data(yield_block).terminator().kind,
1308+
TerminatorKind::Yield { .. }
1309+
),
1310+
"coroutine_drop_cleanup called on block with non-yield terminator."
1311+
);
1312+
let cached_drop = self.diverge_dropline();
12681313
self.scopes.coroutine_drops.add_entry_point(yield_block, cached_drop);
12691314
}
12701315

@@ -1349,16 +1394,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
13491394
}
13501395

13511396
/// Builds drops for `pop_scope` and `leave_top_scope`.
1352-
fn build_scope_drops<'tcx>(
1397+
fn build_scope_drops<'tcx, F>(
13531398
cfg: &mut CFG<'tcx>,
13541399
unwind_drops: &mut DropTree,
1400+
coroutine_drops: &mut DropTree,
13551401
scope: &Scope,
13561402
mut block: BasicBlock,
13571403
mut unwind_to: DropIdx,
1404+
mut dropline_to: Option<DropIdx>,
13581405
storage_dead_on_unwind: bool,
13591406
arg_count: usize,
1360-
) -> BlockAnd<()> {
1361-
debug!("build_scope_drops({:?} -> {:?})", block, scope);
1407+
is_async_drop: F,
1408+
) -> BlockAnd<()>
1409+
where
1410+
F: Fn(Local) -> bool,
1411+
{
1412+
debug!("build_scope_drops({:?} -> {:?}), dropline_to={:?}", block, scope, dropline_to);
13621413

13631414
// Build up the drops in evaluation order. The end result will
13641415
// look like:
@@ -1392,6 +1443,12 @@ fn build_scope_drops<'tcx>(
13921443
debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
13931444
unwind_to = unwind_drops.drops[unwind_to].next;
13941445

1446+
if let Some(idx) = dropline_to {
1447+
debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local);
1448+
debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind);
1449+
dropline_to = Some(coroutine_drops.drops[idx].next);
1450+
}
1451+
13951452
// If the operand has been moved, and we are not on an unwind
13961453
// path, then don't generate the drop. (We only take this into
13971454
// account for non-unwind paths so as not to disturb the
@@ -1401,6 +1458,11 @@ fn build_scope_drops<'tcx>(
14011458
}
14021459

14031460
unwind_drops.add_entry_point(block, unwind_to);
1461+
if let Some(to) = dropline_to
1462+
&& is_async_drop(local)
1463+
{
1464+
coroutine_drops.add_entry_point(block, to);
1465+
}
14041466

14051467
let next = cfg.start_new_block();
14061468
cfg.terminate(
@@ -1423,6 +1485,11 @@ fn build_scope_drops<'tcx>(
14231485
debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
14241486
unwind_to = unwind_drops.drops[unwind_to].next;
14251487
}
1488+
if let Some(idx) = dropline_to {
1489+
debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local);
1490+
debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind);
1491+
dropline_to = Some(coroutine_drops.drops[idx].next);
1492+
}
14261493
// Only temps and vars need their storage dead.
14271494
assert!(local.index() > arg_count);
14281495
cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) });
@@ -1481,6 +1548,39 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
14811548
}
14821549
}
14831550
}
1551+
// Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops
1552+
if is_coroutine
1553+
&& drops.drops.iter().any(|DropNode { data, next: _ }| {
1554+
data.kind == DropKind::Value && self.is_async_drop(data.local)
1555+
})
1556+
{
1557+
let dropline_target = self.diverge_dropline_target(else_scope, span);
1558+
let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1);
1559+
for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) {
1560+
match drop_data.data.kind {
1561+
DropKind::Storage => {
1562+
let coroutine_drop = self
1563+
.scopes
1564+
.coroutine_drops
1565+
.add_drop(drop_data.data, dropline_indices[drop_data.next]);
1566+
dropline_indices.push(coroutine_drop);
1567+
}
1568+
DropKind::Value => {
1569+
let coroutine_drop = self
1570+
.scopes
1571+
.coroutine_drops
1572+
.add_drop(drop_data.data, dropline_indices[drop_data.next]);
1573+
if self.is_async_drop(drop_data.data.local) {
1574+
self.scopes.coroutine_drops.add_entry_point(
1575+
blocks[drop_idx].unwrap(),
1576+
dropline_indices[drop_data.next],
1577+
);
1578+
}
1579+
dropline_indices.push(coroutine_drop);
1580+
}
1581+
}
1582+
}
1583+
}
14841584
blocks[ROOT_NODE].map(BasicBlock::unit)
14851585
}
14861586

@@ -1526,9 +1626,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
15261626
// to be captured by the coroutine. I'm not sure how important this
15271627
// optimization is, but it is here.
15281628
for (drop_idx, drop_node) in drops.drops.iter_enumerated() {
1529-
if let DropKind::Value = drop_node.data.kind {
1629+
if let DropKind::Value = drop_node.data.kind
1630+
&& let Some(bb) = blocks[drop_idx]
1631+
{
15301632
debug_assert!(drop_node.next < drops.drops.next_index());
1531-
drops.entry_points.push((drop_node.next, blocks[drop_idx].unwrap()));
1633+
drops.entry_points.push((drop_node.next, bb));
15321634
}
15331635
}
15341636
Self::build_unwind_tree(cfg, drops, fn_span, resume_block);
@@ -1582,6 +1684,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop {
15821684
let term = cfg.block_data_mut(from).terminator_mut();
15831685
if let TerminatorKind::Yield { ref mut drop, .. } = term.kind {
15841686
*drop = Some(to);
1687+
} else if let TerminatorKind::Drop { ref mut drop, .. } = term.kind {
1688+
*drop = Some(to);
15851689
} else {
15861690
span_bug!(
15871691
term.source_info.span,

0 commit comments

Comments
 (0)