@@ -89,6 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
89
89
use rustc_middle:: middle:: region;
90
90
use rustc_middle:: mir:: * ;
91
91
use rustc_middle:: thir:: { ExprId , LintLevel } ;
92
+ use rustc_middle:: ty:: { self , TyCtxt } ;
92
93
use rustc_middle:: { bug, span_bug} ;
93
94
use rustc_session:: lint:: Level ;
94
95
use rustc_span:: source_map:: Spanned ;
@@ -883,6 +884,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
883
884
block. unit ( )
884
885
}
885
886
887
+ fn is_async_drop_impl (
888
+ tcx : TyCtxt < ' tcx > ,
889
+ local_decls : & IndexVec < Local , LocalDecl < ' tcx > > ,
890
+ typing_env : ty:: TypingEnv < ' tcx > ,
891
+ local : Local ,
892
+ ) -> bool {
893
+ let ty = local_decls[ local] . ty ;
894
+ if ty. is_async_drop ( tcx, typing_env) || ty. is_coroutine ( ) {
895
+ return true ;
896
+ }
897
+ ty. needs_async_drop ( tcx, typing_env)
898
+ }
899
+ fn is_async_drop ( & self , local : Local ) -> bool {
900
+ Self :: is_async_drop_impl ( self . tcx , & self . local_decls , self . typing_env ( ) , local)
901
+ }
902
+
886
903
fn leave_top_scope ( & mut self , block : BasicBlock ) -> BasicBlock {
887
904
// If we are emitting a `drop` statement, we need to have the cached
888
905
// diverge cleanup pads ready in case that drop panics.
@@ -891,14 +908,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
891
908
let unwind_to = if needs_cleanup { self . diverge_cleanup ( ) } else { DropIdx :: MAX } ;
892
909
893
910
let scope = self . scopes . scopes . last ( ) . expect ( "leave_top_scope called with no scopes" ) ;
911
+ let has_async_drops = is_coroutine
912
+ && scope. drops . iter ( ) . any ( |v| v. kind == DropKind :: Value && self . is_async_drop ( v. local ) ) ;
913
+ let dropline_to = if has_async_drops { Some ( self . diverge_dropline ( ) ) } else { None } ;
914
+ let scope = self . scopes . scopes . last ( ) . expect ( "leave_top_scope called with no scopes" ) ;
915
+ let typing_env = self . typing_env ( ) ;
894
916
build_scope_drops (
895
917
& mut self . cfg ,
896
918
& mut self . scopes . unwind_drops ,
919
+ & mut self . scopes . coroutine_drops ,
897
920
scope,
898
921
block,
899
922
unwind_to,
923
+ dropline_to,
900
924
is_coroutine && needs_cleanup,
901
925
self . arg_count ,
926
+ |v : Local | Self :: is_async_drop_impl ( self . tcx , & self . local_decls , typing_env, v) ,
902
927
)
903
928
. into_block ( )
904
929
}
@@ -1314,22 +1339,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1314
1339
self . scopes . unwind_drops . add_entry_point ( start, next_drop) ;
1315
1340
}
1316
1341
1317
- /// Sets up a path that performs all required cleanup for dropping a
1318
- /// coroutine, starting from the given block that ends in
1319
- /// [TerminatorKind::Yield].
1320
- ///
1321
- /// This path terminates in CoroutineDrop.
1322
- pub ( crate ) fn coroutine_drop_cleanup ( & mut self , yield_block : BasicBlock ) {
1342
+ /// Returns the [DropIdx] for the innermost drop for dropline (coroutine drop path).
1343
+ /// The `DropIdx` will be created if it doesn't already exist.
1344
+ fn diverge_dropline ( & mut self ) -> DropIdx {
1345
+ // It is okay to use dummy span because the getting scope index on the topmost scope
1346
+ // must always succeed.
1347
+ self . diverge_dropline_target ( self . scopes . topmost ( ) , DUMMY_SP )
1348
+ }
1349
+
1350
+ /// Similar to diverge_cleanup_target, but for dropline (coroutine drop path)
1351
+ fn diverge_dropline_target ( & mut self , target_scope : region:: Scope , span : Span ) -> DropIdx {
1323
1352
debug_assert ! (
1324
- matches!(
1325
- self . cfg. block_data( yield_block) . terminator( ) . kind,
1326
- TerminatorKind :: Yield { .. }
1327
- ) ,
1328
- "coroutine_drop_cleanup called on block with non-yield terminator."
1353
+ self . coroutine. is_some( ) ,
1354
+ "diverge_dropline_target is valid only for coroutine"
1329
1355
) ;
1330
- let ( uncached_scope, mut cached_drop) = self
1331
- . scopes
1332
- . scopes
1356
+ let target = self . scopes . scope_index ( target_scope, span) ;
1357
+ let ( uncached_scope, mut cached_drop) = self . scopes . scopes [ ..=target]
1333
1358
. iter ( )
1334
1359
. enumerate ( )
1335
1360
. rev ( )
@@ -1338,13 +1363,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1338
1363
} )
1339
1364
. unwrap_or ( ( 0 , ROOT_NODE ) ) ;
1340
1365
1341
- for scope in & mut self . scopes . scopes [ uncached_scope..] {
1366
+ if uncached_scope > target {
1367
+ return cached_drop;
1368
+ }
1369
+
1370
+ for scope in & mut self . scopes . scopes [ uncached_scope..=target] {
1342
1371
for drop in & scope. drops {
1343
1372
cached_drop = self . scopes . coroutine_drops . add_drop ( * drop, cached_drop) ;
1344
1373
}
1345
1374
scope. cached_coroutine_drop_block = Some ( cached_drop) ;
1346
1375
}
1347
1376
1377
+ cached_drop
1378
+ }
1379
+
1380
+ /// Sets up a path that performs all required cleanup for dropping a
1381
+ /// coroutine, starting from the given block that ends in
1382
+ /// [TerminatorKind::Yield].
1383
+ ///
1384
+ /// This path terminates in CoroutineDrop.
1385
+ pub ( crate ) fn coroutine_drop_cleanup ( & mut self , yield_block : BasicBlock ) {
1386
+ debug_assert ! (
1387
+ matches!(
1388
+ self . cfg. block_data( yield_block) . terminator( ) . kind,
1389
+ TerminatorKind :: Yield { .. }
1390
+ ) ,
1391
+ "coroutine_drop_cleanup called on block with non-yield terminator."
1392
+ ) ;
1393
+ let cached_drop = self . diverge_dropline ( ) ;
1348
1394
self . scopes . coroutine_drops . add_entry_point ( yield_block, cached_drop) ;
1349
1395
}
1350
1396
@@ -1438,18 +1484,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1438
1484
/// * `unwind_to`, describes the drops that would occur at this point in the code if a
1439
1485
/// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other
1440
1486
/// instructions on unwinding)
1487
+ /// * `dropline_to`, describes the drops that would occur at this point in the code if a
1488
+ /// coroutine drop occured.
1441
1489
/// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding
1442
1490
/// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those)
1443
- fn build_scope_drops < ' tcx > (
1491
+ fn build_scope_drops < ' tcx , F > (
1444
1492
cfg : & mut CFG < ' tcx > ,
1445
1493
unwind_drops : & mut DropTree ,
1494
+ coroutine_drops : & mut DropTree ,
1446
1495
scope : & Scope ,
1447
1496
block : BasicBlock ,
1448
1497
unwind_to : DropIdx ,
1498
+ dropline_to : Option < DropIdx > ,
1449
1499
storage_dead_on_unwind : bool ,
1450
1500
arg_count : usize ,
1451
- ) -> BlockAnd < ( ) > {
1452
- debug ! ( "build_scope_drops({:?} -> {:?})" , block, scope) ;
1501
+ is_async_drop : F ,
1502
+ ) -> BlockAnd < ( ) >
1503
+ where
1504
+ F : Fn ( Local ) -> bool ,
1505
+ {
1506
+ debug ! ( "build_scope_drops({:?} -> {:?}), dropline_to={:?}" , block, scope, dropline_to) ;
1453
1507
1454
1508
// Build up the drops in evaluation order. The end result will
1455
1509
// look like:
@@ -1482,6 +1536,9 @@ fn build_scope_drops<'tcx>(
1482
1536
// will branch to `drops[n]`.
1483
1537
let mut block = block;
1484
1538
1539
+ // `dropline_to` indicates what needs to be dropped should coroutine drop occur.
1540
+ let mut dropline_to = dropline_to;
1541
+
1485
1542
for drop_data in scope. drops . iter ( ) . rev ( ) {
1486
1543
let source_info = drop_data. source_info ;
1487
1544
let local = drop_data. local ;
@@ -1498,6 +1555,12 @@ fn build_scope_drops<'tcx>(
1498
1555
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
1499
1556
unwind_to = unwind_drops. drops [ unwind_to] . next ;
1500
1557
1558
+ if let Some ( idx) = dropline_to {
1559
+ debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. local, drop_data. local) ;
1560
+ debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. kind, drop_data. kind) ;
1561
+ dropline_to = Some ( coroutine_drops. drops [ idx] . next ) ;
1562
+ }
1563
+
1501
1564
// If the operand has been moved, and we are not on an unwind
1502
1565
// path, then don't generate the drop. (We only take this into
1503
1566
// account for non-unwind paths so as not to disturb the
@@ -1507,6 +1570,12 @@ fn build_scope_drops<'tcx>(
1507
1570
}
1508
1571
1509
1572
unwind_drops. add_entry_point ( block, unwind_to) ;
1573
+ if let Some ( to) = dropline_to
1574
+ && is_async_drop ( local)
1575
+ {
1576
+ coroutine_drops. add_entry_point ( block, to) ;
1577
+ }
1578
+
1510
1579
let next = cfg. start_new_block ( ) ;
1511
1580
cfg. terminate (
1512
1581
block,
@@ -1564,6 +1633,11 @@ fn build_scope_drops<'tcx>(
1564
1633
debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data. kind, drop_data. kind) ;
1565
1634
unwind_to = unwind_drops. drops [ unwind_to] . next ;
1566
1635
}
1636
+ if let Some ( idx) = dropline_to {
1637
+ debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. local, drop_data. local) ;
1638
+ debug_assert_eq ! ( coroutine_drops. drops[ idx] . data. kind, drop_data. kind) ;
1639
+ dropline_to = Some ( coroutine_drops. drops [ idx] . next ) ;
1640
+ }
1567
1641
// Only temps and vars need their storage dead.
1568
1642
assert ! ( local. index( ) > arg_count) ;
1569
1643
cfg. push ( block, Statement { source_info, kind : StatementKind :: StorageDead ( local) } ) ;
@@ -1619,6 +1693,39 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1619
1693
}
1620
1694
}
1621
1695
}
1696
+ // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops
1697
+ if is_coroutine
1698
+ && drops. drops . iter ( ) . any ( |DropNode { data, next : _ } | {
1699
+ data. kind == DropKind :: Value && self . is_async_drop ( data. local )
1700
+ } )
1701
+ {
1702
+ let dropline_target = self . diverge_dropline_target ( else_scope, span) ;
1703
+ let mut dropline_indices = IndexVec :: from_elem_n ( dropline_target, 1 ) ;
1704
+ for ( drop_idx, drop_data) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1705
+ match drop_data. data . kind {
1706
+ DropKind :: Storage | DropKind :: ForLint => {
1707
+ let coroutine_drop = self
1708
+ . scopes
1709
+ . coroutine_drops
1710
+ . add_drop ( drop_data. data , dropline_indices[ drop_data. next ] ) ;
1711
+ dropline_indices. push ( coroutine_drop) ;
1712
+ }
1713
+ DropKind :: Value => {
1714
+ let coroutine_drop = self
1715
+ . scopes
1716
+ . coroutine_drops
1717
+ . add_drop ( drop_data. data , dropline_indices[ drop_data. next ] ) ;
1718
+ if self . is_async_drop ( drop_data. data . local ) {
1719
+ self . scopes . coroutine_drops . add_entry_point (
1720
+ blocks[ drop_idx] . unwrap ( ) ,
1721
+ dropline_indices[ drop_data. next ] ,
1722
+ ) ;
1723
+ }
1724
+ dropline_indices. push ( coroutine_drop) ;
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1622
1729
blocks[ ROOT_NODE ] . map ( BasicBlock :: unit)
1623
1730
}
1624
1731
@@ -1663,9 +1770,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
1663
1770
// to be captured by the coroutine. I'm not sure how important this
1664
1771
// optimization is, but it is here.
1665
1772
for ( drop_idx, drop_node) in drops. drops . iter_enumerated ( ) {
1666
- if let DropKind :: Value = drop_node. data . kind {
1773
+ if let DropKind :: Value = drop_node. data . kind
1774
+ && let Some ( bb) = blocks[ drop_idx]
1775
+ {
1667
1776
debug_assert ! ( drop_node. next < drops. drops. next_index( ) ) ;
1668
- drops. entry_points . push ( ( drop_node. next , blocks [ drop_idx ] . unwrap ( ) ) ) ;
1777
+ drops. entry_points . push ( ( drop_node. next , bb ) ) ;
1669
1778
}
1670
1779
}
1671
1780
Self :: build_unwind_tree ( cfg, drops, fn_span, resume_block) ;
@@ -1717,6 +1826,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop {
1717
1826
let term = cfg. block_data_mut ( from) . terminator_mut ( ) ;
1718
1827
if let TerminatorKind :: Yield { ref mut drop, .. } = term. kind {
1719
1828
* drop = Some ( to) ;
1829
+ } else if let TerminatorKind :: Drop { ref mut drop, .. } = term. kind {
1830
+ * drop = Some ( to) ;
1720
1831
} else {
1721
1832
span_bug ! (
1722
1833
term. source_info. span,
0 commit comments