diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs index efa18587b7ddb..5aaa4bb7b072d 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/mod.rs @@ -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, - ) { + 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, - ) { - 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, - ) { + 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, - ) { + 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) } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs index 75a31628a54b6..6fa94269107f5 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/error_reporting/region_name.rs @@ -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, + + /// 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(¬e); - }, + } 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 { - 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 { 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 { 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 { 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 { + 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 { 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 { // 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 { 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 { 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 { // 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)) } diff --git a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs index 40388722bcac9..7250768699c5f 100644 --- a/src/librustc_mir/borrow_check/nll/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/nll/region_infer/mod.rs @@ -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 crate::borrow_check::nll::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex}; -use crate::borrow_check::nll::region_infer::values::{ - PlaceholderIndices, RegionElement, ToElementIndex, +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::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>>, errors_buffer: &mut Vec, + 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>>, - errors_buffer: &mut Vec, + 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>>, - errors_buffer: &mut Vec, + region_naming: &mut RegionErrorNamingCtx, ) -> Option { // 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) }