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