|
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