1
+ use std:: collections:: VecDeque ;
2
+
1
3
use crate :: borrow_check:: borrow_set:: BorrowData ;
2
4
use crate :: borrow_check:: error_reporting:: UseSpans ;
3
5
use crate :: borrow_check:: nll:: ConstraintDescription ;
@@ -9,6 +11,7 @@ use rustc::mir::{
9
11
Place , Projection , ProjectionElem , Rvalue , Statement , StatementKind ,
10
12
TerminatorKind
11
13
} ;
14
+ use rustc_data_structures:: fx:: FxHashSet ;
12
15
use rustc_errors:: DiagnosticBuilder ;
13
16
use syntax_pos:: Span ;
14
17
@@ -220,7 +223,8 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
220
223
let spans = self . move_spans ( & Place :: Local ( local) , location)
221
224
. or_else ( || self . borrow_spans ( span, location) ) ;
222
225
223
- if self . is_borrow_location_in_loop ( context. loc ) {
226
+ let borrow_location = context. loc ;
227
+ if self . is_use_in_later_iteration_of_loop ( borrow_location, location) {
224
228
let later_use = self . later_use_kind ( borrow, spans, location) ;
225
229
BorrowExplanation :: UsedLaterInLoop ( later_use. 0 , later_use. 1 )
226
230
} else {
@@ -285,76 +289,139 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
285
289
}
286
290
}
287
291
288
- /// Checks if a borrow location is within a loop.
289
- fn is_borrow_location_in_loop (
292
+ /// true if `borrow_location` can reach `use_location` by going through a loop and
293
+ /// `use_location` is also inside of that loop
294
+ fn is_use_in_later_iteration_of_loop (
290
295
& self ,
291
296
borrow_location : Location ,
297
+ use_location : Location ,
292
298
) -> bool {
293
- let mut visited_locations = Vec :: new ( ) ;
294
- let mut pending_locations = vec ! [ borrow_location ] ;
295
- debug ! ( "is_in_loop: borrow_location={:?}" , borrow_location) ;
296
-
297
- while let Some ( location) = pending_locations. pop ( ) {
298
- debug ! ( "is_in_loop: location={:?} pending_locations={:?} visited_locations={:?}" ,
299
- location, pending_locations, visited_locations) ;
300
- if location == borrow_location && visited_locations. contains ( & borrow_location) {
301
- // We've managed to return to where we started (and this isn't the start of the
302
- // search).
303
- debug ! ( "is_in_loop: found!" ) ;
304
- return true ;
305
- }
299
+ let back_edge = self . reach_through_backedge ( borrow_location, use_location) ;
300
+ back_edge. map_or ( false , |back_edge| {
301
+ self . can_reach_head_of_loop ( use_location, back_edge)
302
+ } )
303
+ }
306
304
307
- // Skip locations we've been.
308
- if visited_locations. contains ( & location) { continue ; }
305
+ /// Returns the outmost back edge if `from` location can reach `to` location passing through
306
+ /// that back edge
307
+ fn reach_through_backedge ( & self , from : Location , to : Location ) -> Option < Location > {
308
+ let mut visited_locations = FxHashSet :: default ( ) ;
309
+ let mut pending_locations = VecDeque :: new ( ) ;
310
+ visited_locations. insert ( from) ;
311
+ pending_locations. push_back ( from) ;
312
+ debug ! ( "reach_through_backedge: from={:?} to={:?}" , from, to, ) ;
313
+
314
+ let mut outmost_back_edge = None ;
315
+ while let Some ( location) = pending_locations. pop_front ( ) {
316
+ debug ! (
317
+ "reach_through_backedge: location={:?} outmost_back_edge={:?}
318
+ pending_locations={:?} visited_locations={:?}" ,
319
+ location, outmost_back_edge, pending_locations, visited_locations
320
+ ) ;
321
+
322
+ if location == to && outmost_back_edge. is_some ( ) {
323
+ // We've managed to reach the use location
324
+ debug ! ( "reach_through_backedge: found!" ) ;
325
+ return outmost_back_edge;
326
+ }
309
327
310
328
let block = & self . mir . basic_blocks ( ) [ location. block ] ;
311
- if location. statement_index == block. statements . len ( ) {
312
- // Add start location of the next blocks to pending locations.
313
- match block. terminator ( ) . kind {
314
- TerminatorKind :: Goto { target } => {
315
- pending_locations. push ( target. start_location ( ) ) ;
316
- } ,
317
- TerminatorKind :: SwitchInt { ref targets, .. } => {
318
- pending_locations. extend (
319
- targets. into_iter ( ) . map ( |target| target. start_location ( ) ) ) ;
320
- } ,
321
- TerminatorKind :: Drop { target, unwind, .. } |
322
- TerminatorKind :: DropAndReplace { target, unwind, .. } |
323
- TerminatorKind :: Assert { target, cleanup : unwind, .. } |
324
- TerminatorKind :: Yield { resume : target, drop : unwind, .. } |
325
- TerminatorKind :: FalseUnwind { real_target : target, unwind, .. } => {
326
- pending_locations. push ( target. start_location ( ) ) ;
327
- if let Some ( unwind) = unwind {
328
- pending_locations. push ( unwind. start_location ( ) ) ;
329
- }
330
- } ,
331
- TerminatorKind :: Call { ref destination, cleanup, .. } => {
332
- if let Some ( ( _, destination) ) = destination {
333
- pending_locations. push ( destination. start_location ( ) ) ;
334
- }
335
- if let Some ( cleanup) = cleanup {
336
- pending_locations. push ( cleanup. start_location ( ) ) ;
337
- }
338
- } ,
339
- TerminatorKind :: FalseEdges { real_target, ref imaginary_targets, .. } => {
340
- pending_locations. push ( real_target. start_location ( ) ) ;
341
- pending_locations. extend (
342
- imaginary_targets. into_iter ( ) . map ( |target| target. start_location ( ) ) ) ;
343
- } ,
344
- _ => { } ,
329
+
330
+ if location. statement_index < block. statements . len ( ) {
331
+ let successor = location. successor_within_block ( ) ;
332
+ if visited_locations. insert ( successor) {
333
+ pending_locations. push_back ( successor) ;
345
334
}
346
335
} else {
347
- // Add the next statement to pending locations.
348
- pending_locations. push ( location. successor_within_block ( ) ) ;
336
+ pending_locations. extend (
337
+ block
338
+ . terminator ( )
339
+ . successors ( )
340
+ . map ( |bb| Location {
341
+ statement_index : 0 ,
342
+ block : * bb,
343
+ } )
344
+ . filter ( |s| visited_locations. insert ( * s) )
345
+ . map ( |s| {
346
+ if self . is_back_edge ( location, s) {
347
+ match outmost_back_edge {
348
+ None => {
349
+ outmost_back_edge = Some ( location) ;
350
+ }
351
+
352
+ Some ( back_edge)
353
+ if location. dominates ( back_edge, & self . dominators ) =>
354
+ {
355
+ outmost_back_edge = Some ( location) ;
356
+ }
357
+
358
+ Some ( _) => { }
359
+ }
360
+ }
361
+
362
+ s
363
+ } ) ,
364
+ ) ;
349
365
}
366
+ }
367
+
368
+ None
369
+ }
370
+
371
+ /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the
372
+ /// intermediate nodes
373
+ fn can_reach_head_of_loop ( & self , from : Location , loop_head : Location ) -> bool {
374
+ self . find_loop_head_dfs ( from, loop_head, & mut FxHashSet :: default ( ) )
375
+ }
350
376
351
- // Keep track of where we have visited.
352
- visited_locations. push ( location) ;
377
+ fn find_loop_head_dfs (
378
+ & self ,
379
+ from : Location ,
380
+ loop_head : Location ,
381
+ visited_locations : & mut FxHashSet < Location > ,
382
+ ) -> bool {
383
+ visited_locations. insert ( from) ;
384
+
385
+ if from == loop_head {
386
+ return true ;
387
+ }
388
+
389
+ if loop_head. dominates ( from, & self . dominators ) {
390
+ let block = & self . mir . basic_blocks ( ) [ from. block ] ;
391
+
392
+ if from. statement_index < block. statements . len ( ) {
393
+ let successor = from. successor_within_block ( ) ;
394
+
395
+ if !visited_locations. contains ( & successor)
396
+ && self . find_loop_head_dfs ( successor, loop_head, visited_locations)
397
+ {
398
+ return true ;
399
+ }
400
+ } else {
401
+ for bb in block. terminator ( ) . successors ( ) {
402
+ let successor = Location {
403
+ statement_index : 0 ,
404
+ block : * bb,
405
+ } ;
406
+
407
+ if !visited_locations. contains ( & successor)
408
+ && self . find_loop_head_dfs ( successor, loop_head, visited_locations)
409
+ {
410
+ return true ;
411
+ }
412
+ }
413
+ }
353
414
}
354
415
355
416
false
356
417
}
357
418
419
+ /// True if an edge `source -> target` is a backedge -- in other words, if the target
420
+ /// dominates the source.
421
+ fn is_back_edge ( & self , source : Location , target : Location ) -> bool {
422
+ target. dominates ( source, & self . mir . dominators ( ) )
423
+ }
424
+
358
425
/// Determine how the borrow was later used.
359
426
fn later_use_kind (
360
427
& self ,
0 commit comments