@@ -203,16 +203,31 @@ const ROOT_NODE: DropIdx = DropIdx::from_u32(0);
203203/// in `build_mir`.
204204#[ derive( Debug ) ]
205205struct DropTree {
206- /// Drops in the tree.
207- drops : IndexVec < DropIdx , ( DropData , DropIdx ) > ,
208- /// Map for finding the inverse of the `next_drop` relation:
209- ///
210- /// `previous_drops[(drops[i].1, drops[i].0.local, drops[i].0.kind)] == i`
211- previous_drops : FxHashMap < ( DropIdx , Local , DropKind ) , DropIdx > ,
206+ /// Nodes in the drop tree, containing drop data and a link to the next node.
207+ drops : IndexVec < DropIdx , DropNode > ,
208+ /// Map for finding the index of an existing node, given its contents.
209+ existing_drops_map : FxHashMap < DropNodeKey , DropIdx > ,
212210 /// Edges into the `DropTree` that need to be added once it's lowered.
213211 entry_points : Vec < ( DropIdx , BasicBlock ) > ,
214212}
215213
214+ /// A single node in the drop tree.
215+ #[ derive( Debug ) ]
216+ struct DropNode {
217+ /// Info about the drop to be performed at this node in the drop tree.
218+ data : DropData ,
219+ /// Index of the "next" drop to perform (in drop order, not declaration order).
220+ next : DropIdx ,
221+ }
222+
223+ /// Subset of [`DropNode`] used for reverse lookup in a hash table.
224+ #[ derive( Debug , PartialEq , Eq , Hash ) ]
225+ struct DropNodeKey {
226+ next : DropIdx ,
227+ local : Local ,
228+ kind : DropKind ,
229+ }
230+
216231impl Scope {
217232 /// Whether there's anything to do for the cleanup path, that is,
218233 /// when unwinding through this scope. This includes destructors,
@@ -258,17 +273,22 @@ impl DropTree {
258273 let fake_source_info = SourceInfo :: outermost ( DUMMY_SP ) ;
259274 let fake_data =
260275 DropData { source_info : fake_source_info, local : Local :: MAX , kind : DropKind :: Storage } ;
261- let drop_idx = DropIdx :: MAX ;
262- let drops = IndexVec :: from_elem_n ( ( fake_data, drop_idx) , 1 ) ;
263- Self { drops, entry_points : Vec :: new ( ) , previous_drops : FxHashMap :: default ( ) }
276+ let drops = IndexVec :: from_raw ( vec ! [ DropNode { data: fake_data, next: DropIdx :: MAX } ] ) ;
277+ Self { drops, entry_points : Vec :: new ( ) , existing_drops_map : FxHashMap :: default ( ) }
264278 }
265279
266- fn add_drop ( & mut self , drop : DropData , next : DropIdx ) -> DropIdx {
280+ /// Adds a node to the drop tree, consisting of drop data and the index of
281+ /// the "next" drop (in drop order), which could be the sentinel [`ROOT_NODE`].
282+ ///
283+ /// If there is already an equivalent node in the tree, nothing is added, and
284+ /// that node's index is returned. Otherwise, the new node's index is returned.
285+ fn add_drop ( & mut self , data : DropData , next : DropIdx ) -> DropIdx {
267286 let drops = & mut self . drops ;
268287 * self
269- . previous_drops
270- . entry ( ( next, drop. local , drop. kind ) )
271- . or_insert_with ( || drops. push ( ( drop, next) ) )
288+ . existing_drops_map
289+ . entry ( DropNodeKey { next, local : data. local , kind : data. kind } )
290+ // Create a new node, and also add its index to the map.
291+ . or_insert_with ( || drops. push ( DropNode { data, next } ) )
272292 }
273293
274294 /// Registers `from` as an entry point to this drop tree, at `to`.
@@ -330,7 +350,7 @@ impl DropTree {
330350 let entry_points = & mut self . entry_points ;
331351 entry_points. sort ( ) ;
332352
333- for ( drop_idx, drop_data ) in self . drops . iter_enumerated ( ) . rev ( ) {
353+ for ( drop_idx, drop_node ) in self . drops . iter_enumerated ( ) . rev ( ) {
334354 if entry_points. last ( ) . is_some_and ( |entry_point| entry_point. 0 == drop_idx) {
335355 let block = * blocks[ drop_idx] . get_or_insert_with ( || T :: make_block ( cfg) ) ;
336356 needs_block[ drop_idx] = Block :: Own ;
@@ -348,10 +368,10 @@ impl DropTree {
348368 blocks[ drop_idx] = blocks[ pred] ;
349369 }
350370 }
351- if let DropKind :: Value = drop_data . 0 . kind {
352- needs_block[ drop_data . 1 ] = Block :: Own ;
371+ if let DropKind :: Value = drop_node . data . kind {
372+ needs_block[ drop_node . next ] = Block :: Own ;
353373 } else if drop_idx != ROOT_NODE {
354- match & mut needs_block[ drop_data . 1 ] {
374+ match & mut needs_block[ drop_node . next ] {
355375 pred @ Block :: None => * pred = Block :: Shares ( drop_idx) ,
356376 pred @ Block :: Shares ( _) => * pred = Block :: Own ,
357377 Block :: Own => ( ) ,
@@ -368,34 +388,35 @@ impl DropTree {
368388 cfg : & mut CFG < ' tcx > ,
369389 blocks : & IndexSlice < DropIdx , Option < BasicBlock > > ,
370390 ) {
371- for ( drop_idx, drop_data ) in self . drops . iter_enumerated ( ) . rev ( ) {
391+ for ( drop_idx, drop_node ) in self . drops . iter_enumerated ( ) . rev ( ) {
372392 let Some ( block) = blocks[ drop_idx] else { continue } ;
373- match drop_data . 0 . kind {
393+ match drop_node . data . kind {
374394 DropKind :: Value => {
375395 let terminator = TerminatorKind :: Drop {
376- target : blocks[ drop_data . 1 ] . unwrap ( ) ,
396+ target : blocks[ drop_node . next ] . unwrap ( ) ,
377397 // The caller will handle this if needed.
378398 unwind : UnwindAction :: Terminate ( UnwindTerminateReason :: InCleanup ) ,
379- place : drop_data . 0 . local . into ( ) ,
399+ place : drop_node . data . local . into ( ) ,
380400 replace : false ,
381401 } ;
382- cfg. terminate ( block, drop_data . 0 . source_info , terminator) ;
402+ cfg. terminate ( block, drop_node . data . source_info , terminator) ;
383403 }
384404 // Root nodes don't correspond to a drop.
385405 DropKind :: Storage if drop_idx == ROOT_NODE => { }
386406 DropKind :: Storage => {
387407 let stmt = Statement {
388- source_info : drop_data . 0 . source_info ,
389- kind : StatementKind :: StorageDead ( drop_data . 0 . local ) ,
408+ source_info : drop_node . data . source_info ,
409+ kind : StatementKind :: StorageDead ( drop_node . data . local ) ,
390410 } ;
391411 cfg. push ( block, stmt) ;
392- let target = blocks[ drop_data . 1 ] . unwrap ( ) ;
412+ let target = blocks[ drop_node . next ] . unwrap ( ) ;
393413 if target != block {
394414 // Diagnostics don't use this `Span` but debuginfo
395415 // might. Since we don't want breakpoints to be placed
396416 // here, especially when this is on an unwind path, we
397417 // use `DUMMY_SP`.
398- let source_info = SourceInfo { span : DUMMY_SP , ..drop_data. 0 . source_info } ;
418+ let source_info =
419+ SourceInfo { span : DUMMY_SP , ..drop_node. data . source_info } ;
399420 let terminator = TerminatorKind :: Goto { target } ;
400421 cfg. terminate ( block, source_info, terminator) ;
401422 }
@@ -1277,9 +1298,9 @@ fn build_scope_drops<'tcx>(
12771298 // `unwind_to` should drop the value that we're about to
12781299 // schedule. If dropping this value panics, then we continue
12791300 // with the *next* value on the unwind path.
1280- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . local, drop_data. local) ;
1281- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . kind, drop_data. kind) ;
1282- unwind_to = unwind_drops. drops [ unwind_to] . 1 ;
1301+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . local, drop_data. local) ;
1302+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . kind, drop_data. kind) ;
1303+ unwind_to = unwind_drops. drops [ unwind_to] . next ;
12831304
12841305 // If the operand has been moved, and we are not on an unwind
12851306 // path, then don't generate the drop. (We only take this into
@@ -1306,9 +1327,9 @@ fn build_scope_drops<'tcx>(
13061327 }
13071328 DropKind :: Storage => {
13081329 if storage_dead_on_unwind {
1309- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . local, drop_data. local) ;
1310- debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . 0 . kind, drop_data. kind) ;
1311- unwind_to = unwind_drops. drops [ unwind_to] . 1 ;
1330+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . local, drop_data. local) ;
1331+ debug_assert_eq ! ( unwind_drops. drops[ unwind_to] . data . kind, drop_data. kind) ;
1332+ unwind_to = unwind_drops. drops [ unwind_to] . next ;
13121333 }
13131334 // Only temps and vars need their storage dead.
13141335 assert ! ( local. index( ) > arg_count) ;
@@ -1338,30 +1359,30 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
13381359 let is_coroutine = self . coroutine . is_some ( ) ;
13391360
13401361 // Link the exit drop tree to unwind drop tree.
1341- if drops. drops . iter ( ) . any ( |( drop , _ ) | drop . kind == DropKind :: Value ) {
1362+ if drops. drops . iter ( ) . any ( |drop_node| drop_node . data . kind == DropKind :: Value ) {
13421363 let unwind_target = self . diverge_cleanup_target ( else_scope, span) ;
13431364 let mut unwind_indices = IndexVec :: from_elem_n ( unwind_target, 1 ) ;
1344- for ( drop_idx, drop_data ) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1345- match drop_data . 0 . kind {
1365+ for ( drop_idx, drop_node ) in drops. drops . iter_enumerated ( ) . skip ( 1 ) {
1366+ match drop_node . data . kind {
13461367 DropKind :: Storage => {
13471368 if is_coroutine {
13481369 let unwind_drop = self
13491370 . scopes
13501371 . unwind_drops
1351- . add_drop ( drop_data . 0 , unwind_indices[ drop_data . 1 ] ) ;
1372+ . add_drop ( drop_node . data , unwind_indices[ drop_node . next ] ) ;
13521373 unwind_indices. push ( unwind_drop) ;
13531374 } else {
1354- unwind_indices. push ( unwind_indices[ drop_data . 1 ] ) ;
1375+ unwind_indices. push ( unwind_indices[ drop_node . next ] ) ;
13551376 }
13561377 }
13571378 DropKind :: Value => {
13581379 let unwind_drop = self
13591380 . scopes
13601381 . unwind_drops
1361- . add_drop ( drop_data . 0 , unwind_indices[ drop_data . 1 ] ) ;
1382+ . add_drop ( drop_node . data , unwind_indices[ drop_node . next ] ) ;
13621383 self . scopes . unwind_drops . add_entry_point (
13631384 blocks[ drop_idx] . unwrap ( ) ,
1364- unwind_indices[ drop_data . 1 ] ,
1385+ unwind_indices[ drop_node . next ] ,
13651386 ) ;
13661387 unwind_indices. push ( unwind_drop) ;
13671388 }
@@ -1412,10 +1433,10 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
14121433 // prevent drop elaboration from creating drop flags that would have
14131434 // to be captured by the coroutine. I'm not sure how important this
14141435 // optimization is, but it is here.
1415- for ( drop_idx, drop_data ) in drops. drops . iter_enumerated ( ) {
1416- if let DropKind :: Value = drop_data . 0 . kind {
1417- debug_assert ! ( drop_data . 1 < drops. drops. next_index( ) ) ;
1418- drops. entry_points . push ( ( drop_data . 1 , blocks[ drop_idx] . unwrap ( ) ) ) ;
1436+ for ( drop_idx, drop_node ) in drops. drops . iter_enumerated ( ) {
1437+ if let DropKind :: Value = drop_node . data . kind {
1438+ debug_assert ! ( drop_node . next < drops. drops. next_index( ) ) ;
1439+ drops. entry_points . push ( ( drop_node . next , blocks[ drop_idx] . unwrap ( ) ) ) ;
14191440 }
14201441 }
14211442 Self :: build_unwind_tree ( cfg, drops, fn_span, resume_block) ;
0 commit comments