11use std:: collections:: { HashMap , HashSet } ;
22
33use rustc_index:: bit_set:: DenseBitSet ;
4- use rustc_middle:: mir:: { self , BasicBlock , Body , Local } ;
4+ use rustc_middle:: mir:: { self , BasicBlock , Body , Local , Location } ;
5+ use rustc_middle:: ty:: TyCtxt ;
56use rustc_mir_dataflow:: { impls:: MaybeLiveLocals , ResultsCursor } ;
67
7- /// Implicit-drop targets. A drop is a `Place`; a whole-local drop is just a
8- /// place with an empty projection (semantically a bare `Local`).
8+ /// A set of implicit-drop targets: `drops` are places to drop; `except` are
9+ /// moved-out sub-places to skip when a drop walks into them.
10+ #[ derive( Debug , Clone , Default ) ]
11+ pub struct DropSet < ' tcx > {
12+ pub drops : HashSet < mir:: Place < ' tcx > > ,
13+ pub except : HashSet < mir:: Place < ' tcx > > ,
14+ }
15+
16+ impl < ' tcx > DropSet < ' tcx > {
17+ pub fn insert ( & mut self , place : mir:: Place < ' tcx > ) {
18+ self . drops . insert ( place) ;
19+ }
20+
21+ fn extend ( & mut self , other : & DropSet < ' tcx > ) {
22+ self . drops . extend ( other. drops . iter ( ) . copied ( ) ) ;
23+ self . except . extend ( other. except . iter ( ) . copied ( ) ) ;
24+ }
25+ }
26+
927#[ derive( Debug , Clone , Default ) ]
1028pub struct DropPoints < ' tcx > {
11- pub before_statements : HashSet < mir:: Place < ' tcx > > ,
12- after_statements : Vec < HashSet < mir:: Place < ' tcx > > > ,
13- after_terminator : HashMap < BasicBlock , HashSet < mir:: Place < ' tcx > > > ,
14- /// Drops scheduled after the terminator regardless of the target, in
15- /// addition to the liveness-derived sets above.
16- after_terminator_extra : HashSet < mir:: Place < ' tcx > > ,
29+ pub before_statements : DropSet < ' tcx > ,
30+ after_statements : Vec < DropSet < ' tcx > > ,
31+ after_terminator : HashMap < BasicBlock , DropSet < ' tcx > > ,
32+ after_terminator_extra : DropSet < ' tcx > ,
1733}
1834
1935impl DropPoints < ' _ > {
20- pub fn builder < ' mir , ' tcx > ( body : & ' mir Body < ' tcx > ) -> DropPointsBuilder < ' mir , ' tcx > {
36+ pub fn builder < ' mir , ' tcx > (
37+ tcx : TyCtxt < ' tcx > ,
38+ body : & ' mir Body < ' tcx > ,
39+ ) -> DropPointsBuilder < ' mir , ' tcx > {
2140 DropPointsBuilder {
2241 body,
2342 bb_ins_cache : HashMap :: new ( ) ,
43+ moves : Moves :: collect ( tcx, body) ,
2444 }
2545 }
2646}
2747
2848impl < ' tcx > DropPoints < ' tcx > {
29- pub fn after_statement ( & self , statement_index : usize ) -> HashSet < mir :: Place < ' tcx > > {
49+ pub fn after_statement ( & self , statement_index : usize ) -> DropSet < ' tcx > {
3050 self . after_statements [ statement_index] . clone ( )
3151 }
3252
3353 pub fn insert_after_terminator ( & mut self , place : mir:: Place < ' tcx > ) {
3454 self . after_terminator_extra . insert ( place) ;
3555 }
3656
37- pub fn after_terminator ( & self , target : & BasicBlock ) -> HashSet < mir :: Place < ' tcx > > {
57+ pub fn after_terminator ( & self , target : & BasicBlock ) -> DropSet < ' tcx > {
3858 let mut set = self . after_terminator [ target] . clone ( ) ;
39- set. extend ( self . after_statements . last ( ) . unwrap ( ) . iter ( ) . copied ( ) ) ;
40- set. extend ( self . after_terminator_extra . iter ( ) . copied ( ) ) ;
59+ set. extend ( self . after_statements . last ( ) . unwrap ( ) ) ;
60+ set. extend ( & self . after_terminator_extra ) ;
4161 set
4262 }
4363}
4464
45- #[ derive( Debug , Clone ) ]
65+ #[ derive( Clone ) ]
4666pub struct DropPointsBuilder < ' mir , ' tcx > {
4767 body : & ' mir Body < ' tcx > ,
4868 bb_ins_cache : HashMap < BasicBlock , DenseBitSet < Local > > ,
69+ moves : Moves < ' tcx > ,
4970}
5071
51- /// Locals whose ownership is fully transferred away by the statement (or
52- /// terminator) at `statement_index`. Such a local is left uninitialized, so its
53- /// drop obligation (including resolving any mutable-borrow prophecies it owns)
54- /// moves to the destination and it must not be dropped at the move site.
72+ /// The `move`d operands of a body, excluding reference-typed places.
5573///
56- /// Only owned (non-reference) operands are reported: `move`d references are
57- /// turned into reborrows by `ReborrowVisitor`/`RustCallVisitor`, so the source
58- /// local remains live and must still be dropped.
59- fn moved_locals < ' tcx > (
60- body : & Body < ' tcx > ,
61- bb : BasicBlock ,
62- statement_index : usize ,
63- ) -> DenseBitSet < Local > {
64- struct Visitor < ' a , ' tcx > {
65- body : & ' a Body < ' tcx > ,
66- locals : DenseBitSet < Local > ,
67- }
68- impl < ' tcx > mir:: visit:: Visitor < ' tcx > for Visitor < ' _ , ' tcx > {
69- fn visit_operand ( & mut self , operand : & mir:: Operand < ' tcx > , _location : mir:: Location ) {
70- if let mir:: Operand :: Move ( place) = operand {
71- if place. projection . is_empty ( ) && !self . body . local_decls [ place. local ] . ty . is_ref ( ) {
72- self . locals . insert ( place. local ) ;
74+ /// `move`d references are turned into reborrows by
75+ /// `ReborrowVisitor`/`RustCallVisitor`, so the source still owns its prophecy
76+ /// and must be dropped normally; they are therefore not recorded here.
77+ ///
78+ /// A whole-local move leaves the local uninitialized, transferring its entire
79+ /// drop obligation (including resolving any mutable-borrow prophecies it owns)
80+ /// to the destination, so it must not be dropped at the move site — where the
81+ /// local also becomes dead, hence the per-location keying. A partial (projected)
82+ /// field move transfers only a sub-place, but the parent stays live until its
83+ /// remaining parts die later; dropping it wholesale at that point would walk
84+ /// into the moved-out sub-place and resolve its `&mut` prophecy a second time,
85+ /// so a partially-moved local is excluded from dropping entirely.
86+ #[ derive( Clone , Default ) ]
87+ struct Moves < ' tcx > {
88+ /// Whole-local moves, keyed by the location performing the move.
89+ whole : HashMap < Location , DenseBitSet < Local > > ,
90+ /// Partial field moves, keyed by the parent local.
91+ partial : HashMap < Local , Vec < mir:: Place < ' tcx > > > ,
92+ }
93+
94+ impl < ' tcx > Moves < ' tcx > {
95+ fn collect ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > ) -> Moves < ' tcx > {
96+ struct Visitor < ' a , ' tcx > {
97+ tcx : TyCtxt < ' tcx > ,
98+ body : & ' a Body < ' tcx > ,
99+ moves : Moves < ' tcx > ,
100+ }
101+ impl < ' tcx > mir:: visit:: Visitor < ' tcx > for Visitor < ' _ , ' tcx > {
102+ fn visit_operand ( & mut self , operand : & mir:: Operand < ' tcx > , location : Location ) {
103+ let mir:: Operand :: Move ( place) = operand else {
104+ return ;
105+ } ;
106+ if place. ty ( & self . body . local_decls , self . tcx ) . ty . is_ref ( ) {
107+ return ;
108+ }
109+ if place. projection . is_empty ( ) {
110+ self . moves
111+ . whole
112+ . entry ( location)
113+ . or_insert_with ( || DenseBitSet :: new_empty ( self . body . local_decls . len ( ) ) )
114+ . insert ( place. local ) ;
115+ } else {
116+ let entry = self . moves . partial . entry ( place. local ) . or_default ( ) ;
117+ if !entry. contains ( place) {
118+ entry. push ( * place) ;
119+ }
73120 }
74121 }
75122 }
123+ let mut visitor = Visitor {
124+ tcx,
125+ body,
126+ moves : Moves :: default ( ) ,
127+ } ;
128+ use mir:: visit:: Visitor as _;
129+ visitor. visit_body ( body) ;
130+ visitor. moves
76131 }
77- let mut visitor = Visitor {
78- body,
79- locals : DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ,
80- } ;
81- let loc = mir:: Location {
82- statement_index,
83- block : bb,
84- } ;
85- let data = & body. basic_blocks [ bb] ;
86- use mir:: visit:: Visitor as _;
87- if statement_index < data. statements . len ( ) {
88- visitor. visit_statement ( & data. statements [ statement_index] , loc) ;
89- } else if let Some ( tmnt) = & data. terminator {
90- visitor. visit_terminator ( tmnt, loc) ;
91- }
92- visitor. locals
93132}
94133
95134fn def_local < ' tcx > ( data : & mir:: BasicBlockData < ' tcx > , statement_index : usize ) -> Option < Local > {
@@ -121,6 +160,21 @@ fn def_local<'tcx>(data: &mir::BasicBlockData<'tcx>, statement_index: usize) ->
121160}
122161
123162impl < ' mir , ' tcx > DropPointsBuilder < ' mir , ' tcx > {
163+ /// Turn a set of locals that become dead into the drop targets. A local with
164+ /// a partial field move is excluded: dropping it wholesale would resolve the
165+ /// moved-out sub-place's `&mut` prophecy a second time (it is resolved at the
166+ /// move destination instead).
167+ fn drop_set ( & self , locals : DenseBitSet < Local > ) -> DropSet < ' tcx > {
168+ let mut set = DropSet :: default ( ) ;
169+ for local in locals. iter ( ) {
170+ set. drops . insert ( local. into ( ) ) ;
171+ if let Some ( moved) = self . moves . partial . get ( & local) {
172+ set. except . extend ( moved. iter ( ) . copied ( ) ) ;
173+ }
174+ }
175+ set
176+ }
177+
124178 pub fn build (
125179 & mut self ,
126180 results : & mut ResultsCursor < ' mir , ' tcx , MaybeLiveLocals > ,
@@ -130,7 +184,7 @@ impl<'mir, 'tcx> DropPointsBuilder<'mir, 'tcx> {
130184
131185 let mut after_terminator = HashMap :: new ( ) ;
132186 let mut after_statements = Vec :: new ( ) ;
133- after_statements. resize_with ( data. statements . len ( ) + 1 , HashSet :: new ) ;
187+ after_statements. resize_with ( data. statements . len ( ) + 1 , DropSet :: default ) ;
134188
135189 results. seek_to_block_end ( bb) ;
136190 let live_locals_after_terminator = results. get ( ) . clone ( ) ;
@@ -147,7 +201,7 @@ impl<'mir, 'tcx> DropPointsBuilder<'mir, 'tcx> {
147201 t. subtract ( & self . bb_ins_cache [ & succ_bb] ) ;
148202 t
149203 } ;
150- after_terminator. insert ( succ_bb, edge_drops . iter ( ) . map ( Into :: into ) . collect ( ) ) ;
204+ after_terminator. insert ( succ_bb, self . drop_set ( edge_drops ) ) ;
151205 ins. union ( & self . bb_ins_cache [ & succ_bb] ) ;
152206 }
153207
@@ -170,8 +224,10 @@ impl<'mir, 'tcx> DropPointsBuilder<'mir, 'tcx> {
170224 t. insert ( def) ;
171225 }
172226 t. subtract ( & last_live_locals) ;
173- t. subtract ( & moved_locals ( self . body , bb, statement_index) ) ;
174- t. iter ( ) . map ( Into :: into) . collect ( )
227+ if let Some ( moved) = self . moves . whole . get ( & loc) {
228+ t. subtract ( moved) ;
229+ }
230+ self . drop_set ( t)
175231 } ;
176232 last_live_locals = live_locals;
177233 }
@@ -185,10 +241,10 @@ impl<'mir, 'tcx> DropPointsBuilder<'mir, 'tcx> {
185241 "analyzed implicit drop points"
186242 ) ;
187243 DropPoints {
188- before_statements : HashSet :: default ( ) ,
244+ before_statements : DropSet :: default ( ) ,
189245 after_statements,
190246 after_terminator,
191- after_terminator_extra : HashSet :: default ( ) ,
247+ after_terminator_extra : DropSet :: default ( ) ,
192248 }
193249 }
194250}
0 commit comments