55use rustc_index:: bit_set:: BitSet ;
66use rustc_middle:: mir:: visit:: Visitor ;
77use rustc_middle:: mir:: { self , BasicBlock , Local , Location , Statement , StatementKind } ;
8+ use rustc_mir_dataflow:: fmt:: DebugWithContext ;
9+ use rustc_mir_dataflow:: JoinSemiLattice ;
10+ use rustc_span:: DUMMY_SP ;
811
12+ use std:: fmt;
913use std:: marker:: PhantomData ;
1014
1115use super :: { qualifs, ConstCx , Qualif } ;
1216
1317/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
1418/// `FlowSensitiveAnalysis`.
1519///
16- /// This transfer does nothing when encountering an indirect assignment. Consumers should rely on
17- /// the `MaybeMutBorrowedLocals` dataflow pass to see if a `Local` may have become qualified via
18- /// an indirect assignment or function call.
20+ /// To account for indirect assignments, data flow conservatively assumes that local becomes
21+ /// qualified immediately after it is borrowed or its address escapes. The borrow must allow for
22+ /// mutation, which includes shared borrows of places with interior mutability. The type of
23+ /// borrowed place must contain the qualif.
1924struct TransferFunction < ' a , ' mir , ' tcx , Q > {
2025 ccx : & ' a ConstCx < ' mir , ' tcx > ,
21- qualifs_per_local : & ' a mut BitSet < Local > ,
22-
26+ state : & ' a mut State ,
2327 _qualif : PhantomData < Q > ,
2428}
2529
2630impl < Q > TransferFunction < ' a , ' mir , ' tcx , Q >
2731where
2832 Q : Qualif ,
2933{
30- fn new ( ccx : & ' a ConstCx < ' mir , ' tcx > , qualifs_per_local : & ' a mut BitSet < Local > ) -> Self {
31- TransferFunction { ccx, qualifs_per_local , _qualif : PhantomData }
34+ fn new ( ccx : & ' a ConstCx < ' mir , ' tcx > , state : & ' a mut State ) -> Self {
35+ TransferFunction { ccx, state , _qualif : PhantomData }
3236 }
3337
3438 fn initialize_state ( & mut self ) {
35- self . qualifs_per_local . clear ( ) ;
39+ self . state . qualif . clear ( ) ;
40+ self . state . borrow . clear ( ) ;
3641
3742 for arg in self . ccx . body . args_iter ( ) {
3843 let arg_ty = self . ccx . body . local_decls [ arg] . ty ;
3944 if Q :: in_any_value_of_ty ( self . ccx , arg_ty) {
40- self . qualifs_per_local . insert ( arg) ;
45+ self . state . qualif . insert ( arg) ;
4146 }
4247 }
4348 }
@@ -47,15 +52,15 @@ where
4752
4853 match ( value, place. as_ref ( ) ) {
4954 ( true , mir:: PlaceRef { local, .. } ) => {
50- self . qualifs_per_local . insert ( local) ;
55+ self . state . qualif . insert ( local) ;
5156 }
5257
5358 // For now, we do not clear the qualif if a local is overwritten in full by
5459 // an unqualified rvalue (e.g. `y = 5`). This is to be consistent
5560 // with aggregates where we overwrite all fields with assignments, which would not
5661 // get this feature.
5762 ( false , mir:: PlaceRef { local : _, projection : & [ ] } ) => {
58- // self.qualifs_per_local .remove(*local);
63+ // self.state.qualif .remove(*local);
5964 }
6065
6166 _ => { }
7883 self . assign_qualif_direct ( & return_place, qualif) ;
7984 }
8085 }
86+
87+ fn address_of_allows_mutation ( & self , mt : mir:: Mutability , place : mir:: Place < ' tcx > ) -> bool {
88+ match mt {
89+ mir:: Mutability :: Mut => true ,
90+ mir:: Mutability :: Not => self . shared_borrow_allows_mutation ( place) ,
91+ }
92+ }
93+
94+ fn ref_allows_mutation ( & self , kind : mir:: BorrowKind , place : mir:: Place < ' tcx > ) -> bool {
95+ match kind {
96+ mir:: BorrowKind :: Mut { .. } => true ,
97+ mir:: BorrowKind :: Shared | mir:: BorrowKind :: Shallow | mir:: BorrowKind :: Unique => {
98+ self . shared_borrow_allows_mutation ( place)
99+ }
100+ }
101+ }
102+
103+ fn shared_borrow_allows_mutation ( & self , place : mir:: Place < ' tcx > ) -> bool {
104+ !place
105+ . ty ( self . ccx . body , self . ccx . tcx )
106+ . ty
107+ . is_freeze ( self . ccx . tcx . at ( DUMMY_SP ) , self . ccx . param_env )
108+ }
81109}
82110
83111impl < Q > Visitor < ' tcx > for TransferFunction < ' _ , ' _ , ' tcx , Q >
@@ -95,7 +123,12 @@ where
95123 // it no longer needs to be dropped.
96124 if let mir:: Operand :: Move ( place) = operand {
97125 if let Some ( local) = place. as_local ( ) {
98- self . qualifs_per_local . remove ( local) ;
126+ // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier
127+ // implementation we retain qualif if a local had been borrowed before. This might
128+ // not be strictly necessary since the local is no longer initialized.
129+ if !self . state . borrow . contains ( local) {
130+ self . state . qualif . remove ( local) ;
131+ }
99132 }
100133 }
101134 }
@@ -106,11 +139,8 @@ where
106139 rvalue : & mir:: Rvalue < ' tcx > ,
107140 location : Location ,
108141 ) {
109- let qualif = qualifs:: in_rvalue :: < Q , _ > (
110- self . ccx ,
111- & mut |l| self . qualifs_per_local . contains ( l) ,
112- rvalue,
113- ) ;
142+ let qualif =
143+ qualifs:: in_rvalue :: < Q , _ > ( self . ccx , & mut |l| self . state . qualif . contains ( l) , rvalue) ;
114144 if !place. is_indirect ( ) {
115145 self . assign_qualif_direct ( place, qualif) ;
116146 }
@@ -120,10 +150,53 @@ where
120150 self . super_assign ( place, rvalue, location) ;
121151 }
122152
153+ fn visit_rvalue ( & mut self , rvalue : & mir:: Rvalue < ' tcx > , location : Location ) {
154+ self . super_rvalue ( rvalue, location) ;
155+
156+ match rvalue {
157+ mir:: Rvalue :: AddressOf ( mt, borrowed_place) => {
158+ if !borrowed_place. is_indirect ( )
159+ && self . address_of_allows_mutation ( * mt, * borrowed_place)
160+ {
161+ let place_ty = borrowed_place. ty ( self . ccx . body , self . ccx . tcx ) . ty ;
162+ if Q :: in_any_value_of_ty ( self . ccx , place_ty) {
163+ self . state . qualif . insert ( borrowed_place. local ) ;
164+ self . state . borrow . insert ( borrowed_place. local ) ;
165+ }
166+ }
167+ }
168+
169+ mir:: Rvalue :: Ref ( _, kind, borrowed_place) => {
170+ if !borrowed_place. is_indirect ( ) && self . ref_allows_mutation ( * kind, * borrowed_place)
171+ {
172+ let place_ty = borrowed_place. ty ( self . ccx . body , self . ccx . tcx ) . ty ;
173+ if Q :: in_any_value_of_ty ( self . ccx , place_ty) {
174+ self . state . qualif . insert ( borrowed_place. local ) ;
175+ self . state . borrow . insert ( borrowed_place. local ) ;
176+ }
177+ }
178+ }
179+
180+ mir:: Rvalue :: Cast ( ..)
181+ | mir:: Rvalue :: ShallowInitBox ( ..)
182+ | mir:: Rvalue :: Use ( ..)
183+ | mir:: Rvalue :: ThreadLocalRef ( ..)
184+ | mir:: Rvalue :: Repeat ( ..)
185+ | mir:: Rvalue :: Len ( ..)
186+ | mir:: Rvalue :: BinaryOp ( ..)
187+ | mir:: Rvalue :: CheckedBinaryOp ( ..)
188+ | mir:: Rvalue :: NullaryOp ( ..)
189+ | mir:: Rvalue :: UnaryOp ( ..)
190+ | mir:: Rvalue :: Discriminant ( ..)
191+ | mir:: Rvalue :: Aggregate ( ..) => { }
192+ }
193+ }
194+
123195 fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
124196 match statement. kind {
125197 StatementKind :: StorageDead ( local) => {
126- self . qualifs_per_local . remove ( local) ;
198+ self . state . qualif . remove ( local) ;
199+ self . state . borrow . remove ( local) ;
127200 }
128201 _ => self . super_statement ( statement, location) ,
129202 }
@@ -136,7 +209,7 @@ where
136209 if let mir:: TerminatorKind :: DropAndReplace { value, place, .. } = & terminator. kind {
137210 let qualif = qualifs:: in_operand :: < Q , _ > (
138211 self . ccx ,
139- & mut |l| self . qualifs_per_local . contains ( l) ,
212+ & mut |l| self . state . qualif . contains ( l) ,
140213 value,
141214 ) ;
142215
@@ -145,6 +218,9 @@ where
145218 }
146219 }
147220
221+ // We ignore borrow on drop because custom drop impls are not allowed in consts.
222+ // FIXME: Reconsider if accounting for borrows in drops is necessary for const drop.
223+
148224 // We need to assign qualifs to the dropped location before visiting the operand that
149225 // replaces it since qualifs can be cleared on move.
150226 self . super_terminator ( terminator, location) ;
@@ -165,24 +241,76 @@ where
165241 FlowSensitiveAnalysis { ccx, _qualif : PhantomData }
166242 }
167243
168- fn transfer_function (
169- & self ,
170- state : & ' a mut BitSet < Local > ,
171- ) -> TransferFunction < ' a , ' mir , ' tcx , Q > {
244+ fn transfer_function ( & self , state : & ' a mut State ) -> TransferFunction < ' a , ' mir , ' tcx , Q > {
172245 TransferFunction :: < Q > :: new ( self . ccx , state)
173246 }
174247}
175248
249+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
250+ pub ( super ) struct State {
251+ /// Describes whether a local contains qualif.
252+ pub qualif : BitSet < Local > ,
253+ /// Describes whether a local's address escaped and it might become qualified as a result an
254+ /// indirect mutation.
255+ pub borrow : BitSet < Local > ,
256+ }
257+
258+ impl State {
259+ #[ inline]
260+ pub ( super ) fn contains ( & self , local : Local ) -> bool {
261+ self . qualif . contains ( local)
262+ }
263+ }
264+
265+ impl < C > DebugWithContext < C > for State {
266+ fn fmt_with ( & self , ctxt : & C , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
267+ f. write_str ( "qualif: " ) ?;
268+ self . qualif . fmt_with ( ctxt, f) ?;
269+ f. write_str ( " borrow: " ) ?;
270+ self . borrow . fmt_with ( ctxt, f) ?;
271+ Ok ( ( ) )
272+ }
273+
274+ fn fmt_diff_with ( & self , old : & Self , ctxt : & C , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
275+ if self == old {
276+ return Ok ( ( ) ) ;
277+ }
278+
279+ if self . qualif != old. qualif {
280+ f. write_str ( "qualif: " ) ?;
281+ self . qualif . fmt_diff_with ( & old. qualif , ctxt, f) ?;
282+ f. write_str ( "\n " ) ?;
283+ }
284+
285+ if self . borrow != old. borrow {
286+ f. write_str ( "borrow: " ) ?;
287+ self . qualif . fmt_diff_with ( & old. borrow , ctxt, f) ?;
288+ f. write_str ( "\n " ) ?;
289+ }
290+
291+ Ok ( ( ) )
292+ }
293+ }
294+
295+ impl JoinSemiLattice for State {
296+ fn join ( & mut self , other : & Self ) -> bool {
297+ self . qualif . join ( & other. qualif ) || self . borrow . join ( & other. borrow )
298+ }
299+ }
300+
176301impl < Q > rustc_mir_dataflow:: AnalysisDomain < ' tcx > for FlowSensitiveAnalysis < ' _ , ' _ , ' tcx , Q >
177302where
178303 Q : Qualif ,
179304{
180- type Domain = BitSet < Local > ;
305+ type Domain = State ;
181306
182307 const NAME : & ' static str = Q :: ANALYSIS_NAME ;
183308
184309 fn bottom_value ( & self , body : & mir:: Body < ' tcx > ) -> Self :: Domain {
185- BitSet :: new_empty ( body. local_decls . len ( ) )
310+ State {
311+ qualif : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
312+ borrow : BitSet :: new_empty ( body. local_decls . len ( ) ) ,
313+ }
186314 }
187315
188316 fn initialize_start_block ( & self , _body : & mir:: Body < ' tcx > , state : & mut Self :: Domain ) {
0 commit comments