1
Fork 0

Various refactorings to clean up nll diagnostics

- Create ErrorReportingCtx and ErrorConstraintInfo, vasting reducing the
  number of arguments passed around everywhere in the error reporting code
- Create RegionErrorNamingCtx, making a given lifetime have consistent
  numbering thoughout all error messages for that MIR def.
- Make the error reporting code return the DiagnosticBuilder rather than
  directly buffer the Diagnostic. This makes it easier to modify the
  diagnostic later, e.g. to add suggestions.
This commit is contained in:
Mark Mansi 2019-09-12 16:43:36 -05:00
parent eb48d6bdee
commit 68b1a8741e
3 changed files with 303 additions and 207 deletions

View file

@ -13,7 +13,7 @@ use rustc::infer::NLLRegionVariableOrigin;
use rustc::mir::{ConstraintCategory, Location, Body};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use rustc_errors::DiagnosticBuilder;
use std::collections::VecDeque;
use syntax::errors::Applicability;
use syntax::symbol::kw;
@ -22,7 +22,7 @@ use syntax_pos::Span;
mod region_name;
mod var_name;
crate use self::region_name::{RegionName, RegionNameSource};
crate use self::region_name::{RegionName, RegionNameSource, RegionErrorNamingCtx};
impl ConstraintDescription for ConstraintCategory {
fn description(&self) -> &'static str {
@ -54,6 +54,30 @@ enum Trace {
NotVisited,
}
/// Various pieces of state used when reporting borrow checker errors.
pub struct ErrorReportingCtx<'a, 'b, 'tcx> {
rinfcx: &'b RegionInferenceContext<'tcx>,
infcx: &'b InferCtxt<'a, 'tcx>,
mir_def_id: DefId,
body: &'b Body<'tcx>,
upvars: &'b [Upvar],
}
/// Information about the various region constraints involved in a borrow checker error.
#[derive(Clone, Debug)]
pub struct ErrorConstraintInfo {
// fr: outlived_fr
fr: RegionVid,
fr_is_local: bool,
outlived_fr: RegionVid,
outlived_fr_is_local: bool,
// Category and span for best blame constraint
category: ConstraintCategory,
span: Span,
}
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
@ -257,16 +281,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// ```
///
/// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
pub(super) fn report_error(
&self,
pub(super) fn report_error<'a>(
&'a self,
body: &Body<'tcx>,
upvars: &[Upvar],
infcx: &InferCtxt<'_, 'tcx>,
infcx: &'a InferCtxt<'a, 'tcx>,
mir_def_id: DefId,
fr: RegionVid,
outlived_fr: RegionVid,
errors_buffer: &mut Vec<Diagnostic>,
) {
renctx: &mut RegionErrorNamingCtx,
) -> DiagnosticBuilder<'a> {
debug!("report_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
let (category, _, span) = self.best_blame_constraint(body, fr, |r| {
@ -279,8 +303,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let tables = infcx.tcx.typeck_tables_of(mir_def_id);
let nice = NiceRegionError::new_from_span(infcx, span, o, f, Some(tables));
if let Some(diag) = nice.try_report_from_nll() {
diag.buffer(errors_buffer);
return;
return diag;
}
}
@ -293,45 +316,35 @@ impl<'tcx> RegionInferenceContext<'tcx> {
"report_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
fr_is_local, outlived_fr_is_local, category
);
let errctx = ErrorReportingCtx {
rinfcx: self,
infcx,
mir_def_id,
body,
upvars,
};
let errci = ErrorConstraintInfo {
fr, outlived_fr, fr_is_local, outlived_fr_is_local, category, span
};
match (category, fr_is_local, outlived_fr_is_local) {
(ConstraintCategory::Return, true, false) if self.is_closure_fn_mut(infcx, fr) => {
self.report_fnmut_error(
body,
upvars,
infcx,
mir_def_id,
fr,
outlived_fr,
span,
errors_buffer,
)
self.report_fnmut_error(&errctx, &errci, renctx)
}
(ConstraintCategory::Assignment, true, false)
| (ConstraintCategory::CallArgument, true, false) => self.report_escaping_data_error(
body,
upvars,
infcx,
mir_def_id,
fr,
outlived_fr,
category,
span,
errors_buffer,
),
_ => self.report_general_error(
body,
upvars,
infcx,
mir_def_id,
fr,
fr_is_local,
outlived_fr,
outlived_fr_is_local,
category,
span,
errors_buffer,
),
};
| (ConstraintCategory::CallArgument, true, false) => {
let mut db = self.report_escaping_data_error(&errctx, &errci, renctx);
db
}
_ => {
let mut db = self.report_general_error(&errctx, &errci, renctx);
db
}
}
}
/// We have a constraint `fr1: fr2` that is not satisfied, where
@ -379,19 +392,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// ```
fn report_fnmut_error(
&self,
body: &Body<'tcx>,
upvars: &[Upvar],
infcx: &InferCtxt<'_, 'tcx>,
mir_def_id: DefId,
_fr: RegionVid,
outlived_fr: RegionVid,
span: Span,
errors_buffer: &mut Vec<Diagnostic>,
) {
let mut diag = infcx
errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
errci: &ErrorConstraintInfo,
renctx: &mut RegionErrorNamingCtx,
) -> DiagnosticBuilder<'_> {
let ErrorConstraintInfo {
outlived_fr, span, ..
} = errci;
let mut diag = errctx
.infcx
.tcx
.sess
.struct_span_err(span, "captured variable cannot escape `FnMut` closure body");
.struct_span_err(*span, "captured variable cannot escape `FnMut` closure body");
// 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.
@ -403,11 +416,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
"returns a reference to a captured variable which escapes the closure body"
};
diag.span_label(span, message);
diag.span_label(*span, message);
match self.give_region_a_name(infcx, body, upvars, mir_def_id, outlived_fr, &mut 1)
.unwrap().source
{
match self.give_region_a_name(errctx, renctx, *outlived_fr).unwrap().source {
RegionNameSource::NamedEarlyBoundRegion(fr_span)
| RegionNameSource::NamedFreeRegion(fr_span)
| RegionNameSource::SynthesizedFreeEnvRegion(fr_span, _)
@ -427,7 +438,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
);
diag.note("...therefore, they cannot allow references to captured variables to escape");
diag.buffer(errors_buffer);
diag
}
/// Reports a error specifically for when data is escaping a closure.
@ -444,20 +455,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// ```
fn report_escaping_data_error(
&self,
body: &Body<'tcx>,
upvars: &[Upvar],
infcx: &InferCtxt<'_, 'tcx>,
mir_def_id: DefId,
fr: RegionVid,
outlived_fr: RegionVid,
category: ConstraintCategory,
span: Span,
errors_buffer: &mut Vec<Diagnostic>,
) {
errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
errci: &ErrorConstraintInfo,
renctx: &mut RegionErrorNamingCtx,
) -> DiagnosticBuilder<'_> {
let ErrorReportingCtx {
infcx, body, upvars, ..
} = errctx;
let ErrorConstraintInfo {
span, category, ..
} = errci;
let fr_name_and_span =
self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, fr);
self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, errci.fr);
let outlived_fr_name_and_span =
self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, outlived_fr);
self.get_var_name_and_span_for_region(infcx.tcx, body, upvars, errci.outlived_fr);
let escapes_from = match self.universal_regions.defining_ty {
DefiningTy::Closure(..) => "closure",
@ -469,27 +482,23 @@ impl<'tcx> RegionInferenceContext<'tcx> {
// Revert to the normal error in these cases.
// Assignments aren't "escapes" in function items.
if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
|| (category == ConstraintCategory::Assignment && escapes_from == "function")
|| (*category == ConstraintCategory::Assignment && escapes_from == "function")
|| escapes_from == "const"
{
return self.report_general_error(
body,
upvars,
infcx,
mir_def_id,
fr,
true,
outlived_fr,
false,
category,
span,
errors_buffer,
errctx,
&ErrorConstraintInfo {
fr_is_local: true,
outlived_fr_is_local: false,
.. *errci
},
renctx,
);
}
let mut diag = borrowck_errors::borrowed_data_escapes_closure(
infcx.tcx,
span,
*span,
escapes_from,
);
@ -513,12 +522,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
);
diag.span_label(
span,
*span,
format!("`{}` escapes the {} body here", fr_name, escapes_from),
);
}
diag.buffer(errors_buffer);
diag
}
/// Reports a region inference error for the general case with named/synthesized lifetimes to
@ -538,41 +547,37 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// ```
fn report_general_error(
&self,
body: &Body<'tcx>,
upvars: &[Upvar],
infcx: &InferCtxt<'_, 'tcx>,
mir_def_id: DefId,
fr: RegionVid,
fr_is_local: bool,
outlived_fr: RegionVid,
outlived_fr_is_local: bool,
category: ConstraintCategory,
span: Span,
errors_buffer: &mut Vec<Diagnostic>,
) {
errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
errci: &ErrorConstraintInfo,
renctx: &mut RegionErrorNamingCtx,
) -> DiagnosticBuilder<'_> {
let ErrorReportingCtx {
infcx, mir_def_id, ..
} = errctx;
let ErrorConstraintInfo {
fr, fr_is_local, outlived_fr, outlived_fr_is_local, span, category, ..
} = errci;
let mut diag = infcx.tcx.sess.struct_span_err(
span,
*span,
"lifetime may not live long enough"
);
let counter = &mut 1;
let fr_name = self.give_region_a_name(
infcx, body, upvars, mir_def_id, fr, counter).unwrap();
fr_name.highlight_region_name(&mut diag);
let outlived_fr_name =
self.give_region_a_name(infcx, body, upvars, mir_def_id, outlived_fr, counter).unwrap();
outlived_fr_name.highlight_region_name(&mut diag);
let mir_def_name = if infcx.tcx.is_closure(mir_def_id) {
let mir_def_name = if infcx.tcx.is_closure(*mir_def_id) {
"closure"
} else {
"function"
};
let fr_name = self.give_region_a_name(errctx, renctx, *fr).unwrap();
fr_name.highlight_region_name(&mut diag);
let outlived_fr_name = self.give_region_a_name(errctx, renctx, *outlived_fr).unwrap();
outlived_fr_name.highlight_region_name(&mut diag);
match (category, outlived_fr_is_local, fr_is_local) {
(ConstraintCategory::Return, true, _) => {
diag.span_label(
span,
*span,
format!(
"{} was supposed to return data with lifetime `{}` but it is returning \
data with lifetime `{}`",
@ -582,7 +587,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
_ => {
diag.span_label(
span,
*span,
format!(
"{}requires that `{}` must outlive `{}`",
category.description(),
@ -593,9 +598,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
}
self.add_static_impl_trait_suggestion(infcx, &mut diag, fr, fr_name, outlived_fr);
self.add_static_impl_trait_suggestion(infcx, &mut diag, *fr, fr_name, *outlived_fr);
diag.buffer(errors_buffer);
diag
}
/// Adds a suggestion to errors where a `impl Trait` is returned.
@ -704,8 +709,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
borrow_region,
|r| self.provides_universal_region(r, borrow_region, outlived_region)
);
let outlived_fr_name =
self.give_region_a_name(infcx, body, upvars, mir_def_id, outlived_region, &mut 1);
let mut renctx = RegionErrorNamingCtx::new();
let errctx = ErrorReportingCtx {
infcx, body, upvars, mir_def_id,
rinfcx: self,
};
let outlived_fr_name = self.give_region_a_name(&errctx, &mut renctx, outlived_region);
(category, from_closure, span, outlived_fr_name)
}

View file

@ -1,5 +1,9 @@
use std::fmt::{self, Display};
use crate::borrow_check::nll::region_infer::RegionInferenceContext;
use crate::borrow_check::nll::region_infer::{
RegionInferenceContext,
error_reporting::ErrorReportingCtx,
};
use crate::borrow_check::nll::universal_regions::DefiningTy;
use crate::borrow_check::nll::ToRegionVid;
use crate::borrow_check::Upvar;
@ -13,29 +17,75 @@ use rustc::ty::{self, RegionKind, RegionVid, Ty, TyCtxt};
use rustc::ty::print::RegionHighlightMode;
use rustc_errors::DiagnosticBuilder;
use syntax::symbol::kw;
use syntax_pos::Span;
use syntax_pos::symbol::InternedString;
use rustc_data_structures::fx::FxHashMap;
use syntax_pos::{Span, symbol::InternedString};
#[derive(Debug)]
/// 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`.
#[derive(Debug, Clone)]
crate struct RegionName {
/// The name of the region (interned).
crate name: InternedString,
/// Where the region comes from.
crate source: RegionNameSource,
}
#[derive(Debug)]
/// Denotes the source of a region that is named by a `RegionName`. For example, a free region that
/// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`.
/// This helps to print the right kinds of diagnostics.
#[derive(Debug, Clone)]
crate enum RegionNameSource {
/// A bound (not free) region that was substituted at the def site (not an HRTB).
NamedEarlyBoundRegion(Span),
/// A free region that the user has a name (`'a`) for.
NamedFreeRegion(Span),
/// The `'static` region.
Static,
/// The free region corresponding to the environment of a closure.
SynthesizedFreeEnvRegion(Span, String),
/// The region name corresponds to a region where the type annotation is completely missing
/// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference.
CannotMatchHirTy(Span, String),
/// The region name corresponds a reference that was found by traversing the type in the HIR.
MatchedHirTy(Span),
/// A region name from the generics list of a struct/enum/union.
MatchedAdtAndSegment(Span),
/// The region corresponding to a closure upvar.
AnonRegionFromUpvar(Span, String),
/// The region corresponding to the return type of a closure.
AnonRegionFromOutput(Span, String, String),
AnonRegionFromYieldTy(Span, String),
}
/// 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(),
}
}
crate fn get(&self, region: &RegionVid) -> Option<&RegionName> {
self.renctx.get(region)
}
crate fn insert(&mut self, region: RegionVid, name: RegionName) {
self.renctx.insert(region, name);
}
}
impl RegionName {
#[allow(dead_code)]
crate fn was_named(&self) -> bool {
@ -63,43 +113,40 @@ impl RegionName {
self.name
}
crate fn highlight_region_name(
&self,
diag: &mut DiagnosticBuilder<'_>
) {
crate fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) {
match &self.source {
RegionNameSource::NamedFreeRegion(span) |
RegionNameSource::NamedEarlyBoundRegion(span) => {
diag.span_label(
*span,
format!("lifetime `{}` defined here", self),
);
},
RegionNameSource::NamedFreeRegion(span)
| RegionNameSource::NamedEarlyBoundRegion(span) => {
diag.span_label(*span, format!("lifetime `{}` defined here", self));
}
RegionNameSource::SynthesizedFreeEnvRegion(span, note) => {
diag.span_label(
*span,
format!("lifetime `{}` represents this closure's body", self),
);
diag.note(&note);
},
}
RegionNameSource::CannotMatchHirTy(span, type_name) => {
diag.span_label(*span, format!("has type `{}`", type_name));
},
}
RegionNameSource::MatchedHirTy(span) => {
diag.span_label(
*span,
format!("let's call the lifetime of this reference `{}`", self),
);
},
}
RegionNameSource::MatchedAdtAndSegment(span) => {
diag.span_label(*span, format!("let's call this `{}`", self));
},
}
RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => {
diag.span_label(
*span,
format!("lifetime `{}` appears in the type of `{}`", self, upvar_name),
format!(
"lifetime `{}` appears in the type of `{}`",
self, upvar_name
),
);
},
}
RegionNameSource::AnonRegionFromOutput(span, mir_description, type_name) => {
diag.span_label(
*span,
@ -151,39 +198,49 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// and then return the name `'1` for us to use.
crate fn give_region_a_name(
&self,
infcx: &InferCtxt<'_, 'tcx>,
body: &Body<'tcx>,
upvars: &[Upvar],
mir_def_id: DefId,
errctx: &ErrorReportingCtx<'_, '_, 'tcx>,
renctx: &mut RegionErrorNamingCtx,
fr: RegionVid,
counter: &mut usize,
) -> Option<RegionName> {
debug!("give_region_a_name(fr={:?}, counter={})", fr, counter);
let ErrorReportingCtx {
infcx, body, mir_def_id, upvars, ..
} = errctx;
debug!("give_region_a_name(fr={:?}, counter={:?})", fr, renctx.counter);
assert!(self.universal_regions.is_universal_region(fr));
let value = self.give_name_from_error_region(infcx.tcx, mir_def_id, fr, counter)
if let Some(value) = renctx.get(&fr) {
return Some(value.clone());
}
let value = self
.give_name_from_error_region(infcx.tcx, *mir_def_id, fr, renctx)
.or_else(|| {
self.give_name_if_anonymous_region_appears_in_arguments(
infcx, body, mir_def_id, fr, counter,
infcx, body, *mir_def_id, fr, renctx,
)
})
.or_else(|| {
self.give_name_if_anonymous_region_appears_in_upvars(
infcx.tcx, upvars, fr, counter,
infcx.tcx, upvars, fr, renctx
)
})
.or_else(|| {
self.give_name_if_anonymous_region_appears_in_output(
infcx, body, mir_def_id, fr, counter,
infcx, body, *mir_def_id, fr, renctx,
)
})
.or_else(|| {
self.give_name_if_anonymous_region_appears_in_yield_ty(
infcx, body, mir_def_id, fr, counter,
infcx, body, *mir_def_id, fr, renctx,
)
});
if let Some(ref value) = value {
renctx.insert(fr, value.clone());
}
debug!("give_region_a_name: gave name {:?}", value);
value
}
@ -197,7 +254,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
tcx: TyCtxt<'tcx>,
mir_def_id: DefId,
fr: RegionVid,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let error_region = self.to_error_region(fr)?;
@ -208,7 +265,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let span = self.get_named_span(tcx, error_region, ebr.name);
Some(RegionName {
name: ebr.name,
source: RegionNameSource::NamedEarlyBoundRegion(span)
source: RegionNameSource::NamedEarlyBoundRegion(span),
})
} else {
None
@ -227,12 +284,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
name,
source: RegionNameSource::NamedFreeRegion(span),
})
},
}
ty::BoundRegion::BrEnv => {
let mir_hir_id = tcx.hir()
.as_local_hir_id(mir_def_id)
.expect("non-local mir");
let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
let def_ty = self.universal_regions.defining_ty;
if let DefiningTy::Closure(def_id, substs) = def_ty {
@ -243,7 +298,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
} else {
bug!("Closure is not defined by a closure expr");
};
let region_name = self.synthesize_region_name(counter);
let region_name = self.synthesize_region_name(renctx);
let closure_kind_ty = substs.closure_kind_ty(def_id, tcx);
let note = match closure_kind_ty.to_opt_closure_kind() {
@ -265,7 +320,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
name: region_name,
source: RegionNameSource::SynthesizedFreeEnvRegion(
args_span,
note.to_string()
note.to_string(),
),
})
} else {
@ -335,7 +390,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
body: &Body<'tcx>,
mir_def_id: DefId,
fr: RegionVid,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let implicit_inputs = self.universal_regions.defining_ty.implicit_inputs();
let argument_index = self.get_argument_index_for_region(infcx.tcx, fr)?;
@ -349,12 +404,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
fr,
arg_ty,
argument_index,
counter,
renctx,
) {
return Some(region_name);
}
self.give_name_if_we_cannot_match_hir_ty(infcx, body, fr, arg_ty, counter)
self.give_name_if_we_cannot_match_hir_ty(infcx, body, fr, arg_ty, renctx)
}
fn give_name_if_we_can_match_hir_ty_from_argument(
@ -365,7 +420,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
needle_fr: RegionVid,
argument_ty: Ty<'tcx>,
argument_index: usize,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let mir_hir_id = infcx.tcx.hir().as_local_hir_id(mir_def_id)?;
let fn_decl = infcx.tcx.hir().fn_decl_by_hir_id(mir_hir_id)?;
@ -379,7 +434,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
body,
needle_fr,
argument_ty,
counter,
renctx,
),
_ => self.give_name_if_we_can_match_hir_ty(
@ -387,7 +442,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
needle_fr,
argument_ty,
argument_hir_ty,
counter,
renctx,
),
}
}
@ -409,10 +464,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
body: &Body<'tcx>,
needle_fr: RegionVid,
argument_ty: Ty<'tcx>,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let counter = renctx.counter;
let mut highlight = RegionHighlightMode::default();
highlight.highlighting_region_vid(needle_fr, *counter);
highlight.highlighting_region_vid(needle_fr, counter);
let type_name = infcx.extract_type_name(&argument_ty, Some(highlight)).0;
debug!(
@ -428,7 +484,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: self.synthesize_region_name(counter),
name: self.synthesize_region_name(renctx),
source: RegionNameSource::CannotMatchHirTy(span, type_name),
})
} else {
@ -455,7 +511,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// type. Once we find that, we can use the span of the `hir::Ty`
/// to add the highlight.
///
/// This is a somewhat imperfect process, so long the way we also
/// This is a somewhat imperfect process, so along the way we also
/// keep track of the **closest** type we've found. If we fail to
/// find the exact `&` or `'_` to highlight, then we may fall back
/// to highlighting that closest type instead.
@ -465,7 +521,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
needle_fr: RegionVid,
argument_ty: Ty<'tcx>,
argument_hir_ty: &hir::Ty,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty)> =
&mut vec![(argument_ty, argument_hir_ty)];
@ -483,7 +539,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
hir::TyKind::Rptr(_lifetime, referent_hir_ty),
) => {
if region.to_region_vid() == needle_fr {
let region_name = self.synthesize_region_name(counter);
let region_name = self.synthesize_region_name(renctx);
// Just grab the first character, the `&`.
let source_map = tcx.sess.source_map();
@ -515,7 +571,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
substs,
needle_fr,
last_segment,
counter,
renctx,
search_stack,
) {
return Some(name);
@ -559,18 +615,19 @@ impl<'tcx> RegionInferenceContext<'tcx> {
substs: SubstsRef<'tcx>,
needle_fr: RegionVid,
last_segment: &'hir hir::PathSegment,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty)>,
) -> Option<RegionName> {
// Did the user give explicit arguments? (e.g., `Foo<..>`)
let args = last_segment.args.as_ref()?;
let lifetime = self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
let lifetime =
self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?;
match lifetime.name {
hir::LifetimeName::Param(_)
| hir::LifetimeName::Error
| hir::LifetimeName::Static
| hir::LifetimeName::Underscore => {
let region_name = self.synthesize_region_name(counter);
let region_name = self.synthesize_region_name(renctx);
let ampersand_span = lifetime.span;
Some(RegionName {
name: region_name,
@ -657,12 +714,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
tcx: TyCtxt<'tcx>,
upvars: &[Upvar],
fr: RegionVid,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let upvar_index = self.get_upvar_index_for_region(tcx, fr)?;
let (upvar_name, upvar_span) =
self.get_upvar_name_and_span_for_region(tcx, upvars, upvar_index);
let region_name = self.synthesize_region_name(counter);
let region_name = self.synthesize_region_name(renctx);
Some(RegionName {
name: region_name,
@ -680,7 +737,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
body: &Body<'tcx>,
mir_def_id: DefId,
fr: RegionVid,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
let tcx = infcx.tcx;
@ -694,7 +751,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
let mut highlight = RegionHighlightMode::default();
highlight.highlighting_region_vid(fr, *counter);
highlight.highlighting_region_vid(fr, renctx.counter);
let type_name = infcx.extract_type_name(&return_ty, Some(highlight)).0;
let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
@ -725,11 +782,11 @@ 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: self.synthesize_region_name(counter),
name: self.synthesize_region_name(renctx),
source: RegionNameSource::AnonRegionFromOutput(
return_span,
mir_description.to_string(),
type_name
type_name,
),
})
}
@ -740,7 +797,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
body: &Body<'tcx>,
mir_def_id: DefId,
fr: RegionVid,
counter: &mut usize,
renctx: &mut RegionErrorNamingCtx,
) -> Option<RegionName> {
// Note: generators from `async fn` yield `()`, so we don't have to
// worry about them here.
@ -757,7 +814,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
let mut highlight = RegionHighlightMode::default();
highlight.highlighting_region_vid(fr, *counter);
highlight.highlighting_region_vid(fr, renctx.counter);
let type_name = infcx.extract_type_name(&yield_ty, Some(highlight)).0;
let mir_hir_id = tcx.hir().as_local_hir_id(mir_def_id).expect("non-local mir");
@ -780,16 +837,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
);
Some(RegionName {
name: self.synthesize_region_name(counter),
name: self.synthesize_region_name(renctx),
source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name),
})
}
/// Creates a synthetic region named `'1`, incrementing the
/// counter.
fn synthesize_region_name(&self, counter: &mut usize) -> InternedString {
let c = *counter;
*counter += 1;
/// Creates a synthetic region named `'1`, incrementing the counter.
fn synthesize_region_name(&self, renctx: &mut RegionErrorNamingCtx) -> InternedString {
let c = renctx.counter;
renctx.counter += 1;
InternedString::intern(&format!("'{:?}", c))
}

View file

@ -1,15 +1,21 @@
use super::universal_regions::UniversalRegions;
use crate::borrow_check::nll::constraints::graph::NormalConstraintGraph;
use crate::borrow_check::nll::constraints::{
ConstraintSccIndex, OutlivesConstraint, OutlivesConstraintSet,
use std::rc::Rc;
use crate::borrow_check::nll::{
constraints::{
graph::NormalConstraintGraph,
ConstraintSccIndex,
OutlivesConstraint,
OutlivesConstraintSet,
},
member_constraints::{MemberConstraintSet, NllMemberConstraintIndex},
region_infer::values::{
PlaceholderIndices, RegionElement, ToElementIndex
},
region_infer::error_reporting::outlives_suggestion::OutlivesSuggestionBuilder,
type_check::{free_region_relations::UniversalRegionRelations, Locations},
};
use crate::borrow_check::nll::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
use crate::borrow_check::nll::region_infer::values::{
PlaceholderIndices, RegionElement, ToElementIndex,
};
use crate::borrow_check::nll::type_check::free_region_relations::UniversalRegionRelations;
use crate::borrow_check::nll::type_check::Locations;
use crate::borrow_check::Upvar;
use rustc::hir::def_id::DefId;
use rustc::infer::canonical::QueryOutlivesConstraint;
use rustc::infer::opaque_types;
@ -31,16 +37,16 @@ use rustc_data_structures::indexed_vec::IndexVec;
use rustc_errors::{Diagnostic, DiagnosticBuilder};
use syntax_pos::Span;
use std::rc::Rc;
crate use self::error_reporting::{RegionName, RegionNameSource, RegionErrorNamingCtx};
use self::values::{LivenessValues, RegionValueElements, RegionValues};
use super::universal_regions::UniversalRegions;
use super::ToRegionVid;
mod dump_mir;
mod error_reporting;
crate use self::error_reporting::{RegionName, RegionNameSource};
mod graphviz;
pub mod values;
use self::values::{LivenessValues, RegionValueElements, RegionValues};
use super::ToRegionVid;
pub mod values;
pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region
@ -487,6 +493,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
errors_buffer,
);
// If we produce any errors, we keep track of the names of all regions, so that we can use
// the same error names in any suggestions we produce. Note that we need names to be unique
// across different errors for the same MIR def so that we can make suggestions that fix
// multiple problems.
let mut region_naming = RegionErrorNamingCtx::new();
self.check_universal_regions(
infcx,
body,
@ -494,6 +506,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
mir_def_id,
outlives_requirements.as_mut(),
errors_buffer,
&mut region_naming,
);
self.check_member_constraints(infcx, mir_def_id, errors_buffer);
@ -1312,6 +1325,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
mir_def_id: DefId,
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) {
for (fr, fr_definition) in self.definitions.iter_enumerated() {
match fr_definition.origin {
@ -1326,7 +1340,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
mir_def_id,
fr,
&mut propagated_outlives_requirements,
errors_buffer,
region_naming,
);
}
@ -1357,7 +1371,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
mir_def_id: DefId,
longer_fr: RegionVid,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) {
debug!("check_universal_region(fr={:?})", longer_fr);
@ -1384,7 +1398,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
upvars,
mir_def_id,
propagated_outlives_requirements,
errors_buffer,
region_naming,
);
return;
}
@ -1400,9 +1414,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
upvars,
mir_def_id,
propagated_outlives_requirements,
errors_buffer,
region_naming,
) {
// continuing to iterate just reports more errors than necessary
//
// FIXME It would also allow us to report more Outlives Suggestions, though, so
// it's not clear that that's a bad thing. Somebody should try commenting out this
// line and see it is actually a regression.
return;
}
}
@ -1417,7 +1435,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
upvars: &[Upvar],
mir_def_id: DefId,
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
errors_buffer: &mut Vec<Diagnostic>,
region_naming: &mut RegionErrorNamingCtx,
) -> Option<ErrorReported> {
// If it is known that `fr: o`, carry on.
if self.universal_region_relations.outlives(longer_fr, shorter_fr) {
@ -1466,7 +1484,18 @@ impl<'tcx> RegionInferenceContext<'tcx> {
//
// Note: in this case, we use the unapproximated regions to report the
// error. This gives better error messages in some cases.
self.report_error(body, upvars, infcx, mir_def_id, longer_fr, shorter_fr, errors_buffer);
let db = self.report_error(
body,
upvars,
infcx,
mir_def_id,
longer_fr,
shorter_fr,
region_naming,
);
db.buffer(errors_buffer);
Some(ErrorReported)
}