summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs218
1 files changed, 56 insertions, 162 deletions
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 72aee0267..582d683dd 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -1,8 +1,5 @@
//! Print diagnostics to explain why values are borrowed.
-use std::collections::VecDeque;
-
-use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic};
use rustc_index::vec::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
@@ -15,7 +12,7 @@ use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, DesugaringKind, Span};
-use crate::region_infer::BlameConstraint;
+use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
use crate::{
borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt,
WriteKind,
@@ -38,6 +35,7 @@ pub(crate) enum BorrowExplanation<'tcx> {
span: Span,
region_name: RegionName,
opt_place_desc: Option<String>,
+ extra_info: Vec<ExtraConstraintInfo>,
},
Unexplained,
}
@@ -243,6 +241,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
ref region_name,
ref opt_place_desc,
from_closure: _,
+ ref extra_info,
} => {
region_name.highlight_region_name(err);
@@ -268,18 +267,30 @@ impl<'tcx> BorrowExplanation<'tcx> {
);
};
+ for extra in extra_info {
+ match extra {
+ ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
+ err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+ }
+ }
+ }
+
self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
}
_ => {}
}
}
- pub(crate) fn add_lifetime_bound_suggestion_to_diagnostic(
+
+ fn add_lifetime_bound_suggestion_to_diagnostic(
&self,
err: &mut Diagnostic,
category: &ConstraintCategory<'tcx>,
span: Span,
region_name: &RegionName,
) {
+ if !span.is_desugaring(DesugaringKind::OpaqueTy) {
+ return;
+ }
if let ConstraintCategory::OpaqueType = category {
let suggestable_name =
if region_name.was_named() { region_name.name } else { kw::UnderscoreLifetime };
@@ -305,18 +316,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
&self,
borrow_region: RegionVid,
outlived_region: RegionVid,
- ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>) {
- let BlameConstraint { category, from_closure, cause, variance_info: _ } =
- self.regioncx.best_blame_constraint(
- &self.body,
- borrow_region,
- NllRegionVariableOrigin::FreeRegion,
- |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
- );
+ ) -> (ConstraintCategory<'tcx>, bool, Span, Option<RegionName>, Vec<ExtraConstraintInfo>) {
+ let (blame_constraint, extra_info) = self.regioncx.best_blame_constraint(
+ borrow_region,
+ NllRegionVariableOrigin::FreeRegion,
+ |r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
+ );
+ let BlameConstraint { category, from_closure, cause, .. } = blame_constraint;
let outlived_fr_name = self.give_region_a_name(outlived_region);
- (category, from_closure, cause.span, outlived_fr_name)
+ (category, from_closure, cause.span, outlived_fr_name, extra_info)
}
/// Returns structured explanation for *why* the borrow contains the
@@ -332,37 +342,51 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
/// - second half is the place being accessed
///
/// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
+ #[instrument(level = "debug", skip(self))]
pub(crate) fn explain_why_borrow_contains_point(
&self,
location: Location,
borrow: &BorrowData<'tcx>,
kind_place: Option<(WriteKind, Place<'tcx>)>,
) -> BorrowExplanation<'tcx> {
- debug!(
- "explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})",
- location, borrow, kind_place
- );
-
let regioncx = &self.regioncx;
let body: &Body<'_> = &self.body;
let tcx = self.infcx.tcx;
let borrow_region_vid = borrow.region;
- debug!("explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid);
-
- let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
- debug!("explain_why_borrow_contains_point: region_sub={:?}", region_sub);
+ debug!(?borrow_region_vid);
+
+ let mut region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
+ debug!(?region_sub);
+
+ let mut use_location = location;
+ let mut use_in_later_iteration_of_loop = false;
+
+ if region_sub == borrow_region_vid {
+ // When `region_sub` is the same as `borrow_region_vid` (the location where the borrow is
+ // issued is the same location that invalidates the reference), this is likely a loop iteration
+ // - in this case, try using the loop terminator location in `find_sub_region_live_at`.
+ if let Some(loop_terminator_location) =
+ regioncx.find_loop_terminator_location(borrow.region, body)
+ {
+ region_sub = self
+ .regioncx
+ .find_sub_region_live_at(borrow_region_vid, loop_terminator_location);
+ debug!("explain_why_borrow_contains_point: region_sub in loop={:?}", region_sub);
+ use_location = loop_terminator_location;
+ use_in_later_iteration_of_loop = true;
+ }
+ }
- match find_use::find(body, regioncx, tcx, region_sub, location) {
+ match find_use::find(body, regioncx, tcx, region_sub, use_location) {
Some(Cause::LiveVar(local, location)) => {
let span = body.source_info(location).span;
let spans = self
.move_spans(Place::from(local).as_ref(), location)
.or_else(|| self.borrow_spans(span, location));
- let borrow_location = location;
- if self.is_use_in_later_iteration_of_loop(borrow_location, location) {
- let later_use = self.later_use_kind(borrow, spans, location);
+ if use_in_later_iteration_of_loop {
+ let later_use = self.later_use_kind(borrow, spans, use_location);
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)
} else {
// Check if the location represents a `FakeRead`, and adapt the error
@@ -392,7 +416,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
None => {
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
- let (category, from_closure, span, region_name) =
+ let (category, from_closure, span, region_name, extra_info) =
self.free_region_constraint_info(borrow_region_vid, region);
if let Some(region_name) = region_name {
let opt_place_desc = self.describe_place(borrow.borrowed_place.as_ref());
@@ -402,150 +426,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
span,
region_name,
opt_place_desc,
+ extra_info,
}
} else {
- debug!(
- "explain_why_borrow_contains_point: \
- Could not generate a region name"
- );
+ debug!("Could not generate a region name");
BorrowExplanation::Unexplained
}
} else {
- debug!(
- "explain_why_borrow_contains_point: \
- Could not generate an error region vid"
- );
+ debug!("Could not generate an error region vid");
BorrowExplanation::Unexplained
}
}
}
}
- /// true if `borrow_location` can reach `use_location` by going through a loop and
- /// `use_location` is also inside of that loop
- fn is_use_in_later_iteration_of_loop(
- &self,
- borrow_location: Location,
- use_location: Location,
- ) -> bool {
- let back_edge = self.reach_through_backedge(borrow_location, use_location);
- back_edge.map_or(false, |back_edge| self.can_reach_head_of_loop(use_location, back_edge))
- }
-
- /// Returns the outmost back edge if `from` location can reach `to` location passing through
- /// that back edge
- fn reach_through_backedge(&self, from: Location, to: Location) -> Option<Location> {
- let mut visited_locations = FxHashSet::default();
- let mut pending_locations = VecDeque::new();
- visited_locations.insert(from);
- pending_locations.push_back(from);
- debug!("reach_through_backedge: from={:?} to={:?}", from, to,);
-
- let mut outmost_back_edge = None;
- while let Some(location) = pending_locations.pop_front() {
- debug!(
- "reach_through_backedge: location={:?} outmost_back_edge={:?}
- pending_locations={:?} visited_locations={:?}",
- location, outmost_back_edge, pending_locations, visited_locations
- );
-
- if location == to && outmost_back_edge.is_some() {
- // We've managed to reach the use location
- debug!("reach_through_backedge: found!");
- return outmost_back_edge;
- }
-
- let block = &self.body.basic_blocks()[location.block];
-
- if location.statement_index < block.statements.len() {
- let successor = location.successor_within_block();
- if visited_locations.insert(successor) {
- pending_locations.push_back(successor);
- }
- } else {
- pending_locations.extend(
- block
- .terminator()
- .successors()
- .map(|bb| Location { statement_index: 0, block: bb })
- .filter(|s| visited_locations.insert(*s))
- .map(|s| {
- if self.is_back_edge(location, s) {
- match outmost_back_edge {
- None => {
- outmost_back_edge = Some(location);
- }
-
- Some(back_edge)
- if location.dominates(back_edge, &self.dominators) =>
- {
- outmost_back_edge = Some(location);
- }
-
- Some(_) => {}
- }
- }
-
- s
- }),
- );
- }
- }
-
- None
- }
-
- /// true if `from` location can reach `loop_head` location and `loop_head` dominates all the
- /// intermediate nodes
- fn can_reach_head_of_loop(&self, from: Location, loop_head: Location) -> bool {
- self.find_loop_head_dfs(from, loop_head, &mut FxHashSet::default())
- }
-
- fn find_loop_head_dfs(
- &self,
- from: Location,
- loop_head: Location,
- visited_locations: &mut FxHashSet<Location>,
- ) -> bool {
- visited_locations.insert(from);
-
- if from == loop_head {
- return true;
- }
-
- if loop_head.dominates(from, &self.dominators) {
- let block = &self.body.basic_blocks()[from.block];
-
- if from.statement_index < block.statements.len() {
- let successor = from.successor_within_block();
-
- if !visited_locations.contains(&successor)
- && self.find_loop_head_dfs(successor, loop_head, visited_locations)
- {
- return true;
- }
- } else {
- for bb in block.terminator().successors() {
- let successor = Location { statement_index: 0, block: bb };
-
- if !visited_locations.contains(&successor)
- && self.find_loop_head_dfs(successor, loop_head, visited_locations)
- {
- return true;
- }
- }
- }
- }
-
- false
- }
-
- /// True if an edge `source -> target` is a backedge -- in other words, if the target
- /// dominates the source.
- fn is_back_edge(&self, source: Location, target: Location) -> bool {
- target.dominates(source, &self.dominators)
- }
-
/// Determine how the borrow was later used.
/// First span returned points to the location of the conflicting use
/// Second span if `Some` is returned in the case of closures and points
@@ -564,7 +458,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
UseSpans::PatUse(span)
| UseSpans::OtherUse(span)
| UseSpans::FnSelfUse { var_span: span, .. } => {
- let block = &self.body.basic_blocks()[location.block];
+ let block = &self.body.basic_blocks[location.block];
let kind = if let Some(&Statement {
kind: StatementKind::FakeRead(box (FakeReadCause::ForLet(_), _)),