• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![deny(rustc::untranslatable_diagnostic)]
2 #![deny(rustc::diagnostic_outside_of_impl)]
3 //! Error reporting machinery for lifetime errors.
4 
5 use rustc_data_structures::fx::FxIndexSet;
6 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
7 use rustc_hir as hir;
8 use rustc_hir::def::Res::Def;
9 use rustc_hir::def_id::DefId;
10 use rustc_hir::intravisit::Visitor;
11 use rustc_hir::GenericBound::Trait;
12 use rustc_hir::QPath::Resolved;
13 use rustc_hir::WherePredicate::BoundPredicate;
14 use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
15 use rustc_infer::infer::{
16     error_reporting::nice_region_error::{
17         self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
18         HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor,
19     },
20     error_reporting::unexpected_hidden_region_diagnostic,
21     NllRegionVariableOrigin, RelateParamBound,
22 };
23 use rustc_middle::hir::place::PlaceBase;
24 use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
25 use rustc_middle::ty::subst::InternalSubsts;
26 use rustc_middle::ty::TypeVisitor;
27 use rustc_middle::ty::{self, RegionVid, Ty};
28 use rustc_middle::ty::{Region, TyCtxt};
29 use rustc_span::symbol::{kw, Ident};
30 use rustc_span::{Span, DUMMY_SP};
31 
32 use crate::borrowck_errors;
33 use crate::session_diagnostics::{
34     FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr,
35     LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
36 };
37 
38 use super::{OutlivesSuggestionBuilder, RegionName};
39 use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
40 use crate::{
41     nll::ConstraintDescription,
42     region_infer::{values::RegionElement, TypeTest},
43     universal_regions::DefiningTy,
44     MirBorrowckCtxt,
45 };
46 
47 impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
description(&self) -> &'static str48     fn description(&self) -> &'static str {
49         // Must end with a space. Allows for empty names to be provided.
50         match self {
51             ConstraintCategory::Assignment => "assignment ",
52             ConstraintCategory::Return(_) => "returning this value ",
53             ConstraintCategory::Yield => "yielding this value ",
54             ConstraintCategory::UseAsConst => "using this value as a constant ",
55             ConstraintCategory::UseAsStatic => "using this value as a static ",
56             ConstraintCategory::Cast => "cast ",
57             ConstraintCategory::CallArgument(_) => "argument ",
58             ConstraintCategory::TypeAnnotation => "type annotation ",
59             ConstraintCategory::ClosureBounds => "closure body ",
60             ConstraintCategory::SizedBound => "proving this value is `Sized` ",
61             ConstraintCategory::CopyBound => "copying this value ",
62             ConstraintCategory::OpaqueType => "opaque type ",
63             ConstraintCategory::ClosureUpvar(_) => "closure capture ",
64             ConstraintCategory::Usage => "this usage ",
65             ConstraintCategory::Predicate(_)
66             | ConstraintCategory::Boring
67             | ConstraintCategory::BoringNoLocation
68             | ConstraintCategory::Internal => "",
69         }
70     }
71 }
72 
73 /// A collection of errors encountered during region inference. This is needed to efficiently
74 /// report errors after borrow checking.
75 ///
76 /// Usually we expect this to either be empty or contain a small number of items, so we can avoid
77 /// allocation most of the time.
78 pub(crate) struct RegionErrors<'tcx>(Vec<RegionErrorKind<'tcx>>, TyCtxt<'tcx>);
79 
80 impl<'tcx> RegionErrors<'tcx> {
new(tcx: TyCtxt<'tcx>) -> Self81     pub fn new(tcx: TyCtxt<'tcx>) -> Self {
82         Self(vec![], tcx)
83     }
84     #[track_caller]
push(&mut self, val: impl Into<RegionErrorKind<'tcx>>)85     pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) {
86         let val = val.into();
87         self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}"));
88         self.0.push(val);
89     }
is_empty(&self) -> bool90     pub fn is_empty(&self) -> bool {
91         self.0.is_empty()
92     }
into_iter(self) -> impl Iterator<Item = RegionErrorKind<'tcx>>93     pub fn into_iter(self) -> impl Iterator<Item = RegionErrorKind<'tcx>> {
94         self.0.into_iter()
95     }
96 }
97 
98 #[derive(Clone, Debug)]
99 pub(crate) enum RegionErrorKind<'tcx> {
100     /// A generic bound failure for a type test (`T: 'a`).
101     TypeTestError { type_test: TypeTest<'tcx> },
102 
103     /// An unexpected hidden region for an opaque type.
104     UnexpectedHiddenRegion {
105         /// The span for the member constraint.
106         span: Span,
107         /// The hidden type.
108         hidden_ty: Ty<'tcx>,
109         /// The opaque type.
110         key: ty::OpaqueTypeKey<'tcx>,
111         /// The unexpected region.
112         member_region: ty::Region<'tcx>,
113     },
114 
115     /// Higher-ranked subtyping error.
116     BoundUniversalRegionError {
117         /// The placeholder free region.
118         longer_fr: RegionVid,
119         /// The region element that erroneously must be outlived by `longer_fr`.
120         error_element: RegionElement,
121         /// The placeholder region.
122         placeholder: ty::PlaceholderRegion,
123     },
124 
125     /// Any other lifetime error.
126     RegionError {
127         /// The origin of the region.
128         fr_origin: NllRegionVariableOrigin,
129         /// The region that should outlive `shorter_fr`.
130         longer_fr: RegionVid,
131         /// The region that should be shorter, but we can't prove it.
132         shorter_fr: RegionVid,
133         /// Indicates whether this is a reported error. We currently only report the first error
134         /// encountered and leave the rest unreported so as not to overwhelm the user.
135         is_reported: bool,
136     },
137 }
138 
139 /// Information about the various region constraints involved in a borrow checker error.
140 #[derive(Clone, Debug)]
141 pub struct ErrorConstraintInfo<'tcx> {
142     // fr: outlived_fr
143     pub(super) fr: RegionVid,
144     pub(super) fr_is_local: bool,
145     pub(super) outlived_fr: RegionVid,
146     pub(super) outlived_fr_is_local: bool,
147 
148     // Category and span for best blame constraint
149     pub(super) category: ConstraintCategory<'tcx>,
150     pub(super) span: Span,
151 }
152 
153 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
154     /// Converts a region inference variable into a `ty::Region` that
155     /// we can use for error reporting. If `r` is universally bound,
156     /// then we use the name that we have on record for it. If `r` is
157     /// existentially bound, then we check its inferred value and try
158     /// to find a good name from that. Returns `None` if we can't find
159     /// one (e.g., this is just some random part of the CFG).
to_error_region(&self, r: RegionVid) -> Option<ty::Region<'tcx>>160     pub(super) fn to_error_region(&self, r: RegionVid) -> Option<ty::Region<'tcx>> {
161         self.to_error_region_vid(r).and_then(|r| self.regioncx.region_definition(r).external_name)
162     }
163 
164     /// Returns the `RegionVid` corresponding to the region returned by
165     /// `to_error_region`.
to_error_region_vid(&self, r: RegionVid) -> Option<RegionVid>166     pub(super) fn to_error_region_vid(&self, r: RegionVid) -> Option<RegionVid> {
167         if self.regioncx.universal_regions().is_universal_region(r) {
168             Some(r)
169         } else {
170             // We just want something nameable, even if it's not
171             // actually an upper bound.
172             let upper_bound = self.regioncx.approx_universal_upper_bound(r);
173 
174             if self.regioncx.upper_bound_in_region_scc(r, upper_bound) {
175                 self.to_error_region_vid(upper_bound)
176             } else {
177                 None
178             }
179         }
180     }
181 
182     /// Returns `true` if a closure is inferred to be an `FnMut` closure.
is_closure_fn_mut(&self, fr: RegionVid) -> bool183     fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
184         if let Some(ty::ReFree(free_region)) = self.to_error_region(fr).as_deref()
185             && let ty::BoundRegionKind::BrEnv = free_region.bound_region
186             && let DefiningTy::Closure(_, substs) = self.regioncx.universal_regions().defining_ty
187         {
188             return substs.as_closure().kind() == ty::ClosureKind::FnMut;
189         }
190 
191         false
192     }
193 
194     // For generic associated types (GATs) which implied 'static requirement
195     // from higher-ranked trait bounds (HRTB). Try to locate span of the trait
196     // and the span which bounded to the trait for adding 'static lifetime suggestion
suggest_static_lifetime_for_gat_from_hrtb( &self, diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>, lower_bound: RegionVid, )197     fn suggest_static_lifetime_for_gat_from_hrtb(
198         &self,
199         diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
200         lower_bound: RegionVid,
201     ) {
202         let mut suggestions = vec![];
203         let hir = self.infcx.tcx.hir();
204 
205         // find generic associated types in the given region 'lower_bound'
206         let gat_id_and_generics = self
207             .regioncx
208             .placeholders_contained_in(lower_bound)
209             .map(|placeholder| {
210                 if let Some(id) = placeholder.bound.kind.get_id()
211                     && let Some(placeholder_id) = id.as_local()
212                     && let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id)
213                     && let Some(generics_impl) = hir.get_parent(gat_hir_id).generics()
214                 {
215                     Some((gat_hir_id, generics_impl))
216                 } else {
217                     None
218                 }
219             })
220             .collect::<Vec<_>>();
221         debug!(?gat_id_and_generics);
222 
223         // find higher-ranked trait bounds bounded to the generic associated types
224         let mut hrtb_bounds = vec![];
225         gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
226             for pred in generics.predicates {
227                 let BoundPredicate(
228                         WhereBoundPredicate {
229                             bound_generic_params,
230                             bounds,
231                             ..
232                         }) = pred else { continue; };
233                 if bound_generic_params
234                     .iter()
235                     .rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
236                     .is_some()
237                 {
238                     for bound in *bounds {
239                         hrtb_bounds.push(bound);
240                     }
241                 }
242             }
243         });
244         debug!(?hrtb_bounds);
245 
246         hrtb_bounds.iter().for_each(|bound| {
247             let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; };
248             diag.span_note(
249                 *trait_span,
250                 format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")
251             );
252             let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local()) else { return; };
253             let Def(_, trait_res_defid) = trait_ref.path.res else { return; };
254             debug!(?generics_fn);
255             generics_fn.predicates.iter().for_each(|predicate| {
256                 let BoundPredicate(
257                     WhereBoundPredicate {
258                         span: bounded_span,
259                         bounded_ty,
260                         bounds,
261                         ..
262                     }
263                 ) = predicate else { return; };
264                 bounds.iter().for_each(|bd| {
265                     if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd
266                         && let Def(_, res_defid) = tr_ref.path.res
267                         && res_defid == trait_res_defid // trait id matches
268                         && let TyKind::Path(Resolved(_, path)) = bounded_ty.kind
269                         && let Def(_, defid) = path.res
270                         && generics_fn.params
271                             .iter()
272                             .rfind(|param| param.def_id.to_def_id() == defid)
273                             .is_some() {
274                             suggestions.push((bounded_span.shrink_to_hi(), format!(" + 'static")));
275                         }
276                 });
277             });
278         });
279         if suggestions.len() > 0 {
280             suggestions.dedup();
281             diag.multipart_suggestion_verbose(
282                 format!("consider restricting the type parameter to the `'static` lifetime"),
283                 suggestions,
284                 Applicability::MaybeIncorrect,
285             );
286         }
287     }
288 
289     /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>)290     pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
291         // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
292         // buffered in the `MirBorrowckCtxt`.
293 
294         let mut outlives_suggestion = OutlivesSuggestionBuilder::default();
295         let mut last_unexpected_hidden_region: Option<(Span, Ty<'_>, ty::OpaqueTypeKey<'tcx>)> =
296             None;
297 
298         for nll_error in nll_errors.into_iter() {
299             match nll_error {
300                 RegionErrorKind::TypeTestError { type_test } => {
301                     // Try to convert the lower-bound region into something named we can print for the user.
302                     let lower_bound_region = self.to_error_region(type_test.lower_bound);
303 
304                     let type_test_span = type_test.span;
305 
306                     if let Some(lower_bound_region) = lower_bound_region {
307                         let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
308                         let origin = RelateParamBound(type_test_span, generic_ty, None);
309                         self.buffer_error(self.infcx.err_ctxt().construct_generic_bound_failure(
310                             self.body.source.def_id().expect_local(),
311                             type_test_span,
312                             Some(origin),
313                             type_test.generic_kind,
314                             lower_bound_region,
315                         ));
316                     } else {
317                         // FIXME. We should handle this case better. It
318                         // indicates that we have e.g., some region variable
319                         // whose value is like `'a+'b` where `'a` and `'b` are
320                         // distinct unrelated universal regions that are not
321                         // known to outlive one another. It'd be nice to have
322                         // some examples where this arises to decide how best
323                         // to report it; we could probably handle it by
324                         // iterating over the universal regions and reporting
325                         // an error that multiple bounds are required.
326                         let mut diag =
327                             self.infcx.tcx.sess.create_err(GenericDoesNotLiveLongEnough {
328                                 kind: type_test.generic_kind.to_string(),
329                                 span: type_test_span,
330                             });
331 
332                         // Add notes and suggestions for the case of 'static lifetime
333                         // implied but not specified when a generic associated types
334                         // are from higher-ranked trait bounds
335                         self.suggest_static_lifetime_for_gat_from_hrtb(
336                             &mut diag,
337                             type_test.lower_bound,
338                         );
339 
340                         self.buffer_error(diag);
341                     }
342                 }
343 
344                 RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, key, member_region } => {
345                     let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
346                     let named_key = self.regioncx.name_regions(self.infcx.tcx, key);
347                     let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
348                     let mut diag = unexpected_hidden_region_diagnostic(
349                         self.infcx.tcx,
350                         span,
351                         named_ty,
352                         named_region,
353                         named_key,
354                     );
355                     if last_unexpected_hidden_region != Some((span, named_ty, named_key)) {
356                         self.buffer_error(diag);
357                         last_unexpected_hidden_region = Some((span, named_ty, named_key));
358                     } else {
359                         diag.delay_as_bug();
360                     }
361                 }
362 
363                 RegionErrorKind::BoundUniversalRegionError {
364                     longer_fr,
365                     placeholder,
366                     error_element,
367                 } => {
368                     let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
369 
370                     // Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
371                     let (_, cause) = self.regioncx.find_outlives_blame_span(
372                         longer_fr,
373                         NllRegionVariableOrigin::Placeholder(placeholder),
374                         error_vid,
375                     );
376 
377                     let universe = placeholder.universe;
378                     let universe_info = self.regioncx.universe_info(universe);
379 
380                     universe_info.report_error(self, placeholder, error_element, cause);
381                 }
382 
383                 RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
384                     if is_reported {
385                         self.report_region_error(
386                             longer_fr,
387                             fr_origin,
388                             shorter_fr,
389                             &mut outlives_suggestion,
390                         );
391                     } else {
392                         // We only report the first error, so as not to overwhelm the user. See
393                         // `RegRegionErrorKind` docs.
394                         //
395                         // FIXME: currently we do nothing with these, but perhaps we can do better?
396                         // FIXME: try collecting these constraints on the outlives suggestion
397                         // builder. Does it make the suggestions any better?
398                         debug!(
399                             "Unreported region error: can't prove that {:?}: {:?}",
400                             longer_fr, shorter_fr
401                         );
402                     }
403                 }
404             }
405         }
406 
407         // Emit one outlives suggestions for each MIR def we borrowck
408         outlives_suggestion.add_suggestion(self);
409     }
410 
411     /// Report an error because the universal region `fr` was required to outlive
412     /// `outlived_fr` but it is not known to do so. For example:
413     ///
414     /// ```compile_fail
415     /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
416     /// ```
417     ///
418     /// Here we would be invoked with `fr = 'a` and `outlived_fr = 'b`.
report_region_error( &mut self, fr: RegionVid, fr_origin: NllRegionVariableOrigin, outlived_fr: RegionVid, outlives_suggestion: &mut OutlivesSuggestionBuilder, )419     pub(crate) fn report_region_error(
420         &mut self,
421         fr: RegionVid,
422         fr_origin: NllRegionVariableOrigin,
423         outlived_fr: RegionVid,
424         outlives_suggestion: &mut OutlivesSuggestionBuilder,
425     ) {
426         debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
427 
428         let (blame_constraint, extra_info) =
429             self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
430                 self.regioncx.provides_universal_region(r, fr, outlived_fr)
431             });
432         let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
433 
434         debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
435 
436         // Check if we can use one of the "nice region errors".
437         if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
438             let infer_err = self.infcx.err_ctxt();
439             let nice = NiceRegionError::new_from_span(&infer_err, cause.span, o, f);
440             if let Some(diag) = nice.try_report_from_nll() {
441                 self.buffer_error(diag);
442                 return;
443             }
444         }
445 
446         let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
447             self.regioncx.universal_regions().is_local_free_region(fr),
448             self.regioncx.universal_regions().is_local_free_region(outlived_fr),
449         );
450 
451         debug!(
452             "report_region_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
453             fr_is_local, outlived_fr_is_local, category
454         );
455 
456         let errci = ErrorConstraintInfo {
457             fr,
458             outlived_fr,
459             fr_is_local,
460             outlived_fr_is_local,
461             category,
462             span: cause.span,
463         };
464 
465         let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
466             (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => {
467                 self.report_fnmut_error(&errci, kind)
468             }
469             (ConstraintCategory::Assignment, true, false)
470             | (ConstraintCategory::CallArgument(_), true, false) => {
471                 let mut db = self.report_escaping_data_error(&errci);
472 
473                 outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
474                 outlives_suggestion.collect_constraint(fr, outlived_fr);
475 
476                 db
477             }
478             _ => {
479                 let mut db = self.report_general_error(&errci);
480 
481                 outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
482                 outlives_suggestion.collect_constraint(fr, outlived_fr);
483 
484                 db
485             }
486         };
487 
488         match variance_info {
489             ty::VarianceDiagInfo::None => {}
490             ty::VarianceDiagInfo::Invariant { ty, param_index } => {
491                 let (desc, note) = match ty.kind() {
492                     ty::RawPtr(ty_mut) => {
493                         assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut);
494                         (
495                             format!("a mutable pointer to `{}`", ty_mut.ty),
496                             "mutable pointers are invariant over their type parameter".to_string(),
497                         )
498                     }
499                     ty::Ref(_, inner_ty, mutbl) => {
500                         assert_eq!(*mutbl, rustc_hir::Mutability::Mut);
501                         (
502                             format!("a mutable reference to `{inner_ty}`"),
503                             "mutable references are invariant over their type parameter"
504                                 .to_string(),
505                         )
506                     }
507                     ty::Adt(adt, substs) => {
508                         let generic_arg = substs[param_index as usize];
509                         let identity_substs =
510                             InternalSubsts::identity_for_item(self.infcx.tcx, adt.did());
511                         let base_ty = Ty::new_adt(self.infcx.tcx, *adt, identity_substs);
512                         let base_generic_arg = identity_substs[param_index as usize];
513                         let adt_desc = adt.descr();
514 
515                         let desc = format!(
516                             "the type `{ty}`, which makes the generic argument `{generic_arg}` invariant"
517                         );
518                         let note = format!(
519                             "the {adt_desc} `{base_ty}` is invariant over the parameter `{base_generic_arg}`"
520                         );
521                         (desc, note)
522                     }
523                     ty::FnDef(def_id, _) => {
524                         let name = self.infcx.tcx.item_name(*def_id);
525                         let identity_substs =
526                             InternalSubsts::identity_for_item(self.infcx.tcx, *def_id);
527                         let desc = format!("a function pointer to `{name}`");
528                         let note = format!(
529                             "the function `{name}` is invariant over the parameter `{}`",
530                             identity_substs[param_index as usize]
531                         );
532                         (desc, note)
533                     }
534                     _ => panic!("Unexpected type {ty:?}"),
535                 };
536                 diag.note(format!("requirement occurs because of {desc}",));
537                 diag.note(note);
538                 diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
539             }
540         }
541 
542         for extra in extra_info {
543             match extra {
544                 ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
545                     diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
546                 }
547             }
548         }
549 
550         self.buffer_error(diag);
551     }
552 
553     /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
554     /// This function expects `fr` to be local and `outlived_fr` to not be local.
555     ///
556     /// ```text
557     /// error: captured variable cannot escape `FnMut` closure body
558     ///   --> $DIR/issue-53040.rs:15:8
559     ///    |
560     /// LL |     || &mut v;
561     ///    |     -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
562     ///    |     |
563     ///    |     inferred to be a `FnMut` closure
564     ///    |
565     ///    = note: `FnMut` closures only have access to their captured variables while they are
566     ///            executing...
567     ///    = note: ...therefore, returned references to captured variables will escape the closure
568     /// ```
report_fnmut_error( &self, errci: &ErrorConstraintInfo<'tcx>, kind: ReturnConstraint, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>569     fn report_fnmut_error(
570         &self,
571         errci: &ErrorConstraintInfo<'tcx>,
572         kind: ReturnConstraint,
573     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
574         let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
575 
576         let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
577         if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *output_ty.kind() {
578             output_ty = self.infcx.tcx.type_of(def_id).subst_identity()
579         };
580 
581         debug!("report_fnmut_error: output_ty={:?}", output_ty);
582 
583         let err = FnMutError {
584             span: *span,
585             ty_err: match output_ty.kind() {
586                 ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
587                     FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
588                 }
589                 _ if output_ty.contains_closure() => {
590                     FnMutReturnTypeErr::ReturnClosure { span: *span }
591                 }
592                 _ => FnMutReturnTypeErr::ReturnRef { span: *span },
593             },
594         };
595 
596         let mut diag = self.infcx.tcx.sess.create_err(err);
597 
598         if let ReturnConstraint::ClosureUpvar(upvar_field) = kind {
599             let def_id = match self.regioncx.universal_regions().defining_ty {
600                 DefiningTy::Closure(def_id, _) => def_id,
601                 ty => bug!("unexpected DefiningTy {:?}", ty),
602             };
603 
604             let captured_place = &self.upvars[upvar_field.index()].place;
605             let defined_hir = match captured_place.place.base {
606                 PlaceBase::Local(hirid) => Some(hirid),
607                 PlaceBase::Upvar(upvar) => Some(upvar.var_path.hir_id),
608                 _ => None,
609             };
610 
611             if let Some(def_hir) = defined_hir {
612                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
613                 let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
614                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
615                 diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
616                 diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
617             }
618         }
619 
620         if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
621             diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span });
622         }
623 
624         self.suggest_move_on_borrowing_closure(&mut diag);
625 
626         diag
627     }
628 
629     /// Reports an error specifically for when data is escaping a closure.
630     ///
631     /// ```text
632     /// error: borrowed data escapes outside of function
633     ///   --> $DIR/lifetime-bound-will-change-warning.rs:44:5
634     ///    |
635     /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
636     ///    |              - `x` is a reference that is only valid in the function body
637     /// LL |     // but ref_obj will not, so warn.
638     /// LL |     ref_obj(x)
639     ///    |     ^^^^^^^^^^ `x` escapes the function body here
640     /// ```
641     #[instrument(level = "debug", skip(self))]
report_escaping_data_error( &self, errci: &ErrorConstraintInfo<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>642     fn report_escaping_data_error(
643         &self,
644         errci: &ErrorConstraintInfo<'tcx>,
645     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
646         let ErrorConstraintInfo { span, category, .. } = errci;
647 
648         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
649             self.infcx.tcx,
650             &self.body,
651             &self.local_names,
652             &self.upvars,
653             errci.fr,
654         );
655         let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
656             self.infcx.tcx,
657             &self.body,
658             &self.local_names,
659             &self.upvars,
660             errci.outlived_fr,
661         );
662 
663         let escapes_from =
664             self.infcx.tcx.def_descr(self.regioncx.universal_regions().defining_ty.def_id());
665 
666         // Revert to the normal error in these cases.
667         // Assignments aren't "escapes" in function items.
668         if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
669             || (*category == ConstraintCategory::Assignment
670                 && self.regioncx.universal_regions().defining_ty.is_fn_def())
671             || self.regioncx.universal_regions().defining_ty.is_const()
672         {
673             return self.report_general_error(&ErrorConstraintInfo {
674                 fr_is_local: true,
675                 outlived_fr_is_local: false,
676                 ..*errci
677             });
678         }
679 
680         let mut diag =
681             borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from);
682 
683         if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
684             diag.span_label(
685                 outlived_fr_span,
686                 format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",),
687             );
688         }
689 
690         if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
691             diag.span_label(
692                 fr_span,
693                 format!(
694                     "`{fr_name}` is a reference that is only valid in the {escapes_from} body",
695                 ),
696             );
697 
698             diag.span_label(*span, format!("`{fr_name}` escapes the {escapes_from} body here"));
699         }
700 
701         // Only show an extra note if we can find an 'error region' for both of the region
702         // variables. This avoids showing a noisy note that just mentions 'synthetic' regions
703         // that don't help the user understand the error.
704         match (self.to_error_region(errci.fr), self.to_error_region(errci.outlived_fr)) {
705             (Some(f), Some(o)) => {
706                 self.maybe_suggest_constrain_dyn_trait_impl(&mut diag, f, o, category);
707 
708                 let fr_region_name = self.give_region_a_name(errci.fr).unwrap();
709                 fr_region_name.highlight_region_name(&mut diag);
710                 let outlived_fr_region_name = self.give_region_a_name(errci.outlived_fr).unwrap();
711                 outlived_fr_region_name.highlight_region_name(&mut diag);
712 
713                 diag.span_label(
714                     *span,
715                     format!(
716                         "{}requires that `{}` must outlive `{}`",
717                         category.description(),
718                         fr_region_name,
719                         outlived_fr_region_name,
720                     ),
721                 );
722             }
723             _ => {}
724         }
725 
726         diag
727     }
728 
729     /// Reports a region inference error for the general case with named/synthesized lifetimes to
730     /// explain what is happening.
731     ///
732     /// ```text
733     /// error: unsatisfied lifetime constraints
734     ///   --> $DIR/regions-creating-enums3.rs:17:5
735     ///    |
736     /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
737     ///    |                -- -- lifetime `'b` defined here
738     ///    |                |
739     ///    |                lifetime `'a` defined here
740     /// LL |     ast::add(x, y)
741     ///    |     ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
742     ///    |                    is returning data with lifetime `'b`
743     /// ```
report_general_error( &self, errci: &ErrorConstraintInfo<'tcx>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>744     fn report_general_error(
745         &self,
746         errci: &ErrorConstraintInfo<'tcx>,
747     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
748         let ErrorConstraintInfo {
749             fr,
750             fr_is_local,
751             outlived_fr,
752             outlived_fr_is_local,
753             span,
754             category,
755             ..
756         } = errci;
757 
758         let mir_def_name = self.infcx.tcx.def_descr(self.mir_def_id().to_def_id());
759 
760         let err = LifetimeOutliveErr { span: *span };
761         let mut diag = self.infcx.tcx.sess.create_err(err);
762 
763         let fr_name = self.give_region_a_name(*fr).unwrap();
764         fr_name.highlight_region_name(&mut diag);
765         let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
766         outlived_fr_name.highlight_region_name(&mut diag);
767 
768         let err_category = match (category, outlived_fr_is_local, fr_is_local) {
769             (ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn {
770                 span: *span,
771                 mir_def_name,
772                 outlived_fr_name,
773                 fr_name: &fr_name,
774             },
775             _ => LifetimeReturnCategoryErr::ShortReturn {
776                 span: *span,
777                 category_desc: category.description(),
778                 free_region_name: &fr_name,
779                 outlived_fr_name,
780             },
781         };
782 
783         diag.subdiagnostic(err_category);
784 
785         self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
786         self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
787         self.suggest_move_on_borrowing_closure(&mut diag);
788 
789         diag
790     }
791 
792     /// Adds a suggestion to errors where an `impl Trait` is returned.
793     ///
794     /// ```text
795     /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
796     ///       a constraint
797     ///    |
798     /// LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
799     ///    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
800     /// ```
add_static_impl_trait_suggestion( &self, diag: &mut Diagnostic, fr: RegionVid, fr_name: RegionName, outlived_fr: RegionVid, )801     fn add_static_impl_trait_suggestion(
802         &self,
803         diag: &mut Diagnostic,
804         fr: RegionVid,
805         // We need to pass `fr_name` - computing it again will label it twice.
806         fr_name: RegionName,
807         outlived_fr: RegionVid,
808     ) {
809         if let (Some(f), Some(outlived_f)) =
810             (self.to_error_region(fr), self.to_error_region(outlived_fr))
811         {
812             if *outlived_f != ty::ReStatic {
813                 return;
814             }
815             let suitable_region = self.infcx.tcx.is_suitable_region(f);
816             let Some(suitable_region) = suitable_region else { return; };
817 
818             let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id);
819 
820             let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
821                 param
822             } else {
823                 return;
824             };
825 
826             let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
827 
828             let arg = match param.param.pat.simple_ident() {
829                 Some(simple_ident) => format!("argument `{simple_ident}`"),
830                 None => "the argument".to_string(),
831             };
832             let captures = format!("captures data from {arg}");
833 
834             if !fn_returns.is_empty() {
835                 nice_region_error::suggest_new_region_bound(
836                     self.infcx.tcx,
837                     diag,
838                     fn_returns,
839                     lifetime.to_string(),
840                     Some(arg),
841                     captures,
842                     Some((param.param_ty_span, param.param_ty.to_string())),
843                     Some(suitable_region.def_id),
844                 );
845                 return;
846             }
847 
848             let Some((alias_tys, alias_span, lt_addition_span)) = self
849                 .infcx
850                 .tcx
851                 .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
852 
853             // in case the return type of the method is a type alias
854             let mut spans_suggs: Vec<_> = Vec::new();
855             for alias_ty in alias_tys {
856                 if alias_ty.span.desugaring_kind().is_some() {
857                     // Skip `async` desugaring `impl Future`.
858                     ()
859                 }
860                 if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
861                     if lt.ident.name == kw::Empty {
862                         spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
863                     } else {
864                         spans_suggs.push((lt.ident.span, "'a".to_string()));
865                     }
866                 }
867             }
868 
869             if let Some(lt_addition_span) = lt_addition_span {
870                 spans_suggs.push((lt_addition_span, "'a, ".to_string()));
871             } else {
872                 spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
873             }
874 
875             diag.multipart_suggestion_verbose(
876                 format!(
877                     "to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
878                 ),
879                 spans_suggs,
880                 Applicability::MaybeIncorrect,
881             );
882         }
883     }
884 
maybe_suggest_constrain_dyn_trait_impl( &self, diag: &mut Diagnostic, f: Region<'tcx>, o: Region<'tcx>, category: &ConstraintCategory<'tcx>, )885     fn maybe_suggest_constrain_dyn_trait_impl(
886         &self,
887         diag: &mut Diagnostic,
888         f: Region<'tcx>,
889         o: Region<'tcx>,
890         category: &ConstraintCategory<'tcx>,
891     ) {
892         if !o.is_static() {
893             return;
894         }
895 
896         let tcx = self.infcx.tcx;
897 
898         let instance = if let ConstraintCategory::CallArgument(Some(func_ty)) = category {
899             let (fn_did, substs) = match func_ty.kind() {
900                 ty::FnDef(fn_did, substs) => (fn_did, substs),
901                 _ => return,
902             };
903             debug!(?fn_did, ?substs);
904 
905             // Only suggest this on function calls, not closures
906             let ty = tcx.type_of(fn_did).subst_identity();
907             debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind());
908             if let ty::Closure(_, _) = ty.kind() {
909                 return;
910             }
911 
912             if let Ok(Some(instance)) = ty::Instance::resolve(
913                 tcx,
914                 self.param_env,
915                 *fn_did,
916                 self.infcx.resolve_vars_if_possible(substs),
917             ) {
918                 instance
919             } else {
920                 return;
921             }
922         } else {
923             return;
924         };
925 
926         let param = match find_param_with_region(tcx, f, o) {
927             Some(param) => param,
928             None => return,
929         };
930         debug!(?param);
931 
932         let mut visitor = TraitObjectVisitor(FxIndexSet::default());
933         visitor.visit_ty(param.param_ty);
934 
935         let Some((ident, self_ty)) =
936             NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &visitor.0) else { return; };
937 
938         self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty);
939     }
940 
941     #[instrument(skip(self, err), level = "debug")]
suggest_constrain_dyn_trait_in_impl( &self, err: &mut Diagnostic, found_dids: &FxIndexSet<DefId>, ident: Ident, self_ty: &hir::Ty<'_>, ) -> bool942     fn suggest_constrain_dyn_trait_in_impl(
943         &self,
944         err: &mut Diagnostic,
945         found_dids: &FxIndexSet<DefId>,
946         ident: Ident,
947         self_ty: &hir::Ty<'_>,
948     ) -> bool {
949         debug!("err: {:#?}", err);
950         let mut suggested = false;
951         for found_did in found_dids {
952             let mut traits = vec![];
953             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
954             hir_v.visit_ty(&self_ty);
955             debug!("trait spans found: {:?}", traits);
956             for span in &traits {
957                 let mut multi_span: MultiSpan = vec![*span].into();
958                 multi_span
959                     .push_span_label(*span, "this has an implicit `'static` lifetime requirement");
960                 multi_span.push_span_label(
961                     ident.span,
962                     "calling this method introduces the `impl`'s `'static` requirement",
963                 );
964                 err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
965                 err.span_suggestion_verbose(
966                     span.shrink_to_hi(),
967                     "consider relaxing the implicit `'static` requirement",
968                     " + '_",
969                     Applicability::MaybeIncorrect,
970                 );
971                 suggested = true;
972             }
973         }
974         suggested
975     }
976 
suggest_adding_lifetime_params( &self, diag: &mut Diagnostic, sub: RegionVid, sup: RegionVid, )977     fn suggest_adding_lifetime_params(
978         &self,
979         diag: &mut Diagnostic,
980         sub: RegionVid,
981         sup: RegionVid,
982     ) {
983         let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
984             return
985         };
986 
987         let Some((ty_sub, _)) = self
988             .infcx
989             .tcx
990             .is_suitable_region(sub)
991             .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
992             return
993         };
994 
995         let Some((ty_sup, _)) = self
996             .infcx
997             .tcx
998             .is_suitable_region(sup)
999             .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
1000             return
1001         };
1002 
1003         suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
1004     }
1005 
suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic)1006     fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
1007         let map = self.infcx.tcx.hir();
1008         let body_id = map.body_owned_by(self.mir_def_id());
1009         let expr = &map.body(body_id).value.peel_blocks();
1010         let mut closure_span = None::<rustc_span::Span>;
1011         match expr.kind {
1012             hir::ExprKind::MethodCall(.., args, _) => {
1013                 for arg in args {
1014                     if let hir::ExprKind::Closure(hir::Closure {
1015                         capture_clause: hir::CaptureBy::Ref,
1016                         ..
1017                     }) = arg.kind
1018                     {
1019                         closure_span = Some(arg.span.shrink_to_lo());
1020                         break;
1021                     }
1022                 }
1023             }
1024             hir::ExprKind::Closure(hir::Closure {
1025                 capture_clause: hir::CaptureBy::Ref,
1026                 body,
1027                 ..
1028             }) => {
1029                 let body = map.body(*body);
1030                 if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
1031                     closure_span = Some(expr.span.shrink_to_lo());
1032                 }
1033             }
1034             _ => {}
1035         }
1036         if let Some(closure_span) = closure_span {
1037             diag.span_suggestion_verbose(
1038                 closure_span,
1039                 "consider adding 'move' keyword before the nested closure",
1040                 "move ",
1041                 Applicability::MaybeIncorrect,
1042             );
1043         }
1044     }
1045 }
1046