|
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