@@ -159,6 +159,20 @@ struct DropData {
159159
160160 /// Whether this is a value Drop or a StorageDead.
161161 kind : DropKind ,
162+
163+ /// Whether this drop's `StorageDead` should be emitted on the unwind path.
164+ /// For coroutines with yield points, `StorageDead` on the unwind path is
165+ /// needed to prevent the coroutine drop handler from re-dropping locals
166+ /// already dropped during unwinding. However, emitting them for every drop
167+ /// in a coroutine causes O(n^2) MIR basic blocks for large aggregate
168+ /// initialisers (each element's unwind chain becomes unique due to distinct
169+ /// `StorageDead` locals, preventing tail-sharing).
170+ ///
171+ /// To avoid the quadratic blowup, this flag is only set when the coroutine
172+ /// body contains yield points. A coroutine without yields (e.g. an async fn
173+ /// with no `.await`) cannot have locals live across a yield, so the drop
174+ /// handler issue does not arise.
175+ storage_dead_on_unwind : bool ,
162176}
163177
164178#[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
@@ -263,12 +277,13 @@ impl Scope {
263277 /// * polluting the cleanup MIR with StorageDead creates
264278 /// landing pads even though there's no actual destructors
265279 /// * freeing up stack space has no effect during unwinding
266- /// Note that for coroutines we do emit StorageDeads, for the
267- /// use of optimizations in the MIR coroutine transform.
280+ /// Note that for coroutines we may also emit `StorageDead` on
281+ /// unwind for drops scheduled after a yield point — see
282+ /// `DropData::storage_dead_on_unwind`.
268283 fn needs_cleanup ( & self ) -> bool {
269284 self . drops . iter ( ) . any ( |drop| match drop. kind {
270285 DropKind :: Value | DropKind :: ForLint => true ,
271- DropKind :: Storage => false ,
286+ DropKind :: Storage => drop . storage_dead_on_unwind ,
272287 } )
273288 }
274289
@@ -296,8 +311,12 @@ impl DropTree {
296311 // represents the block in the tree that should be jumped to once all
297312 // of the required drops have been performed.
298313 let fake_source_info = SourceInfo :: outermost ( DUMMY_SP ) ;
299- let fake_data =
300- DropData { source_info : fake_source_info, local : Local :: MAX , kind : DropKind :: Storage } ;
314+ let fake_data = DropData {
315+ source_info : fake_source_info,
316+ local : Local :: MAX ,
317+ kind : DropKind :: Storage ,
318+ storage_dead_on_unwind : false ,
319+ } ;
301320 let drop_nodes = IndexVec :: from_raw ( vec ! [ DropNode { data: fake_data, next: DropIdx :: MAX } ] ) ;
302321 Self { drop_nodes, entry_points : Vec :: new ( ) , existing_drops_map : FxHashMap :: default ( ) }
303322 }
@@ -1111,7 +1130,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
11111130 return None ;
11121131 }
11131132
1114- Some ( DropData { source_info, local, kind : DropKind :: Value } )
1133+ Some ( DropData {
1134+ source_info,
1135+ local,
1136+ kind : DropKind :: Value ,
1137+ storage_dead_on_unwind : false ,
1138+ } )
11151139 }
11161140 Operand :: Constant ( _) | Operand :: RuntimeChecks ( _) => None ,
11171141 } )
@@ -1239,7 +1263,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
12391263 block,
12401264 unwind_to,
12411265 dropline_to,
1242- is_coroutine && needs_cleanup,
12431266 self . arg_count ,
12441267 |v : Local | Self :: is_async_drop_impl ( self . tcx , & self . local_decls , typing_env, v) ,
12451268 )
@@ -1474,10 +1497,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14741497 // path, we only need to invalidate the cache for drops that happen on
14751498 // the unwind or coroutine drop paths. This means that for
14761499 // non-coroutines we don't need to invalidate caches for `DropKind::Storage`.
1477- let invalidate_caches = needs_drop || self . coroutine . is_some ( ) ;
1500+ //
1501+ // For coroutines, `StorageDead` on the unwind path is needed for
1502+ // correct drop handling: without it, the coroutine drop handler may
1503+ // re-drop locals already dropped during unwinding. However, this is
1504+ // only relevant when the coroutine body contains yield points, because
1505+ // locals can only span a yield if one exists. For coroutines without
1506+ // yields (e.g. an async fn whose body has no `.await`), we skip
1507+ // `StorageDead` on unwind, avoiding O(n^2) MIR blowup. See #115327.
1508+ let storage_dead_on_unwind = self . coroutine_has_yields ;
1509+ let invalidate_unwind = needs_drop || storage_dead_on_unwind;
1510+ let invalidate_dropline = needs_drop || self . coroutine . is_some ( ) ;
14781511 for scope in self . scopes . scopes . iter_mut ( ) . rev ( ) {
1479- if invalidate_caches {
1480- scope. invalidate_cache ( ) ;
1512+ if invalidate_unwind {
1513+ scope. cached_unwind_block = None ;
1514+ }
1515+ if invalidate_dropline {
1516+ scope. cached_coroutine_drop_block = None ;
14811517 }
14821518
14831519 if scope. region_scope == region_scope {
@@ -1489,6 +1525,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
14891525 source_info : SourceInfo { span : scope_end, scope : scope. source_scope } ,
14901526 local,
14911527 kind : drop_kind,
1528+ storage_dead_on_unwind,
14921529 } ) ;
14931530
14941531 return ;
@@ -1520,6 +1557,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15201557 source_info : SourceInfo { span : scope_end, scope : scope. source_scope } ,
15211558 local,
15221559 kind : DropKind :: ForLint ,
1560+ storage_dead_on_unwind : false ,
15231561 } ) ;
15241562
15251563 return ;
@@ -1622,10 +1660,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16221660 return cached_drop;
16231661 }
16241662
1625- let is_coroutine = self . coroutine . is_some ( ) ;
16261663 for scope in & mut self . scopes . scopes [ uncached_scope..=target] {
16271664 for drop in & scope. drops {
1628- if is_coroutine || drop. kind == DropKind :: Value {
1665+ if drop. kind == DropKind :: Value || drop . storage_dead_on_unwind {
16291666 cached_drop = self . scopes . unwind_drops . add_drop ( * drop, cached_drop) ;
16301667 }
16311668 }
@@ -1811,7 +1848,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18111848/// instructions on unwinding)
18121849/// * `dropline_to`, describes the drops that would occur at this point in the code if a
18131850/// coroutine drop occurred.
1814- /// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding
18151851/// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those)
18161852fn build_scope_drops < ' tcx , F > (
18171853 cfg : & mut CFG < ' tcx > ,
@@ -1821,7 +1857,6 @@ fn build_scope_drops<'tcx, F>(
18211857 block : BasicBlock ,
18221858 unwind_to : DropIdx ,
18231859 dropline_to : Option < DropIdx > ,
1824- storage_dead_on_unwind : bool ,
18251860 arg_count : usize ,
18261861 is_async_drop : F ,
18271862) -> BlockAnd < ( ) >
@@ -1844,10 +1879,10 @@ where
18441879 // drops panic (panicking while unwinding will abort, so there's no need for
18451880 // another set of arrows).
18461881 //
1847- // For coroutines, we unwind from a drop on a local to its StorageDead
1848- // statement. For other functions we don't worry about StorageDead. The
1849- // drops for the unwind path should have already been generated by
1850- // `diverge_cleanup_gen`.
1882+ // For drops in coroutines that were scheduled after a yield point, we
1883+ // unwind from a drop on a local to its StorageDead statement. For other
1884+ // drops we don't worry about StorageDead. The drops for the unwind path
1885+ // should have already been generated by `diverge_cleanup_gen`.
18511886
18521887 // `unwind_to` indicates what needs to be dropped should unwinding occur.
18531888 // This is a subset of what needs to be dropped when exiting the scope.
@@ -1922,7 +1957,7 @@ where
19221957 // so we can just leave `unwind_to` unmodified, but in some
19231958 // cases we emit things ALSO on the unwind path, so we need to adjust
19241959 // `unwind_to` in that case.
1925- if storage_dead_on_unwind {
1960+ if drop_data . storage_dead_on_unwind {
19261961 debug_assert_eq ! (
19271962 unwind_drops. drop_nodes[ unwind_to] . data. local,
19281963 drop_data. local
@@ -1953,10 +1988,11 @@ where
19531988 DropKind :: Storage => {
19541989 // Ordinarily, storage-dead nodes are not emitted on unwind, so we don't
19551990 // need to adjust `unwind_to` on this path. However, in some specific cases
1956- // we *do* emit storage-dead nodes on the unwind path, and in that case now that
1957- // the storage-dead has completed, we need to adjust the `unwind_to` pointer
1958- // so that any future drops we emit will not register storage-dead.
1959- if storage_dead_on_unwind {
1991+ // (drops scheduled after a yield in a coroutine) we *do* emit storage-dead
1992+ // nodes on the unwind path, and in that case now that the storage-dead has
1993+ // completed, we need to adjust the `unwind_to` pointer so that any future
1994+ // drops we emit will not register storage-dead.
1995+ if drop_data. storage_dead_on_unwind {
19601996 debug_assert_eq ! (
19611997 unwind_drops. drop_nodes[ unwind_to] . data. local,
19621998 drop_data. local
0 commit comments