@@ -13,6 +13,7 @@ use rustc::hir;
13
13
use rustc:: hir:: def_id:: DefId ;
14
14
use rustc:: middle:: region;
15
15
use rustc:: mir:: { self , Location , Place , Mir } ;
16
+ use rustc:: mir:: traversal;
16
17
use rustc:: mir:: visit:: { PlaceContext , Visitor } ;
17
18
use rustc:: ty:: { self , Region , TyCtxt } ;
18
19
use rustc:: ty:: RegionKind ;
@@ -85,6 +86,9 @@ pub struct BorrowData<'tcx> {
85
86
/// Location where the borrow reservation starts.
86
87
/// In many cases, this will be equal to the activation location but not always.
87
88
pub ( crate ) reserve_location : Location ,
89
+ /// Location where the borrow is activated. None if this is not a
90
+ /// 2-phase borrow.
91
+ pub ( crate ) activation_location : Option < Location > ,
88
92
/// What kind of borrow this is
89
93
pub ( crate ) kind : mir:: BorrowKind ,
90
94
/// The region for which this borrow is live
@@ -143,9 +147,26 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
143
147
region_map : FxHashMap ( ) ,
144
148
local_map : FxHashMap ( ) ,
145
149
region_span_map : FxHashMap ( ) ,
146
- nonlexical_regioncx : nonlexical_regioncx. clone ( )
150
+ nonlexical_regioncx : nonlexical_regioncx. clone ( ) ,
151
+ pending_activations : FxHashMap ( ) ,
147
152
} ;
148
- visitor. visit_mir ( mir) ;
153
+ for ( block, block_data) in traversal:: preorder ( mir) {
154
+ visitor. visit_basic_block_data ( block, block_data) ;
155
+ }
156
+
157
+ // Double check: We should have found an activation for every pending
158
+ // activation.
159
+ assert_eq ! (
160
+ visitor
161
+ . pending_activations
162
+ . iter( )
163
+ . find( |& ( _local, & borrow_index) | {
164
+ visitor. idx_vec[ borrow_index] . activation_location. is_none( )
165
+ } ) ,
166
+ None ,
167
+ "never found an activation for this borrow!" ,
168
+ ) ;
169
+
149
170
return Borrows { tcx : tcx,
150
171
mir : mir,
151
172
borrows : visitor. idx_vec ,
@@ -168,6 +189,16 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
168
189
local_map : FxHashMap < mir:: Local , FxHashSet < BorrowIndex > > ,
169
190
region_span_map : FxHashMap < RegionKind , Span > ,
170
191
nonlexical_regioncx : Option < Rc < RegionInferenceContext < ' tcx > > > ,
192
+
193
+ /// When we encounter a 2-phase borrow statement, it will always
194
+ /// be assigning into a temporary TEMP:
195
+ ///
196
+ /// TEMP = &foo
197
+ ///
198
+ /// We add TEMP into this map with `b`, where `b` is the index of
199
+ /// the borrow. When we find a later use of this activation, we
200
+ /// remove from the map (and add to the "tombstone" set below).
201
+ pending_activations : FxHashMap < mir:: Local , BorrowIndex > ,
171
202
}
172
203
173
204
impl < ' a , ' gcx , ' tcx > Visitor < ' tcx > for GatherBorrows < ' a , ' gcx , ' tcx > {
@@ -187,20 +218,25 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
187
218
if let mir:: Rvalue :: Ref ( region, kind, ref borrowed_place) = * rvalue {
188
219
if is_unsafe_place ( self . tcx , self . mir , borrowed_place) { return ; }
189
220
190
- let activate_location = self . compute_activation_location ( location,
191
- & assigned_place,
192
- region,
193
- kind) ;
194
221
let borrow = BorrowData {
195
- kind, region,
222
+ kind,
223
+ region,
196
224
reserve_location : location,
225
+ activation_location : None ,
197
226
borrowed_place : borrowed_place. clone ( ) ,
198
227
assigned_place : assigned_place. clone ( ) ,
199
228
} ;
200
229
let idx = self . idx_vec . push ( borrow) ;
201
230
self . location_map . insert ( location, idx) ;
202
231
203
- insert ( & mut self . activation_map , & activate_location, idx) ;
232
+ self . insert_as_pending_if_two_phase (
233
+ location,
234
+ & assigned_place,
235
+ region,
236
+ kind,
237
+ idx,
238
+ ) ;
239
+
204
240
insert ( & mut self . region_map , & region, idx) ;
205
241
if let Some ( local) = root_local ( borrowed_place) {
206
242
insert ( & mut self . local_map , & local, idx) ;
@@ -220,25 +256,69 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
220
256
}
221
257
}
222
258
259
+ fn visit_place (
260
+ & mut self ,
261
+ place : & mir:: Place < ' tcx > ,
262
+ context : PlaceContext < ' tcx > ,
263
+ location : Location ,
264
+ ) {
265
+ self . super_place ( place, context, location) ;
266
+
267
+ // We found a use of some temporary TEMP...
268
+ if let Place :: Local ( temp) = place {
269
+ // ... check whether we (earlier) saw a 2-phase borrow like
270
+ //
271
+ // TMP = &mut place
272
+ match self . pending_activations . get ( temp) {
273
+ Some ( & borrow_index) => {
274
+ let borrow_data = & mut self . idx_vec [ borrow_index] ;
275
+
276
+ // Watch out: the use of TMP in the borrow
277
+ // itself doesn't count as an
278
+ // activation. =)
279
+ if borrow_data. reserve_location == location
280
+ && context == PlaceContext :: Store
281
+ {
282
+ return ;
283
+ }
284
+
285
+ if let Some ( other_activation) = borrow_data. activation_location {
286
+ span_bug ! (
287
+ self . mir. source_info( location) . span,
288
+ "found two activations for 2-phase borrow temporary {:?}: \
289
+ {:?} and {:?}",
290
+ temp,
291
+ location,
292
+ other_activation,
293
+ ) ;
294
+ }
295
+
296
+ // Otherwise, this is the unique later use
297
+ // that we expect.
298
+ borrow_data. activation_location = Some ( location) ;
299
+ self . activation_map
300
+ . entry ( location)
301
+ . or_insert ( FxHashSet ( ) )
302
+ . insert ( borrow_index) ;
303
+ }
304
+
305
+ None => { }
306
+ }
307
+ }
308
+ }
309
+
223
310
fn visit_rvalue ( & mut self ,
224
311
rvalue : & mir:: Rvalue < ' tcx > ,
225
312
location : mir:: Location ) {
226
313
if let mir:: Rvalue :: Ref ( region, kind, ref place) = * rvalue {
227
314
// double-check that we already registered a BorrowData for this
228
315
229
- let mut found_it = false ;
230
- for idx in & self . region_map [ region] {
231
- let bd = & self . idx_vec [ * idx] ;
232
- if bd. reserve_location == location &&
233
- bd. kind == kind &&
234
- bd. region == region &&
235
- bd. borrowed_place == * place
236
- {
237
- found_it = true ;
238
- break ;
239
- }
240
- }
241
- assert ! ( found_it, "Ref {:?} at {:?} missing BorrowData" , rvalue, location) ;
316
+ let borrow_index = self . location_map [ & location] ;
317
+ let borrow_data = & self . idx_vec [ borrow_index] ;
318
+ assert_eq ! ( borrow_data. reserve_location, location) ;
319
+ assert_eq ! ( borrow_data. kind, kind) ;
320
+ assert_eq ! ( borrow_data. region, region) ;
321
+ assert_eq ! ( borrow_data. borrowed_place, * place) ;
242
322
}
243
323
244
324
return self . super_rvalue ( rvalue, location) ;
@@ -380,87 +460,48 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
380
460
false
381
461
}
382
462
383
- /// Computes the activation location of a borrow.
384
- /// The general idea is to start at the beginning of the region and perform a DFS
385
- /// until we exit the region, either via an explicit EndRegion or because NLL tells
386
- /// us so. If we find more than one valid activation point, we currently panic the
387
- /// compiler since two-phase borrows are only currently supported for compiler-
388
- /// generated code. More precisely, we only allow two-phase borrows for:
389
- /// - Function calls (fn some_func(&mut self, ....))
390
- /// - *Assign operators (a += b -> fn add_assign(&mut self, other: Self))
391
- /// See
392
- /// - https://github.com/rust-lang/rust/issues/48431
393
- /// for detailed design notes.
394
- /// See the FIXME in the body of the function for notes on extending support to more
395
- /// general two-phased borrows.
396
- fn compute_activation_location ( & self ,
397
- start_location : Location ,
398
- assigned_place : & mir:: Place < ' tcx > ,
399
- region : Region < ' tcx > ,
400
- kind : mir:: BorrowKind ) -> Location {
401
- debug ! ( "Borrows::compute_activation_location({:?}, {:?}, {:?})" ,
402
- start_location,
403
- assigned_place,
404
- region) ;
463
+ /// If this is a two-phase borrow, then we will record it
464
+ /// as "pending" until we find the activating use.
465
+ fn insert_as_pending_if_two_phase (
466
+ & mut self ,
467
+ start_location : Location ,
468
+ assigned_place : & mir:: Place < ' tcx > ,
469
+ region : Region < ' tcx > ,
470
+ kind : mir:: BorrowKind ,
471
+ borrow_index : BorrowIndex ,
472
+ ) {
473
+ debug ! (
474
+ "Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?}, {:?})" ,
475
+ start_location, assigned_place, region, borrow_index,
476
+ ) ;
477
+
405
478
if !self . allow_two_phase_borrow ( kind) {
406
479
debug ! ( " -> {:?}" , start_location) ;
407
- return start_location;
408
- }
409
-
410
- // Perform the DFS.
411
- // `stack` is the stack of locations still under consideration
412
- // `visited` is the set of points we have already visited
413
- // `found_use` is an Option that becomes Some when we find a use
414
- let mut stack = vec ! [ start_location] ;
415
- let mut visited = FxHashSet ( ) ;
416
- let mut found_use = None ;
417
- while let Some ( curr_loc) = stack. pop ( ) {
418
- let block_data = & self . mir . basic_blocks ( )
419
- . get ( curr_loc. block )
420
- . unwrap_or_else ( || {
421
- panic ! ( "could not find block at location {:?}" , curr_loc) ;
422
- } ) ;
423
-
424
- if self . region_terminated_after ( region, curr_loc) {
425
- // No need to process this statement.
426
- // It's either an EndRegion (and thus couldn't use assigned_place) or not
427
- // contained in the NLL region and thus a use would be invalid
428
- continue ;
429
- }
430
-
431
- if !visited. insert ( curr_loc) {
432
- debug ! ( " Already visited {:?}" , curr_loc) ;
433
- continue ;
434
- }
435
-
436
- if self . location_contains_use ( curr_loc, assigned_place) {
437
- // FIXME: Handle this case a little more gracefully. Perhaps collect
438
- // all uses in a vector, and find the point in the CFG that dominates
439
- // all of them?
440
- // Right now this is sufficient though since there should only be exactly
441
- // one borrow-activating use of the borrow.
442
- assert ! ( found_use. is_none( ) , "Found secondary use of place" ) ;
443
- found_use = Some ( curr_loc) ;
444
- }
445
-
446
- // Push the points we should consider next.
447
- if curr_loc. statement_index < block_data. statements . len ( ) {
448
- stack. push ( curr_loc. successor_within_block ( ) ) ;
449
- } else {
450
- stack. extend ( block_data. terminator ( ) . successors ( ) . iter ( ) . map (
451
- |& basic_block| {
452
- Location {
453
- statement_index : 0 ,
454
- block : basic_block
455
- }
456
- }
457
- ) )
458
- }
480
+ return ;
459
481
}
460
482
461
- let found_use = found_use. expect ( "Did not find use of two-phase place" ) ;
462
- debug ! ( " -> {:?}" , found_use) ;
463
- found_use
483
+ // When we encounter a 2-phase borrow statement, it will always
484
+ // be assigning into a temporary TEMP:
485
+ //
486
+ // TEMP = &foo
487
+ //
488
+ // so extract `temp`.
489
+ let temp = if let & mir:: Place :: Local ( temp) = assigned_place {
490
+ temp
491
+ } else {
492
+ span_bug ! (
493
+ self . mir. source_info( start_location) . span,
494
+ "expected 2-phase borrow to assign to a local, not `{:?}`" ,
495
+ assigned_place,
496
+ ) ;
497
+ } ;
498
+
499
+ // Insert `temp` into the list of pending activations. From
500
+ // now on, we'll be on the lookout for a use of it. Note that
501
+ // we are guaranteed that this use will come after the
502
+ // assignment.
503
+ let old_value = self . pending_activations . insert ( temp, borrow_index) ;
504
+ assert ! ( old_value. is_none( ) ) ;
464
505
}
465
506
}
466
507
}
0 commit comments