]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_borrowck/src/diagnostics/region_errors.rs
Rollup merge of #106157 - LeSeulArtichaut:106126-thir-unsafeck-good-path-bug-2, r...
[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_id::DefId;
9 use rustc_hir::intravisit::Visitor;
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     /// Report an error because the universal region `fr` was required to outlive
295     /// `outlived_fr` but it is not known to do so. For example:
296     ///
297     /// ```compile_fail
298     /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
299     /// ```
300     ///
301     /// Here we would be invoked with `fr = 'a` and `outlived_fr = `'b`.
302     pub(crate) fn report_region_error(
303         &mut self,
304         fr: RegionVid,
305         fr_origin: NllRegionVariableOrigin,
306         outlived_fr: RegionVid,
307         outlives_suggestion: &mut OutlivesSuggestionBuilder,
308     ) {
309         debug!("report_region_error(fr={:?}, outlived_fr={:?})", fr, outlived_fr);
310
311         let (blame_constraint, extra_info) =
312             self.regioncx.best_blame_constraint(fr, fr_origin, |r| {
313                 self.regioncx.provides_universal_region(r, fr, outlived_fr)
314             });
315         let BlameConstraint { category, cause, variance_info, .. } = blame_constraint;
316
317         debug!("report_region_error: category={:?} {:?} {:?}", category, cause, variance_info);
318
319         // Check if we can use one of the "nice region errors".
320         if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
321             let infer_err = self.infcx.err_ctxt();
322             let nice = NiceRegionError::new_from_span(&infer_err, cause.span, o, f);
323             if let Some(diag) = nice.try_report_from_nll() {
324                 self.buffer_error(diag);
325                 return;
326             }
327         }
328
329         let (fr_is_local, outlived_fr_is_local): (bool, bool) = (
330             self.regioncx.universal_regions().is_local_free_region(fr),
331             self.regioncx.universal_regions().is_local_free_region(outlived_fr),
332         );
333
334         debug!(
335             "report_region_error: fr_is_local={:?} outlived_fr_is_local={:?} category={:?}",
336             fr_is_local, outlived_fr_is_local, category
337         );
338
339         let errci = ErrorConstraintInfo {
340             fr,
341             outlived_fr,
342             fr_is_local,
343             outlived_fr_is_local,
344             category,
345             span: cause.span,
346         };
347
348         let mut diag = match (category, fr_is_local, outlived_fr_is_local) {
349             (ConstraintCategory::Return(kind), true, false) if self.is_closure_fn_mut(fr) => {
350                 self.report_fnmut_error(&errci, kind)
351             }
352             (ConstraintCategory::Assignment, true, false)
353             | (ConstraintCategory::CallArgument(_), true, false) => {
354                 let mut db = self.report_escaping_data_error(&errci);
355
356                 outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
357                 outlives_suggestion.collect_constraint(fr, outlived_fr);
358
359                 db
360             }
361             _ => {
362                 let mut db = self.report_general_error(&errci);
363
364                 outlives_suggestion.intermediate_suggestion(self, &errci, &mut db);
365                 outlives_suggestion.collect_constraint(fr, outlived_fr);
366
367                 db
368             }
369         };
370
371         match variance_info {
372             ty::VarianceDiagInfo::None => {}
373             ty::VarianceDiagInfo::Invariant { ty, param_index } => {
374                 let (desc, note) = match ty.kind() {
375                     ty::RawPtr(ty_mut) => {
376                         assert_eq!(ty_mut.mutbl, rustc_hir::Mutability::Mut);
377                         (
378                             format!("a mutable pointer to `{}`", ty_mut.ty),
379                             "mutable pointers are invariant over their type parameter".to_string(),
380                         )
381                     }
382                     ty::Ref(_, inner_ty, mutbl) => {
383                         assert_eq!(*mutbl, rustc_hir::Mutability::Mut);
384                         (
385                             format!("a mutable reference to `{inner_ty}`"),
386                             "mutable references are invariant over their type parameter"
387                                 .to_string(),
388                         )
389                     }
390                     ty::Adt(adt, substs) => {
391                         let generic_arg = substs[param_index as usize];
392                         let identity_substs =
393                             InternalSubsts::identity_for_item(self.infcx.tcx, adt.did());
394                         let base_ty = self.infcx.tcx.mk_adt(*adt, identity_substs);
395                         let base_generic_arg = identity_substs[param_index as usize];
396                         let adt_desc = adt.descr();
397
398                         let desc = format!(
399                             "the type `{ty}`, which makes the generic argument `{generic_arg}` invariant"
400                         );
401                         let note = format!(
402                             "the {adt_desc} `{base_ty}` is invariant over the parameter `{base_generic_arg}`"
403                         );
404                         (desc, note)
405                     }
406                     ty::FnDef(def_id, _) => {
407                         let name = self.infcx.tcx.item_name(*def_id);
408                         let identity_substs =
409                             InternalSubsts::identity_for_item(self.infcx.tcx, *def_id);
410                         let desc = format!("a function pointer to `{name}`");
411                         let note = format!(
412                             "the function `{name}` is invariant over the parameter `{}`",
413                             identity_substs[param_index as usize]
414                         );
415                         (desc, note)
416                     }
417                     _ => panic!("Unexpected type {:?}", ty),
418                 };
419                 diag.note(&format!("requirement occurs because of {desc}",));
420                 diag.note(&note);
421                 diag.help("see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance");
422             }
423         }
424
425         for extra in extra_info {
426             match extra {
427                 ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
428                     diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
429                 }
430             }
431         }
432
433         self.buffer_error(diag);
434     }
435
436     /// Report a specialized error when `FnMut` closures return a reference to a captured variable.
437     /// This function expects `fr` to be local and `outlived_fr` to not be local.
438     ///
439     /// ```text
440     /// error: captured variable cannot escape `FnMut` closure body
441     ///   --> $DIR/issue-53040.rs:15:8
442     ///    |
443     /// LL |     || &mut v;
444     ///    |     -- ^^^^^^ creates a reference to a captured variable which escapes the closure body
445     ///    |     |
446     ///    |     inferred to be a `FnMut` closure
447     ///    |
448     ///    = note: `FnMut` closures only have access to their captured variables while they are
449     ///            executing...
450     ///    = note: ...therefore, returned references to captured variables will escape the closure
451     /// ```
452     fn report_fnmut_error(
453         &self,
454         errci: &ErrorConstraintInfo<'tcx>,
455         kind: ReturnConstraint,
456     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
457         let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
458
459         let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
460         if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *output_ty.kind() {
461             output_ty = self.infcx.tcx.type_of(def_id)
462         };
463
464         debug!("report_fnmut_error: output_ty={:?}", output_ty);
465
466         let err = FnMutError {
467             span: *span,
468             ty_err: match output_ty.kind() {
469                 ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
470                 ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
471                     FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
472                 }
473                 _ => FnMutReturnTypeErr::ReturnRef { span: *span },
474             },
475         };
476
477         let mut diag = self.infcx.tcx.sess.create_err(err);
478
479         if let ReturnConstraint::ClosureUpvar(upvar_field) = kind {
480             let def_id = match self.regioncx.universal_regions().defining_ty {
481                 DefiningTy::Closure(def_id, _) => def_id,
482                 ty => bug!("unexpected DefiningTy {:?}", ty),
483             };
484
485             let captured_place = &self.upvars[upvar_field.index()].place;
486             let defined_hir = match captured_place.place.base {
487                 PlaceBase::Local(hirid) => Some(hirid),
488                 PlaceBase::Upvar(upvar) => Some(upvar.var_path.hir_id),
489                 _ => None,
490             };
491
492             if let Some(def_hir) = defined_hir {
493                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
494                 let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
495                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
496                 diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
497                 diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
498             }
499         }
500
501         if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
502             diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span });
503         }
504
505         self.suggest_move_on_borrowing_closure(&mut diag);
506
507         diag
508     }
509
510     /// Reports an error specifically for when data is escaping a closure.
511     ///
512     /// ```text
513     /// error: borrowed data escapes outside of function
514     ///   --> $DIR/lifetime-bound-will-change-warning.rs:44:5
515     ///    |
516     /// LL | fn test2<'a>(x: &'a Box<Fn()+'a>) {
517     ///    |              - `x` is a reference that is only valid in the function body
518     /// LL |     // but ref_obj will not, so warn.
519     /// LL |     ref_obj(x)
520     ///    |     ^^^^^^^^^^ `x` escapes the function body here
521     /// ```
522     #[instrument(level = "debug", skip(self))]
523     fn report_escaping_data_error(
524         &self,
525         errci: &ErrorConstraintInfo<'tcx>,
526     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
527         let ErrorConstraintInfo { span, category, .. } = errci;
528
529         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
530             self.infcx.tcx,
531             &self.body,
532             &self.local_names,
533             &self.upvars,
534             errci.fr,
535         );
536         let outlived_fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
537             self.infcx.tcx,
538             &self.body,
539             &self.local_names,
540             &self.upvars,
541             errci.outlived_fr,
542         );
543
544         let (_, escapes_from) = self
545             .infcx
546             .tcx
547             .article_and_description(self.regioncx.universal_regions().defining_ty.def_id());
548
549         // Revert to the normal error in these cases.
550         // Assignments aren't "escapes" in function items.
551         if (fr_name_and_span.is_none() && outlived_fr_name_and_span.is_none())
552             || (*category == ConstraintCategory::Assignment
553                 && self.regioncx.universal_regions().defining_ty.is_fn_def())
554             || self.regioncx.universal_regions().defining_ty.is_const()
555         {
556             return self.report_general_error(&ErrorConstraintInfo {
557                 fr_is_local: true,
558                 outlived_fr_is_local: false,
559                 ..*errci
560             });
561         }
562
563         let mut diag =
564             borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from);
565
566         if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
567             diag.span_label(
568                 outlived_fr_span,
569                 format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",),
570             );
571         }
572
573         if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
574             diag.span_label(
575                 fr_span,
576                 format!(
577                     "`{fr_name}` is a reference that is only valid in the {escapes_from} body",
578                 ),
579             );
580
581             diag.span_label(*span, format!("`{fr_name}` escapes the {escapes_from} body here"));
582         }
583
584         // Only show an extra note if we can find an 'error region' for both of the region
585         // variables. This avoids showing a noisy note that just mentions 'synthetic' regions
586         // that don't help the user understand the error.
587         match (self.to_error_region(errci.fr), self.to_error_region(errci.outlived_fr)) {
588             (Some(f), Some(o)) => {
589                 self.maybe_suggest_constrain_dyn_trait_impl(&mut diag, f, o, category);
590
591                 let fr_region_name = self.give_region_a_name(errci.fr).unwrap();
592                 fr_region_name.highlight_region_name(&mut diag);
593                 let outlived_fr_region_name = self.give_region_a_name(errci.outlived_fr).unwrap();
594                 outlived_fr_region_name.highlight_region_name(&mut diag);
595
596                 diag.span_label(
597                     *span,
598                     format!(
599                         "{}requires that `{}` must outlive `{}`",
600                         category.description(),
601                         fr_region_name,
602                         outlived_fr_region_name,
603                     ),
604                 );
605             }
606             _ => {}
607         }
608
609         diag
610     }
611
612     /// Reports a region inference error for the general case with named/synthesized lifetimes to
613     /// explain what is happening.
614     ///
615     /// ```text
616     /// error: unsatisfied lifetime constraints
617     ///   --> $DIR/regions-creating-enums3.rs:17:5
618     ///    |
619     /// LL | fn mk_add_bad1<'a,'b>(x: &'a ast<'a>, y: &'b ast<'b>) -> ast<'a> {
620     ///    |                -- -- lifetime `'b` defined here
621     ///    |                |
622     ///    |                lifetime `'a` defined here
623     /// LL |     ast::add(x, y)
624     ///    |     ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
625     ///    |                    is returning data with lifetime `'b`
626     /// ```
627     fn report_general_error(
628         &self,
629         errci: &ErrorConstraintInfo<'tcx>,
630     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
631         let ErrorConstraintInfo {
632             fr,
633             fr_is_local,
634             outlived_fr,
635             outlived_fr_is_local,
636             span,
637             category,
638             ..
639         } = errci;
640
641         let (_, mir_def_name) =
642             self.infcx.tcx.article_and_description(self.mir_def_id().to_def_id());
643
644         let err = LifetimeOutliveErr { span: *span };
645         let mut diag = self.infcx.tcx.sess.create_err(err);
646
647         let fr_name = self.give_region_a_name(*fr).unwrap();
648         fr_name.highlight_region_name(&mut diag);
649         let outlived_fr_name = self.give_region_a_name(*outlived_fr).unwrap();
650         outlived_fr_name.highlight_region_name(&mut diag);
651
652         let err_category = match (category, outlived_fr_is_local, fr_is_local) {
653             (ConstraintCategory::Return(_), true, _) => LifetimeReturnCategoryErr::WrongReturn {
654                 span: *span,
655                 mir_def_name,
656                 outlived_fr_name,
657                 fr_name: &fr_name,
658             },
659             _ => LifetimeReturnCategoryErr::ShortReturn {
660                 span: *span,
661                 category_desc: category.description(),
662                 free_region_name: &fr_name,
663                 outlived_fr_name,
664             },
665         };
666
667         diag.subdiagnostic(err_category);
668
669         self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
670         self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
671         self.suggest_move_on_borrowing_closure(&mut diag);
672
673         diag
674     }
675
676     /// Adds a suggestion to errors where an `impl Trait` is returned.
677     ///
678     /// ```text
679     /// help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as
680     ///       a constraint
681     ///    |
682     /// LL |     fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
683     ///    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
684     /// ```
685     fn add_static_impl_trait_suggestion(
686         &self,
687         diag: &mut Diagnostic,
688         fr: RegionVid,
689         // We need to pass `fr_name` - computing it again will label it twice.
690         fr_name: RegionName,
691         outlived_fr: RegionVid,
692     ) {
693         if let (Some(f), Some(outlived_f)) =
694             (self.to_error_region(fr), self.to_error_region(outlived_fr))
695         {
696             if *outlived_f != ty::ReStatic {
697                 return;
698             }
699
700             let fn_returns = self
701                 .infcx
702                 .tcx
703                 .is_suitable_region(f)
704                 .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
705                 .unwrap_or_default();
706
707             if fn_returns.is_empty() {
708                 return;
709             }
710
711             let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
712                 param
713             } else {
714                 return;
715             };
716
717             let lifetime = if f.has_name() { fr_name.name } else { kw::UnderscoreLifetime };
718
719             let arg = match param.param.pat.simple_ident() {
720                 Some(simple_ident) => format!("argument `{}`", simple_ident),
721                 None => "the argument".to_string(),
722             };
723             let captures = format!("captures data from {}", arg);
724
725             return nice_region_error::suggest_new_region_bound(
726                 self.infcx.tcx,
727                 diag,
728                 fn_returns,
729                 lifetime.to_string(),
730                 Some(arg),
731                 captures,
732                 Some((param.param_ty_span, param.param_ty.to_string())),
733             );
734         }
735     }
736
737     fn maybe_suggest_constrain_dyn_trait_impl(
738         &self,
739         diag: &mut Diagnostic,
740         f: Region<'tcx>,
741         o: Region<'tcx>,
742         category: &ConstraintCategory<'tcx>,
743     ) {
744         if !o.is_static() {
745             return;
746         }
747
748         let tcx = self.infcx.tcx;
749
750         let instance = if let ConstraintCategory::CallArgument(Some(func_ty)) = category {
751             let (fn_did, substs) = match func_ty.kind() {
752                 ty::FnDef(fn_did, substs) => (fn_did, substs),
753                 _ => return,
754             };
755             debug!(?fn_did, ?substs);
756
757             // Only suggest this on function calls, not closures
758             let ty = tcx.type_of(fn_did);
759             debug!("ty: {:?}, ty.kind: {:?}", ty, ty.kind());
760             if let ty::Closure(_, _) = ty.kind() {
761                 return;
762             }
763
764             if let Ok(Some(instance)) = ty::Instance::resolve(
765                 tcx,
766                 self.param_env,
767                 *fn_did,
768                 self.infcx.resolve_vars_if_possible(substs),
769             ) {
770                 instance
771             } else {
772                 return;
773             }
774         } else {
775             return;
776         };
777
778         let param = match find_param_with_region(tcx, f, o) {
779             Some(param) => param,
780             None => return,
781         };
782         debug!(?param);
783
784         let mut visitor = TraitObjectVisitor(FxIndexSet::default());
785         visitor.visit_ty(param.param_ty);
786
787         let Some((ident, self_ty)) =
788             NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &visitor.0) else { return; };
789
790         self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty);
791     }
792
793     #[instrument(skip(self, err), level = "debug")]
794     fn suggest_constrain_dyn_trait_in_impl(
795         &self,
796         err: &mut Diagnostic,
797         found_dids: &FxIndexSet<DefId>,
798         ident: Ident,
799         self_ty: &hir::Ty<'_>,
800     ) -> bool {
801         debug!("err: {:#?}", err);
802         let mut suggested = false;
803         for found_did in found_dids {
804             let mut traits = vec![];
805             let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did);
806             hir_v.visit_ty(&self_ty);
807             debug!("trait spans found: {:?}", traits);
808             for span in &traits {
809                 let mut multi_span: MultiSpan = vec![*span].into();
810                 multi_span
811                     .push_span_label(*span, "this has an implicit `'static` lifetime requirement");
812                 multi_span.push_span_label(
813                     ident.span,
814                     "calling this method introduces the `impl`'s 'static` requirement",
815                 );
816                 err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
817                 err.span_suggestion_verbose(
818                     span.shrink_to_hi(),
819                     "consider relaxing the implicit `'static` requirement",
820                     " + '_",
821                     Applicability::MaybeIncorrect,
822                 );
823                 suggested = true;
824             }
825         }
826         suggested
827     }
828
829     fn suggest_adding_lifetime_params(
830         &self,
831         diag: &mut Diagnostic,
832         sub: RegionVid,
833         sup: RegionVid,
834     ) {
835         let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
836             return
837         };
838
839         let Some((ty_sub, _)) = self
840             .infcx
841             .tcx
842             .is_suitable_region(sub)
843             .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
844             return
845         };
846
847         let Some((ty_sup, _)) = self
848             .infcx
849             .tcx
850             .is_suitable_region(sup)
851             .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
852             return
853         };
854
855         suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
856     }
857
858     fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
859         let map = self.infcx.tcx.hir();
860         let body_id = map.body_owned_by(self.mir_def_id());
861         let expr = &map.body(body_id).value;
862         let mut closure_span = None::<rustc_span::Span>;
863         match expr.kind {
864             hir::ExprKind::MethodCall(.., args, _) => {
865                 for arg in args {
866                     if let hir::ExprKind::Closure(hir::Closure {
867                         capture_clause: hir::CaptureBy::Ref,
868                         ..
869                     }) = arg.kind
870                     {
871                         closure_span = Some(arg.span.shrink_to_lo());
872                         break;
873                     }
874                 }
875             }
876             hir::ExprKind::Block(blk, _) => {
877                 if let Some(expr) = blk.expr {
878                     // only when the block is a closure
879                     if let hir::ExprKind::Closure(hir::Closure {
880                         capture_clause: hir::CaptureBy::Ref,
881                         body,
882                         ..
883                     }) = expr.kind
884                     {
885                         let body = map.body(*body);
886                         if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
887                             closure_span = Some(expr.span.shrink_to_lo());
888                         }
889                     }
890                 }
891             }
892             _ => {}
893         }
894         if let Some(closure_span) = closure_span {
895             diag.span_suggestion_verbose(
896                 closure_span,
897                 "consider adding 'move' keyword before the nested closure",
898                 "move ",
899                 Applicability::MaybeIncorrect,
900             );
901         }
902     }
903 }