| 
1 | 1 | //! Print diagnostics to explain why values are borrowed.  | 
2 | 2 | 
  | 
3 |  | -use std::collections::VecDeque;  | 
4 |  | - | 
5 |  | -use rustc_data_structures::fx::FxHashSet;  | 
6 | 3 | use rustc_errors::{Applicability, Diagnostic};  | 
7 | 4 | use rustc_index::vec::IndexVec;  | 
8 | 5 | use rustc_infer::infer::NllRegionVariableOrigin;  | 
@@ -359,19 +356,37 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {  | 
359 | 356 |         let borrow_region_vid = borrow.region;  | 
360 | 357 |         debug!(?borrow_region_vid);  | 
361 | 358 | 
 
  | 
362 |  | -        let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);  | 
 | 359 | +        let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);  | 
363 | 360 |         debug!(?region_sub);  | 
364 | 361 | 
 
  | 
365 |  | -        match find_use::find(body, regioncx, tcx, region_sub, location) {  | 
 | 362 | +        let mut use_location = location;  | 
 | 363 | +        let mut use_in_later_iteration_of_loop = false;  | 
 | 364 | + | 
 | 365 | +        if region_sub == borrow_region_vid {  | 
 | 366 | +            // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is  | 
 | 367 | +            // issued is the same location that invalidates the reference), this is likely a loop iteration  | 
 | 368 | +            // - in this case, try using the loop terminator location in `find_sub_region_live_at`.  | 
 | 369 | +            if let Some(loop_terminator_location) =  | 
 | 370 | +                regioncx.find_loop_terminator_location(borrow.region, body)  | 
 | 371 | +            {  | 
 | 372 | +                region_sub = self  | 
 | 373 | +                    .regioncx  | 
 | 374 | +                    .find_sub_region_live_at(borrow_region_vid, loop_terminator_location);  | 
 | 375 | +                debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);  | 
 | 376 | +                use_location = loop_terminator_location;  | 
 | 377 | +                use_in_later_iteration_of_loop = true;  | 
 | 378 | +            }  | 
 | 379 | +        }  | 
 | 380 | + | 
 | 381 | +        match find_use::find(body, regioncx, tcx, region_sub, use_location) {  | 
366 | 382 |             Some(Cause::LiveVar(local, location)) => {  | 
367 | 383 |                 let span = body.source_info(location).span;  | 
368 | 384 |                 let spans = self  | 
369 | 385 |                     .move_spans(Place::from(local).as_ref(), location)  | 
370 | 386 |                     .or_else(|| self.borrow_spans(span, location));  | 
371 | 387 | 
 
  | 
372 |  | -                let borrow_location = location;  | 
373 |  | -                if self.is_use_in_later_iteration_of_loop(borrow_location, location) {  | 
374 |  | -                    let later_use = self.later_use_kind(borrow, spans, location);  | 
 | 388 | +                if use_in_later_iteration_of_loop {  | 
 | 389 | +                    let later_use = self.later_use_kind(borrow, spans, use_location);  | 
375 | 390 |                     BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)  | 
376 | 391 |                 } else {  | 
377 | 392 |                     // Check if the location represents a `FakeRead`, and adapt the error  | 
@@ -425,131 +440,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {  | 
425 | 440 |         }  | 
426 | 441 |     }  | 
427 | 442 | 
 
  | 
428 |  | -    /// true if `borrow_location` can reach `use_location` by going through a loop and  | 
429 |  | -    /// `use_location` is also inside of that loop  | 
430 |  | -    fn is_use_in_later_iteration_of_loop(  | 
431 |  | -        &self,  | 
432 |  | -        borrow_location: Location,  | 
433 |  | -        use_location: Location,  | 
434 |  | -    ) -> bool {  | 
435 |  | -        let back_edge = self.reach_through_backedge(borrow_location, use_location);  | 
436 |  | -        back_edge.map_or(false, |back_edge| self.can_reach_head_of_loop(use_location, back_edge))  | 
437 |  | -    }  | 
438 |  | - | 
439 |  | -    /// Returns the outmost back edge if `from` location can reach `to` location passing through  | 
440 |  | -    /// that back edge  | 
441 |  | -    fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> {  | 
442 |  | -        let mut visited_locations = FxHashSet::default();  | 
443 |  | -        let mut pending_locations = VecDeque::new();  | 
444 |  | -        visited_locations.insert(from);  | 
445 |  | -        pending_locations.push_back(from);  | 
446 |  | -        debug!("reach_through_backedge: from={:?} to={:?}", from, to,);  | 
447 |  | - | 
448 |  | -        let mut outmost_back_edge = None;  | 
449 |  | -        while let Some(location) = pending_locations.pop_front() {  | 
450 |  | -            debug!(  | 
451 |  | -                "reach_through_backedge: location={:?} outmost_back_edge={:?}  | 
452 |  | -                   pending_locations={:?} visited_locations={:?}",  | 
453 |  | -                location, outmost_back_edge, pending_locations, visited_locations  | 
454 |  | -            );  | 
455 |  | - | 
456 |  | -            if location == to && outmost_back_edge.is_some() {  | 
457 |  | -                // We've managed to reach the use location  | 
458 |  | -                debug!("reach_through_backedge: found!");  | 
459 |  | -                return outmost_back_edge;  | 
460 |  | -            }  | 
461 |  | - | 
462 |  | -            let block = &self.body.basic_blocks[location.block];  | 
463 |  | - | 
464 |  | -            if location.statement_index < block.statements.len() {  | 
465 |  | -                let successor = location.successor_within_block();  | 
466 |  | -                if visited_locations.insert(successor) {  | 
467 |  | -                    pending_locations.push_back(successor);  | 
468 |  | -                }  | 
469 |  | -            } else {  | 
470 |  | -                pending_locations.extend(  | 
471 |  | -                    block  | 
472 |  | -                        .terminator()  | 
473 |  | -                        .successors()  | 
474 |  | -                        .map(|bb| Location { statement_index: 0, block: bb })  | 
475 |  | -                        .filter(|s| visited_locations.insert(*s))  | 
476 |  | -                        .map(|s| {  | 
477 |  | -                            if self.is_back_edge(location, s) {  | 
478 |  | -                                match outmost_back_edge {  | 
479 |  | -                                    None => {  | 
480 |  | -                                        outmost_back_edge = Some(location);  | 
481 |  | -                                    }  | 
482 |  | - | 
483 |  | -                                    Some(back_edge)  | 
484 |  | -                                        if location.dominates(back_edge, &self.dominators) =>  | 
485 |  | -                                    {  | 
486 |  | -                                        outmost_back_edge = Some(location);  | 
487 |  | -                                    }  | 
488 |  | - | 
489 |  | -                                    Some(_) => {}  | 
490 |  | -                                }  | 
491 |  | -                            }  | 
492 |  | - | 
493 |  | -                            s  | 
494 |  | -                        }),  | 
495 |  | -                );  | 
496 |  | -            }  | 
497 |  | -        }  | 
498 |  | - | 
499 |  | -        None  | 
500 |  | -    }  | 
501 |  | - | 
502 |  | -    /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the  | 
503 |  | -    /// intermediate nodes  | 
504 |  | -    fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool {  | 
505 |  | -        self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default())  | 
506 |  | -    }  | 
507 |  | - | 
508 |  | -    fn find_loop_head_dfs(  | 
509 |  | -        &self,  | 
510 |  | -        from: Location,  | 
511 |  | -        loop_head: Location,  | 
512 |  | -        visited_locations: &mut FxHashSet<Location>,  | 
513 |  | -    ) -> bool {  | 
514 |  | -        visited_locations.insert(from);  | 
515 |  | - | 
516 |  | -        if from == loop_head {  | 
517 |  | -            return true;  | 
518 |  | -        }  | 
519 |  | - | 
520 |  | -        if loop_head.dominates(from, &self.dominators) {  | 
521 |  | -            let block = &self.body.basic_blocks[from.block];  | 
522 |  | - | 
523 |  | -            if from.statement_index < block.statements.len() {  | 
524 |  | -                let successor = from.successor_within_block();  | 
525 |  | - | 
526 |  | -                if !visited_locations.contains(&successor)  | 
527 |  | -                    && self.find_loop_head_dfs(successor, loop_head, visited_locations)  | 
528 |  | -                {  | 
529 |  | -                    return true;  | 
530 |  | -                }  | 
531 |  | -            } else {  | 
532 |  | -                for bb in block.terminator().successors() {  | 
533 |  | -                    let successor = Location { statement_index: 0, block: bb };  | 
534 |  | - | 
535 |  | -                    if !visited_locations.contains(&successor)  | 
536 |  | -                        && self.find_loop_head_dfs(successor, loop_head, visited_locations)  | 
537 |  | -                    {  | 
538 |  | -                        return true;  | 
539 |  | -                    }  | 
540 |  | -                }  | 
541 |  | -            }  | 
542 |  | -        }  | 
543 |  | - | 
544 |  | -        false  | 
545 |  | -    }  | 
546 |  | - | 
547 |  | -    /// True if an edge `source -> target` is a backedge -- in other words, if the target  | 
548 |  | -    /// dominates the source.  | 
549 |  | -    fn is_back_edge(&self, source: Location, target: Location) -> bool {  | 
550 |  | -        target.dominates(source, &self.dominators)  | 
551 |  | -    }  | 
552 |  | - | 
553 | 443 |     /// Determine how the borrow was later used.  | 
554 | 444 |     /// First span returned points to the location of the conflicting use  | 
555 | 445 |     /// Second span if `Some` is returned in the case of closures and points  | 
 | 
0 commit comments