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:
bors 2020-01-17 21:43:23 +00:00
commit d8dcb6345b
10 changed files with 787 additions and 879 deletions

View File

@ -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 @@ pub(in crate::borrow_check) fn add_lifetime_bound_suggestion_to_diagnostic<'tcx>
}
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 @@ pub(in crate::borrow_check) fn explain_why_borrow_contains_point(
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 @@ pub(in crate::borrow_check) fn explain_why_borrow_contains_point(
}
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 @@ pub(in crate::borrow_check) fn explain_why_borrow_contains_point(
} 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
}

View File

@ -32,7 +32,7 @@
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);

View File

@ -12,7 +12,7 @@
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 @@ fn region_name_is_suggestable(name: &RegionName) -> bool {
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 @@ fn compile_all_suggestions(
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 @@ fn compile_all_suggestions(
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 @@ fn compile_all_suggestions(
&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 @@ fn compile_all_suggestions(
/// 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 @@ fn compile_all_suggestions(
}
// 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() {

View File

@ -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 @@ fn description(&self) -> &'static str {
}
}
#[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 @@ enum Trace {
#[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 @@ enum Trace {
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 @@ pub fn to_error_region_vid(&self, r: RegionVid) -> Option<RegionVid> {
}
}
/// 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 @@ fn find_constraint_paths_between_regions(
/// ```
///
/// 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 @@ pub(in crate::borrow_check) fn report_error<'a>(
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 @@ fn provides_universal_region(&self, r: RegionVid, fr1: RegionVid, fr2: RegionVid
/// 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 @@ fn report_fnmut_error(
// 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 @@ fn report_fnmut_error(
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 @@ fn report_fnmut_error(
/// 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 @@ fn report_escaping_data_error(
|| (*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 @@ fn report_escaping_data_error(
/// | ^^^^^^^^^^^^^^ 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 @@ fn report_general_error(
} = 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 @@ fn report_general_error(
}
}
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 @@ fn report_general_error(
/// ```
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 @@ fn add_static_impl_trait_suggestion(
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 @@ fn add_static_impl_trait_suggestion(
//
// 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 @@ fn add_static_impl_trait_suggestion(
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 @@ fn add_static_impl_trait_suggestion(
}
}
}
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
}
}
}
}

View File

@ -2,18 +2,14 @@
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 @@
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 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
}
}
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 @@ fn give_name_from_error_region(
// 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 @@ fn give_name_from_error_region(
}
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 @@ fn give_name_from_error_region(
} 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 @@ fn give_name_from_error_region(
/// ```
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 @@ fn give_name_if_we_can_match_hir_ty_from_argument(
// (`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 @@ fn give_name_if_we_can_match_hir_ty_from_argument(
/// ```
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 @@ fn give_name_if_we_cannot_match_hir_ty(
);
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 @@ fn give_name_if_we_cannot_match_hir_ty(
// 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 @@ fn give_name_if_we_cannot_match_hir_ty(
/// 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 @@ fn give_name_if_we_can_match_hir_ty(
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 @@ fn give_name_if_we_can_match_hir_ty(
substs,
needle_fr,
last_segment,
renctx,
search_stack,
) {
return Some(name);
@ -570,7 +513,6 @@ fn match_adt_and_segment<'hir>(
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 @@ fn match_adt_and_segment<'hir>(
| 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 @@ fn try_match_adt_and_generic_args<'hir>(
/// | 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 @@ fn give_name_if_anonymous_region_appears_in_upvars(
/// 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 @@ fn give_name_if_anonymous_region_appears_in_output(
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 @@ fn give_name_if_anonymous_region_appears_in_output(
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 @@ fn give_name_if_anonymous_region_appears_in_yield_ty(
);
Some(RegionName {
name: renctx.synthesize_region_name(),
name: self.synthesize_region_name(),
source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name),
})
}

View File

@ -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);

View File

@ -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::{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::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::{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 @@ fn do_mir_borrowck<'a, '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 @@ fn do_mir_borrowck<'a, '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 @@ fn visit_terminator_entry(
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 @@ fn check_activations(&mut self, location: Location, span: Span, flow_state: &Flo
// 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 @@ fn is_mutable<'d>(
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) {

View File

@ -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 @@ fn check_type_tests(
}
// 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 @@ fn check_polonius_subset_errors(
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 @@ fn check_bound_universal_region(
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 @@ fn check_member_constraints(
});
}
}
/// 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> {

View File

@ -114,7 +114,7 @@ pub struct PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" }
/// 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),

View File

@ -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