From 9a92526b98ed5c0e3b5faad5b0809936170ba818 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 8 Jul 2024 20:35:26 -0400 Subject: [PATCH] Consolidate region error reporting in rustc_infer --- .../src/diagnostics/region_errors.rs | 12 +- .../src/infer/error_reporting/mod.rs | 851 +---------- .../src/infer/error_reporting/note.rs | 421 ------ .../src/infer/error_reporting/region.rs | 1259 +++++++++++++++++ 4 files changed, 1273 insertions(+), 1270 deletions(-) delete mode 100644 compiler/rustc_infer/src/infer/error_reporting/note.rs create mode 100644 compiler/rustc_infer/src/infer/error_reporting/region.rs diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 13839214adc05..55147ee337fde 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -10,14 +10,12 @@ use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicate::BoundPredicate; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; -use rustc_infer::infer::{ - error_reporting::nice_region_error::{ - self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, - HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, - }, - error_reporting::unexpected_hidden_region_diagnostic, - NllRegionVariableOrigin, RelateParamBound, +use rustc_infer::infer::error_reporting::nice_region_error::{ + self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, + HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, }; +use rustc_infer::infer::error_reporting::region::unexpected_hidden_region_diagnostic; +use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index cebb9d09a4798..bb1285ee8135d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -45,13 +45,10 @@ //! ported to this system, and which relies on string concatenation at the //! time of error detection. -use super::lexical_region_resolve::RegionResolutionError; -use super::region_constraints::GenericKind; -use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; +use super::{InferCtxt, TypeTrace, ValuePairs}; -use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags}; +use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; -use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::ExpectedFound; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, @@ -61,38 +58,36 @@ use crate::traits::{ use crate::infer::relate::{self, RelateResult, TypeRelation}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, - DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart, + pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart, }; use rustc_hir::def::DefKind; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir, ParamName}; +use rustc_hir::{self as hir}; use rustc_macros::extension; use rustc_middle::bug; use rustc_middle::dep_graph::DepContext; use rustc_middle::ty::error::TypeErrorToStringExt; use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _}; -use rustc_middle::ty::Upcast; use rustc_middle::ty::{ - self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable, - TypeSuperVisitable, TypeVisitable, TypeVisitableExt, + self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitableExt, }; -use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; +use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; use std::borrow::Cow; use std::ops::{ControlFlow, Deref}; use std::path::PathBuf; use std::{cmp, fmt, iter}; -mod note; mod note_and_explain; mod suggest; pub(crate) mod need_type_info; pub mod sub_relations; pub use need_type_info::TypeAnnotationNeeded; +pub mod region; pub mod nice_region_error; @@ -159,245 +154,6 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { } } -pub(super) fn note_and_explain_region<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - prefix: &str, - region: ty::Region<'tcx>, - suffix: &str, - alt_span: Option, -) { - let (description, span) = match *region { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { - msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) - } - - ty::ReError(_) => return, - - // FIXME(#125431): `ReVar` shouldn't reach here. - ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), - - ty::ReBound(..) | ty::ReErased => { - bug!("unexpected region for note_and_explain_region: {:?}", region); - } - }; - - emit_msg_span(err, prefix, description, span, suffix); -} - -fn explain_free_region<'tcx>( - tcx: TyCtxt<'tcx>, - err: &mut Diag<'_>, - generic_param_scope: LocalDefId, - prefix: &str, - region: ty::Region<'tcx>, - suffix: &str, -) { - let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None); - - label_msg_span(err, prefix, description, span, suffix); -} - -fn msg_span_from_named_region<'tcx>( - tcx: TyCtxt<'tcx>, - generic_param_scope: LocalDefId, - region: ty::Region<'tcx>, - alt_span: Option, -) -> (String, Option) { - match *region { - ty::ReEarlyParam(br) => { - let scope = tcx - .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) - .expect_local(); - let span = if let Some(param) = - tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) - { - param.span - } else { - tcx.def_span(scope) - }; - let text = if br.has_name() { - format!("the lifetime `{}` as defined here", br.name) - } else { - "the anonymous lifetime as defined here".to_string() - }; - (text, Some(span)) - } - ty::ReLateParam(ref fr) => { - if !fr.bound_region.is_named() - && let Some((ty, _)) = - find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) - { - ("the anonymous lifetime defined here".to_string(), Some(ty.span)) - } else { - match fr.bound_region { - ty::BoundRegionKind::BrNamed(_, name) => { - let span = if let Some(param) = tcx - .hir() - .get_generics(generic_param_scope) - .and_then(|generics| generics.get_named(name)) - { - param.span - } else { - tcx.def_span(generic_param_scope) - }; - let text = if name == kw::UnderscoreLifetime { - "the anonymous lifetime as defined here".to_string() - } else { - format!("the lifetime `{name}` as defined here") - }; - (text, Some(span)) - } - ty::BrAnon => ( - "the anonymous lifetime as defined here".to_string(), - Some(tcx.def_span(generic_param_scope)), - ), - _ => ( - format!("the lifetime `{region}` as defined here"), - Some(tcx.def_span(generic_param_scope)), - ), - } - } - } - ty::ReStatic => ("the static lifetime".to_owned(), alt_span), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. }, - .. - }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), - ty::RePlaceholder(ty::PlaceholderRegion { - bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. }, - .. - }) => ("an anonymous lifetime".to_owned(), None), - _ => bug!("{:?}", region), - } -} - -fn emit_msg_span( - err: &mut Diag<'_>, - prefix: &str, - description: String, - span: Option, - suffix: &str, -) { - let message = format!("{prefix}{description}{suffix}"); - - if let Some(span) = span { - err.span_note(span, message); - } else { - err.note(message); - } -} - -fn label_msg_span( - err: &mut Diag<'_>, - prefix: &str, - description: String, - span: Option, - suffix: &str, -) { - let message = format!("{prefix}{description}{suffix}"); - - if let Some(span) = span { - err.span_label(span, message); - } else { - err.note(message); - } -} - -#[instrument(level = "trace", skip(infcx))] -pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( - infcx: &'a InferCtxt<'tcx>, - generic_param_scope: LocalDefId, - span: Span, - hidden_ty: Ty<'tcx>, - hidden_region: ty::Region<'tcx>, - opaque_ty_key: ty::OpaqueTypeKey<'tcx>, -) -> Diag<'a> { - let tcx = infcx.tcx; - let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime { - span, - opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args), - opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), - }); - - // Explain the region we are capturing. - match *hidden_region { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { - // Assuming regionck succeeded (*), we ought to always be - // capturing *some* region from the fn header, and hence it - // ought to be free. So under normal circumstances, we will go - // down this path which gives a decent human readable - // explanation. - // - // (*) if not, the `tainted_by_errors` field would be set to - // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all. - explain_free_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{hidden_ty}` captures "), - hidden_region, - "", - ); - if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) { - let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); - nice_region_error::suggest_new_region_bound( - tcx, - &mut err, - fn_returns, - hidden_region.to_string(), - None, - format!("captures `{hidden_region}`"), - None, - Some(reg_info.def_id), - ) - } - } - ty::RePlaceholder(_) => { - explain_free_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{}` captures ", hidden_ty), - hidden_region, - "", - ); - } - ty::ReError(_) => { - err.downgrade_to_delayed_bug(); - } - _ => { - // Ugh. This is a painful case: the hidden region is not one - // that we can easily summarize or explain. This can happen - // in a case like - // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: - // - // ``` - // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { - // if condition() { a } else { b } - // } - // ``` - // - // Here the captured lifetime is the intersection of `'a` and - // `'b`, which we can't quite express. - - // We can at least report a really cryptic error for now. - note_and_explain_region( - tcx, - &mut err, - generic_param_scope, - &format!("hidden type `{hidden_ty}` captures "), - hidden_region, - "", - None, - ); - } - } - - err -} - impl<'tcx> InferCtxt<'tcx> { pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option> { let (def_id, args) = match *ty.kind() { @@ -438,193 +194,6 @@ impl<'tcx> InferCtxt<'tcx> { } impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub fn report_region_errors( - &self, - generic_param_scope: LocalDefId, - errors: &[RegionResolutionError<'tcx>], - ) -> ErrorGuaranteed { - assert!(!errors.is_empty()); - - if let Some(guaranteed) = self.infcx.tainted_by_errors() { - return guaranteed; - } - - debug!("report_region_errors(): {} errors to start", errors.len()); - - // try to pre-process the errors, which will group some of them - // together into a `ProcessedErrors` group: - let errors = self.process_errors(errors); - - debug!("report_region_errors: {} errors after preprocessing", errors.len()); - - let mut guar = None; - for error in errors { - debug!("report_region_errors: error = {:?}", error); - - let e = if let Some(guar) = - self.try_report_nice_region_error(generic_param_scope, &error) - { - guar - } else { - match error.clone() { - // These errors could indicate all manner of different - // problems with many different solutions. Rather - // than generate a "one size fits all" error, what we - // attempt to do is go through a number of specific - // scenarios and try to find the best way to present - // the error. If all of these fails, we fall back to a rather - // general bit of code that displays the error information - RegionResolutionError::ConcreteFailure(origin, sub, sup) => { - if sub.is_placeholder() || sup.is_placeholder() { - self.report_placeholder_failure(generic_param_scope, origin, sub, sup) - .emit() - } else { - self.report_concrete_failure(generic_param_scope, origin, sub, sup) - .emit() - } - } - - RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self - .report_generic_bound_failure( - generic_param_scope, - origin.span(), - Some(origin), - param_ty, - sub, - ), - - RegionResolutionError::SubSupConflict( - _, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - _, - ) => { - if sub_r.is_placeholder() { - self.report_placeholder_failure( - generic_param_scope, - sub_origin, - sub_r, - sup_r, - ) - .emit() - } else if sup_r.is_placeholder() { - self.report_placeholder_failure( - generic_param_scope, - sup_origin, - sub_r, - sup_r, - ) - .emit() - } else { - self.report_sub_sup_conflict( - generic_param_scope, - var_origin, - sub_origin, - sub_r, - sup_origin, - sup_r, - ) - } - } - - RegionResolutionError::UpperBoundUniverseConflict( - _, - _, - _, - sup_origin, - sup_r, - ) => { - assert!(sup_r.is_placeholder()); - - // Make a dummy value for the "sub region" -- - // this is the initial value of the - // placeholder. In practice, we expect more - // tailored errors that don't really use this - // value. - let sub_r = self.tcx.lifetimes.re_erased; - - self.report_placeholder_failure( - generic_param_scope, - sup_origin, - sub_r, - sup_r, - ) - .emit() - } - - RegionResolutionError::CannotNormalize(clause, origin) => { - let clause: ty::Clause<'tcx> = - clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx); - self.tcx - .dcx() - .struct_span_err(origin.span(), format!("cannot normalize `{clause}`")) - .emit() - } - } - }; - - guar = Some(e) - } - - guar.unwrap() - } - - // This method goes through all the errors and try to group certain types - // of error together, for the purpose of suggesting explicit lifetime - // parameters to the user. This is done so that we can have a more - // complete view of what lifetimes should be the same. - // If the return value is an empty vector, it means that processing - // failed (so the return value of this method should not be used). - // - // The method also attempts to weed out messages that seem like - // duplicates that will be unhelpful to the end-user. But - // obviously it never weeds out ALL errors. - fn process_errors( - &self, - errors: &[RegionResolutionError<'tcx>], - ) -> Vec> { - debug!("process_errors()"); - - // We want to avoid reporting generic-bound failures if we can - // avoid it: these have a very high rate of being unhelpful in - // practice. This is because they are basically secondary - // checks that test the state of the region graph after the - // rest of inference is done, and the other kinds of errors - // indicate that the region constraint graph is internally - // inconsistent, so these test results are likely to be - // meaningless. - // - // Therefore, we filter them out of the list unless they are - // the only thing in the list. - - let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { - RegionResolutionError::GenericBoundFailure(..) => true, - RegionResolutionError::ConcreteFailure(..) - | RegionResolutionError::SubSupConflict(..) - | RegionResolutionError::UpperBoundUniverseConflict(..) - | RegionResolutionError::CannotNormalize(..) => false, - }; - - let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { - errors.to_owned() - } else { - errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() - }; - - // sort the errors by span, for better error message stability. - errors.sort_by_key(|u| match *u { - RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), - RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), - RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), - RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(), - }); - errors - } - /// Adds a note if the types come from similarly named crates fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { use hir::def_id::CrateNum; @@ -2341,359 +1910,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { )) } - pub fn report_generic_bound_failure( - &self, - generic_param_scope: LocalDefId, - span: Span, - origin: Option>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>, - ) -> ErrorGuaranteed { - self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) - .emit() - } - - pub fn construct_generic_bound_failure( - &self, - generic_param_scope: LocalDefId, - span: Span, - origin: Option>, - bound_kind: GenericKind<'tcx>, - sub: Region<'tcx>, - ) -> Diag<'a> { - if let Some(SubregionOrigin::CompareImplItemObligation { - span, - impl_item_def_id, - trait_item_def_id, - }) = origin - { - return self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{bound_kind}: {sub}`"), - ); - } - - let labeled_user_string = match bound_kind { - GenericKind::Param(ref p) => format!("the parameter type `{p}`"), - GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), - GenericKind::Alias(ref p) => match p.kind(self.tcx) { - ty::Projection | ty::Inherent => { - format!("the associated type `{p}`") - } - ty::Weak => format!("the type alias `{p}`"), - ty::Opaque => format!("the opaque type `{p}`"), - }, - }; - - let mut err = self - .tcx - .dcx() - .struct_span_err(span, format!("{labeled_user_string} may not live long enough")); - err.code(match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309, - ty::ReStatic => E0310, - _ => E0311, - }); - - '_explain: { - let (description, span) = match sub.kind() { - ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { - msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span)) - } - _ => (format!("lifetime `{sub}`"), Some(span)), - }; - let prefix = format!("{labeled_user_string} must be valid for "); - label_msg_span(&mut err, &prefix, description, span, "..."); - if let Some(origin) = origin { - self.note_region_origin(&mut err, &origin); - } - } - - 'suggestion: { - let msg = "consider adding an explicit lifetime bound"; - - if (bound_kind, sub).has_infer_regions() - || (bound_kind, sub).has_placeholders() - || !bound_kind.is_suggestable(self.tcx, false) - { - let lt_name = sub.get_name_or_anon().to_string(); - err.help(format!("{msg} `{bound_kind}: {lt_name}`...")); - break 'suggestion; - } - - let mut generic_param_scope = generic_param_scope; - while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy { - generic_param_scope = self.tcx.local_parent(generic_param_scope); - } - - // type_param_sugg_span is (span, has_bounds, needs_parentheses) - let (type_scope, type_param_sugg_span) = match bound_kind { - GenericKind::Param(param) => { - let generics = self.tcx.generics_of(generic_param_scope); - let type_param = generics.type_param(param, self.tcx); - let def_id = type_param.def_id.expect_local(); - let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; - // Get the `hir::Param` to verify whether it already has any bounds. - // We do this to avoid suggesting code that ends up as `T: 'a'b`, - // instead we suggest `T: 'a + 'b` in that case. - let hir_generics = self.tcx.hir().get_generics(scope).unwrap(); - let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) { - Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), - // If `param` corresponds to `Self`, no usable suggestion span. - None if generics.has_self && param.index == 0 => None, - None => { - let span = if let Some(param) = - hir_generics.params.iter().find(|param| param.def_id == def_id) - && let ParamName::Plain(ident) = param.name - { - ident.span.shrink_to_hi() - } else { - let span = self.tcx.def_span(def_id); - span.shrink_to_hi() - }; - Some((span, false, None)) - } - }; - (scope, sugg_span) - } - _ => (generic_param_scope, None), - }; - let suggestion_scope = { - let lifetime_scope = match sub.kind() { - ty::ReStatic => hir::def_id::CRATE_DEF_ID, - _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { - Some(info) => info.def_id, - None => generic_param_scope, - }, - }; - match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { - true => type_scope, - false => lifetime_scope, - } - }; - - let mut suggs = vec![]; - let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); - - if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span - && suggestion_scope == type_scope - { - let suggestion = - if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") }; - - if let Some(open_paren_sp) = open_paren_sp { - suggs.push((open_paren_sp, "(".to_string())); - suggs.push((sp, format!("){suggestion}"))); - } else { - suggs.push((sp, suggestion)) - } - } else if let GenericKind::Alias(ref p) = bound_kind - && let ty::Projection = p.kind(self.tcx) - && let DefKind::AssocTy = self.tcx.def_kind(p.def_id) - && let Some(ty::ImplTraitInTraitData::Trait { .. }) = - self.tcx.opt_rpitit_info(p.def_id) - { - // The lifetime found in the `impl` is longer than the one on the RPITIT. - // Do not suggest `::{opaque}: 'static`. - } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) { - let pred = format!("{bound_kind}: {lt_name}"); - let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); - suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) - } else { - let consider = format!("{msg} `{bound_kind}: {sub}`..."); - err.help(consider); - } - - if !suggs.is_empty() { - err.multipart_suggestion_verbose( - msg, - suggs, - Applicability::MaybeIncorrect, // Issue #41966 - ); - } - } - - err - } - - pub fn suggest_name_region( - &self, - generic_param_scope: LocalDefId, - lifetime: Region<'tcx>, - add_lt_suggs: &mut Vec<(Span, String)>, - ) -> String { - struct LifetimeReplaceVisitor<'tcx, 'a> { - tcx: TyCtxt<'tcx>, - needle: hir::LifetimeName, - new_lt: &'a str, - add_lt_suggs: &'a mut Vec<(Span, String)>, - } - - impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> { - fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { - if lt.res == self.needle { - let (pos, span) = lt.suggestion_position(); - let new_lt = &self.new_lt; - let sugg = match pos { - hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"), - hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "), - hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"), - hir::LifetimeSuggestionPosition::ElidedPathArgument => { - format!("{new_lt}, ") - } - hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"), - }; - self.add_lt_suggs.push((span, sugg)); - } - } - - fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { - let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { - return hir::intravisit::walk_ty(self, ty); - }; - let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); - if let Some(&(_, b)) = - opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle) - { - let prev_needle = - std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b)); - for bound in opaque_ty.bounds { - self.visit_param_bound(bound); - } - self.needle = prev_needle; - } - } - } - - let (lifetime_def_id, lifetime_scope) = - match self.tcx.is_suitable_region(generic_param_scope, lifetime) { - Some(info) if !lifetime.has_name() => { - (info.bound_region.get_id().unwrap().expect_local(), info.def_id) - } - _ => return lifetime.get_name_or_anon().to_string(), - }; - - let new_lt = { - let generics = self.tcx.generics_of(lifetime_scope); - let mut used_names = - iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) - .flat_map(|g| &g.own_params) - .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) - .map(|p| p.name) - .collect::>(); - let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope); - // consider late-bound lifetimes ... - used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map( - |p| match p { - ty::BoundVariableKind::Region(lt) => lt.get_name(), - _ => None, - }, - )); - (b'a'..=b'z') - .map(|c| format!("'{}", c as char)) - .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate)) - .unwrap_or("'lt".to_string()) - }; - - let mut visitor = LifetimeReplaceVisitor { - tcx: self.tcx, - needle: hir::LifetimeName::Param(lifetime_def_id), - add_lt_suggs, - new_lt: &new_lt, - }; - match self.tcx.expect_hir_owner_node(lifetime_scope) { - hir::OwnerNode::Item(i) => visitor.visit_item(i), - hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i), - hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), - hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), - hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), - hir::OwnerNode::Synthetic => unreachable!(), - } - - let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); - let sugg = ast_generics - .span_for_lifetime_suggestion() - .map(|span| (span, format!("{new_lt}, "))) - .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>"))); - add_lt_suggs.push(sugg); - - new_lt - } - - fn report_sub_sup_conflict( - &self, - generic_param_scope: LocalDefId, - var_origin: RegionVariableOrigin, - sub_origin: SubregionOrigin<'tcx>, - sub_region: Region<'tcx>, - sup_origin: SubregionOrigin<'tcx>, - sup_region: Region<'tcx>, - ) -> ErrorGuaranteed { - let mut err = self.report_inference_failure(var_origin); - - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "first, the lifetime cannot outlive ", - sup_region, - "...", - None, - ); - - debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); - debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); - debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); - debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); - debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); - - if let infer::Subtype(ref sup_trace) = sup_origin - && let infer::Subtype(ref sub_trace) = sub_origin - && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values) - && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values) - && sub_expected == sup_expected - && sub_found == sup_found - { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...but the lifetime must also be valid for ", - sub_region, - "...", - None, - ); - err.span_note( - sup_trace.cause.span, - format!("...so that the {}", sup_trace.cause.as_requirement_str()), - ); - - err.note_expected_found(&"", sup_expected, &"", sup_found); - return if sub_region.is_error() | sup_region.is_error() { - err.delay_as_bug() - } else { - err.emit() - }; - } - - self.note_region_origin(&mut err, &sup_origin); - - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "but, the lifetime must be valid for ", - sub_region, - "...", - None, - ); - - self.note_region_origin(&mut err, &sub_origin); - if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } - } - /// Determine whether an error associated with the given span and definition /// should be treated as being caused by the implicit `From` conversion /// within `?` desugaring. @@ -2792,55 +2008,6 @@ impl<'tcx> TypeRelation> for SameTypeModuloInfer<'_, 'tcx> { } } -impl<'tcx> InferCtxt<'tcx> { - fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> { - let br_string = |br: ty::BoundRegionKind| { - let mut s = match br { - ty::BrNamed(_, name) => name.to_string(), - _ => String::new(), - }; - if !s.is_empty() { - s.push(' '); - } - s - }; - let var_description = match var_origin { - infer::MiscVariable(_) => String::new(), - infer::PatternRegion(_) => " for pattern".to_string(), - infer::AddrOfRegion(_) => " for borrow expression".to_string(), - infer::Autoref(_) => " for autoref".to_string(), - infer::Coercion(_) => " for automatic coercion".to_string(), - infer::BoundRegion(_, br, infer::FnCall) => { - format!(" for lifetime parameter {}in function call", br_string(br)) - } - infer::BoundRegion(_, br, infer::HigherRankedType) => { - format!(" for lifetime parameter {}in generic type", br_string(br)) - } - infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( - " for lifetime parameter {}in trait containing associated type `{}`", - br_string(br), - self.tcx.associated_item(def_id).name - ), - infer::RegionParameterDefinition(_, name) => { - format!(" for lifetime parameter `{name}`") - } - infer::UpvarRegion(ref upvar_id, _) => { - let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); - format!(" for capture of `{var_name}` by closure") - } - infer::Nll(..) => bug!("NLL variable found in lexical phase"), - }; - - struct_span_code_err!( - self.dcx(), - var_origin.span(), - E0495, - "cannot infer an appropriate lifetime{} due to conflicting requirements", - var_description - ) - } -} - pub enum FailureCode { Error0317, Error0580, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs deleted file mode 100644 index d1fc9c9f140b1..0000000000000 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ /dev/null @@ -1,421 +0,0 @@ -use crate::errors::{ - note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, - RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, -}; -use crate::fluent_generated as fluent; -use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; -use crate::infer::{self, SubregionOrigin}; -use rustc_errors::{Diag, Subdiagnostic}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::traits::ObligationCauseCode; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::{self, IsSuggestable, Region, Ty}; -use rustc_span::symbol::kw; - -use super::ObligationCauseAsDiagArg; - -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { - match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), - } - .add_to_diag(err), - infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) - } - infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diag(err); - } - infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diag(err); - } - infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diag(err); - if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); - } - } - infer::RelateRegionParamBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); - } - infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(err); - } - infer::CheckAssociatedTypeBounds { ref parent, .. } => { - self.note_region_origin(err, parent); - } - infer::AscribeUserTypeProvePredicate(span) => { - RegionOriginNote::Plain { - span, - msg: fluent::infer_ascribe_user_type_prove_predicate, - } - .add_to_diag(err); - } - } - } - - pub(super) fn report_concrete_failure( - &self, - generic_param_scope: LocalDefId, - origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - let mut err = match origin { - infer::Subtype(box trace) => { - let terr = TypeError::RegionsDoesNotOutlive(sup, sub); - let mut err = self.report_and_explain_type_error(trace, terr); - match (*sub, *sup) { - (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} - (ty::RePlaceholder(_), _) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - " doesn't meet the lifetime requirements", - None, - ); - } - (_, ty::RePlaceholder(_)) => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "the required lifetime does not necessarily outlive ", - sub, - "", - None, - ); - } - _ => { - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "", - sup, - "...", - None, - ); - note_and_explain_region( - self.tcx, - &mut err, - generic_param_scope, - "...does not necessarily outlive ", - sub, - "", - None, - ); - } - } - err - } - infer::Reborrow(span) => { - let reference_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::RefValidFor, - note_and_explain::SuffixKind::Continues, - ); - let content_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::ContentValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesContent { - span, - notes: reference_valid.into_iter().chain(content_valid).collect(), - }) - } - infer::RelateObjectBound(span) => { - let object_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::TypeObjValidFor, - note_and_explain::SuffixKind::Empty, - ); - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::SourcePointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(OutlivesBound { - span, - notes: object_valid.into_iter().chain(pointer_valid).collect(), - }) - } - infer::RelateParamBound(span, ty, opt_span) => { - let prefix = match *sub { - ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, - _ => note_and_explain::PrefixKind::TypeOutlive, - }; - let suffix = if opt_span.is_some() { - note_and_explain::SuffixKind::ReqByBinding - } else { - note_and_explain::SuffixKind::Empty - }; - let note = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - opt_span, - prefix, - suffix, - ); - self.dcx().create_err(FulfillReqLifetime { - span, - ty: self.resolve_vars_if_possible(ty), - note, - }) - } - infer::RelateRegionParamBound(span) => { - let param_instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfParamInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let param_must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfParamMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), - }) - } - infer::ReferenceOutlivesReferent(ty, span) => { - let pointer_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::PointerValidFor, - note_and_explain::SuffixKind::Empty, - ); - let data_valid = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::DataValidFor, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(RefLongerThanData { - span, - ty: self.resolve_vars_if_possible(ty), - notes: pointer_valid.into_iter().chain(data_valid).collect(), - }) - } - infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.infcx.report_extra_impl_obligation( - span, - impl_item_def_id, - trait_item_def_id, - &format!("`{sup}: {sub}`"), - ); - // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause - if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) - && generics.where_clause_span.contains(span) - { - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - } - err - } - infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { - let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); - - // Don't mention the item name if it's an RPITIT, since that'll just confuse - // folks. - if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { - let trait_item_span = self.tcx.def_span(trait_item_def_id); - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label( - trait_item_span, - format!("definition of `{item_name}` from trait"), - ); - } - - self.suggest_copy_trait_method_bounds( - trait_item_def_id, - impl_item_def_id, - &mut err, - ); - err - } - infer::AscribeUserTypeProvePredicate(span) => { - let instantiated = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sup, - None, - note_and_explain::PrefixKind::LfInstantiatedWith, - note_and_explain::SuffixKind::Empty, - ); - let must_outlive = note_and_explain::RegionExplanation::new( - self.tcx, - generic_param_scope, - sub, - None, - note_and_explain::PrefixKind::LfMustOutlive, - note_and_explain::SuffixKind::Empty, - ); - self.dcx().create_err(LfBoundNotSatisfied { - span, - notes: instantiated.into_iter().chain(must_outlive).collect(), - }) - } - }; - if sub.is_error() || sup.is_error() { - err.downgrade_to_delayed_bug(); - } - err - } - - pub fn suggest_copy_trait_method_bounds( - &self, - trait_item_def_id: DefId, - impl_item_def_id: LocalDefId, - err: &mut Diag<'_>, - ) { - // FIXME(compiler-errors): Right now this is only being used for region - // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, - // but right now it's not really very smart when it comes to implicit `Sized` - // predicates and bounds on the trait itself. - - let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) - else { - return; - }; - let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { - return; - }; - let trait_args = trait_ref - .instantiate_identity() - // Replace the explicit self type with `Self` for better suggestion rendering - .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) - .args; - let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) - .rebase_onto(self.tcx, impl_def_id, trait_args); - - let Ok(trait_predicates) = - self.tcx - .explicit_predicates_of(trait_item_def_id) - .instantiate_own(self.tcx, trait_item_args) - .map(|(pred, _)| { - if pred.is_suggestable(self.tcx, false) { - Ok(pred.to_string()) - } else { - Err(()) - } - }) - .collect::, ()>>() - else { - return; - }; - - let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { - return; - }; - - let suggestion = if trait_predicates.is_empty() { - WhereClauseSuggestions::Remove { span: generics.where_clause_span } - } else { - let space = if generics.where_clause_span.is_empty() { " " } else { "" }; - WhereClauseSuggestions::CopyPredicates { - span: generics.where_clause_span, - space, - trait_predicates: trait_predicates.join(", "), - } - }; - err.subdiagnostic(suggestion); - } - - pub(super) fn report_placeholder_failure( - &self, - generic_param_scope: LocalDefId, - placeholder_origin: SubregionOrigin<'tcx>, - sub: Region<'tcx>, - sup: Region<'tcx>, - ) -> Diag<'a> { - // I can't think how to do better than this right now. -nikomatsakis - debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); - match placeholder_origin { - infer::Subtype(box ref trace) - if matches!( - &trace.cause.code().peel_derives(), - ObligationCauseCode::WhereClause(..) - | ObligationCauseCode::WhereClauseInExpr(..) - ) => - { - // Hack to get around the borrow checker because trace.cause has an `Rc`. - if let ObligationCauseCode::WhereClause(_, span) - | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = - &trace.cause.code().peel_derives() - && !span.is_dummy() - { - let span = *span; - self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) - .with_span_note(span, "the lifetime requirement is introduced here") - } else { - unreachable!( - "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." - ) - } - } - infer::Subtype(box trace) => { - let terr = TypeError::RegionsPlaceholderMismatch; - return self.report_and_explain_type_error(trace, terr); - } - _ => { - return self.report_concrete_failure( - generic_param_scope, - placeholder_origin, - sub, - sup, - ); - } - } - } -} diff --git a/compiler/rustc_infer/src/infer/error_reporting/region.rs b/compiler/rustc_infer/src/infer/error_reporting/region.rs new file mode 100644 index 0000000000000..5a465f46e47dc --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/region.rs @@ -0,0 +1,1259 @@ +use std::iter; + +use rustc_errors::{ + struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495, +}; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{self as hir, ParamName}; +use rustc_middle::bug; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _}; +use rustc_span::symbol::kw; +use rustc_span::{ErrorGuaranteed, Span}; +use rustc_type_ir::Upcast as _; + +use super::nice_region_error::find_anon_type; +use super::{nice_region_error, ObligationCauseAsDiagArg}; +use crate::errors::{ + self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, + OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, +}; +use crate::fluent_generated as fluent; +use crate::infer::error_reporting::{ObligationCauseExt as _, TypeErrCtxt}; +use crate::infer::region_constraints::GenericKind; +use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin}; + +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn report_region_errors( + &self, + generic_param_scope: LocalDefId, + errors: &[RegionResolutionError<'tcx>], + ) -> ErrorGuaranteed { + assert!(!errors.is_empty()); + + if let Some(guaranteed) = self.infcx.tainted_by_errors() { + return guaranteed; + } + + debug!("report_region_errors(): {} errors to start", errors.len()); + + // try to pre-process the errors, which will group some of them + // together into a `ProcessedErrors` group: + let errors = self.process_errors(errors); + + debug!("report_region_errors: {} errors after preprocessing", errors.len()); + + let mut guar = None; + for error in errors { + debug!("report_region_errors: error = {:?}", error); + + let e = if let Some(guar) = + self.try_report_nice_region_error(generic_param_scope, &error) + { + guar + } else { + match error.clone() { + // These errors could indicate all manner of different + // problems with many different solutions. Rather + // than generate a "one size fits all" error, what we + // attempt to do is go through a number of specific + // scenarios and try to find the best way to present + // the error. If all of these fails, we fall back to a rather + // general bit of code that displays the error information + RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + if sub.is_placeholder() || sup.is_placeholder() { + self.report_placeholder_failure(generic_param_scope, origin, sub, sup) + .emit() + } else { + self.report_concrete_failure(generic_param_scope, origin, sub, sup) + .emit() + } + } + + RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self + .report_generic_bound_failure( + generic_param_scope, + origin.span(), + Some(origin), + param_ty, + sub, + ), + + RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + _, + ) => { + if sub_r.is_placeholder() { + self.report_placeholder_failure( + generic_param_scope, + sub_origin, + sub_r, + sup_r, + ) + .emit() + } else if sup_r.is_placeholder() { + self.report_placeholder_failure( + generic_param_scope, + sup_origin, + sub_r, + sup_r, + ) + .emit() + } else { + self.report_sub_sup_conflict( + generic_param_scope, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + ) + } + } + + RegionResolutionError::UpperBoundUniverseConflict( + _, + _, + _, + sup_origin, + sup_r, + ) => { + assert!(sup_r.is_placeholder()); + + // Make a dummy value for the "sub region" -- + // this is the initial value of the + // placeholder. In practice, we expect more + // tailored errors that don't really use this + // value. + let sub_r = self.tcx.lifetimes.re_erased; + + self.report_placeholder_failure( + generic_param_scope, + sup_origin, + sub_r, + sup_r, + ) + .emit() + } + + RegionResolutionError::CannotNormalize(clause, origin) => { + let clause: ty::Clause<'tcx> = + clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx); + self.tcx + .dcx() + .struct_span_err(origin.span(), format!("cannot normalize `{clause}`")) + .emit() + } + } + }; + + guar = Some(e) + } + + guar.unwrap() + } + + // This method goes through all the errors and try to group certain types + // of error together, for the purpose of suggesting explicit lifetime + // parameters to the user. This is done so that we can have a more + // complete view of what lifetimes should be the same. + // If the return value is an empty vector, it means that processing + // failed (so the return value of this method should not be used). + // + // The method also attempts to weed out messages that seem like + // duplicates that will be unhelpful to the end-user. But + // obviously it never weeds out ALL errors. + fn process_errors( + &self, + errors: &[RegionResolutionError<'tcx>], + ) -> Vec> { + debug!("process_errors()"); + + // We want to avoid reporting generic-bound failures if we can + // avoid it: these have a very high rate of being unhelpful in + // practice. This is because they are basically secondary + // checks that test the state of the region graph after the + // rest of inference is done, and the other kinds of errors + // indicate that the region constraint graph is internally + // inconsistent, so these test results are likely to be + // meaningless. + // + // Therefore, we filter them out of the list unless they are + // the only thing in the list. + + let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { + RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::ConcreteFailure(..) + | RegionResolutionError::SubSupConflict(..) + | RegionResolutionError::UpperBoundUniverseConflict(..) + | RegionResolutionError::CannotNormalize(..) => false, + }; + + let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { + errors.to_owned() + } else { + errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() + }; + + // sort the errors by span, for better error message stability. + errors.sort_by_key(|u| match *u { + RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), + RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), + RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(), + }); + errors + } + + pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) { + match *origin { + infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { + span: trace.cause.span, + requirement: ObligationCauseAsDiagArg(trace.cause.clone()), + expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), + } + .add_to_diag(err), + infer::Reborrow(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) + } + infer::RelateObjectBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } + .add_to_diag(err); + } + infer::ReferenceOutlivesReferent(ty, span) => { + RegionOriginNote::WithName { + span, + msg: fluent::infer_reference_outlives_referent, + name: &self.ty_to_string(ty), + continues: false, + } + .add_to_diag(err); + } + infer::RelateParamBound(span, ty, opt_span) => { + RegionOriginNote::WithName { + span, + msg: fluent::infer_relate_param_bound, + name: &self.ty_to_string(ty), + continues: opt_span.is_some(), + } + .add_to_diag(err); + if let Some(span) = opt_span { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } + .add_to_diag(err); + } + } + infer::RelateRegionParamBound(span) => { + RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } + .add_to_diag(err); + } + infer::CompareImplItemObligation { span, .. } => { + RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } + .add_to_diag(err); + } + infer::CheckAssociatedTypeBounds { ref parent, .. } => { + self.note_region_origin(err, parent); + } + infer::AscribeUserTypeProvePredicate(span) => { + RegionOriginNote::Plain { + span, + msg: fluent::infer_ascribe_user_type_prove_predicate, + } + .add_to_diag(err); + } + } + } + + pub(super) fn report_concrete_failure( + &self, + generic_param_scope: LocalDefId, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> Diag<'a> { + let mut err = match origin { + infer::Subtype(box trace) => { + let terr = TypeError::RegionsDoesNotOutlive(sup, sub); + let mut err = self.report_and_explain_type_error(trace, terr); + match (*sub, *sup) { + (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} + (ty::RePlaceholder(_), _) => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "", + sup, + " doesn't meet the lifetime requirements", + None, + ); + } + (_, ty::RePlaceholder(_)) => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "the required lifetime does not necessarily outlive ", + sub, + "", + None, + ); + } + _ => { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "", + sup, + "...", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "...does not necessarily outlive ", + sub, + "", + None, + ); + } + } + err + } + infer::Reborrow(span) => { + let reference_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::RefValidFor, + note_and_explain::SuffixKind::Continues, + ); + let content_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::ContentValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(OutlivesContent { + span, + notes: reference_valid.into_iter().chain(content_valid).collect(), + }) + } + infer::RelateObjectBound(span) => { + let object_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::TypeObjValidFor, + note_and_explain::SuffixKind::Empty, + ); + let pointer_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::SourcePointerValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(OutlivesBound { + span, + notes: object_valid.into_iter().chain(pointer_valid).collect(), + }) + } + infer::RelateParamBound(span, ty, opt_span) => { + let prefix = match *sub { + ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy, + _ => note_and_explain::PrefixKind::TypeOutlive, + }; + let suffix = if opt_span.is_some() { + note_and_explain::SuffixKind::ReqByBinding + } else { + note_and_explain::SuffixKind::Empty + }; + let note = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + opt_span, + prefix, + suffix, + ); + self.dcx().create_err(FulfillReqLifetime { + span, + ty: self.resolve_vars_if_possible(ty), + note, + }) + } + infer::RelateRegionParamBound(span) => { + let param_instantiated = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::LfParamInstantiatedWith, + note_and_explain::SuffixKind::Empty, + ); + let param_must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::LfParamMustOutlive, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(LfBoundNotSatisfied { + span, + notes: param_instantiated.into_iter().chain(param_must_outlive).collect(), + }) + } + infer::ReferenceOutlivesReferent(ty, span) => { + let pointer_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::PointerValidFor, + note_and_explain::SuffixKind::Empty, + ); + let data_valid = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::DataValidFor, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(RefLongerThanData { + span, + ty: self.resolve_vars_if_possible(ty), + notes: pointer_valid.into_iter().chain(data_valid).collect(), + }) + } + infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { + let mut err = self.infcx.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{sup}: {sub}`"), + ); + // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause + if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) + && generics.where_clause_span.contains(span) + { + self.suggest_copy_trait_method_bounds( + trait_item_def_id, + impl_item_def_id, + &mut err, + ); + } + err + } + infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { + let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup); + + // Don't mention the item name if it's an RPITIT, since that'll just confuse + // folks. + if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) { + let trait_item_span = self.tcx.def_span(trait_item_def_id); + let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); + err.span_label( + trait_item_span, + format!("definition of `{item_name}` from trait"), + ); + } + + self.suggest_copy_trait_method_bounds( + trait_item_def_id, + impl_item_def_id, + &mut err, + ); + err + } + infer::AscribeUserTypeProvePredicate(span) => { + let instantiated = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sup, + None, + note_and_explain::PrefixKind::LfInstantiatedWith, + note_and_explain::SuffixKind::Empty, + ); + let must_outlive = note_and_explain::RegionExplanation::new( + self.tcx, + generic_param_scope, + sub, + None, + note_and_explain::PrefixKind::LfMustOutlive, + note_and_explain::SuffixKind::Empty, + ); + self.dcx().create_err(LfBoundNotSatisfied { + span, + notes: instantiated.into_iter().chain(must_outlive).collect(), + }) + } + }; + if sub.is_error() || sup.is_error() { + err.downgrade_to_delayed_bug(); + } + err + } + + pub fn suggest_copy_trait_method_bounds( + &self, + trait_item_def_id: DefId, + impl_item_def_id: LocalDefId, + err: &mut Diag<'_>, + ) { + // FIXME(compiler-errors): Right now this is only being used for region + // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches, + // but right now it's not really very smart when it comes to implicit `Sized` + // predicates and bounds on the trait itself. + + let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx) + else { + return; + }; + let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else { + return; + }; + let trait_args = trait_ref + .instantiate_identity() + // Replace the explicit self type with `Self` for better suggestion rendering + .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper)) + .args; + let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id) + .rebase_onto(self.tcx, impl_def_id, trait_args); + + let Ok(trait_predicates) = + self.tcx + .explicit_predicates_of(trait_item_def_id) + .instantiate_own(self.tcx, trait_item_args) + .map(|(pred, _)| { + if pred.is_suggestable(self.tcx, false) { + Ok(pred.to_string()) + } else { + Err(()) + } + }) + .collect::, ()>>() + else { + return; + }; + + let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { + return; + }; + + let suggestion = if trait_predicates.is_empty() { + WhereClauseSuggestions::Remove { span: generics.where_clause_span } + } else { + let space = if generics.where_clause_span.is_empty() { " " } else { "" }; + WhereClauseSuggestions::CopyPredicates { + span: generics.where_clause_span, + space, + trait_predicates: trait_predicates.join(", "), + } + }; + err.subdiagnostic(suggestion); + } + + pub(super) fn report_placeholder_failure( + &self, + generic_param_scope: LocalDefId, + placeholder_origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> Diag<'a> { + // I can't think how to do better than this right now. -nikomatsakis + debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); + match placeholder_origin { + infer::Subtype(box ref trace) + if matches!( + &trace.cause.code().peel_derives(), + ObligationCauseCode::WhereClause(..) + | ObligationCauseCode::WhereClauseInExpr(..) + ) => + { + // Hack to get around the borrow checker because trace.cause has an `Rc`. + if let ObligationCauseCode::WhereClause(_, span) + | ObligationCauseCode::WhereClauseInExpr(_, span, ..) = + &trace.cause.code().peel_derives() + && !span.is_dummy() + { + let span = *span; + self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup) + .with_span_note(span, "the lifetime requirement is introduced here") + } else { + unreachable!( + "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..." + ) + } + } + infer::Subtype(box trace) => { + let terr = TypeError::RegionsPlaceholderMismatch; + return self.report_and_explain_type_error(trace, terr); + } + _ => { + return self.report_concrete_failure( + generic_param_scope, + placeholder_origin, + sub, + sup, + ); + } + } + } + + pub fn report_generic_bound_failure( + &self, + generic_param_scope: LocalDefId, + span: Span, + origin: Option>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) -> ErrorGuaranteed { + self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) + .emit() + } + + pub fn construct_generic_bound_failure( + &self, + generic_param_scope: LocalDefId, + span: Span, + origin: Option>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) -> Diag<'a> { + if let Some(SubregionOrigin::CompareImplItemObligation { + span, + impl_item_def_id, + trait_item_def_id, + }) = origin + { + return self.infcx.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{bound_kind}: {sub}`"), + ); + } + + let labeled_user_string = match bound_kind { + GenericKind::Param(ref p) => format!("the parameter type `{p}`"), + GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"), + GenericKind::Alias(ref p) => match p.kind(self.tcx) { + ty::Projection | ty::Inherent => { + format!("the associated type `{p}`") + } + ty::Weak => format!("the type alias `{p}`"), + ty::Opaque => format!("the opaque type `{p}`"), + }, + }; + + let mut err = self + .tcx + .dcx() + .struct_span_err(span, format!("{labeled_user_string} may not live long enough")); + err.code(match sub.kind() { + ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309, + ty::ReStatic => E0310, + _ => E0311, + }); + + '_explain: { + let (description, span) = match sub.kind() { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { + msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span)) + } + _ => (format!("lifetime `{sub}`"), Some(span)), + }; + let prefix = format!("{labeled_user_string} must be valid for "); + label_msg_span(&mut err, &prefix, description, span, "..."); + if let Some(origin) = origin { + self.note_region_origin(&mut err, &origin); + } + } + + 'suggestion: { + let msg = "consider adding an explicit lifetime bound"; + + if (bound_kind, sub).has_infer_regions() + || (bound_kind, sub).has_placeholders() + || !bound_kind.is_suggestable(self.tcx, false) + { + let lt_name = sub.get_name_or_anon().to_string(); + err.help(format!("{msg} `{bound_kind}: {lt_name}`...")); + break 'suggestion; + } + + let mut generic_param_scope = generic_param_scope; + while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy { + generic_param_scope = self.tcx.local_parent(generic_param_scope); + } + + // type_param_sugg_span is (span, has_bounds, needs_parentheses) + let (type_scope, type_param_sugg_span) = match bound_kind { + GenericKind::Param(param) => { + let generics = self.tcx.generics_of(generic_param_scope); + let type_param = generics.type_param(param, self.tcx); + let def_id = type_param.def_id.expect_local(); + let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id; + // Get the `hir::Param` to verify whether it already has any bounds. + // We do this to avoid suggesting code that ends up as `T: 'a'b`, + // instead we suggest `T: 'a + 'b` in that case. + let hir_generics = self.tcx.hir().get_generics(scope).unwrap(); + let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) { + Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)), + // If `param` corresponds to `Self`, no usable suggestion span. + None if generics.has_self && param.index == 0 => None, + None => { + let span = if let Some(param) = + hir_generics.params.iter().find(|param| param.def_id == def_id) + && let ParamName::Plain(ident) = param.name + { + ident.span.shrink_to_hi() + } else { + let span = self.tcx.def_span(def_id); + span.shrink_to_hi() + }; + Some((span, false, None)) + } + }; + (scope, sugg_span) + } + _ => (generic_param_scope, None), + }; + let suggestion_scope = { + let lifetime_scope = match sub.kind() { + ty::ReStatic => hir::def_id::CRATE_DEF_ID, + _ => match self.tcx.is_suitable_region(generic_param_scope, sub) { + Some(info) => info.def_id, + None => generic_param_scope, + }, + }; + match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { + true => type_scope, + false => lifetime_scope, + } + }; + + let mut suggs = vec![]; + let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs); + + if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span + && suggestion_scope == type_scope + { + let suggestion = + if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") }; + + if let Some(open_paren_sp) = open_paren_sp { + suggs.push((open_paren_sp, "(".to_string())); + suggs.push((sp, format!("){suggestion}"))); + } else { + suggs.push((sp, suggestion)) + } + } else if let GenericKind::Alias(ref p) = bound_kind + && let ty::Projection = p.kind(self.tcx) + && let DefKind::AssocTy = self.tcx.def_kind(p.def_id) + && let Some(ty::ImplTraitInTraitData::Trait { .. }) = + self.tcx.opt_rpitit_info(p.def_id) + { + // The lifetime found in the `impl` is longer than the one on the RPITIT. + // Do not suggest `::{opaque}: 'static`. + } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) { + let pred = format!("{bound_kind}: {lt_name}"); + let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred); + suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion)) + } else { + let consider = format!("{msg} `{bound_kind}: {sub}`..."); + err.help(consider); + } + + if !suggs.is_empty() { + err.multipart_suggestion_verbose( + msg, + suggs, + Applicability::MaybeIncorrect, // Issue #41966 + ); + } + } + + err + } + + pub fn suggest_name_region( + &self, + generic_param_scope: LocalDefId, + lifetime: Region<'tcx>, + add_lt_suggs: &mut Vec<(Span, String)>, + ) -> String { + struct LifetimeReplaceVisitor<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + needle: hir::LifetimeName, + new_lt: &'a str, + add_lt_suggs: &'a mut Vec<(Span, String)>, + } + + impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> { + fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) { + if lt.res == self.needle { + let (pos, span) = lt.suggestion_position(); + let new_lt = &self.new_lt; + let sugg = match pos { + hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"), + hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "), + hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"), + hir::LifetimeSuggestionPosition::ElidedPathArgument => { + format!("{new_lt}, ") + } + hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"), + }; + self.add_lt_suggs.push((span, sugg)); + } + } + + fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { + let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else { + return hir::intravisit::walk_ty(self, ty); + }; + let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty(); + if let Some(&(_, b)) = + opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle) + { + let prev_needle = + std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b)); + for bound in opaque_ty.bounds { + self.visit_param_bound(bound); + } + self.needle = prev_needle; + } + } + } + + let (lifetime_def_id, lifetime_scope) = + match self.tcx.is_suitable_region(generic_param_scope, lifetime) { + Some(info) if !lifetime.has_name() => { + (info.bound_region.get_id().unwrap().expect_local(), info.def_id) + } + _ => return lifetime.get_name_or_anon().to_string(), + }; + + let new_lt = { + let generics = self.tcx.generics_of(lifetime_scope); + let mut used_names = + iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) + .flat_map(|g| &g.own_params) + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name) + .collect::>(); + let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope); + // consider late-bound lifetimes ... + used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map( + |p| match p { + ty::BoundVariableKind::Region(lt) => lt.get_name(), + _ => None, + }, + )); + (b'a'..=b'z') + .map(|c| format!("'{}", c as char)) + .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate)) + .unwrap_or("'lt".to_string()) + }; + + let mut visitor = LifetimeReplaceVisitor { + tcx: self.tcx, + needle: hir::LifetimeName::Param(lifetime_def_id), + add_lt_suggs, + new_lt: &new_lt, + }; + match self.tcx.expect_hir_owner_node(lifetime_scope) { + hir::OwnerNode::Item(i) => visitor.visit_item(i), + hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i), + hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), + hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), + hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), + hir::OwnerNode::Synthetic => unreachable!(), + } + + let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); + let sugg = ast_generics + .span_for_lifetime_suggestion() + .map(|span| (span, format!("{new_lt}, "))) + .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>"))); + add_lt_suggs.push(sugg); + + new_lt + } + + fn report_sub_sup_conflict( + &self, + generic_param_scope: LocalDefId, + var_origin: RegionVariableOrigin, + sub_origin: SubregionOrigin<'tcx>, + sub_region: Region<'tcx>, + sup_origin: SubregionOrigin<'tcx>, + sup_region: Region<'tcx>, + ) -> ErrorGuaranteed { + let mut err = self.report_inference_failure(var_origin); + + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "first, the lifetime cannot outlive ", + sup_region, + "...", + None, + ); + + debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); + debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); + debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); + debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); + debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); + + if let infer::Subtype(ref sup_trace) = sup_origin + && let infer::Subtype(ref sub_trace) = sub_origin + && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values) + && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values) + && sub_expected == sup_expected + && sub_found == sup_found + { + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "...but the lifetime must also be valid for ", + sub_region, + "...", + None, + ); + err.span_note( + sup_trace.cause.span, + format!("...so that the {}", sup_trace.cause.as_requirement_str()), + ); + + err.note_expected_found(&"", sup_expected, &"", sup_found); + return if sub_region.is_error() | sup_region.is_error() { + err.delay_as_bug() + } else { + err.emit() + }; + } + + self.note_region_origin(&mut err, &sup_origin); + + note_and_explain_region( + self.tcx, + &mut err, + generic_param_scope, + "but, the lifetime must be valid for ", + sub_region, + "...", + None, + ); + + self.note_region_origin(&mut err, &sub_origin); + if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() } + } + + fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> { + let br_string = |br: ty::BoundRegionKind| { + let mut s = match br { + ty::BrNamed(_, name) => name.to_string(), + _ => String::new(), + }; + if !s.is_empty() { + s.push(' '); + } + s + }; + let var_description = match var_origin { + infer::MiscVariable(_) => String::new(), + infer::PatternRegion(_) => " for pattern".to_string(), + infer::AddrOfRegion(_) => " for borrow expression".to_string(), + infer::Autoref(_) => " for autoref".to_string(), + infer::Coercion(_) => " for automatic coercion".to_string(), + infer::BoundRegion(_, br, infer::FnCall) => { + format!(" for lifetime parameter {}in function call", br_string(br)) + } + infer::BoundRegion(_, br, infer::HigherRankedType) => { + format!(" for lifetime parameter {}in generic type", br_string(br)) + } + infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( + " for lifetime parameter {}in trait containing associated type `{}`", + br_string(br), + self.tcx.associated_item(def_id).name + ), + infer::RegionParameterDefinition(_, name) => { + format!(" for lifetime parameter `{name}`") + } + infer::UpvarRegion(ref upvar_id, _) => { + let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); + format!(" for capture of `{var_name}` by closure") + } + infer::Nll(..) => bug!("NLL variable found in lexical phase"), + }; + + struct_span_code_err!( + self.dcx(), + var_origin.span(), + E0495, + "cannot infer an appropriate lifetime{} due to conflicting requirements", + var_description + ) + } +} + +pub(super) fn note_and_explain_region<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_>, + generic_param_scope: LocalDefId, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, + alt_span: Option, +) { + let (description, span) = match *region { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => { + msg_span_from_named_region(tcx, generic_param_scope, region, alt_span) + } + + ty::ReError(_) => return, + + // FIXME(#125431): `ReVar` shouldn't reach here. + ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span), + + ty::ReBound(..) | ty::ReErased => { + bug!("unexpected region for note_and_explain_region: {:?}", region); + } + }; + + emit_msg_span(err, prefix, description, span, suffix); +} + +fn explain_free_region<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut Diag<'_>, + generic_param_scope: LocalDefId, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, +) { + let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None); + + label_msg_span(err, prefix, description, span, suffix); +} + +fn msg_span_from_named_region<'tcx>( + tcx: TyCtxt<'tcx>, + generic_param_scope: LocalDefId, + region: ty::Region<'tcx>, + alt_span: Option, +) -> (String, Option) { + match *region { + ty::ReEarlyParam(br) => { + let scope = tcx + .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id) + .expect_local(); + let span = if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) + { + param.span + } else { + tcx.def_span(scope) + }; + let text = if br.has_name() { + format!("the lifetime `{}` as defined here", br.name) + } else { + "the anonymous lifetime as defined here".to_string() + }; + (text, Some(span)) + } + ty::ReLateParam(ref fr) => { + if !fr.bound_region.is_named() + && let Some((ty, _)) = + find_anon_type(tcx, generic_param_scope, region, &fr.bound_region) + { + ("the anonymous lifetime defined here".to_string(), Some(ty.span)) + } else { + match fr.bound_region { + ty::BoundRegionKind::BrNamed(_, name) => { + let span = if let Some(param) = tcx + .hir() + .get_generics(generic_param_scope) + .and_then(|generics| generics.get_named(name)) + { + param.span + } else { + tcx.def_span(generic_param_scope) + }; + let text = if name == kw::UnderscoreLifetime { + "the anonymous lifetime as defined here".to_string() + } else { + format!("the lifetime `{name}` as defined here") + }; + (text, Some(span)) + } + ty::BrAnon => ( + "the anonymous lifetime as defined here".to_string(), + Some(tcx.def_span(generic_param_scope)), + ), + _ => ( + format!("the lifetime `{region}` as defined here"), + Some(tcx.def_span(generic_param_scope)), + ), + } + } + } + ty::ReStatic => ("the static lifetime".to_owned(), alt_span), + ty::RePlaceholder(ty::PlaceholderRegion { + bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. }, + .. + }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))), + ty::RePlaceholder(ty::PlaceholderRegion { + bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. }, + .. + }) => ("an anonymous lifetime".to_owned(), None), + _ => bug!("{:?}", region), + } +} + +fn emit_msg_span( + err: &mut Diag<'_>, + prefix: &str, + description: String, + span: Option, + suffix: &str, +) { + let message = format!("{prefix}{description}{suffix}"); + + if let Some(span) = span { + err.span_note(span, message); + } else { + err.note(message); + } +} + +fn label_msg_span( + err: &mut Diag<'_>, + prefix: &str, + description: String, + span: Option, + suffix: &str, +) { + let message = format!("{prefix}{description}{suffix}"); + + if let Some(span) = span { + err.span_label(span, message); + } else { + err.note(message); + } +} + +#[instrument(level = "trace", skip(infcx))] +pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>( + infcx: &'a InferCtxt<'tcx>, + generic_param_scope: LocalDefId, + span: Span, + hidden_ty: Ty<'tcx>, + hidden_region: ty::Region<'tcx>, + opaque_ty_key: ty::OpaqueTypeKey<'tcx>, +) -> Diag<'a> { + let tcx = infcx.tcx; + let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime { + span, + opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args), + opaque_ty_span: tcx.def_span(opaque_ty_key.def_id), + }); + + // Explain the region we are capturing. + match *hidden_region { + ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => { + // Assuming regionck succeeded (*), we ought to always be + // capturing *some* region from the fn header, and hence it + // ought to be free. So under normal circumstances, we will go + // down this path which gives a decent human readable + // explanation. + // + // (*) if not, the `tainted_by_errors` field would be set to + // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all. + explain_free_region( + tcx, + &mut err, + generic_param_scope, + &format!("hidden type `{hidden_ty}` captures "), + hidden_region, + "", + ); + if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) { + let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); + nice_region_error::suggest_new_region_bound( + tcx, + &mut err, + fn_returns, + hidden_region.to_string(), + None, + format!("captures `{hidden_region}`"), + None, + Some(reg_info.def_id), + ) + } + } + ty::RePlaceholder(_) => { + explain_free_region( + tcx, + &mut err, + generic_param_scope, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } + ty::ReError(_) => { + err.downgrade_to_delayed_bug(); + } + _ => { + // Ugh. This is a painful case: the hidden region is not one + // that we can easily summarize or explain. This can happen + // in a case like + // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // + // ``` + // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { + // if condition() { a } else { b } + // } + // ``` + // + // Here the captured lifetime is the intersection of `'a` and + // `'b`, which we can't quite express. + + // We can at least report a really cryptic error for now. + note_and_explain_region( + tcx, + &mut err, + generic_param_scope, + &format!("hidden type `{hidden_ty}` captures "), + hidden_region, + "", + None, + ); + } + } + + err +}