]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Rollup merge of #107248 - erikdesjardins:addrspace, r=oli-obk
[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 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> {
48     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> {
81     pub fn new(tcx: TyCtxt<'tcx>) -> Self {
82         Self(vec![], tcx)
83     }
84     #[track_caller]
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     }
90     pub fn is_empty(&self) -> bool {
91         self.0.is_empty()
92     }
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).
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`.
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.
183     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
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.name.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`.
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`.
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 = self.infcx.tcx.mk_adt(*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     /// ```
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)
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::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
587                 ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
588                     FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
589                 }
590                 _ => FnMutReturnTypeErr::ReturnRef { span: *span },
591             },
592         };
593
594         let mut diag = self.infcx.tcx.sess.create_err(err);
595
596         if let ReturnConstraint::ClosureUpvar(upvar_field) = kind {
597             let def_id = match self.regioncx.universal_regions().defining_ty {
598                 DefiningTy::Closure(def_id, _) => def_id,
599                 ty => bug!("unexpected DefiningTy {:?}", ty),
600             };
601
602             let captured_place = &self.upvars[upvar_field.index()].place;
603             let defined_hir = match captured_place.place.base {
604                 PlaceBase::Local(hirid) => Some(hirid),
605                 PlaceBase::Upvar(upvar) => Some(upvar.var_path.hir_id),
606                 _ => None,
607             };
608
609             if let Some(def_hir) = defined_hir {
610                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
611                 let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
612                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
613                 diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
614                 diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
615             }
616         }
617
618         if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
619             diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span });
620         }
621
622         self.suggest_move_on_borrowing_closure(&mut diag);
623
624         diag
625     }
626
627     /// Reports an error specifically for when data is escaping a closure.
628     ///
629     /// ```text
630     /// error: borrowed data escapes outside of function
631     ///   --> $DIR/lifetime-bound-will-change-warning.rs:44:5
632     ///    |
633     /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
634     ///    |              - `x` is a reference that is only valid in the function body
635     /// LL |     // but ref_obj will not, so warn.
636     /// LL |     ref_obj(x)
637     ///    |     ^^^^^^^^^^ `x` escapes the function body here
638     /// ```
639     #[instrument(level = "debug", skip(self))]
640     fn report_escaping_data_error(
641         &self,
642         errci: &ErrorConstraintInfo<'tcx>,
643     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
644         let ErrorConstraintInfo { span, category, .. } = errci;
645
646         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
647             self.infcx.tcx,
648             &self.body,
649             &self.local_names,
650             &self.upvars,
651             errci.fr,
652         );
653         let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
654             self.infcx.tcx,
655             &self.body,
656             &self.local_names,
657             &self.upvars,
658             errci.outlived_fr,
659         );
660
661         let (_, escapes_from) = self
662             .infcx
663             .tcx
664             .article_and_description(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     /// ```
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) =
759             self.infcx.tcx.article_and_description(self.mir_def_id().to_def_id());
760
761         let err = LifetimeOutliveErr { span: *span };
762         let mut diag = self.infcx.tcx.sess.create_err(err);
763
764         let fr_name = self.give_region_a_name(*fr).unwrap();
765         fr_name.highlight_region_name(&mut diag);
766         let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
767         outlived_fr_name.highlight_region_name(&mut diag);
768
769         let err_category = match (category, outlived_fr_is_local, fr_is_local) {
770             (ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn {
771                 span: *span,
772                 mir_def_name,
773                 outlived_fr_name,
774                 fr_name: &fr_name,
775             },
776             _ => LifetimeReturnCategoryErr::ShortReturn {
777                 span: *span,
778                 category_desc: category.description(),
779                 free_region_name: &fr_name,
780                 outlived_fr_name,
781             },
782         };
783
784         diag.subdiagnostic(err_category);
785
786         self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
787         self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
788         self.suggest_move_on_borrowing_closure(&mut diag);
789
790         diag
791     }
792
793     /// Adds a suggestion to errors where an `impl Trait` is returned.
794     ///
795     /// ```text
796     /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
797     ///       a constraint
798     ///    |
799     /// LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
800     ///    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
801     /// ```
802     fn add_static_impl_trait_suggestion(
803         &self,
804         diag: &mut Diagnostic,
805         fr: RegionVid,
806         // We need to pass `fr_name` - computing it again will label it twice.
807         fr_name: RegionName,
808         outlived_fr: RegionVid,
809     ) {
810         if let (Some(f), Some(outlived_f)) =
811             (self.to_error_region(fr), self.to_error_region(outlived_fr))
812         {
813             if *outlived_f != ty::ReStatic {
814                 return;
815             }
816             let suitable_region = self.infcx.tcx.is_suitable_region(f);
817             let Some(suitable_region) = suitable_region else { return; };
818
819             let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id);
820
821             let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
822                 param
823             } else {
824                 return;
825             };
826
827             let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
828
829             let arg = match param.param.pat.simple_ident() {
830                 Some(simple_ident) => format!("argument `{simple_ident}`"),
831                 None => "the argument".to_string(),
832             };
833             let captures = format!("captures data from {arg}");
834
835             if !fn_returns.is_empty() {
836                 nice_region_error::suggest_new_region_bound(
837                     self.infcx.tcx,
838                     diag,
839                     fn_returns,
840                     lifetime.to_string(),
841                     Some(arg),
842                     captures,
843                     Some((param.param_ty_span, param.param_ty.to_string())),
844                     Some(suitable_region.def_id),
845                 );
846                 return;
847             }
848
849             let Some((alias_tys, alias_span)) = self
850                 .infcx
851                 .tcx
852                 .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
853
854             // in case the return type of the method is a type alias
855             let mut spans_suggs: Vec<_> = Vec::new();
856             for alias_ty in alias_tys {
857                 if alias_ty.span.desugaring_kind().is_some() {
858                     // Skip `async` desugaring `impl Future`.
859                     ()
860                 }
861                 if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
862                     spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
863                 }
864             }
865             spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
866             diag.multipart_suggestion_verbose(
867                 &format!(
868                     "to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
869                 ),
870                 spans_suggs,
871                 Applicability::MaybeIncorrect,
872             );
873         }
874     }
875
876     fn maybe_suggest_constrain_dyn_trait_impl(
877         &self,
878         diag: &mut Diagnostic,
879         f: Region<'tcx>,
880         o: Region<'tcx>,
881         category: &ConstraintCategory<'tcx>,
882     ) {
883         if !o.is_static() {
884             return;
885         }
886
887         let tcx = self.infcx.tcx;
888
889         let instance = if let ConstraintCategory::CallArgument(Some(func_ty)) = category {
890             let (fn_did, substs) = match func_ty.kind() {
891                 ty::FnDef(fn_did, substs) => (fn_did, substs),
892                 _ => return,
893             };
894             debug!(?fn_did, ?substs);
895
896             // Only suggest this on function calls, not closures
897             let ty = tcx.type_of(fn_did);
898             debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind());
899             if let ty::Closure(_, _) = ty.kind() {
900                 return;
901             }
902
903             if let Ok(Some(instance)) = ty::Instance::resolve(
904                 tcx,
905                 self.param_env,
906                 *fn_did,
907                 self.infcx.resolve_vars_if_possible(substs),
908             ) {
909                 instance
910             } else {
911                 return;
912             }
913         } else {
914             return;
915         };
916
917         let param = match find_param_with_region(tcx, f, o) {
918             Some(param) => param,
919             None => return,
920         };
921         debug!(?param);
922
923         let mut visitor = TraitObjectVisitor(FxIndexSet::default());
924         visitor.visit_ty(param.param_ty);
925
926         let Some((ident, self_ty)) =
927             NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &visitor.0) else { return; };
928
929         self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty);
930     }
931
932     #[instrument(skip(self, err), level = "debug")]
933     fn suggest_constrain_dyn_trait_in_impl(
934         &self,
935         err: &mut Diagnostic,
936         found_dids: &FxIndexSet<DefId>,
937         ident: Ident,
938         self_ty: &hir::Ty<'_>,
939     ) -> bool {
940         debug!("err: {:#?}", err);
941         let mut suggested = false;
942         for found_did in found_dids {
943             let mut traits = vec![];
944             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
945             hir_v.visit_ty(&self_ty);
946             debug!("trait spans found: {:?}", traits);
947             for span in &traits {
948                 let mut multi_span: MultiSpan = vec![*span].into();
949                 multi_span
950                     .push_span_label(*span, "this has an implicit `'static` lifetime requirement");
951                 multi_span.push_span_label(
952                     ident.span,
953                     "calling this method introduces the `impl`'s 'static` requirement",
954                 );
955                 err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
956                 err.span_suggestion_verbose(
957                     span.shrink_to_hi(),
958                     "consider relaxing the implicit `'static` requirement",
959                     " + '_",
960                     Applicability::MaybeIncorrect,
961                 );
962                 suggested = true;
963             }
964         }
965         suggested
966     }
967
968     fn suggest_adding_lifetime_params(
969         &self,
970         diag: &mut Diagnostic,
971         sub: RegionVid,
972         sup: RegionVid,
973     ) {
974         let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
975             return
976         };
977
978         let Some((ty_sub, _)) = self
979             .infcx
980             .tcx
981             .is_suitable_region(sub)
982             .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
983             return
984         };
985
986         let Some((ty_sup, _)) = self
987             .infcx
988             .tcx
989             .is_suitable_region(sup)
990             .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
991             return
992         };
993
994         suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
995     }
996
997     fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
998         let map = self.infcx.tcx.hir();
999         let body_id = map.body_owned_by(self.mir_def_id());
1000         let expr = &map.body(body_id).value;
1001         let mut closure_span = None::<rustc_span::Span>;
1002         match expr.kind {
1003             hir::ExprKind::MethodCall(.., args, _) => {
1004                 for arg in args {
1005                     if let hir::ExprKind::Closure(hir::Closure {
1006                         capture_clause: hir::CaptureBy::Ref,
1007                         ..
1008                     }) = arg.kind
1009                     {
1010                         closure_span = Some(arg.span.shrink_to_lo());
1011                         break;
1012                     }
1013                 }
1014             }
1015             hir::ExprKind::Block(blk, _) => {
1016                 if let Some(expr) = blk.expr {
1017                     // only when the block is a closure
1018                     if let hir::ExprKind::Closure(hir::Closure {
1019                         capture_clause: hir::CaptureBy::Ref,
1020                         body,
1021                         ..
1022                     }) = expr.kind
1023                     {
1024                         let body = map.body(*body);
1025                         if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
1026                             closure_span = Some(expr.span.shrink_to_lo());
1027                         }
1028                     }
1029                 }
1030             }
1031             _ => {}
1032         }
1033         if let Some(closure_span) = closure_span {
1034             diag.span_suggestion_verbose(
1035                 closure_span,
1036                 "consider adding 'move' keyword before the nested closure",
1037                 "move ",
1038                 Applicability::MaybeIncorrect,
1039             );
1040         }
1041     }
1042 }