Auto merge of #67476 - mark-i-m:simplify-borrow_check-5, r=matthewjasper
Region naming refactoring [6/N] Followup to #67474 EDIT: this PR is probably best read commit-by-commit... The major changes in this PR include: - moving many functions around to modules that better suit them. In particular, a lot of methods were moved from `borrow_check::diagnostics::region_errors` to `borrow_check::region_infer`, and `report_region_errors` was moved from `borrow_check` to `borrow_check::diagnostics::region_errors`. - `borrow_check::diagnostics::{region_errors, region_name}` are now most comprised of methods on `MirBorrowckCtxt` instead of `RegionInferenceContext`, allowing us to get rid of the annoying `pub(in crate::borrow_check)` on most of the fields of the latter, along with a number of method arguments on many methods. - I renamed `MirBorrowckCtxt.nonlexical_regioncx` to just `regioncx` because their is no lexical lifetimes any more, and the old name was annoyingly verbose, causing many lines to wrap unnecessarily. - I got rid of `ErrorRegionNamingContext`. Region naming is implemented as inherent methods on `MirBorrowckCtxt`, so we just move the naming stuff into that struct. The PR is rather large, but the commits are fairly self-contained (though they don't all compile). There was one minor output change to one test with `compare-mode=nll`, which I think is acceptable. Between this PR and the last one, a net of 200 lines are removed, most of which was function parameters and context structs 🎉 Some samples: ```diff - self.nonlexical_regioncx.free_region_constraint_info( - &self.body, - &self.local_names, - &self.upvars, - self.mir_def_id, - self.infcx, - borrow_region_vid, - region, - ); + self.free_region_constraint_info(borrow_region_vid, region); ``` ```diff - .or_else(|| { - self.give_name_if_anonymous_region_appears_in_yield_ty( - infcx, - body, - *mir_def_id, - fr, - renctx, - ) - }); + .or_else(|| self.give_name_if_anonymous_region_appears_in_arguments(fr)) ``` r? @matthewjasper cc @eddyb
This commit is contained in:
commit
d8dcb6345b
@ -2,12 +2,13 @@
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use rustc::infer::NLLRegionVariableOrigin;
|
||||
use rustc::mir::{
|
||||
Body, CastKind, ConstraintCategory, FakeReadCause, Local, Location, Operand, Place, Rvalue,
|
||||
Statement, StatementKind, TerminatorKind,
|
||||
};
|
||||
use rustc::ty::adjustment::PointerCast;
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
use rustc::ty::{self, RegionVid, TyCtxt};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_index::vec::IndexVec;
|
||||
@ -254,6 +255,23 @@ impl BorrowExplanation {
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
fn free_region_constraint_info(
|
||||
&self,
|
||||
borrow_region: RegionVid,
|
||||
outlived_region: RegionVid,
|
||||
) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
|
||||
let (category, from_closure, span) = self.regioncx.best_blame_constraint(
|
||||
&self.body,
|
||||
borrow_region,
|
||||
NLLRegionVariableOrigin::FreeRegion,
|
||||
|r| self.regioncx.provides_universal_region(r, borrow_region, outlived_region),
|
||||
);
|
||||
|
||||
let outlived_fr_name = self.give_region_a_name(outlived_region);
|
||||
|
||||
(category, from_closure, span, outlived_fr_name)
|
||||
}
|
||||
|
||||
/// Returns structured explanation for *why* the borrow contains the
|
||||
/// point from `location`. This is key for the "3-point errors"
|
||||
/// [described in the NLL RFC][d].
|
||||
@ -278,14 +296,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
location, borrow, kind_place
|
||||
);
|
||||
|
||||
let regioncx = &self.nonlexical_regioncx;
|
||||
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 = regioncx.find_sub_region_live_at(borrow_region_vid, location);
|
||||
let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
|
||||
debug!("explain_why_borrow_contains_point: region_sub={:?}", region_sub);
|
||||
|
||||
match find_use::find(body, regioncx, tcx, region_sub, location) {
|
||||
@ -329,10 +347,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
}
|
||||
|
||||
None => {
|
||||
if let Some(region) = regioncx.to_error_region_vid(borrow_region_vid) {
|
||||
let (category, from_closure, span, region_name) = self
|
||||
.nonlexical_regioncx
|
||||
.free_region_constraint_info(self, borrow_region_vid, region);
|
||||
if let Some(region) = self.to_error_region_vid(borrow_region_vid) {
|
||||
let (category, from_closure, span, region_name) =
|
||||
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());
|
||||
BorrowExplanation::MustBeValidFor {
|
||||
@ -345,14 +362,14 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
} else {
|
||||
debug!(
|
||||
"explain_why_borrow_contains_point: \
|
||||
Could not generate a region name"
|
||||
Could not generate a region name"
|
||||
);
|
||||
BorrowExplanation::Unexplained
|
||||
}
|
||||
} else {
|
||||
debug!(
|
||||
"explain_why_borrow_contains_point: \
|
||||
Could not generate an error region vid"
|
||||
Could not generate an error region vid"
|
||||
);
|
||||
BorrowExplanation::Unexplained
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ mod region_errors;
|
||||
crate use mutability_errors::AccessKind;
|
||||
crate use outlives_suggestion::OutlivesSuggestionBuilder;
|
||||
crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
|
||||
crate use region_name::{RegionErrorNamingCtx, RegionName, RegionNameSource};
|
||||
crate use region_name::{RegionName, RegionNameSource};
|
||||
|
||||
pub(super) struct IncludingDowncast(pub(super) bool);
|
||||
|
||||
|
@ -12,7 +12,7 @@ use smallvec::SmallVec;
|
||||
|
||||
use crate::borrow_check::MirBorrowckCtxt;
|
||||
|
||||
use super::{ErrorConstraintInfo, RegionErrorNamingCtx, RegionName, RegionNameSource};
|
||||
use super::{ErrorConstraintInfo, RegionName, RegionNameSource};
|
||||
|
||||
/// The different things we could suggest.
|
||||
enum SuggestedConstraint {
|
||||
@ -77,19 +77,15 @@ impl OutlivesSuggestionBuilder {
|
||||
fn region_vid_to_name(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
region: RegionVid,
|
||||
) -> Option<RegionName> {
|
||||
mbcx.nonlexical_regioncx
|
||||
.give_region_a_name(mbcx, renctx, region)
|
||||
.filter(Self::region_name_is_suggestable)
|
||||
mbcx.give_region_a_name(region).filter(Self::region_name_is_suggestable)
|
||||
}
|
||||
|
||||
/// Compiles a list of all suggestions to be printed in the final big suggestion.
|
||||
fn compile_all_suggestions(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> SmallVec<[SuggestedConstraint; 2]> {
|
||||
let mut suggested = SmallVec::new();
|
||||
|
||||
@ -98,7 +94,7 @@ impl OutlivesSuggestionBuilder {
|
||||
let mut unified_already = FxHashSet::default();
|
||||
|
||||
for (fr, outlived) in &self.constraints_to_add {
|
||||
let fr_name = if let Some(fr_name) = self.region_vid_to_name(mbcx, renctx, *fr) {
|
||||
let fr_name = if let Some(fr_name) = self.region_vid_to_name(mbcx, *fr) {
|
||||
fr_name
|
||||
} else {
|
||||
continue;
|
||||
@ -107,9 +103,7 @@ impl OutlivesSuggestionBuilder {
|
||||
let outlived = outlived
|
||||
.iter()
|
||||
// if there is a `None`, we will just omit that constraint
|
||||
.filter_map(|fr| {
|
||||
self.region_vid_to_name(mbcx, renctx, *fr).map(|rname| (fr, rname))
|
||||
})
|
||||
.filter_map(|fr| self.region_vid_to_name(mbcx, *fr).map(|rname| (fr, rname)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// No suggestable outlived lifetimes.
|
||||
@ -173,12 +167,11 @@ impl OutlivesSuggestionBuilder {
|
||||
&mut self,
|
||||
mbcx: &MirBorrowckCtxt<'_, '_>,
|
||||
errci: &ErrorConstraintInfo,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
diag: &mut DiagnosticBuilder<'_>,
|
||||
) {
|
||||
// Emit an intermediate note.
|
||||
let fr_name = self.region_vid_to_name(mbcx, renctx, errci.fr);
|
||||
let outlived_fr_name = self.region_vid_to_name(mbcx, renctx, errci.outlived_fr);
|
||||
let fr_name = self.region_vid_to_name(mbcx, errci.fr);
|
||||
let outlived_fr_name = self.region_vid_to_name(mbcx, errci.outlived_fr);
|
||||
|
||||
if let (Some(fr_name), Some(outlived_fr_name)) = (fr_name, outlived_fr_name) {
|
||||
if let RegionNameSource::Static = outlived_fr_name.source {
|
||||
@ -194,11 +187,7 @@ impl OutlivesSuggestionBuilder {
|
||||
|
||||
/// If there is a suggestion to emit, add a diagnostic to the buffer. This is the final
|
||||
/// suggestion including all collected constraints.
|
||||
crate fn add_suggestion(
|
||||
&self,
|
||||
mbcx: &mut MirBorrowckCtxt<'_, '_>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) {
|
||||
crate fn add_suggestion(&self, mbcx: &mut MirBorrowckCtxt<'_, '_>) {
|
||||
// No constraints to add? Done.
|
||||
if self.constraints_to_add.is_empty() {
|
||||
debug!("No constraints to suggest.");
|
||||
@ -215,7 +204,7 @@ impl OutlivesSuggestionBuilder {
|
||||
}
|
||||
|
||||
// Get all suggestable constraints.
|
||||
let suggested = self.compile_all_suggestions(mbcx, renctx);
|
||||
let suggested = self.compile_all_suggestions(mbcx);
|
||||
|
||||
// If there are no suggestable constraints...
|
||||
if suggested.is_empty() {
|
||||
|
@ -1,27 +1,25 @@
|
||||
//! Error reporting machinery for lifetime errors.
|
||||
|
||||
use rustc::infer::{
|
||||
error_reporting::nice_region_error::NiceRegionError, region_constraints::GenericKind,
|
||||
InferCtxt, NLLRegionVariableOrigin,
|
||||
error_reporting::nice_region_error::NiceRegionError, opaque_types, NLLRegionVariableOrigin,
|
||||
};
|
||||
use rustc::mir::{Body, ConstraintCategory, Location};
|
||||
use rustc::mir::ConstraintCategory;
|
||||
use rustc::ty::{self, RegionVid, Ty};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::Span;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use crate::util::borrowck_errors;
|
||||
|
||||
use crate::borrow_check::{
|
||||
constraints::OutlivesConstraint, nll::ConstraintDescription,
|
||||
region_infer::RegionInferenceContext, type_check::Locations, universal_regions::DefiningTy,
|
||||
nll::ConstraintDescription,
|
||||
region_infer::{values::RegionElement, TypeTest},
|
||||
universal_regions::DefiningTy,
|
||||
MirBorrowckCtxt,
|
||||
};
|
||||
|
||||
use super::{OutlivesSuggestionBuilder, RegionErrorNamingCtx, RegionName, RegionNameSource};
|
||||
use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource};
|
||||
|
||||
impl ConstraintDescription for ConstraintCategory {
|
||||
fn description(&self) -> &'static str {
|
||||
@ -46,13 +44,6 @@ impl ConstraintDescription for ConstraintCategory {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum Trace {
|
||||
StartRegion,
|
||||
FromOutlivesConstraint(OutlivesConstraint),
|
||||
NotVisited,
|
||||
}
|
||||
|
||||
/// A collection of errors encountered during region inference. This is needed to efficiently
|
||||
/// report errors after borrow checking.
|
||||
///
|
||||
@ -62,23 +53,8 @@ crate type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
crate enum RegionErrorKind<'tcx> {
|
||||
/// An error for a type test: `T: 'a` does not live long enough.
|
||||
TypeTestDoesNotLiveLongEnough {
|
||||
/// The span of the type test.
|
||||
span: Span,
|
||||
/// The generic type of the type test.
|
||||
generic: GenericKind<'tcx>,
|
||||
},
|
||||
|
||||
/// A generic bound failure for a type test.
|
||||
TypeTestGenericBoundError {
|
||||
/// The span of the type test.
|
||||
span: Span,
|
||||
/// The generic type of the type test.
|
||||
generic: GenericKind<'tcx>,
|
||||
/// The lower bound region.
|
||||
lower_bound_region: ty::Region<'tcx>,
|
||||
},
|
||||
/// A generic bound failure for a type test (`T: 'a`).
|
||||
TypeTestError { type_test: TypeTest<'tcx> },
|
||||
|
||||
/// An unexpected hidden region for an opaque type.
|
||||
UnexpectedHiddenRegion {
|
||||
@ -94,8 +70,8 @@ crate enum RegionErrorKind<'tcx> {
|
||||
BoundUniversalRegionError {
|
||||
/// The placeholder free region.
|
||||
longer_fr: RegionVid,
|
||||
/// The region that erroneously must be outlived by `longer_fr`.
|
||||
error_region: RegionVid,
|
||||
/// The region element that erroneously must be outlived by `longer_fr`.
|
||||
error_element: RegionElement,
|
||||
/// The origin of the placeholder region.
|
||||
fr_origin: NLLRegionVariableOrigin,
|
||||
},
|
||||
@ -128,26 +104,26 @@ pub struct ErrorConstraintInfo {
|
||||
pub(super) span: Span,
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
/// Converts a region inference variable into a `ty::Region` that
|
||||
/// we can use for error reporting. If `r` is universally bound,
|
||||
/// then we use the name that we have on record for it. If `r` is
|
||||
/// existentially bound, then we check its inferred value and try
|
||||
/// to find a good name from that. Returns `None` if we can't find
|
||||
/// one (e.g., this is just some random part of the CFG).
|
||||
pub fn to_error_region(&self, r: RegionVid) -> Option<ty::Region<'tcx>> {
|
||||
self.to_error_region_vid(r).and_then(|r| self.definitions[r].external_name)
|
||||
pub(super) fn to_error_region(&self, r: RegionVid) -> Option<ty::Region<'tcx>> {
|
||||
self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name)
|
||||
}
|
||||
|
||||
/// Returns the [RegionVid] corresponding to the region returned by
|
||||
/// Returns the `RegionVid` corresponding to the region returned by
|
||||
/// `to_error_region`.
|
||||
pub fn to_error_region_vid(&self, r: RegionVid) -> Option<RegionVid> {
|
||||
if self.universal_regions.is_universal_region(r) {
|
||||
pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option<RegionVid> {
|
||||
if self.regioncx.universal_regions().is_universal_region(r) {
|
||||
Some(r)
|
||||
} else {
|
||||
let r_scc = self.constraint_sccs.scc(r);
|
||||
let upper_bound = self.universal_upper_bound(r);
|
||||
if self.scc_values.contains(r_scc, upper_bound) {
|
||||
let upper_bound = self.regioncx.universal_upper_bound(r);
|
||||
|
||||
if self.regioncx.upper_bound_in_region_scc(r, upper_bound) {
|
||||
self.to_error_region_vid(upper_bound)
|
||||
} else {
|
||||
None
|
||||
@ -155,270 +131,134 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to find the best constraint to blame for the fact that
|
||||
/// `R: from_region`, where `R` is some region that meets
|
||||
/// `target_test`. This works by following the constraint graph,
|
||||
/// creating a constraint path that forces `R` to outlive
|
||||
/// `from_region`, and then finding the best choices within that
|
||||
/// path to blame.
|
||||
fn best_blame_constraint(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
from_region: RegionVid,
|
||||
from_region_origin: NLLRegionVariableOrigin,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> (ConstraintCategory, bool, Span) {
|
||||
debug!(
|
||||
"best_blame_constraint(from_region={:?}, from_region_origin={:?})",
|
||||
from_region, from_region_origin
|
||||
);
|
||||
|
||||
// Find all paths
|
||||
let (path, target_region) =
|
||||
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
|
||||
debug!(
|
||||
"best_blame_constraint: path={:#?}",
|
||||
path.iter()
|
||||
.map(|&c| format!(
|
||||
"{:?} ({:?}: {:?})",
|
||||
c,
|
||||
self.constraint_sccs.scc(c.sup),
|
||||
self.constraint_sccs.scc(c.sub),
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// Classify each of the constraints along the path.
|
||||
let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path
|
||||
.iter()
|
||||
.map(|constraint| {
|
||||
if constraint.category == ConstraintCategory::ClosureBounds {
|
||||
self.retrieve_closure_constraint_info(body, &constraint)
|
||||
} else {
|
||||
(constraint.category, false, constraint.locations.span(body))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
debug!("best_blame_constraint: categorized_path={:#?}", categorized_path);
|
||||
|
||||
// To find the best span to cite, we first try to look for the
|
||||
// final constraint that is interesting and where the `sup` is
|
||||
// not unified with the ultimate target region. The reason
|
||||
// for this is that we have a chain of constraints that lead
|
||||
// from the source to the target region, something like:
|
||||
//
|
||||
// '0: '1 ('0 is the source)
|
||||
// '1: '2
|
||||
// '2: '3
|
||||
// '3: '4
|
||||
// '4: '5
|
||||
// '5: '6 ('6 is the target)
|
||||
//
|
||||
// Some of those regions are unified with `'6` (in the same
|
||||
// SCC). We want to screen those out. After that point, the
|
||||
// "closest" constraint we have to the end is going to be the
|
||||
// most likely to be the point where the value escapes -- but
|
||||
// we still want to screen for an "interesting" point to
|
||||
// highlight (e.g., a call site or something).
|
||||
let target_scc = self.constraint_sccs.scc(target_region);
|
||||
let mut range = 0..path.len();
|
||||
|
||||
// As noted above, when reporting an error, there is typically a chain of constraints
|
||||
// leading from some "source" region which must outlive some "target" region.
|
||||
// In most cases, we prefer to "blame" the constraints closer to the target --
|
||||
// but there is one exception. When constraints arise from higher-ranked subtyping,
|
||||
// we generally prefer to blame the source value,
|
||||
// as the "target" in this case tends to be some type annotation that the user gave.
|
||||
// Therefore, if we find that the region origin is some instantiation
|
||||
// of a higher-ranked region, we start our search from the "source" point
|
||||
// rather than the "target", and we also tweak a few other things.
|
||||
//
|
||||
// An example might be this bit of Rust code:
|
||||
//
|
||||
// ```rust
|
||||
// let x: fn(&'static ()) = |_| {};
|
||||
// let y: for<'a> fn(&'a ()) = x;
|
||||
// ```
|
||||
//
|
||||
// In MIR, this will be converted into a combination of assignments and type ascriptions.
|
||||
// In particular, the 'static is imposed through a type ascription:
|
||||
//
|
||||
// ```rust
|
||||
// x = ...;
|
||||
// AscribeUserType(x, fn(&'static ())
|
||||
// y = x;
|
||||
// ```
|
||||
//
|
||||
// We wind up ultimately with constraints like
|
||||
//
|
||||
// ```rust
|
||||
// !a: 'temp1 // from the `y = x` statement
|
||||
// 'temp1: 'temp2
|
||||
// 'temp2: 'static // from the AscribeUserType
|
||||
// ```
|
||||
//
|
||||
// and here we prefer to blame the source (the y = x statement).
|
||||
let blame_source = match from_region_origin {
|
||||
NLLRegionVariableOrigin::FreeRegion
|
||||
| NLLRegionVariableOrigin::Existential { from_forall: false } => true,
|
||||
NLLRegionVariableOrigin::Placeholder(_)
|
||||
| NLLRegionVariableOrigin::Existential { from_forall: true } => false,
|
||||
};
|
||||
|
||||
let find_region = |i: &usize| {
|
||||
let constraint = path[*i];
|
||||
|
||||
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
|
||||
|
||||
if blame_source {
|
||||
match categorized_path[*i].0 {
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal => false,
|
||||
ConstraintCategory::TypeAnnotation
|
||||
| ConstraintCategory::Return
|
||||
| ConstraintCategory::Yield => true,
|
||||
_ => constraint_sup_scc != target_scc,
|
||||
}
|
||||
} else {
|
||||
match categorized_path[*i].0 {
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let best_choice =
|
||||
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
|
||||
|
||||
debug!(
|
||||
"best_blame_constraint: best_choice={:?} blame_source={}",
|
||||
best_choice, blame_source
|
||||
);
|
||||
|
||||
if let Some(i) = best_choice {
|
||||
if let Some(next) = categorized_path.get(i + 1) {
|
||||
if categorized_path[i].0 == ConstraintCategory::Return
|
||||
&& next.0 == ConstraintCategory::OpaqueType
|
||||
/// Returns `true` if a closure is inferred to be an `FnMut` closure.
|
||||
fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
|
||||
if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
|
||||
if let ty::BoundRegion::BrEnv = free_region.bound_region {
|
||||
if let DefiningTy::Closure(def_id, substs) =
|
||||
self.regioncx.universal_regions().defining_ty
|
||||
{
|
||||
// The return expression is being influenced by the return type being
|
||||
// impl Trait, point at the return type and not the return expr.
|
||||
return *next;
|
||||
return substs.as_closure().kind(def_id, self.infcx.tcx)
|
||||
== ty::ClosureKind::FnMut;
|
||||
}
|
||||
}
|
||||
return categorized_path[i];
|
||||
}
|
||||
|
||||
// If that search fails, that is.. unusual. Maybe everything
|
||||
// is in the same SCC or something. In that case, find what
|
||||
// appears to be the most interesting point to report to the
|
||||
// user via an even more ad-hoc guess.
|
||||
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
|
||||
debug!("`: sorted_path={:#?}", categorized_path);
|
||||
|
||||
*categorized_path.first().unwrap()
|
||||
false
|
||||
}
|
||||
|
||||
/// Walks the graph of constraints (where `'a: 'b` is considered
|
||||
/// an edge `'a -> 'b`) to find all paths from `from_region` to
|
||||
/// `to_region`. The paths are accumulated into the vector
|
||||
/// `results`. The paths are stored as a series of
|
||||
/// `ConstraintIndex` values -- in other words, a list of *edges*.
|
||||
///
|
||||
/// Returns: a series of constraints as well as the region `R`
|
||||
/// that passed the target test.
|
||||
fn find_constraint_paths_between_regions(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> Option<(Vec<OutlivesConstraint>, RegionVid)> {
|
||||
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
|
||||
context[from_region] = Trace::StartRegion;
|
||||
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
|
||||
pub(in crate::borrow_check) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
|
||||
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
|
||||
// buffered in the `MirBorrowckCtxt`.
|
||||
|
||||
// Use a deque so that we do a breadth-first search. We will
|
||||
// stop at the first match, which ought to be the shortest
|
||||
// path (fewest constraints).
|
||||
let mut deque = VecDeque::new();
|
||||
deque.push_back(from_region);
|
||||
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
|
||||
|
||||
while let Some(r) = deque.pop_front() {
|
||||
debug!(
|
||||
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
|
||||
from_region,
|
||||
r,
|
||||
self.region_value_str(r),
|
||||
);
|
||||
for nll_error in nll_errors.into_iter() {
|
||||
match nll_error {
|
||||
RegionErrorKind::TypeTestError { type_test } => {
|
||||
// Try to convert the lower-bound region into something named we can print for the user.
|
||||
let lower_bound_region = self.to_error_region(type_test.lower_bound);
|
||||
|
||||
// Check if we reached the region we were looking for. If so,
|
||||
// we can reconstruct the path that led to it and return it.
|
||||
if target_test(r) {
|
||||
let mut result = vec![];
|
||||
let mut p = r;
|
||||
loop {
|
||||
match context[p] {
|
||||
Trace::NotVisited => {
|
||||
bug!("found unvisited region {:?} on path to {:?}", p, r)
|
||||
}
|
||||
let type_test_span = type_test.locations.span(&self.body);
|
||||
|
||||
Trace::FromOutlivesConstraint(c) => {
|
||||
result.push(c);
|
||||
p = c.sup;
|
||||
}
|
||||
if let Some(lower_bound_region) = lower_bound_region {
|
||||
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
|
||||
self.infcx
|
||||
.construct_generic_bound_failure(
|
||||
region_scope_tree,
|
||||
type_test_span,
|
||||
None,
|
||||
type_test.generic_kind,
|
||||
lower_bound_region,
|
||||
)
|
||||
.buffer(&mut self.errors_buffer);
|
||||
} else {
|
||||
// FIXME. We should handle this case better. It
|
||||
// indicates that we have e.g., some region variable
|
||||
// whose value is like `'a+'b` where `'a` and `'b` are
|
||||
// distinct unrelated univesal regions that are not
|
||||
// known to outlive one another. It'd be nice to have
|
||||
// some examples where this arises to decide how best
|
||||
// to report it; we could probably handle it by
|
||||
// iterating over the universal regions and reporting
|
||||
// an error that multiple bounds are required.
|
||||
self.infcx
|
||||
.tcx
|
||||
.sess
|
||||
.struct_span_err(
|
||||
type_test_span,
|
||||
&format!("`{}` does not live long enough", type_test.generic_kind),
|
||||
)
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
Trace::StartRegion => {
|
||||
result.reverse();
|
||||
return Some((result, r));
|
||||
}
|
||||
RegionErrorKind::UnexpectedHiddenRegion {
|
||||
opaque_type_def_id,
|
||||
hidden_ty,
|
||||
member_region,
|
||||
} => {
|
||||
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
|
||||
opaque_types::unexpected_hidden_region_diagnostic(
|
||||
self.infcx.tcx,
|
||||
Some(region_scope_tree),
|
||||
opaque_type_def_id,
|
||||
hidden_ty,
|
||||
member_region,
|
||||
)
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
RegionErrorKind::BoundUniversalRegionError {
|
||||
longer_fr,
|
||||
fr_origin,
|
||||
error_element,
|
||||
} => {
|
||||
let error_region = self.regioncx.region_from_element(longer_fr, error_element);
|
||||
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let (_, span) = self.regioncx.find_outlives_blame_span(
|
||||
&self.body,
|
||||
longer_fr,
|
||||
fr_origin,
|
||||
error_region,
|
||||
);
|
||||
|
||||
// FIXME: improve this error message
|
||||
self.infcx
|
||||
.tcx
|
||||
.sess
|
||||
.struct_span_err(span, "higher-ranked subtype error")
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
|
||||
if is_reported {
|
||||
self.report_region_error(
|
||||
longer_fr,
|
||||
fr_origin,
|
||||
shorter_fr,
|
||||
&mut outlives_suggestion,
|
||||
);
|
||||
} else {
|
||||
// We only report the first error, so as not to overwhelm the user. See
|
||||
// `RegRegionErrorKind` docs.
|
||||
//
|
||||
// FIXME: currently we do nothing with these, but perhaps we can do better?
|
||||
// FIXME: try collecting these constraints on the outlives suggestion
|
||||
// builder. Does it make the suggestions any better?
|
||||
debug!(
|
||||
"Unreported region error: can't prove that {:?}: {:?}",
|
||||
longer_fr, shorter_fr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, walk over the outgoing constraints and
|
||||
// enqueue any regions we find, keeping track of how we
|
||||
// reached them.
|
||||
|
||||
// A constraint like `'r: 'x` can come from our constraint
|
||||
// graph.
|
||||
let fr_static = self.universal_regions.fr_static;
|
||||
let outgoing_edges_from_graph =
|
||||
self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
|
||||
|
||||
// Always inline this closure because it can be hot.
|
||||
let mut handle_constraint = #[inline(always)]
|
||||
|constraint: OutlivesConstraint| {
|
||||
debug_assert_eq!(constraint.sup, r);
|
||||
let sub_region = constraint.sub;
|
||||
if let Trace::NotVisited = context[sub_region] {
|
||||
context[sub_region] = Trace::FromOutlivesConstraint(constraint);
|
||||
deque.push_back(sub_region);
|
||||
}
|
||||
};
|
||||
|
||||
// This loop can be hot.
|
||||
for constraint in outgoing_edges_from_graph {
|
||||
handle_constraint(constraint);
|
||||
}
|
||||
|
||||
// Member constraints can also give rise to `'r: 'x` edges that
|
||||
// were not part of the graph initially, so watch out for those.
|
||||
// (But they are extremely rare; this loop is very cold.)
|
||||
for constraint in self.applied_member_constraints(r) {
|
||||
let p_c = &self.member_constraints[constraint.member_constraint_index];
|
||||
let constraint = OutlivesConstraint {
|
||||
sup: r,
|
||||
sub: constraint.min_choice,
|
||||
locations: Locations::All(p_c.definition_span),
|
||||
category: ConstraintCategory::OpaqueType,
|
||||
};
|
||||
handle_constraint(constraint);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
// Emit one outlives suggestions for each MIR def we borrowck
|
||||
outlives_suggestion.add_suggestion(self);
|
||||
}
|
||||
|
||||
/// Report an error because the universal region `fr` was required to outlive
|
||||
@ -429,38 +269,38 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// ```
|
||||
///
|
||||
/// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
|
||||
pub(in crate::borrow_check) fn report_error<'a>(
|
||||
&'a self,
|
||||
mbcx: &MirBorrowckCtxt<'a, 'tcx>,
|
||||
pub(in crate::borrow_check) fn report_region_error(
|
||||
&mut self,
|
||||
fr: RegionVid,
|
||||
fr_origin: NLLRegionVariableOrigin,
|
||||
outlived_fr: RegionVid,
|
||||
outlives_suggestion: &mut OutlivesSuggestionBuilder,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> DiagnosticBuilder<'a> {
|
||||
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
) {
|
||||
debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
|
||||
|
||||
let (category, _, span) = self.best_blame_constraint(&mbcx.body, fr, fr_origin, |r| {
|
||||
self.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
let (category, _, span) =
|
||||
self.regioncx.best_blame_constraint(&self.body, fr, fr_origin, |r| {
|
||||
self.regioncx.provides_universal_region(r, fr, outlived_fr)
|
||||
});
|
||||
|
||||
debug!("report_error: category={:?} {:?}", category, span);
|
||||
debug!("report_region_error: category={:?} {:?}", category, span);
|
||||
// Check if we can use one of the "nice region errors".
|
||||
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
|
||||
let tables = mbcx.infcx.tcx.typeck_tables_of(mbcx.mir_def_id);
|
||||
let nice = NiceRegionError::new_from_span(mbcx.infcx, span, o, f, Some(tables));
|
||||
let tables = self.infcx.tcx.typeck_tables_of(self.mir_def_id);
|
||||
let nice = NiceRegionError::new_from_span(self.infcx, span, o, f, Some(tables));
|
||||
if let Some(diag) = nice.try_report_from_nll() {
|
||||
return diag;
|
||||
diag.buffer(&mut self.errors_buffer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
|
||||
self.universal_regions.is_local_free_region(fr),
|
||||
self.universal_regions.is_local_free_region(outlived_fr),
|
||||
self.regioncx.universal_regions().is_local_free_region(fr),
|
||||
self.regioncx.universal_regions().is_local_free_region(outlived_fr),
|
||||
);
|
||||
|
||||
debug!(
|
||||
"report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
|
||||
"report_region_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
|
||||
fr_is_local, outlived_fr_is_local, category
|
||||
);
|
||||
|
||||
@ -473,52 +313,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
span,
|
||||
};
|
||||
|
||||
match (category, fr_is_local, outlived_fr_is_local) {
|
||||
(ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(mbcx.infcx, fr) => {
|
||||
self.report_fnmut_error(mbcx, &errci, renctx)
|
||||
let diag = match (category, fr_is_local, outlived_fr_is_local) {
|
||||
(ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(fr) => {
|
||||
self.report_fnmut_error(&errci)
|
||||
}
|
||||
(ConstraintCategory::Assignment, true, false)
|
||||
| (ConstraintCategory::CallArgument, true, false) => {
|
||||
let mut db = self.report_escaping_data_error(mbcx, &errci, renctx);
|
||||
let mut db = self.report_escaping_data_error(&errci);
|
||||
|
||||
outlives_suggestion.intermediate_suggestion(mbcx, &errci, renctx, &mut db);
|
||||
outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
|
||||
outlives_suggestion.collect_constraint(fr, outlived_fr);
|
||||
|
||||
db
|
||||
}
|
||||
_ => {
|
||||
let mut db = self.report_general_error(mbcx, &errci, renctx);
|
||||
let mut db = self.report_general_error(&errci);
|
||||
|
||||
outlives_suggestion.intermediate_suggestion(mbcx, &errci, renctx, &mut db);
|
||||
outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
|
||||
outlives_suggestion.collect_constraint(fr, outlived_fr);
|
||||
|
||||
db
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We have a constraint `fr1: fr2` that is not satisfied, where
|
||||
/// `fr2` represents some universal region. Here, `r` is some
|
||||
/// region where we know that `fr1: r` and this function has the
|
||||
/// job of determining whether `r` is "to blame" for the fact that
|
||||
/// `fr1: fr2` is required.
|
||||
///
|
||||
/// This is true under two conditions:
|
||||
///
|
||||
/// - `r == fr2`
|
||||
/// - `fr2` is `'static` and `r` is some placeholder in a universe
|
||||
/// that cannot be named by `fr1`; in that case, we will require
|
||||
/// that `fr1: 'static` because it is the only way to `fr1: r` to
|
||||
/// be satisfied. (See `add_incompatible_universe`.)
|
||||
fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid) -> bool {
|
||||
debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
|
||||
let result = {
|
||||
r == fr2 || {
|
||||
fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
|
||||
}
|
||||
};
|
||||
debug!("provides_universal_region: result = {:?}", result);
|
||||
result
|
||||
|
||||
diag.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
/// Report a specialized error when `FnMut` closures return a reference to a captured variable.
|
||||
@ -537,15 +355,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// executing...
|
||||
/// = note: ...therefore, returned references to captured variables will escape the closure
|
||||
/// ```
|
||||
fn report_fnmut_error(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
errci: &ErrorConstraintInfo,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
fn report_fnmut_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
|
||||
let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
|
||||
|
||||
let mut diag = mbcx
|
||||
let mut diag = self
|
||||
.infcx
|
||||
.tcx
|
||||
.sess
|
||||
@ -553,7 +366,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
// We should check if the return type of this closure is in fact a closure - in that
|
||||
// case, we can special case the error further.
|
||||
let return_type_is_closure = self.universal_regions.unnormalized_output_ty.is_closure();
|
||||
let return_type_is_closure =
|
||||
self.regioncx.universal_regions().unnormalized_output_ty.is_closure();
|
||||
let message = if return_type_is_closure {
|
||||
"returns a closure that contains a reference to a captured variable, which then \
|
||||
escapes the closure body"
|
||||
@ -563,7 +377,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
diag.span_label(*span, message);
|
||||
|
||||
match self.give_region_a_name(mbcx, renctx, *outlived_fr).unwrap().source {
|
||||
match self.give_region_a_name(*outlived_fr).unwrap().source {
|
||||
RegionNameSource::NamedEarlyBoundRegion(fr_span)
|
||||
| RegionNameSource::NamedFreeRegion(fr_span)
|
||||
| RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _)
|
||||
@ -598,30 +412,25 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// LL | ref_obj(x)
|
||||
/// | ^^^^^^^^^^ `x` escapes the function body here
|
||||
/// ```
|
||||
fn report_escaping_data_error(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
errci: &ErrorConstraintInfo,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
|
||||
let ErrorConstraintInfo { span, category, .. } = errci;
|
||||
|
||||
let fr_name_and_span = self.get_var_name_and_span_for_region(
|
||||
mbcx.infcx.tcx,
|
||||
&mbcx.body,
|
||||
&mbcx.local_names,
|
||||
&mbcx.upvars,
|
||||
let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
&self.local_names,
|
||||
&self.upvars,
|
||||
errci.fr,
|
||||
);
|
||||
let outlived_fr_name_and_span = self.get_var_name_and_span_for_region(
|
||||
mbcx.infcx.tcx,
|
||||
&mbcx.body,
|
||||
&mbcx.local_names,
|
||||
&mbcx.upvars,
|
||||
let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
|
||||
self.infcx.tcx,
|
||||
&self.body,
|
||||
&self.local_names,
|
||||
&self.upvars,
|
||||
errci.outlived_fr,
|
||||
);
|
||||
|
||||
let escapes_from = match self.universal_regions.defining_ty {
|
||||
let escapes_from = match self.regioncx.universal_regions().defining_ty {
|
||||
DefiningTy::Closure(..) => "closure",
|
||||
DefiningTy::Generator(..) => "generator",
|
||||
DefiningTy::FnDef(..) => "function",
|
||||
@ -634,15 +443,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|| (*category == ConstraintCategory::Assignment && escapes_from == "function")
|
||||
|| escapes_from == "const"
|
||||
{
|
||||
return self.report_general_error(
|
||||
mbcx,
|
||||
&ErrorConstraintInfo { fr_is_local: true, outlived_fr_is_local: false, ..*errci },
|
||||
renctx,
|
||||
);
|
||||
return self.report_general_error(&ErrorConstraintInfo {
|
||||
fr_is_local: true,
|
||||
outlived_fr_is_local: false,
|
||||
..*errci
|
||||
});
|
||||
}
|
||||
|
||||
let mut diag =
|
||||
borrowck_errors::borrowed_data_escapes_closure(mbcx.infcx.tcx, *span, escapes_from);
|
||||
borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from);
|
||||
|
||||
if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
|
||||
diag.span_label(
|
||||
@ -684,12 +493,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
|
||||
/// | is returning data with lifetime `'b`
|
||||
/// ```
|
||||
fn report_general_error(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
errci: &ErrorConstraintInfo,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> DiagnosticBuilder<'_> {
|
||||
fn report_general_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
|
||||
let ErrorConstraintInfo {
|
||||
fr,
|
||||
fr_is_local,
|
||||
@ -701,14 +505,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
} = errci;
|
||||
|
||||
let mut diag =
|
||||
mbcx.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough");
|
||||
self.infcx.tcx.sess.struct_span_err(*span, "lifetime may not live long enough");
|
||||
|
||||
let mir_def_name =
|
||||
if mbcx.infcx.tcx.is_closure(mbcx.mir_def_id) { "closure" } else { "function" };
|
||||
if self.infcx.tcx.is_closure(self.mir_def_id) { "closure" } else { "function" };
|
||||
|
||||
let fr_name = self.give_region_a_name(mbcx, renctx, *fr).unwrap();
|
||||
let fr_name = self.give_region_a_name(*fr).unwrap();
|
||||
fr_name.highlight_region_name(&mut diag);
|
||||
let outlived_fr_name = self.give_region_a_name(mbcx, renctx, *outlived_fr).unwrap();
|
||||
let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
|
||||
outlived_fr_name.highlight_region_name(&mut diag);
|
||||
|
||||
match (category, outlived_fr_is_local, fr_is_local) {
|
||||
@ -735,7 +539,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
self.add_static_impl_trait_suggestion(mbcx.infcx, &mut diag, *fr, fr_name, *outlived_fr);
|
||||
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
|
||||
|
||||
diag
|
||||
}
|
||||
@ -751,8 +555,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// ```
|
||||
fn add_static_impl_trait_suggestion(
|
||||
&self,
|
||||
infcx: &InferCtxt<'_, 'tcx>,
|
||||
diag: &mut DiagnosticBuilder<'_>,
|
||||
diag: &mut DiagnosticBuilder<'tcx>,
|
||||
fr: RegionVid,
|
||||
// We need to pass `fr_name` - computing it again will label it twice.
|
||||
fr_name: RegionName,
|
||||
@ -761,11 +564,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
if let (Some(f), Some(ty::RegionKind::ReStatic)) =
|
||||
(self.to_error_region(fr), self.to_error_region(outlived_fr))
|
||||
{
|
||||
if let Some((ty::TyS { kind: ty::Opaque(did, substs), .. }, _)) = infcx
|
||||
if let Some((ty::TyS { kind: ty::Opaque(did, substs), .. }, _)) = self
|
||||
.infcx
|
||||
.tcx
|
||||
.is_suitable_region(f)
|
||||
.map(|r| r.def_id)
|
||||
.map(|id| infcx.tcx.return_type_impl_trait(id))
|
||||
.map(|id| self.infcx.tcx.return_type_impl_trait(id))
|
||||
.unwrap_or(None)
|
||||
{
|
||||
// Check whether or not the impl trait return type is intended to capture
|
||||
@ -773,8 +577,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
//
|
||||
// eg. check for `impl Trait + 'static` instead of `impl Trait`.
|
||||
let has_static_predicate = {
|
||||
let predicates_of = infcx.tcx.predicates_of(*did);
|
||||
let bounds = predicates_of.instantiate(infcx.tcx, substs);
|
||||
let predicates_of = self.infcx.tcx.predicates_of(*did);
|
||||
let bounds = predicates_of.instantiate(self.infcx.tcx, substs);
|
||||
|
||||
let mut found = false;
|
||||
for predicate in bounds.predicates {
|
||||
@ -802,8 +606,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
diag.help(&format!("consider replacing `{}` with `{}`", fr_name, static_str));
|
||||
} else {
|
||||
// Otherwise, we should suggest adding a constraint on the return type.
|
||||
let span = infcx.tcx.def_span(*did);
|
||||
if let Ok(snippet) = infcx.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let span = self.infcx.tcx.def_span(*did);
|
||||
if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let suggestable_fr_name = if fr_name.was_named() {
|
||||
fr_name.to_string()
|
||||
} else {
|
||||
@ -830,130 +634,4 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate fn free_region_constraint_info(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
borrow_region: RegionVid,
|
||||
outlived_region: RegionVid,
|
||||
) -> (ConstraintCategory, bool, Span, Option<RegionName>) {
|
||||
let (category, from_closure, span) = self.best_blame_constraint(
|
||||
&mbcx.body,
|
||||
borrow_region,
|
||||
NLLRegionVariableOrigin::FreeRegion,
|
||||
|r| self.provides_universal_region(r, borrow_region, outlived_region),
|
||||
);
|
||||
|
||||
let mut renctx = RegionErrorNamingCtx::new();
|
||||
let outlived_fr_name = self.give_region_a_name(mbcx, &mut renctx, outlived_region);
|
||||
|
||||
(category, from_closure, span, outlived_fr_name)
|
||||
}
|
||||
|
||||
// Finds some region R such that `fr1: R` and `R` is live at
|
||||
// `elem`.
|
||||
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
|
||||
debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
|
||||
debug!(
|
||||
"find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
|
||||
r,
|
||||
self.liveness_constraints.region_value_str(r),
|
||||
);
|
||||
self.liveness_constraints.contains(r, elem)
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find that, we may find some `r` such that
|
||||
// `fr1: r` and `r` is a placeholder from some universe
|
||||
// `fr1` cannot name. This would force `fr1` to be
|
||||
// `'static`.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.cannot_name_placeholder(fr1, r)
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find THAT, it may be that `fr1` is a
|
||||
// placeholder that cannot "fit" into its SCC. In that
|
||||
// case, there should be some `r` where `fr1: r`, both
|
||||
// `fr1` and `r` are in the same SCC, and `fr1` is a
|
||||
// placeholder that `r` cannot name. We can blame that
|
||||
// edge.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
|
||||
&& self.cannot_name_placeholder(r, fr1)
|
||||
})
|
||||
})
|
||||
.map(|(_path, r)| r)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
// Finds a good span to blame for the fact that `fr1` outlives `fr2`.
|
||||
crate fn find_outlives_blame_span(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
fr1: RegionVid,
|
||||
fr1_origin: NLLRegionVariableOrigin,
|
||||
fr2: RegionVid,
|
||||
) -> (ConstraintCategory, Span) {
|
||||
let (category, _, span) = self.best_blame_constraint(body, fr1, fr1_origin, |r| {
|
||||
self.provides_universal_region(r, fr1, fr2)
|
||||
});
|
||||
(category, span)
|
||||
}
|
||||
|
||||
fn retrieve_closure_constraint_info(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
constraint: &OutlivesConstraint,
|
||||
) -> (ConstraintCategory, bool, Span) {
|
||||
let loc = match constraint.locations {
|
||||
Locations::All(span) => return (constraint.category, false, span),
|
||||
Locations::Single(loc) => loc,
|
||||
};
|
||||
|
||||
let opt_span_category =
|
||||
self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
|
||||
opt_span_category.map(|&(category, span)| (category, true, span)).unwrap_or((
|
||||
constraint.category,
|
||||
false,
|
||||
body.source_info(loc).span,
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns `true` if a closure is inferred to be an `FnMut` closure.
|
||||
crate fn is_closure_fn_mut(&self, infcx: &InferCtxt<'_, 'tcx>, fr: RegionVid) -> bool {
|
||||
if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
|
||||
if let ty::BoundRegion::BrEnv = free_region.bound_region {
|
||||
if let DefiningTy::Closure(def_id, substs) = self.universal_regions.defining_ty {
|
||||
let closure_kind_ty = substs.as_closure().kind_ty(def_id, infcx.tcx);
|
||||
return Some(ty::ClosureKind::FnMut) == closure_kind_ty.to_opt_closure_kind();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// If `r2` represents a placeholder region, then this returns
|
||||
/// `true` if `r1` cannot name that placeholder in its
|
||||
/// value; otherwise, returns `false`.
|
||||
fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
|
||||
debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
|
||||
|
||||
match self.definitions[r2].origin {
|
||||
NLLRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
let universe1 = self.definitions[r1].universe;
|
||||
debug!(
|
||||
"cannot_name_value_of: universe1={:?} placeholder={:?}",
|
||||
universe1, placeholder
|
||||
);
|
||||
universe1.cannot_name(placeholder.universe)
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,14 @@ use std::fmt::{self, Display};
|
||||
|
||||
use rustc::ty::print::RegionHighlightMode;
|
||||
use rustc::ty::subst::{GenericArgKind, SubstsRef};
|
||||
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc::ty::{self, RegionVid, Ty};
|
||||
use rustc_errors::DiagnosticBuilder;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::{symbol::Symbol, Span, DUMMY_SP};
|
||||
|
||||
use crate::borrow_check::{
|
||||
nll::ToRegionVid, region_infer::RegionInferenceContext, universal_regions::DefiningTy,
|
||||
MirBorrowckCtxt,
|
||||
};
|
||||
use crate::borrow_check::{nll::ToRegionVid, universal_regions::DefiningTy, MirBorrowckCtxt};
|
||||
|
||||
/// A name for a particular region used in emitting diagnostics. This name could be a generated
|
||||
/// name like `'1`, a name used by the user like `'a`, or a name like `'static`.
|
||||
@ -55,46 +51,6 @@ crate enum RegionNameSource {
|
||||
AnonRegionFromAsyncFn(Span),
|
||||
}
|
||||
|
||||
/// Records region names that have been assigned before so that we can use the same ones in later
|
||||
/// diagnostics.
|
||||
#[derive(Debug, Clone)]
|
||||
crate struct RegionErrorNamingCtx {
|
||||
/// Record the region names generated for each region in the given
|
||||
/// MIR def so that we can reuse them later in help/error messages.
|
||||
renctx: FxHashMap<RegionVid, RegionName>,
|
||||
|
||||
/// The counter for generating new region names.
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl RegionErrorNamingCtx {
|
||||
crate fn new() -> Self {
|
||||
Self { counter: 1, renctx: FxHashMap::default() }
|
||||
}
|
||||
|
||||
/// Get the name of `region` if it has previously been named.
|
||||
crate fn get(&self, region: &RegionVid) -> Option<&RegionName> {
|
||||
self.renctx.get(region)
|
||||
}
|
||||
|
||||
/// Give `region` the name `name`.
|
||||
crate fn insert(&mut self, region: RegionVid, name: RegionName) {
|
||||
self.renctx.insert(region, name);
|
||||
}
|
||||
|
||||
/// Creates a synthetic region named `'N`, where `N` is the next value of the counter. Then,
|
||||
/// increment the counter.
|
||||
///
|
||||
/// The name is not memoized. A separate call to `insert` should be made later. (Currently,
|
||||
/// this happens at the end of `give_region_a_name`).
|
||||
crate fn synthesize_region_name(&mut self) -> Symbol {
|
||||
let c = self.counter;
|
||||
self.counter += 1;
|
||||
|
||||
Symbol::intern(&format!("'{:?}", c))
|
||||
}
|
||||
}
|
||||
|
||||
impl RegionName {
|
||||
crate fn was_named(&self) -> bool {
|
||||
match self.source {
|
||||
@ -161,7 +117,16 @@ impl Display for RegionName {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
/// Generate a synthetic region named `'N`, where `N` is the next value of the counter. Then,
|
||||
/// increment the counter.
|
||||
///
|
||||
/// This is _not_ idempotent. Call `give_region_a_name` when possible.
|
||||
fn synthesize_region_name(&self) -> Symbol {
|
||||
let c = self.next_region_name.replace_with(|counter| *counter + 1);
|
||||
Symbol::intern(&format!("'{:?}", c))
|
||||
}
|
||||
|
||||
/// Maps from an internal MIR region vid to something that we can
|
||||
/// report to the user. In some cases, the region vids will map
|
||||
/// directly to lifetimes that the user has a name for (e.g.,
|
||||
@ -170,6 +135,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// that end, this function takes a "diagnostic" so that it can
|
||||
/// create auxiliary notes as needed.
|
||||
///
|
||||
/// The names are memoized, so this is both cheap to recompute and idempotent.
|
||||
///
|
||||
/// Example (function arguments):
|
||||
///
|
||||
/// Suppose we are trying to give a name to the lifetime of the
|
||||
@ -187,29 +154,28 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// ```
|
||||
///
|
||||
/// and then return the name `'1` for us to use.
|
||||
crate fn give_region_a_name(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
fr: RegionVid,
|
||||
) -> Option<RegionName> {
|
||||
debug!("give_region_a_name(fr={:?}, counter={:?})", fr, renctx.counter);
|
||||
crate fn give_region_a_name(&self, fr: RegionVid) -> Option<RegionName> {
|
||||
debug!(
|
||||
"give_region_a_name(fr={:?}, counter={:?})",
|
||||
fr,
|
||||
self.next_region_name.try_borrow().unwrap()
|
||||
);
|
||||
|
||||
assert!(self.universal_regions.is_universal_region(fr));
|
||||
assert!(self.regioncx.universal_regions().is_universal_region(fr));
|
||||
|
||||
if let Some(value) = renctx.get(&fr) {
|
||||
if let Some(value) = self.region_names.try_borrow_mut().unwrap().get(&fr) {
|
||||
return Some(value.clone());
|
||||
}
|
||||
|
||||
let value = self
|
||||
.give_name_from_error_region(mbcx, fr, renctx)
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_arguments(mbcx, fr, renctx))
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(mbcx, fr, renctx))
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_output(mbcx, fr, renctx))
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(mbcx, fr, renctx));
|
||||
.give_name_from_error_region(fr)
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_arguments(fr))
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(fr))
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_output(fr))
|
||||
.or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr));
|
||||
|
||||
if let Some(ref value) = value {
|
||||
renctx.insert(fr, value.clone());
|
||||
self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone());
|
||||
}
|
||||
|
||||
debug!("give_region_a_name: gave name {:?}", value);
|
||||
@ -220,15 +186,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// *user* has a name for. In that case, we'll be able to map
|
||||
/// `fr` to a `Region<'tcx>`, and that region will be one of
|
||||
/// named variants.
|
||||
fn give_name_from_error_region(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
fr: RegionVid,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
|
||||
let error_region = self.to_error_region(fr)?;
|
||||
|
||||
let tcx = mbcx.infcx.tcx;
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
debug!("give_region_a_name: error_region = {:?}", error_region);
|
||||
match error_region {
|
||||
@ -267,7 +228,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// happen if we have an elided name in an async fn for example: the
|
||||
// compiler will generate a region named `'_`, but reporting such a name is
|
||||
// not actually useful, so we synthesize a name for it instead.
|
||||
let name = renctx.synthesize_region_name();
|
||||
let name = self.synthesize_region_name();
|
||||
Some(RegionName {
|
||||
name,
|
||||
source: RegionNameSource::AnonRegionFromAsyncFn(span),
|
||||
@ -276,13 +237,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
|
||||
ty::BoundRegion::BrEnv => {
|
||||
let mir_hir_id = mbcx
|
||||
let mir_hir_id = self
|
||||
.infcx
|
||||
.tcx
|
||||
.hir()
|
||||
.as_local_hir_id(mbcx.mir_def_id)
|
||||
.as_local_hir_id(self.mir_def_id)
|
||||
.expect("non-local mir");
|
||||
let def_ty = self.universal_regions.defining_ty;
|
||||
let def_ty = self.regioncx.universal_regions().defining_ty;
|
||||
|
||||
if let DefiningTy::Closure(def_id, substs) = def_ty {
|
||||
let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) =
|
||||
@ -292,7 +253,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
} else {
|
||||
bug!("Closure is not defined by a closure expr");
|
||||
};
|
||||
let region_name = renctx.synthesize_region_name();
|
||||
let region_name = self.synthesize_region_name();
|
||||
|
||||
let closure_kind_ty = substs.as_closure().kind_ty(def_id, tcx);
|
||||
let note = match closure_kind_ty.to_opt_closure_kind() {
|
||||
@ -346,38 +307,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// ```
|
||||
fn give_name_if_anonymous_region_appears_in_arguments(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
fr: RegionVid,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
|
||||
let argument_index = self.get_argument_index_for_region(mbcx.infcx.tcx, fr)?;
|
||||
let implicit_inputs = self.regioncx.universal_regions().defining_ty.implicit_inputs();
|
||||
let argument_index = self.regioncx.get_argument_index_for_region(self.infcx.tcx, fr)?;
|
||||
|
||||
let arg_ty =
|
||||
self.universal_regions.unnormalized_input_tys[implicit_inputs + argument_index];
|
||||
if let Some(region_name) = self.give_name_if_we_can_match_hir_ty_from_argument(
|
||||
mbcx,
|
||||
fr,
|
||||
arg_ty,
|
||||
argument_index,
|
||||
renctx,
|
||||
) {
|
||||
let arg_ty = self.regioncx.universal_regions().unnormalized_input_tys
|
||||
[implicit_inputs + argument_index];
|
||||
if let Some(region_name) =
|
||||
self.give_name_if_we_can_match_hir_ty_from_argument(fr, arg_ty, argument_index)
|
||||
{
|
||||
return Some(region_name);
|
||||
}
|
||||
|
||||
self.give_name_if_we_cannot_match_hir_ty(mbcx, fr, arg_ty, renctx)
|
||||
self.give_name_if_we_cannot_match_hir_ty(fr, arg_ty)
|
||||
}
|
||||
|
||||
fn give_name_if_we_can_match_hir_ty_from_argument(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
needle_fr: RegionVid,
|
||||
argument_ty: Ty<'tcx>,
|
||||
argument_index: usize,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
let mir_hir_id = mbcx.infcx.tcx.hir().as_local_hir_id(mbcx.mir_def_id)?;
|
||||
let fn_decl = mbcx.infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?;
|
||||
let mir_hir_id = self.infcx.tcx.hir().as_local_hir_id(self.mir_def_id)?;
|
||||
let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?;
|
||||
let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?;
|
||||
match argument_hir_ty.kind {
|
||||
// This indicates a variable with no type annotation, like
|
||||
@ -387,13 +340,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// (`give_name_if_anonymous_region_appears_in_arguments`).
|
||||
hir::TyKind::Infer => None,
|
||||
|
||||
_ => self.give_name_if_we_can_match_hir_ty(
|
||||
mbcx.infcx.tcx,
|
||||
needle_fr,
|
||||
argument_ty,
|
||||
argument_hir_ty,
|
||||
renctx,
|
||||
),
|
||||
_ => self.give_name_if_we_can_match_hir_ty(needle_fr, argument_ty, argument_hir_ty),
|
||||
}
|
||||
}
|
||||
|
||||
@ -410,15 +357,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// ```
|
||||
fn give_name_if_we_cannot_match_hir_ty(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
needle_fr: RegionVid,
|
||||
argument_ty: Ty<'tcx>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
let counter = renctx.counter;
|
||||
let counter = *self.next_region_name.try_borrow().unwrap();
|
||||
let mut highlight = RegionHighlightMode::default();
|
||||
highlight.highlighting_region_vid(needle_fr, counter);
|
||||
let type_name = mbcx.infcx.extract_type_name(&argument_ty, Some(highlight)).0;
|
||||
let type_name = self.infcx.extract_type_name(&argument_ty, Some(highlight)).0;
|
||||
|
||||
debug!(
|
||||
"give_name_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}",
|
||||
@ -426,10 +371,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
);
|
||||
let assigned_region_name = if type_name.find(&format!("'{}", counter)).is_some() {
|
||||
// Only add a label if we can confirm that a region was labelled.
|
||||
let argument_index = self.get_argument_index_for_region(mbcx.infcx.tcx, needle_fr)?;
|
||||
let (_, span) = self.get_argument_name_and_span_for_region(
|
||||
&mbcx.body,
|
||||
&mbcx.local_names,
|
||||
let argument_index =
|
||||
self.regioncx.get_argument_index_for_region(self.infcx.tcx, needle_fr)?;
|
||||
let (_, span) = self.regioncx.get_argument_name_and_span_for_region(
|
||||
&self.body,
|
||||
&self.local_names,
|
||||
argument_index,
|
||||
);
|
||||
|
||||
@ -437,7 +383,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// This counter value will already have been used, so this function will increment
|
||||
// it so the next value will be used next and return the region name that would
|
||||
// have been used.
|
||||
name: renctx.synthesize_region_name(),
|
||||
name: self.synthesize_region_name(),
|
||||
source: RegionNameSource::CannotMatchHirTy(span, type_name),
|
||||
})
|
||||
} else {
|
||||
@ -470,11 +416,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// to highlighting that closest type instead.
|
||||
fn give_name_if_we_can_match_hir_ty(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
needle_fr: RegionVid,
|
||||
argument_ty: Ty<'tcx>,
|
||||
argument_hir_ty: &hir::Ty<'_>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty<'_>)> =
|
||||
&mut vec![(argument_ty, argument_hir_ty)];
|
||||
@ -492,10 +436,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
hir::TyKind::Rptr(_lifetime, referent_hir_ty),
|
||||
) => {
|
||||
if region.to_region_vid() == needle_fr {
|
||||
let region_name = renctx.synthesize_region_name();
|
||||
let region_name = self.synthesize_region_name();
|
||||
|
||||
// Just grab the first character, the `&`.
|
||||
let source_map = tcx.sess.source_map();
|
||||
let source_map = self.infcx.tcx.sess.source_map();
|
||||
let ampersand_span = source_map.start_point(hir_ty.span);
|
||||
|
||||
return Some(RegionName {
|
||||
@ -525,7 +469,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
substs,
|
||||
needle_fr,
|
||||
last_segment,
|
||||
renctx,
|
||||
search_stack,
|
||||
) {
|
||||
return Some(name);
|
||||
@ -570,7 +513,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
substs: SubstsRef<'tcx>,
|
||||
needle_fr: RegionVid,
|
||||
last_segment: &'hir hir::PathSegment<'hir>,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>,
|
||||
) -> Option<RegionName> {
|
||||
// Did the user give explicit arguments? (e.g., `Foo<..>`)
|
||||
@ -582,7 +524,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
| hir::LifetimeName::Error
|
||||
| hir::LifetimeName::Static
|
||||
| hir::LifetimeName::Underscore => {
|
||||
let region_name = renctx.synthesize_region_name();
|
||||
let region_name = self.synthesize_region_name();
|
||||
let ampersand_span = lifetime.span;
|
||||
Some(RegionName {
|
||||
name: region_name,
|
||||
@ -663,16 +605,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// | let x = Some(&22);
|
||||
/// - fully elaborated type of `x` is `Option<&'1 u32>`
|
||||
/// ```
|
||||
fn give_name_if_anonymous_region_appears_in_upvars(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
fr: RegionVid,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
let upvar_index = self.get_upvar_index_for_region(mbcx.infcx.tcx, fr)?;
|
||||
let (upvar_name, upvar_span) =
|
||||
self.get_upvar_name_and_span_for_region(mbcx.infcx.tcx, &mbcx.upvars, upvar_index);
|
||||
let region_name = renctx.synthesize_region_name();
|
||||
fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> {
|
||||
let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?;
|
||||
let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region(
|
||||
self.infcx.tcx,
|
||||
&self.upvars,
|
||||
upvar_index,
|
||||
);
|
||||
let region_name = self.synthesize_region_name();
|
||||
|
||||
Some(RegionName {
|
||||
name: region_name,
|
||||
@ -684,25 +624,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// must be a closure since, in a free fn, such an argument would
|
||||
/// have to either also appear in an argument (if using elision)
|
||||
/// or be early bound (named, not in argument).
|
||||
fn give_name_if_anonymous_region_appears_in_output(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
fr: RegionVid,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
let tcx = mbcx.infcx.tcx;
|
||||
fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> {
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
let return_ty = self.universal_regions.unnormalized_output_ty;
|
||||
let return_ty = self.regioncx.universal_regions().unnormalized_output_ty;
|
||||
debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty);
|
||||
if !tcx.any_free_region_meets(&return_ty, |r| r.to_region_vid() == fr) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut highlight = RegionHighlightMode::default();
|
||||
highlight.highlighting_region_vid(fr, renctx.counter);
|
||||
let type_name = mbcx.infcx.extract_type_name(&return_ty, Some(highlight)).0;
|
||||
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
|
||||
let type_name = self.infcx.extract_type_name(&return_ty, Some(highlight)).0;
|
||||
|
||||
let mir_hir_id = tcx.hir().as_local_hir_id(mbcx.mir_def_id).expect("non-local mir");
|
||||
let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id).expect("non-local mir");
|
||||
|
||||
let (return_span, mir_description) = match tcx.hir().get(mir_hir_id) {
|
||||
hir::Node::Expr(hir::Expr {
|
||||
@ -719,14 +654,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
kind: hir::ImplItemKind::Method(method_sig, _),
|
||||
..
|
||||
}) => (method_sig.decl.output.span(), ""),
|
||||
_ => (mbcx.body.span, ""),
|
||||
_ => (self.body.span, ""),
|
||||
};
|
||||
|
||||
Some(RegionName {
|
||||
// This counter value will already have been used, so this function will increment it
|
||||
// so the next value will be used next and return the region name that would have been
|
||||
// used.
|
||||
name: renctx.synthesize_region_name(),
|
||||
name: self.synthesize_region_name(),
|
||||
source: RegionNameSource::AnonRegionFromOutput(
|
||||
return_span,
|
||||
mir_description.to_string(),
|
||||
@ -737,32 +672,30 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
fn give_name_if_anonymous_region_appears_in_yield_ty(
|
||||
&self,
|
||||
mbcx: &MirBorrowckCtxt<'_, 'tcx>,
|
||||
fr: RegionVid,
|
||||
renctx: &mut RegionErrorNamingCtx,
|
||||
) -> Option<RegionName> {
|
||||
// Note: generators from `async fn` yield `()`, so we don't have to
|
||||
// worry about them here.
|
||||
let yield_ty = self.universal_regions.yield_ty?;
|
||||
let yield_ty = self.regioncx.universal_regions().yield_ty?;
|
||||
debug!("give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}", yield_ty,);
|
||||
|
||||
let tcx = mbcx.infcx.tcx;
|
||||
let tcx = self.infcx.tcx;
|
||||
|
||||
if !tcx.any_free_region_meets(&yield_ty, |r| r.to_region_vid() == fr) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut highlight = RegionHighlightMode::default();
|
||||
highlight.highlighting_region_vid(fr, renctx.counter);
|
||||
let type_name = mbcx.infcx.extract_type_name(&yield_ty, Some(highlight)).0;
|
||||
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
|
||||
let type_name = self.infcx.extract_type_name(&yield_ty, Some(highlight)).0;
|
||||
|
||||
let mir_hir_id = tcx.hir().as_local_hir_id(mbcx.mir_def_id).expect("non-local mir");
|
||||
let mir_hir_id = tcx.hir().as_local_hir_id(self.mir_def_id).expect("non-local mir");
|
||||
|
||||
let yield_span = match tcx.hir().get(mir_hir_id) {
|
||||
hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Closure(_, _, _, span, _), ..
|
||||
}) => (tcx.sess.source_map().end_point(*span)),
|
||||
_ => mbcx.body.span,
|
||||
_ => self.body.span,
|
||||
};
|
||||
|
||||
debug!(
|
||||
@ -772,7 +705,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
);
|
||||
|
||||
Some(RegionName {
|
||||
name: renctx.synthesize_region_name(),
|
||||
name: self.synthesize_region_name(),
|
||||
source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name),
|
||||
})
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fr: RegionVid,
|
||||
) -> Option<(Option<Symbol>, Span)> {
|
||||
debug!("get_var_name_and_span_for_region(fr={:?})", fr);
|
||||
assert!(self.universal_regions.is_universal_region(fr));
|
||||
assert!(self.universal_regions().is_universal_region(fr));
|
||||
|
||||
debug!("get_var_name_and_span_for_region: attempting upvar");
|
||||
self.get_upvar_index_for_region(tcx, fr)
|
||||
@ -35,7 +35,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Search the upvars (if any) to find one that references fr. Return its index.
|
||||
crate fn get_upvar_index_for_region(&self, tcx: TyCtxt<'tcx>, fr: RegionVid) -> Option<usize> {
|
||||
let upvar_index =
|
||||
self.universal_regions.defining_ty.upvar_tys(tcx).position(|upvar_ty| {
|
||||
self.universal_regions().defining_ty.upvar_tys(tcx).position(|upvar_ty| {
|
||||
debug!("get_upvar_index_for_region: upvar_ty={:?}", upvar_ty);
|
||||
tcx.any_free_region_meets(&upvar_ty, |r| {
|
||||
let r = r.to_region_vid();
|
||||
@ -44,7 +44,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
})
|
||||
})?;
|
||||
|
||||
let upvar_ty = self.universal_regions.defining_ty.upvar_tys(tcx).nth(upvar_index);
|
||||
let upvar_ty = self.universal_regions().defining_ty.upvar_tys(tcx).nth(upvar_index);
|
||||
|
||||
debug!(
|
||||
"get_upvar_index_for_region: found {:?} in upvar {} which has type {:?}",
|
||||
@ -85,9 +85,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
fr: RegionVid,
|
||||
) -> Option<usize> {
|
||||
let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
|
||||
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
|
||||
let argument_index =
|
||||
self.universal_regions.unnormalized_input_tys.iter().skip(implicit_inputs).position(
|
||||
self.universal_regions().unnormalized_input_tys.iter().skip(implicit_inputs).position(
|
||||
|arg_ty| {
|
||||
debug!("get_argument_index_for_region: arg_ty = {:?}", arg_ty);
|
||||
tcx.any_free_region_meets(arg_ty, |r| r.to_region_vid() == fr)
|
||||
@ -96,7 +96,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
debug!(
|
||||
"get_argument_index_for_region: found {:?} in argument {} which has type {:?}",
|
||||
fr, argument_index, self.universal_regions.unnormalized_input_tys[argument_index],
|
||||
fr,
|
||||
argument_index,
|
||||
self.universal_regions().unnormalized_input_tys[argument_index],
|
||||
);
|
||||
|
||||
Some(argument_index)
|
||||
@ -110,7 +112,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
local_names: &IndexVec<Local, Option<Symbol>>,
|
||||
argument_index: usize,
|
||||
) -> (Option<Symbol>, Span) {
|
||||
let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
|
||||
let implicit_inputs = self.universal_regions().defining_ty.implicit_inputs();
|
||||
let argument_local = Local::new(implicit_inputs + argument_index + 1);
|
||||
debug!("get_argument_name_and_span_for_region: argument_local={:?}", argument_local);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! This query borrow-checks the MIR to (further) ensure it is not broken.
|
||||
|
||||
use rustc::infer::{opaque_types, InferCtxt};
|
||||
use rustc::infer::InferCtxt;
|
||||
use rustc::lint::builtin::MUTABLE_BORROW_RESERVATION_CONFLICT;
|
||||
use rustc::lint::builtin::UNUSED_MUT;
|
||||
use rustc::mir::{
|
||||
@ -11,7 +11,8 @@ use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind};
|
||||
use rustc::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind};
|
||||
use rustc::mir::{Terminator, TerminatorKind};
|
||||
use rustc::ty::query::Providers;
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
use rustc::ty::{self, RegionVid, TyCtxt};
|
||||
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
|
||||
@ -21,6 +22,7 @@ use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::IndexVec;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
@ -39,9 +41,7 @@ use crate::dataflow::{do_dataflow, DebugFormatted};
|
||||
use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces};
|
||||
use crate::transform::MirSource;
|
||||
|
||||
use self::diagnostics::{
|
||||
AccessKind, OutlivesSuggestionBuilder, RegionErrorKind, RegionErrorNamingCtx, RegionErrors,
|
||||
};
|
||||
use self::diagnostics::{AccessKind, RegionName};
|
||||
use self::flows::Flows;
|
||||
use self::location::LocationTable;
|
||||
use self::prefixes::PrefixSet;
|
||||
@ -285,13 +285,15 @@ fn do_mir_borrowck<'a, 'tcx>(
|
||||
move_error_reported: BTreeMap::new(),
|
||||
uninitialized_error_reported: Default::default(),
|
||||
errors_buffer,
|
||||
nonlexical_regioncx: regioncx,
|
||||
regioncx,
|
||||
used_mut: Default::default(),
|
||||
used_mut_upvars: SmallVec::new(),
|
||||
borrow_set,
|
||||
dominators,
|
||||
upvars,
|
||||
local_names,
|
||||
region_names: RefCell::default(),
|
||||
next_region_name: RefCell::new(1),
|
||||
};
|
||||
|
||||
// Compute and report region errors, if any.
|
||||
@ -476,10 +478,9 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
|
||||
/// If the function we're checking is a closure, then we'll need to report back the list of
|
||||
/// mutable upvars that have been used. This field keeps track of them.
|
||||
used_mut_upvars: SmallVec<[Field; 8]>,
|
||||
/// Non-lexical region inference context, if NLL is enabled. This
|
||||
/// contains the results from region inference and lets us e.g.
|
||||
/// Region inference context. This contains the results from region inference and lets us e.g.
|
||||
/// find out which CFG points are contained in each borrow region.
|
||||
nonlexical_regioncx: Rc<RegionInferenceContext<'tcx>>,
|
||||
regioncx: Rc<RegionInferenceContext<'tcx>>,
|
||||
|
||||
/// The set of borrows extracted from the MIR
|
||||
borrow_set: Rc<BorrowSet<'tcx>>,
|
||||
@ -492,6 +493,13 @@ crate struct MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
/// Names of local (user) variables (extracted from `var_debug_info`).
|
||||
local_names: IndexVec<Local, Option<Name>>,
|
||||
|
||||
/// Record the region names generated for each region in the given
|
||||
/// MIR def so that we can reuse them later in help/error messages.
|
||||
region_names: RefCell<FxHashMap<RegionVid, RegionName>>,
|
||||
|
||||
/// The counter for generating new region names.
|
||||
next_region_name: RefCell<usize>,
|
||||
}
|
||||
|
||||
// Check that:
|
||||
@ -631,7 +639,7 @@ impl<'cx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'tcx
|
||||
|
||||
debug!(
|
||||
"visit_terminator_drop \
|
||||
loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}",
|
||||
loc: {:?} term: {:?} drop_place: {:?} drop_place_ty: {:?} span: {:?}",
|
||||
loc, term, drop_place, drop_place_ty, span
|
||||
);
|
||||
|
||||
@ -1465,120 +1473,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// initial reservation.
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
|
||||
fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
|
||||
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
|
||||
// buffered in the `MirBorrowckCtxt`.
|
||||
|
||||
// FIXME(mark-i-m): Would be great to get rid of the naming context.
|
||||
let mut region_naming = RegionErrorNamingCtx::new();
|
||||
let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
|
||||
|
||||
for nll_error in nll_errors.into_iter() {
|
||||
match nll_error {
|
||||
RegionErrorKind::TypeTestDoesNotLiveLongEnough { span, generic } => {
|
||||
// FIXME. We should handle this case better. It
|
||||
// indicates that we have e.g., some region variable
|
||||
// whose value is like `'a+'b` where `'a` and `'b` are
|
||||
// distinct unrelated univesal regions that are not
|
||||
// known to outlive one another. It'd be nice to have
|
||||
// some examples where this arises to decide how best
|
||||
// to report it; we could probably handle it by
|
||||
// iterating over the universal regions and reporting
|
||||
// an error that multiple bounds are required.
|
||||
self.infcx
|
||||
.tcx
|
||||
.sess
|
||||
.struct_span_err(span, &format!("`{}` does not live long enough", generic))
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
RegionErrorKind::TypeTestGenericBoundError {
|
||||
span,
|
||||
generic,
|
||||
lower_bound_region,
|
||||
} => {
|
||||
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
|
||||
self.infcx
|
||||
.construct_generic_bound_failure(
|
||||
region_scope_tree,
|
||||
span,
|
||||
None,
|
||||
generic,
|
||||
lower_bound_region,
|
||||
)
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
RegionErrorKind::UnexpectedHiddenRegion {
|
||||
opaque_type_def_id,
|
||||
hidden_ty,
|
||||
member_region,
|
||||
} => {
|
||||
let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id);
|
||||
opaque_types::unexpected_hidden_region_diagnostic(
|
||||
self.infcx.tcx,
|
||||
Some(region_scope_tree),
|
||||
opaque_type_def_id,
|
||||
hidden_ty,
|
||||
member_region,
|
||||
)
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
RegionErrorKind::BoundUniversalRegionError {
|
||||
longer_fr,
|
||||
fr_origin,
|
||||
error_region,
|
||||
} => {
|
||||
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
|
||||
let (_, span) = self.nonlexical_regioncx.find_outlives_blame_span(
|
||||
&self.body,
|
||||
longer_fr,
|
||||
fr_origin,
|
||||
error_region,
|
||||
);
|
||||
|
||||
// FIXME: improve this error message
|
||||
self.infcx
|
||||
.tcx
|
||||
.sess
|
||||
.struct_span_err(span, "higher-ranked subtype error")
|
||||
.buffer(&mut self.errors_buffer);
|
||||
}
|
||||
|
||||
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
|
||||
if is_reported {
|
||||
let db = self.nonlexical_regioncx.report_error(
|
||||
self,
|
||||
longer_fr,
|
||||
fr_origin,
|
||||
shorter_fr,
|
||||
&mut outlives_suggestion,
|
||||
&mut region_naming,
|
||||
);
|
||||
|
||||
db.buffer(&mut self.errors_buffer);
|
||||
} else {
|
||||
// We only report the first error, so as not to overwhelm the user. See
|
||||
// `RegRegionErrorKind` docs.
|
||||
//
|
||||
// FIXME: currently we do nothing with these, but perhaps we can do better?
|
||||
// FIXME: try collecting these constraints on the outlives suggestion
|
||||
// builder. Does it make the suggestions any better?
|
||||
debug!(
|
||||
"Unreported region error: can't prove that {:?}: {:?}",
|
||||
longer_fr, shorter_fr
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Emit one outlives suggestions for each MIR def we borrowck
|
||||
outlives_suggestion.add_suggestion(self, &mut region_naming);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
@ -2225,7 +2119,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
let upvar = &self.upvars[field.index()];
|
||||
debug!(
|
||||
"upvar.mutability={:?} local_mutation_is_allowed={:?} \
|
||||
place={:?}",
|
||||
place={:?}",
|
||||
upvar, is_local_mutation_allowed, place
|
||||
);
|
||||
match (upvar.mutability, is_local_mutation_allowed) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::rc::Rc;
|
||||
|
||||
use rustc::infer::canonical::QueryOutlivesConstraint;
|
||||
@ -43,49 +44,48 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// variables are identified by their index (`RegionVid`). The
|
||||
/// definition contains information about where the region came
|
||||
/// from as well as its final inferred value.
|
||||
pub(in crate::borrow_check) definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
|
||||
|
||||
/// The liveness constraints added to each region. For most
|
||||
/// regions, these start out empty and steadily grow, though for
|
||||
/// each universally quantified region R they start out containing
|
||||
/// the entire CFG and `end(R)`.
|
||||
pub(in crate::borrow_check) liveness_constraints: LivenessValues<RegionVid>,
|
||||
liveness_constraints: LivenessValues<RegionVid>,
|
||||
|
||||
/// The outlives constraints computed by the type-check.
|
||||
pub(in crate::borrow_check) constraints: Rc<OutlivesConstraintSet>,
|
||||
constraints: Rc<OutlivesConstraintSet>,
|
||||
|
||||
/// The constraint-set, but in graph form, making it easy to traverse
|
||||
/// the constraints adjacent to a particular region. Used to construct
|
||||
/// the SCC (see `constraint_sccs`) and for error reporting.
|
||||
pub(in crate::borrow_check) constraint_graph: Rc<NormalConstraintGraph>,
|
||||
constraint_graph: Rc<NormalConstraintGraph>,
|
||||
|
||||
/// The SCC computed from `constraints` and the constraint
|
||||
/// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
|
||||
/// compute the values of each region.
|
||||
pub(in crate::borrow_check) constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
|
||||
constraint_sccs: Rc<Sccs<RegionVid, ConstraintSccIndex>>,
|
||||
|
||||
/// Reverse of the SCC constraint graph -- i.e., an edge `A -> B`
|
||||
/// exists if `B: A`. Computed lazilly.
|
||||
pub(in crate::borrow_check) rev_constraint_graph: Option<Rc<VecGraph<ConstraintSccIndex>>>,
|
||||
rev_constraint_graph: Option<Rc<VecGraph<ConstraintSccIndex>>>,
|
||||
|
||||
/// The "R0 member of [R1..Rn]" constraints, indexed by SCC.
|
||||
pub(in crate::borrow_check) member_constraints:
|
||||
Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
|
||||
member_constraints: Rc<MemberConstraintSet<'tcx, ConstraintSccIndex>>,
|
||||
|
||||
/// Records the member constraints that we applied to each scc.
|
||||
/// This is useful for error reporting. Once constraint
|
||||
/// propagation is done, this vector is sorted according to
|
||||
/// `member_region_scc`.
|
||||
pub(in crate::borrow_check) member_constraints_applied: Vec<AppliedMemberConstraint>,
|
||||
member_constraints_applied: Vec<AppliedMemberConstraint>,
|
||||
|
||||
/// Map closure bounds to a `Span` that should be used for error reporting.
|
||||
pub(in crate::borrow_check) closure_bounds_mapping:
|
||||
closure_bounds_mapping:
|
||||
FxHashMap<Location, FxHashMap<(RegionVid, RegionVid), (ConstraintCategory, Span)>>,
|
||||
|
||||
/// Contains the minimum universe of any variable within the same
|
||||
/// SCC. We will ensure that no SCC contains values that are not
|
||||
/// visible from this index.
|
||||
pub(in crate::borrow_check) scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
|
||||
scc_universes: IndexVec<ConstraintSccIndex, ty::UniverseIndex>,
|
||||
|
||||
/// Contains a "representative" from each SCC. This will be the
|
||||
/// minimal RegionVid belonging to that universe. It is used as a
|
||||
@ -94,23 +94,23 @@ pub struct RegionInferenceContext<'tcx> {
|
||||
/// of its SCC and be sure that -- if they have the same repr --
|
||||
/// they *must* be equal (though not having the same repr does not
|
||||
/// mean they are unequal).
|
||||
pub(in crate::borrow_check) scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
|
||||
scc_representatives: IndexVec<ConstraintSccIndex, ty::RegionVid>,
|
||||
|
||||
/// The final inferred values of the region variables; we compute
|
||||
/// one value per SCC. To get the value for any given *region*,
|
||||
/// you first find which scc it is a part of.
|
||||
pub(in crate::borrow_check) scc_values: RegionValues<ConstraintSccIndex>,
|
||||
scc_values: RegionValues<ConstraintSccIndex>,
|
||||
|
||||
/// Type constraints that we check after solving.
|
||||
pub(in crate::borrow_check) type_tests: Vec<TypeTest<'tcx>>,
|
||||
type_tests: Vec<TypeTest<'tcx>>,
|
||||
|
||||
/// Information about the universally quantified regions in scope
|
||||
/// on this function.
|
||||
pub(in crate::borrow_check) universal_regions: Rc<UniversalRegions<'tcx>>,
|
||||
universal_regions: Rc<UniversalRegions<'tcx>>,
|
||||
|
||||
/// Information about how the universally quantified regions in
|
||||
/// scope on this function relate to one another.
|
||||
pub(in crate::borrow_check) universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
|
||||
universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
|
||||
}
|
||||
|
||||
/// Each time that `apply_member_constraint` is successful, it appends
|
||||
@ -225,6 +225,13 @@ enum RegionRelationCheckResult {
|
||||
Error,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum Trace {
|
||||
StartRegion,
|
||||
FromOutlivesConstraint(OutlivesConstraint),
|
||||
NotVisited,
|
||||
}
|
||||
|
||||
impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Creates a new region inference context with a total of
|
||||
/// `num_region_variables` valid inference variables; the first N
|
||||
@ -838,39 +845,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
|
||||
// Type-test failed. Report the error.
|
||||
|
||||
// Try to convert the lower-bound region into something named we can print for the user.
|
||||
let lower_bound_region = self.to_error_region(type_test.lower_bound);
|
||||
let erased_generic_kind = infcx.tcx.erase_regions(&type_test.generic_kind);
|
||||
|
||||
// Skip duplicate-ish errors.
|
||||
let type_test_span = type_test.locations.span(body);
|
||||
let erased_generic_kind = tcx.erase_regions(&type_test.generic_kind);
|
||||
if !deduplicate_errors.insert((
|
||||
if deduplicate_errors.insert((
|
||||
erased_generic_kind,
|
||||
lower_bound_region,
|
||||
type_test.lower_bound,
|
||||
type_test.locations,
|
||||
)) {
|
||||
continue;
|
||||
} else {
|
||||
debug!(
|
||||
"check_type_test: reporting error for erased_generic_kind={:?}, \
|
||||
lower_bound_region={:?}, \
|
||||
type_test.locations={:?}",
|
||||
erased_generic_kind, lower_bound_region, type_test.locations,
|
||||
erased_generic_kind, type_test.lower_bound, type_test.locations,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(lower_bound_region) = lower_bound_region {
|
||||
errors_buffer.push(RegionErrorKind::TypeTestGenericBoundError {
|
||||
span: type_test_span,
|
||||
generic: type_test.generic_kind,
|
||||
lower_bound_region,
|
||||
});
|
||||
} else {
|
||||
errors_buffer.push(RegionErrorKind::TypeTestDoesNotLiveLongEnough {
|
||||
span: type_test_span,
|
||||
generic: type_test.generic_kind,
|
||||
});
|
||||
errors_buffer.push(RegionErrorKind::TypeTestError { type_test: type_test.clone() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1355,7 +1345,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
for (longer_fr, shorter_fr) in subset_errors.into_iter() {
|
||||
debug!(
|
||||
"check_polonius_subset_errors: subset_error longer_fr={:?},\
|
||||
shorter_fr={:?}",
|
||||
shorter_fr={:?}",
|
||||
longer_fr, shorter_fr
|
||||
);
|
||||
|
||||
@ -1572,23 +1562,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
debug!("check_bound_universal_region: error_element = {:?}", error_element);
|
||||
|
||||
// Find the region that introduced this `error_element`.
|
||||
let error_region = match error_element {
|
||||
RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l),
|
||||
RegionElement::RootUniversalRegion(r) => r,
|
||||
RegionElement::PlaceholderRegion(error_placeholder) => self
|
||||
.definitions
|
||||
.iter_enumerated()
|
||||
.filter_map(|(r, definition)| match definition.origin {
|
||||
NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
errors_buffer.push(RegionErrorKind::BoundUniversalRegionError {
|
||||
longer_fr,
|
||||
error_region,
|
||||
error_element,
|
||||
fr_origin: NLLRegionVariableOrigin::Placeholder(placeholder),
|
||||
});
|
||||
}
|
||||
@ -1628,6 +1604,425 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// We have a constraint `fr1: fr2` that is not satisfied, where
|
||||
/// `fr2` represents some universal region. Here, `r` is some
|
||||
/// region where we know that `fr1: r` and this function has the
|
||||
/// job of determining whether `r` is "to blame" for the fact that
|
||||
/// `fr1: fr2` is required.
|
||||
///
|
||||
/// This is true under two conditions:
|
||||
///
|
||||
/// - `r == fr2`
|
||||
/// - `fr2` is `'static` and `r` is some placeholder in a universe
|
||||
/// that cannot be named by `fr1`; in that case, we will require
|
||||
/// that `fr1: 'static` because it is the only way to `fr1: r` to
|
||||
/// be satisfied. (See `add_incompatible_universe`.)
|
||||
crate fn provides_universal_region(
|
||||
&self,
|
||||
r: RegionVid,
|
||||
fr1: RegionVid,
|
||||
fr2: RegionVid,
|
||||
) -> bool {
|
||||
debug!("provides_universal_region(r={:?}, fr1={:?}, fr2={:?})", r, fr1, fr2);
|
||||
let result = {
|
||||
r == fr2 || {
|
||||
fr2 == self.universal_regions.fr_static && self.cannot_name_placeholder(fr1, r)
|
||||
}
|
||||
};
|
||||
debug!("provides_universal_region: result = {:?}", result);
|
||||
result
|
||||
}
|
||||
|
||||
/// If `r2` represents a placeholder region, then this returns
|
||||
/// `true` if `r1` cannot name that placeholder in its
|
||||
/// value; otherwise, returns `false`.
|
||||
crate fn cannot_name_placeholder(&self, r1: RegionVid, r2: RegionVid) -> bool {
|
||||
debug!("cannot_name_value_of(r1={:?}, r2={:?})", r1, r2);
|
||||
|
||||
match self.definitions[r2].origin {
|
||||
NLLRegionVariableOrigin::Placeholder(placeholder) => {
|
||||
let universe1 = self.definitions[r1].universe;
|
||||
debug!(
|
||||
"cannot_name_value_of: universe1={:?} placeholder={:?}",
|
||||
universe1, placeholder
|
||||
);
|
||||
universe1.cannot_name(placeholder.universe)
|
||||
}
|
||||
|
||||
NLLRegionVariableOrigin::FreeRegion | NLLRegionVariableOrigin::Existential { .. } => {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate fn retrieve_closure_constraint_info(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
constraint: &OutlivesConstraint,
|
||||
) -> (ConstraintCategory, bool, Span) {
|
||||
let loc = match constraint.locations {
|
||||
Locations::All(span) => return (constraint.category, false, span),
|
||||
Locations::Single(loc) => loc,
|
||||
};
|
||||
|
||||
let opt_span_category =
|
||||
self.closure_bounds_mapping[&loc].get(&(constraint.sup, constraint.sub));
|
||||
opt_span_category.map(|&(category, span)| (category, true, span)).unwrap_or((
|
||||
constraint.category,
|
||||
false,
|
||||
body.source_info(loc).span,
|
||||
))
|
||||
}
|
||||
|
||||
/// Finds a good span to blame for the fact that `fr1` outlives `fr2`.
|
||||
crate fn find_outlives_blame_span(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
fr1: RegionVid,
|
||||
fr1_origin: NLLRegionVariableOrigin,
|
||||
fr2: RegionVid,
|
||||
) -> (ConstraintCategory, Span) {
|
||||
let (category, _, span) = self.best_blame_constraint(body, fr1, fr1_origin, |r| {
|
||||
self.provides_universal_region(r, fr1, fr2)
|
||||
});
|
||||
(category, span)
|
||||
}
|
||||
|
||||
/// Walks the graph of constraints (where `'a: 'b` is considered
|
||||
/// an edge `'a -> 'b`) to find all paths from `from_region` to
|
||||
/// `to_region`. The paths are accumulated into the vector
|
||||
/// `results`. The paths are stored as a series of
|
||||
/// `ConstraintIndex` values -- in other words, a list of *edges*.
|
||||
///
|
||||
/// Returns: a series of constraints as well as the region `R`
|
||||
/// that passed the target test.
|
||||
crate fn find_constraint_paths_between_regions(
|
||||
&self,
|
||||
from_region: RegionVid,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> Option<(Vec<OutlivesConstraint>, RegionVid)> {
|
||||
let mut context = IndexVec::from_elem(Trace::NotVisited, &self.definitions);
|
||||
context[from_region] = Trace::StartRegion;
|
||||
|
||||
// Use a deque so that we do a breadth-first search. We will
|
||||
// stop at the first match, which ought to be the shortest
|
||||
// path (fewest constraints).
|
||||
let mut deque = VecDeque::new();
|
||||
deque.push_back(from_region);
|
||||
|
||||
while let Some(r) = deque.pop_front() {
|
||||
debug!(
|
||||
"find_constraint_paths_between_regions: from_region={:?} r={:?} value={}",
|
||||
from_region,
|
||||
r,
|
||||
self.region_value_str(r),
|
||||
);
|
||||
|
||||
// Check if we reached the region we were looking for. If so,
|
||||
// we can reconstruct the path that led to it and return it.
|
||||
if target_test(r) {
|
||||
let mut result = vec![];
|
||||
let mut p = r;
|
||||
loop {
|
||||
match context[p] {
|
||||
Trace::NotVisited => {
|
||||
bug!("found unvisited region {:?} on path to {:?}", p, r)
|
||||
}
|
||||
|
||||
Trace::FromOutlivesConstraint(c) => {
|
||||
result.push(c);
|
||||
p = c.sup;
|
||||
}
|
||||
|
||||
Trace::StartRegion => {
|
||||
result.reverse();
|
||||
return Some((result, r));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, walk over the outgoing constraints and
|
||||
// enqueue any regions we find, keeping track of how we
|
||||
// reached them.
|
||||
|
||||
// A constraint like `'r: 'x` can come from our constraint
|
||||
// graph.
|
||||
let fr_static = self.universal_regions.fr_static;
|
||||
let outgoing_edges_from_graph =
|
||||
self.constraint_graph.outgoing_edges(r, &self.constraints, fr_static);
|
||||
|
||||
// Always inline this closure because it can be hot.
|
||||
let mut handle_constraint = #[inline(always)]
|
||||
|constraint: OutlivesConstraint| {
|
||||
debug_assert_eq!(constraint.sup, r);
|
||||
let sub_region = constraint.sub;
|
||||
if let Trace::NotVisited = context[sub_region] {
|
||||
context[sub_region] = Trace::FromOutlivesConstraint(constraint);
|
||||
deque.push_back(sub_region);
|
||||
}
|
||||
};
|
||||
|
||||
// This loop can be hot.
|
||||
for constraint in outgoing_edges_from_graph {
|
||||
handle_constraint(constraint);
|
||||
}
|
||||
|
||||
// Member constraints can also give rise to `'r: 'x` edges that
|
||||
// were not part of the graph initially, so watch out for those.
|
||||
// (But they are extremely rare; this loop is very cold.)
|
||||
for constraint in self.applied_member_constraints(r) {
|
||||
let p_c = &self.member_constraints[constraint.member_constraint_index];
|
||||
let constraint = OutlivesConstraint {
|
||||
sup: r,
|
||||
sub: constraint.min_choice,
|
||||
locations: Locations::All(p_c.definition_span),
|
||||
category: ConstraintCategory::OpaqueType,
|
||||
};
|
||||
handle_constraint(constraint);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Finds some region R such that `fr1: R` and `R` is live at `elem`.
|
||||
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
|
||||
debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
|
||||
debug!(
|
||||
"find_sub_region_live_at: liveness_constraints for {:?} are {:?}",
|
||||
r,
|
||||
self.liveness_constraints.region_value_str(r),
|
||||
);
|
||||
self.liveness_constraints.contains(r, elem)
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find that, we may find some `r` such that
|
||||
// `fr1: r` and `r` is a placeholder from some universe
|
||||
// `fr1` cannot name. This would force `fr1` to be
|
||||
// `'static`.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.cannot_name_placeholder(fr1, r)
|
||||
})
|
||||
})
|
||||
.or_else(|| {
|
||||
// If we fail to find THAT, it may be that `fr1` is a
|
||||
// placeholder that cannot "fit" into its SCC. In that
|
||||
// case, there should be some `r` where `fr1: r`, both
|
||||
// `fr1` and `r` are in the same SCC, and `fr1` is a
|
||||
// placeholder that `r` cannot name. We can blame that
|
||||
// edge.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
|
||||
&& self.cannot_name_placeholder(r, fr1)
|
||||
})
|
||||
})
|
||||
.map(|(_path, r)| r)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Get the region outlived by `longer_fr` and live at `element`.
|
||||
crate fn region_from_element(&self, longer_fr: RegionVid, element: RegionElement) -> RegionVid {
|
||||
match element {
|
||||
RegionElement::Location(l) => self.find_sub_region_live_at(longer_fr, l),
|
||||
RegionElement::RootUniversalRegion(r) => r,
|
||||
RegionElement::PlaceholderRegion(error_placeholder) => self
|
||||
.definitions
|
||||
.iter_enumerated()
|
||||
.filter_map(|(r, definition)| match definition.origin {
|
||||
NLLRegionVariableOrigin::Placeholder(p) if p == error_placeholder => Some(r),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the region definition of `r`.
|
||||
crate fn region_definition(&self, r: RegionVid) -> &RegionDefinition<'tcx> {
|
||||
&self.definitions[r]
|
||||
}
|
||||
|
||||
/// Check if the SCC of `r` contains `upper`.
|
||||
crate fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool {
|
||||
let r_scc = self.constraint_sccs.scc(r);
|
||||
self.scc_values.contains(r_scc, upper)
|
||||
}
|
||||
|
||||
crate fn universal_regions(&self) -> &UniversalRegions<'tcx> {
|
||||
self.universal_regions.as_ref()
|
||||
}
|
||||
|
||||
/// Tries to find the best constraint to blame for the fact that
|
||||
/// `R: from_region`, where `R` is some region that meets
|
||||
/// `target_test`. This works by following the constraint graph,
|
||||
/// creating a constraint path that forces `R` to outlive
|
||||
/// `from_region`, and then finding the best choices within that
|
||||
/// path to blame.
|
||||
crate fn best_blame_constraint(
|
||||
&self,
|
||||
body: &Body<'tcx>,
|
||||
from_region: RegionVid,
|
||||
from_region_origin: NLLRegionVariableOrigin,
|
||||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> (ConstraintCategory, bool, Span) {
|
||||
debug!(
|
||||
"best_blame_constraint(from_region={:?}, from_region_origin={:?})",
|
||||
from_region, from_region_origin
|
||||
);
|
||||
|
||||
// Find all paths
|
||||
let (path, target_region) =
|
||||
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
|
||||
debug!(
|
||||
"best_blame_constraint: path={:#?}",
|
||||
path.iter()
|
||||
.map(|&c| format!(
|
||||
"{:?} ({:?}: {:?})",
|
||||
c,
|
||||
self.constraint_sccs.scc(c.sup),
|
||||
self.constraint_sccs.scc(c.sub),
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
// Classify each of the constraints along the path.
|
||||
let mut categorized_path: Vec<(ConstraintCategory, bool, Span)> = path
|
||||
.iter()
|
||||
.map(|constraint| {
|
||||
if constraint.category == ConstraintCategory::ClosureBounds {
|
||||
self.retrieve_closure_constraint_info(body, &constraint)
|
||||
} else {
|
||||
(constraint.category, false, constraint.locations.span(body))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
debug!("best_blame_constraint: categorized_path={:#?}", categorized_path);
|
||||
|
||||
// To find the best span to cite, we first try to look for the
|
||||
// final constraint that is interesting and where the `sup` is
|
||||
// not unified with the ultimate target region. The reason
|
||||
// for this is that we have a chain of constraints that lead
|
||||
// from the source to the target region, something like:
|
||||
//
|
||||
// '0: '1 ('0 is the source)
|
||||
// '1: '2
|
||||
// '2: '3
|
||||
// '3: '4
|
||||
// '4: '5
|
||||
// '5: '6 ('6 is the target)
|
||||
//
|
||||
// Some of those regions are unified with `'6` (in the same
|
||||
// SCC). We want to screen those out. After that point, the
|
||||
// "closest" constraint we have to the end is going to be the
|
||||
// most likely to be the point where the value escapes -- but
|
||||
// we still want to screen for an "interesting" point to
|
||||
// highlight (e.g., a call site or something).
|
||||
let target_scc = self.constraint_sccs.scc(target_region);
|
||||
let mut range = 0..path.len();
|
||||
|
||||
// As noted above, when reporting an error, there is typically a chain of constraints
|
||||
// leading from some "source" region which must outlive some "target" region.
|
||||
// In most cases, we prefer to "blame" the constraints closer to the target --
|
||||
// but there is one exception. When constraints arise from higher-ranked subtyping,
|
||||
// we generally prefer to blame the source value,
|
||||
// as the "target" in this case tends to be some type annotation that the user gave.
|
||||
// Therefore, if we find that the region origin is some instantiation
|
||||
// of a higher-ranked region, we start our search from the "source" point
|
||||
// rather than the "target", and we also tweak a few other things.
|
||||
//
|
||||
// An example might be this bit of Rust code:
|
||||
//
|
||||
// ```rust
|
||||
// let x: fn(&'static ()) = |_| {};
|
||||
// let y: for<'a> fn(&'a ()) = x;
|
||||
// ```
|
||||
//
|
||||
// In MIR, this will be converted into a combination of assignments and type ascriptions.
|
||||
// In particular, the 'static is imposed through a type ascription:
|
||||
//
|
||||
// ```rust
|
||||
// x = ...;
|
||||
// AscribeUserType(x, fn(&'static ())
|
||||
// y = x;
|
||||
// ```
|
||||
//
|
||||
// We wind up ultimately with constraints like
|
||||
//
|
||||
// ```rust
|
||||
// !a: 'temp1 // from the `y = x` statement
|
||||
// 'temp1: 'temp2
|
||||
// 'temp2: 'static // from the AscribeUserType
|
||||
// ```
|
||||
//
|
||||
// and here we prefer to blame the source (the y = x statement).
|
||||
let blame_source = match from_region_origin {
|
||||
NLLRegionVariableOrigin::FreeRegion
|
||||
| NLLRegionVariableOrigin::Existential { from_forall: false } => true,
|
||||
NLLRegionVariableOrigin::Placeholder(_)
|
||||
| NLLRegionVariableOrigin::Existential { from_forall: true } => false,
|
||||
};
|
||||
|
||||
let find_region = |i: &usize| {
|
||||
let constraint = path[*i];
|
||||
|
||||
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
|
||||
|
||||
if blame_source {
|
||||
match categorized_path[*i].0 {
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal => false,
|
||||
ConstraintCategory::TypeAnnotation
|
||||
| ConstraintCategory::Return
|
||||
| ConstraintCategory::Yield => true,
|
||||
_ => constraint_sup_scc != target_scc,
|
||||
}
|
||||
} else {
|
||||
match categorized_path[*i].0 {
|
||||
ConstraintCategory::OpaqueType
|
||||
| ConstraintCategory::Boring
|
||||
| ConstraintCategory::BoringNoLocation
|
||||
| ConstraintCategory::Internal => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let best_choice =
|
||||
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
|
||||
|
||||
debug!(
|
||||
"best_blame_constraint: best_choice={:?} blame_source={}",
|
||||
best_choice, blame_source
|
||||
);
|
||||
|
||||
if let Some(i) = best_choice {
|
||||
if let Some(next) = categorized_path.get(i + 1) {
|
||||
if categorized_path[i].0 == ConstraintCategory::Return
|
||||
&& next.0 == ConstraintCategory::OpaqueType
|
||||
{
|
||||
// The return expression is being influenced by the return type being
|
||||
// impl Trait, point at the return type and not the return expr.
|
||||
return *next;
|
||||
}
|
||||
}
|
||||
return categorized_path[i];
|
||||
}
|
||||
|
||||
// If that search fails, that is.. unusual. Maybe everything
|
||||
// is in the same SCC or something. In that case, find what
|
||||
// appears to be the most interesting point to report to the
|
||||
// user via an even more ad-hoc guess.
|
||||
categorized_path.sort_by(|p0, p1| p0.0.cmp(&p1.0));
|
||||
debug!("`: sorted_path={:#?}", categorized_path);
|
||||
|
||||
*categorized_path.first().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RegionDefinition<'tcx> {
|
||||
|
@ -114,7 +114,7 @@ rustc_index::newtype_index! {
|
||||
|
||||
/// An individual element in a region value -- the value of a
|
||||
/// particular region variable consists of a set of these elements.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
crate enum RegionElement {
|
||||
/// A point in the control-flow graph.
|
||||
Location(Location),
|
||||
|
@ -87,12 +87,12 @@ error[E0597]: `ap1` does not live long enough
|
||||
--> $DIR/variadic-ffi-4.rs:24:11
|
||||
|
|
||||
LL | pub unsafe extern "C" fn no_escape4(_: usize, ap0: &mut VaListImpl, mut ap1: ...) {
|
||||
| - let's call the lifetime of this reference `'1`
|
||||
| - let's call the lifetime of this reference `'3`
|
||||
LL | ap0 = &mut ap1;
|
||||
| ------^^^^^^^^
|
||||
| | |
|
||||
| | borrowed value does not live long enough
|
||||
| assignment requires that `ap1` is borrowed for `'1`
|
||||
| assignment requires that `ap1` is borrowed for `'3`
|
||||
...
|
||||
LL | }
|
||||
| - `ap1` dropped here while still borrowed
|
||||
|
Loading…
Reference in New Issue
Block a user