5
5
use rustc_index:: bit_set:: BitSet ;
6
6
use rustc_middle:: mir:: visit:: Visitor ;
7
7
use 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 ;
8
11
12
+ use std:: fmt;
9
13
use std:: marker:: PhantomData ;
10
14
11
15
use super :: { qualifs, ConstCx , Qualif } ;
12
16
13
17
/// A `Visitor` that propagates qualifs between locals. This defines the transfer function of
14
18
/// `FlowSensitiveAnalysis`.
15
19
///
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.
19
24
struct TransferFunction < ' a , ' mir , ' tcx , Q > {
20
25
ccx : & ' a ConstCx < ' mir , ' tcx > ,
21
- qualifs_per_local : & ' a mut BitSet < Local > ,
22
-
26
+ state : & ' a mut State ,
23
27
_qualif : PhantomData < Q > ,
24
28
}
25
29
26
30
impl < Q > TransferFunction < ' a , ' mir , ' tcx , Q >
27
31
where
28
32
Q : Qualif ,
29
33
{
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 }
32
36
}
33
37
34
38
fn initialize_state ( & mut self ) {
35
- self . qualifs_per_local . clear ( ) ;
39
+ self . state . qualif . clear ( ) ;
40
+ self . state . borrow . clear ( ) ;
36
41
37
42
for arg in self . ccx . body . args_iter ( ) {
38
43
let arg_ty = self . ccx . body . local_decls [ arg] . ty ;
39
44
if Q :: in_any_value_of_ty ( self . ccx , arg_ty) {
40
- self . qualifs_per_local . insert ( arg) ;
45
+ self . state . qualif . insert ( arg) ;
41
46
}
42
47
}
43
48
}
@@ -47,15 +52,15 @@ where
47
52
48
53
match ( value, place. as_ref ( ) ) {
49
54
( true , mir:: PlaceRef { local, .. } ) => {
50
- self . qualifs_per_local . insert ( local) ;
55
+ self . state . qualif . insert ( local) ;
51
56
}
52
57
53
58
// For now, we do not clear the qualif if a local is overwritten in full by
54
59
// an unqualified rvalue (e.g. `y = 5`). This is to be consistent
55
60
// with aggregates where we overwrite all fields with assignments, which would not
56
61
// get this feature.
57
62
( false , mir:: PlaceRef { local : _, projection : & [ ] } ) => {
58
- // self.qualifs_per_local .remove(*local);
63
+ // self.state.qualif .remove(*local);
59
64
}
60
65
61
66
_ => { }
78
83
self . assign_qualif_direct ( & return_place, qualif) ;
79
84
}
80
85
}
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
+ }
81
109
}
82
110
83
111
impl < Q > Visitor < ' tcx > for TransferFunction < ' _ , ' _ , ' tcx , Q >
@@ -95,7 +123,12 @@ where
95
123
// it no longer needs to be dropped.
96
124
if let mir:: Operand :: Move ( place) = operand {
97
125
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
+ }
99
132
}
100
133
}
101
134
}
@@ -106,11 +139,8 @@ where
106
139
rvalue : & mir:: Rvalue < ' tcx > ,
107
140
location : Location ,
108
141
) {
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) ;
114
144
if !place. is_indirect ( ) {
115
145
self . assign_qualif_direct ( place, qualif) ;
116
146
}
@@ -120,10 +150,53 @@ where
120
150
self . super_assign ( place, rvalue, location) ;
121
151
}
122
152
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
+
123
195
fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
124
196
match statement. kind {
125
197
StatementKind :: StorageDead ( local) => {
126
- self . qualifs_per_local . remove ( local) ;
198
+ self . state . qualif . remove ( local) ;
199
+ self . state . borrow . remove ( local) ;
127
200
}
128
201
_ => self . super_statement ( statement, location) ,
129
202
}
@@ -136,7 +209,7 @@ where
136
209
if let mir:: TerminatorKind :: DropAndReplace { value, place, .. } = & terminator. kind {
137
210
let qualif = qualifs:: in_operand :: < Q , _ > (
138
211
self . ccx ,
139
- & mut |l| self . qualifs_per_local . contains ( l) ,
212
+ & mut |l| self . state . qualif . contains ( l) ,
140
213
value,
141
214
) ;
142
215
@@ -145,6 +218,9 @@ where
145
218
}
146
219
}
147
220
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
+
148
224
// We need to assign qualifs to the dropped location before visiting the operand that
149
225
// replaces it since qualifs can be cleared on move.
150
226
self . super_terminator ( terminator, location) ;
@@ -165,24 +241,76 @@ where
165
241
FlowSensitiveAnalysis { ccx, _qualif : PhantomData }
166
242
}
167
243
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 > {
172
245
TransferFunction :: < Q > :: new ( self . ccx , state)
173
246
}
174
247
}
175
248
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
+
176
301
impl < Q > rustc_mir_dataflow:: AnalysisDomain < ' tcx > for FlowSensitiveAnalysis < ' _ , ' _ , ' tcx , Q >
177
302
where
178
303
Q : Qualif ,
179
304
{
180
- type Domain = BitSet < Local > ;
305
+ type Domain = State ;
181
306
182
307
const NAME : & ' static str = Q :: ANALYSIS_NAME ;
183
308
184
309
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
+ }
186
314
}
187
315
188
316
fn initialize_start_block ( & self , _body : & mir:: Body < ' tcx > , state : & mut Self :: Domain ) {
0 commit comments