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