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