1 use std::fmt::{self, Display}; 2 use std::iter; 3 4 use rustc_errors::Diagnostic; 5 use rustc_hir as hir; 6 use rustc_hir::def::{DefKind, Res}; 7 use rustc_middle::ty::print::RegionHighlightMode; 8 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; 9 use rustc_middle::ty::{self, RegionVid, Ty}; 10 use rustc_span::symbol::{kw, sym, Ident, Symbol}; 11 use rustc_span::{Span, DUMMY_SP}; 12 13 use crate::{universal_regions::DefiningTy, MirBorrowckCtxt}; 14 15 /// A name for a particular region used in emitting diagnostics. This name could be a generated 16 /// name like `'1`, a name used by the user like `'a`, or a name like `'static`. 17 #[derive(Debug, Clone)] 18 pub(crate) struct RegionName { 19 /// The name of the region (interned). 20 pub(crate) name: Symbol, 21 /// Where the region comes from. 22 pub(crate) source: RegionNameSource, 23 } 24 25 /// Denotes the source of a region that is named by a `RegionName`. For example, a free region that 26 /// was named by the user would get `NamedFreeRegion` and `'static` lifetime would get `Static`. 27 /// This helps to print the right kinds of diagnostics. 28 #[derive(Debug, Clone)] 29 pub(crate) enum RegionNameSource { 30 /// A bound (not free) region that was substituted at the def site (not an HRTB). 31 NamedEarlyBoundRegion(Span), 32 /// A free region that the user has a name (`'a`) for. 33 NamedFreeRegion(Span), 34 /// The `'static` region. 35 Static, 36 /// The free region corresponding to the environment of a closure. 37 SynthesizedFreeEnvRegion(Span, &'static str), 38 /// The region corresponding to an argument. 39 AnonRegionFromArgument(RegionNameHighlight), 40 /// The region corresponding to a closure upvar. 41 AnonRegionFromUpvar(Span, Symbol), 42 /// The region corresponding to the return type of a closure. 43 AnonRegionFromOutput(RegionNameHighlight, &'static str), 44 /// The region from a type yielded by a generator. 45 AnonRegionFromYieldTy(Span, String), 46 /// An anonymous region from an async fn. 47 AnonRegionFromAsyncFn(Span), 48 /// An anonymous region from an impl self type or trait 49 AnonRegionFromImplSignature(Span, &'static str), 50 } 51 52 /// Describes what to highlight to explain to the user that we're giving an anonymous region a 53 /// synthesized name, and how to highlight it. 54 #[derive(Debug, Clone)] 55 pub(crate) enum RegionNameHighlight { 56 /// The anonymous region corresponds to a reference that was found by traversing the type in the HIR. 57 MatchedHirTy(Span), 58 /// The anonymous region corresponds to a `'_` in the generics list of a struct/enum/union. 59 MatchedAdtAndSegment(Span), 60 /// The anonymous region corresponds to a region where the type annotation is completely missing 61 /// from the code, e.g. in a closure arguments `|x| { ... }`, where `x` is a reference. 62 CannotMatchHirTy(Span, String), 63 /// The anonymous region corresponds to a region where the type annotation is completely missing 64 /// from the code, and *even if* we print out the full name of the type, the region name won't 65 /// be included. This currently occurs for opaque types like `impl Future`. 66 Occluded(Span, String), 67 } 68 69 impl RegionName { was_named(&self) -> bool70 pub(crate) fn was_named(&self) -> bool { 71 match self.source { 72 RegionNameSource::NamedEarlyBoundRegion(..) 73 | RegionNameSource::NamedFreeRegion(..) 74 | RegionNameSource::Static => true, 75 RegionNameSource::SynthesizedFreeEnvRegion(..) 76 | RegionNameSource::AnonRegionFromArgument(..) 77 | RegionNameSource::AnonRegionFromUpvar(..) 78 | RegionNameSource::AnonRegionFromOutput(..) 79 | RegionNameSource::AnonRegionFromYieldTy(..) 80 | RegionNameSource::AnonRegionFromAsyncFn(..) 81 | RegionNameSource::AnonRegionFromImplSignature(..) => false, 82 } 83 } 84 span(&self) -> Option<Span>85 pub(crate) fn span(&self) -> Option<Span> { 86 match self.source { 87 RegionNameSource::Static => None, 88 RegionNameSource::NamedEarlyBoundRegion(span) 89 | RegionNameSource::NamedFreeRegion(span) 90 | RegionNameSource::SynthesizedFreeEnvRegion(span, _) 91 | RegionNameSource::AnonRegionFromUpvar(span, _) 92 | RegionNameSource::AnonRegionFromYieldTy(span, _) 93 | RegionNameSource::AnonRegionFromAsyncFn(span) 94 | RegionNameSource::AnonRegionFromImplSignature(span, _) => Some(span), 95 RegionNameSource::AnonRegionFromArgument(ref highlight) 96 | RegionNameSource::AnonRegionFromOutput(ref highlight, _) => match *highlight { 97 RegionNameHighlight::MatchedHirTy(span) 98 | RegionNameHighlight::MatchedAdtAndSegment(span) 99 | RegionNameHighlight::CannotMatchHirTy(span, _) 100 | RegionNameHighlight::Occluded(span, _) => Some(span), 101 }, 102 } 103 } 104 highlight_region_name(&self, diag: &mut Diagnostic)105 pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) { 106 match &self.source { 107 RegionNameSource::NamedFreeRegion(span) 108 | RegionNameSource::NamedEarlyBoundRegion(span) => { 109 diag.span_label(*span, format!("lifetime `{self}` defined here")); 110 } 111 RegionNameSource::SynthesizedFreeEnvRegion(span, note) => { 112 diag.span_label(*span, format!("lifetime `{self}` represents this closure's body")); 113 diag.note(*note); 114 } 115 RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::CannotMatchHirTy( 116 span, 117 type_name, 118 )) => { 119 diag.span_label(*span, format!("has type `{type_name}`")); 120 } 121 RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::MatchedHirTy(span)) 122 | RegionNameSource::AnonRegionFromOutput(RegionNameHighlight::MatchedHirTy(span), _) 123 | RegionNameSource::AnonRegionFromAsyncFn(span) => { 124 diag.span_label( 125 *span, 126 format!("let's call the lifetime of this reference `{self}`"), 127 ); 128 } 129 RegionNameSource::AnonRegionFromArgument( 130 RegionNameHighlight::MatchedAdtAndSegment(span), 131 ) 132 | RegionNameSource::AnonRegionFromOutput( 133 RegionNameHighlight::MatchedAdtAndSegment(span), 134 _, 135 ) => { 136 diag.span_label(*span, format!("let's call this `{self}`")); 137 } 138 RegionNameSource::AnonRegionFromArgument(RegionNameHighlight::Occluded( 139 span, 140 type_name, 141 )) => { 142 diag.span_label( 143 *span, 144 format!("lifetime `{self}` appears in the type {type_name}"), 145 ); 146 } 147 RegionNameSource::AnonRegionFromOutput( 148 RegionNameHighlight::Occluded(span, type_name), 149 mir_description, 150 ) => { 151 diag.span_label( 152 *span, 153 format!( 154 "return type{mir_description} `{type_name}` contains a lifetime `{self}`" 155 ), 156 ); 157 } 158 RegionNameSource::AnonRegionFromUpvar(span, upvar_name) => { 159 diag.span_label( 160 *span, 161 format!("lifetime `{self}` appears in the type of `{upvar_name}`"), 162 ); 163 } 164 RegionNameSource::AnonRegionFromOutput( 165 RegionNameHighlight::CannotMatchHirTy(span, type_name), 166 mir_description, 167 ) => { 168 diag.span_label(*span, format!("return type{mir_description} is {type_name}")); 169 } 170 RegionNameSource::AnonRegionFromYieldTy(span, type_name) => { 171 diag.span_label(*span, format!("yield type is {type_name}")); 172 } 173 RegionNameSource::AnonRegionFromImplSignature(span, location) => { 174 diag.span_label( 175 *span, 176 format!("lifetime `{self}` appears in the `impl`'s {location}"), 177 ); 178 } 179 RegionNameSource::Static => {} 180 } 181 } 182 } 183 184 impl Display for RegionName { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 186 write!(f, "{}", self.name) 187 } 188 } 189 190 impl rustc_errors::IntoDiagnosticArg for RegionName { into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static>191 fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { 192 self.to_string().into_diagnostic_arg() 193 } 194 } 195 196 impl<'tcx> MirBorrowckCtxt<'_, 'tcx> { mir_def_id(&self) -> hir::def_id::LocalDefId197 pub(crate) fn mir_def_id(&self) -> hir::def_id::LocalDefId { 198 self.body.source.def_id().expect_local() 199 } 200 mir_hir_id(&self) -> hir::HirId201 pub(crate) fn mir_hir_id(&self) -> hir::HirId { 202 self.infcx.tcx.hir().local_def_id_to_hir_id(self.mir_def_id()) 203 } 204 205 /// Generate a synthetic region named `'N`, where `N` is the next value of the counter. Then, 206 /// increment the counter. 207 /// 208 /// This is _not_ idempotent. Call `give_region_a_name` when possible. synthesize_region_name(&self) -> Symbol209 pub(crate) fn synthesize_region_name(&self) -> Symbol { 210 let c = self.next_region_name.replace_with(|counter| *counter + 1); 211 Symbol::intern(&format!("'{c:?}")) 212 } 213 214 /// Maps from an internal MIR region vid to something that we can 215 /// report to the user. In some cases, the region vids will map 216 /// directly to lifetimes that the user has a name for (e.g., 217 /// `'static`). But frequently they will not, in which case we 218 /// have to find some way to identify the lifetime to the user. To 219 /// that end, this function takes a "diagnostic" so that it can 220 /// create auxiliary notes as needed. 221 /// 222 /// The names are memoized, so this is both cheap to recompute and idempotent. 223 /// 224 /// Example (function arguments): 225 /// 226 /// Suppose we are trying to give a name to the lifetime of the 227 /// reference `x`: 228 /// 229 /// ```ignore (pseudo-rust) 230 /// fn foo(x: &u32) { .. } 231 /// ``` 232 /// 233 /// This function would create a label like this: 234 /// 235 /// ```text 236 /// | fn foo(x: &u32) { .. } 237 /// ------- fully elaborated type of `x` is `&'1 u32` 238 /// ``` 239 /// 240 /// and then return the name `'1` for us to use. give_region_a_name(&self, fr: RegionVid) -> Option<RegionName>241 pub(crate) fn give_region_a_name(&self, fr: RegionVid) -> Option<RegionName> { 242 debug!( 243 "give_region_a_name(fr={:?}, counter={:?})", 244 fr, 245 self.next_region_name.try_borrow().unwrap() 246 ); 247 248 assert!(self.regioncx.universal_regions().is_universal_region(fr)); 249 250 if let Some(value) = self.region_names.try_borrow_mut().unwrap().get(&fr) { 251 return Some(value.clone()); 252 } 253 254 let value = self 255 .give_name_from_error_region(fr) 256 .or_else(|| self.give_name_if_anonymous_region_appears_in_arguments(fr)) 257 .or_else(|| self.give_name_if_anonymous_region_appears_in_upvars(fr)) 258 .or_else(|| self.give_name_if_anonymous_region_appears_in_output(fr)) 259 .or_else(|| self.give_name_if_anonymous_region_appears_in_yield_ty(fr)) 260 .or_else(|| self.give_name_if_anonymous_region_appears_in_impl_signature(fr)) 261 .or_else(|| self.give_name_if_anonymous_region_appears_in_arg_position_impl_trait(fr)); 262 263 if let Some(value) = &value { 264 self.region_names.try_borrow_mut().unwrap().insert(fr, value.clone()); 265 } 266 267 debug!("give_region_a_name: gave name {:?}", value); 268 value 269 } 270 271 /// Checks for the case where `fr` maps to something that the 272 /// *user* has a name for. In that case, we'll be able to map 273 /// `fr` to a `Region<'tcx>`, and that region will be one of 274 /// named variants. 275 #[instrument(level = "trace", skip(self))] give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName>276 fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> { 277 let error_region = self.to_error_region(fr)?; 278 279 let tcx = self.infcx.tcx; 280 281 debug!("give_region_a_name: error_region = {:?}", error_region); 282 match *error_region { 283 ty::ReEarlyBound(ebr) => ebr.has_name().then(|| { 284 let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP); 285 RegionName { name: ebr.name, source: RegionNameSource::NamedEarlyBoundRegion(span) } 286 }), 287 288 ty::ReStatic => { 289 Some(RegionName { name: kw::StaticLifetime, source: RegionNameSource::Static }) 290 } 291 292 ty::ReFree(free_region) => match free_region.bound_region { 293 ty::BoundRegionKind::BrNamed(region_def_id, name) => { 294 // Get the span to point to, even if we don't use the name. 295 let span = tcx.hir().span_if_local(region_def_id).unwrap_or(DUMMY_SP); 296 debug!( 297 "bound region named: {:?}, is_named: {:?}", 298 name, 299 free_region.bound_region.is_named() 300 ); 301 302 if free_region.bound_region.is_named() { 303 // A named region that is actually named. 304 Some(RegionName { name, source: RegionNameSource::NamedFreeRegion(span) }) 305 } else if let hir::IsAsync::Async = tcx.asyncness(self.mir_hir_id().owner) { 306 // If we spuriously thought that the region is named, we should let the 307 // system generate a true name for error messages. Currently this can 308 // happen if we have an elided name in an async fn for example: the 309 // compiler will generate a region named `'_`, but reporting such a name is 310 // not actually useful, so we synthesize a name for it instead. 311 let name = self.synthesize_region_name(); 312 Some(RegionName { 313 name, 314 source: RegionNameSource::AnonRegionFromAsyncFn(span), 315 }) 316 } else { 317 None 318 } 319 } 320 321 ty::BoundRegionKind::BrEnv => { 322 let def_ty = self.regioncx.universal_regions().defining_ty; 323 324 let DefiningTy::Closure(_, substs) = def_ty else { 325 // Can't have BrEnv in functions, constants or generators. 326 bug!("BrEnv outside of closure."); 327 }; 328 let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) 329 = tcx.hir().expect_expr(self.mir_hir_id()).kind 330 else { 331 bug!("Closure is not defined by a closure expr"); 332 }; 333 let region_name = self.synthesize_region_name(); 334 335 let closure_kind_ty = substs.as_closure().kind_ty(); 336 let note = match closure_kind_ty.to_opt_closure_kind() { 337 Some(ty::ClosureKind::Fn) => { 338 "closure implements `Fn`, so references to captured variables \ 339 can't escape the closure" 340 } 341 Some(ty::ClosureKind::FnMut) => { 342 "closure implements `FnMut`, so references to captured variables \ 343 can't escape the closure" 344 } 345 Some(ty::ClosureKind::FnOnce) => { 346 bug!("BrEnv in a `FnOnce` closure"); 347 } 348 None => bug!("Closure kind not inferred in borrow check"), 349 }; 350 351 Some(RegionName { 352 name: region_name, 353 source: RegionNameSource::SynthesizedFreeEnvRegion(fn_decl_span, note), 354 }) 355 } 356 357 ty::BoundRegionKind::BrAnon(..) => None, 358 }, 359 360 ty::ReLateBound(..) 361 | ty::ReVar(..) 362 | ty::RePlaceholder(..) 363 | ty::ReErased 364 | ty::ReError(_) => None, 365 } 366 } 367 368 /// Finds an argument that contains `fr` and label it with a fully 369 /// elaborated type, returning something like `'1`. Result looks 370 /// like: 371 /// 372 /// ```text 373 /// | fn foo(x: &u32) { .. } 374 /// ------- fully elaborated type of `x` is `&'1 u32` 375 /// ``` 376 #[instrument(level = "trace", skip(self))] give_name_if_anonymous_region_appears_in_arguments( &self, fr: RegionVid, ) -> Option<RegionName>377 fn give_name_if_anonymous_region_appears_in_arguments( 378 &self, 379 fr: RegionVid, 380 ) -> Option<RegionName> { 381 let implicit_inputs = self.regioncx.universal_regions().defining_ty.implicit_inputs(); 382 let argument_index = self.regioncx.get_argument_index_for_region(self.infcx.tcx, fr)?; 383 384 let arg_ty = self.regioncx.universal_regions().unnormalized_input_tys 385 [implicit_inputs + argument_index]; 386 let (_, span) = self.regioncx.get_argument_name_and_span_for_region( 387 &self.body, 388 &self.local_names, 389 argument_index, 390 ); 391 392 let highlight = self 393 .get_argument_hir_ty_for_highlighting(argument_index) 394 .and_then(|arg_hir_ty| self.highlight_if_we_can_match_hir_ty(fr, arg_ty, arg_hir_ty)) 395 .unwrap_or_else(|| { 396 // `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to 397 // the anonymous region. If it succeeds, the `synthesize_region_name` call below 398 // will increment the counter, "reserving" the number we just used. 399 let counter = *self.next_region_name.try_borrow().unwrap(); 400 self.highlight_if_we_cannot_match_hir_ty(fr, arg_ty, span, counter) 401 }); 402 403 Some(RegionName { 404 name: self.synthesize_region_name(), 405 source: RegionNameSource::AnonRegionFromArgument(highlight), 406 }) 407 } 408 get_argument_hir_ty_for_highlighting( &self, argument_index: usize, ) -> Option<&hir::Ty<'tcx>>409 fn get_argument_hir_ty_for_highlighting( 410 &self, 411 argument_index: usize, 412 ) -> Option<&hir::Ty<'tcx>> { 413 let fn_decl = self.infcx.tcx.hir().fn_decl_by_hir_id(self.mir_hir_id())?; 414 let argument_hir_ty: &hir::Ty<'_> = fn_decl.inputs.get(argument_index)?; 415 match argument_hir_ty.kind { 416 // This indicates a variable with no type annotation, like 417 // `|x|`... in that case, we can't highlight the type but 418 // must highlight the variable. 419 // NOTE(eddyb) this is handled in/by the sole caller 420 // (`give_name_if_anonymous_region_appears_in_arguments`). 421 hir::TyKind::Infer => None, 422 423 _ => Some(argument_hir_ty), 424 } 425 } 426 427 /// Attempts to highlight the specific part of a type in an argument 428 /// that has no type annotation. 429 /// For example, we might produce an annotation like this: 430 /// 431 /// ```text 432 /// | foo(|a, b| b) 433 /// | - - 434 /// | | | 435 /// | | has type `&'1 u32` 436 /// | has type `&'2 u32` 437 /// ``` highlight_if_we_cannot_match_hir_ty( &self, needle_fr: RegionVid, ty: Ty<'tcx>, span: Span, counter: usize, ) -> RegionNameHighlight438 fn highlight_if_we_cannot_match_hir_ty( 439 &self, 440 needle_fr: RegionVid, 441 ty: Ty<'tcx>, 442 span: Span, 443 counter: usize, 444 ) -> RegionNameHighlight { 445 let mut highlight = RegionHighlightMode::new(self.infcx.tcx); 446 highlight.highlighting_region_vid(needle_fr, counter); 447 let type_name = 448 self.infcx.extract_inference_diagnostics_data(ty.into(), Some(highlight)).name; 449 450 debug!( 451 "highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", 452 type_name, needle_fr 453 ); 454 if type_name.contains(&format!("'{counter}")) { 455 // Only add a label if we can confirm that a region was labelled. 456 RegionNameHighlight::CannotMatchHirTy(span, type_name) 457 } else { 458 RegionNameHighlight::Occluded(span, type_name) 459 } 460 } 461 462 /// Attempts to highlight the specific part of a type annotation 463 /// that contains the anonymous reference we want to give a name 464 /// to. For example, we might produce an annotation like this: 465 /// 466 /// ```text 467 /// | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item = &T>> { 468 /// | - let's call the lifetime of this reference `'1` 469 /// ``` 470 /// 471 /// the way this works is that we match up `ty`, which is 472 /// a `Ty<'tcx>` (the internal form of the type) with 473 /// `hir_ty`, a `hir::Ty` (the syntax of the type 474 /// annotation). We are descending through the types stepwise, 475 /// looking in to find the region `needle_fr` in the internal 476 /// type. Once we find that, we can use the span of the `hir::Ty` 477 /// to add the highlight. 478 /// 479 /// This is a somewhat imperfect process, so along the way we also 480 /// keep track of the **closest** type we've found. If we fail to 481 /// find the exact `&` or `'_` to highlight, then we may fall back 482 /// to highlighting that closest type instead. highlight_if_we_can_match_hir_ty( &self, needle_fr: RegionVid, ty: Ty<'tcx>, hir_ty: &hir::Ty<'_>, ) -> Option<RegionNameHighlight>483 fn highlight_if_we_can_match_hir_ty( 484 &self, 485 needle_fr: RegionVid, 486 ty: Ty<'tcx>, 487 hir_ty: &hir::Ty<'_>, 488 ) -> Option<RegionNameHighlight> { 489 let search_stack: &mut Vec<(Ty<'tcx>, &hir::Ty<'_>)> = &mut vec![(ty, hir_ty)]; 490 491 while let Some((ty, hir_ty)) = search_stack.pop() { 492 match (ty.kind(), &hir_ty.kind) { 493 // Check if the `ty` is `&'X ..` where `'X` 494 // is the region we are looking for -- if so, and we have a `&T` 495 // on the RHS, then we want to highlight the `&` like so: 496 // 497 // & 498 // - let's call the lifetime of this reference `'1` 499 (ty::Ref(region, referent_ty, _), hir::TyKind::Ref(_lifetime, referent_hir_ty)) => { 500 if region.as_var() == needle_fr { 501 // Just grab the first character, the `&`. 502 let source_map = self.infcx.tcx.sess.source_map(); 503 let ampersand_span = source_map.start_point(hir_ty.span); 504 505 return Some(RegionNameHighlight::MatchedHirTy(ampersand_span)); 506 } 507 508 // Otherwise, let's descend into the referent types. 509 search_stack.push((*referent_ty, &referent_hir_ty.ty)); 510 } 511 512 // Match up something like `Foo<'1>` 513 ( 514 ty::Adt(_adt_def, substs), 515 hir::TyKind::Path(hir::QPath::Resolved(None, path)), 516 ) => { 517 match path.res { 518 // Type parameters of the type alias have no reason to 519 // be the same as those of the ADT. 520 // FIXME: We should be able to do something similar to 521 // match_adt_and_segment in this case. 522 Res::Def(DefKind::TyAlias, _) => (), 523 _ => { 524 if let Some(last_segment) = path.segments.last() { 525 if let Some(highlight) = self.match_adt_and_segment( 526 substs, 527 needle_fr, 528 last_segment, 529 search_stack, 530 ) { 531 return Some(highlight); 532 } 533 } 534 } 535 } 536 } 537 538 // The following cases don't have lifetimes, so we 539 // just worry about trying to match up the rustc type 540 // with the HIR types: 541 (&ty::Tuple(elem_tys), hir::TyKind::Tup(elem_hir_tys)) => { 542 search_stack.extend(iter::zip(elem_tys, *elem_hir_tys)); 543 } 544 545 (ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty)) 546 | (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => { 547 search_stack.push((*elem_ty, elem_hir_ty)); 548 } 549 550 (ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => { 551 search_stack.push((mut_ty.ty, &mut_hir_ty.ty)); 552 } 553 554 _ => { 555 // FIXME there are other cases that we could trace 556 } 557 } 558 } 559 560 None 561 } 562 563 /// We've found an enum/struct/union type with the substitutions 564 /// `substs` and -- in the HIR -- a path type with the final 565 /// segment `last_segment`. Try to find a `'_` to highlight in 566 /// the generic args (or, if not, to produce new zipped pairs of 567 /// types+hir to search through). match_adt_and_segment<'hir>( &self, substs: SubstsRef<'tcx>, needle_fr: RegionVid, last_segment: &'hir hir::PathSegment<'hir>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, ) -> Option<RegionNameHighlight>568 fn match_adt_and_segment<'hir>( 569 &self, 570 substs: SubstsRef<'tcx>, 571 needle_fr: RegionVid, 572 last_segment: &'hir hir::PathSegment<'hir>, 573 search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, 574 ) -> Option<RegionNameHighlight> { 575 // Did the user give explicit arguments? (e.g., `Foo<..>`) 576 let args = last_segment.args.as_ref()?; 577 let lifetime = 578 self.try_match_adt_and_generic_args(substs, needle_fr, args, search_stack)?; 579 if lifetime.is_anonymous() { 580 None 581 } else { 582 Some(RegionNameHighlight::MatchedAdtAndSegment(lifetime.ident.span)) 583 } 584 } 585 586 /// We've found an enum/struct/union type with the substitutions 587 /// `substs` and -- in the HIR -- a path with the generic 588 /// arguments `args`. If `needle_fr` appears in the args, return 589 /// the `hir::Lifetime` that corresponds to it. If not, push onto 590 /// `search_stack` the types+hir to search through. try_match_adt_and_generic_args<'hir>( &self, substs: SubstsRef<'tcx>, needle_fr: RegionVid, args: &'hir hir::GenericArgs<'hir>, search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, ) -> Option<&'hir hir::Lifetime>591 fn try_match_adt_and_generic_args<'hir>( 592 &self, 593 substs: SubstsRef<'tcx>, 594 needle_fr: RegionVid, 595 args: &'hir hir::GenericArgs<'hir>, 596 search_stack: &mut Vec<(Ty<'tcx>, &'hir hir::Ty<'hir>)>, 597 ) -> Option<&'hir hir::Lifetime> { 598 for (kind, hir_arg) in iter::zip(substs, args.args) { 599 match (kind.unpack(), hir_arg) { 600 (GenericArgKind::Lifetime(r), hir::GenericArg::Lifetime(lt)) => { 601 if r.as_var() == needle_fr { 602 return Some(lt); 603 } 604 } 605 606 (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => { 607 search_stack.push((ty, hir_ty)); 608 } 609 610 (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => { 611 // Lifetimes cannot be found in consts, so we don't need 612 // to search anything here. 613 } 614 615 ( 616 GenericArgKind::Lifetime(_) 617 | GenericArgKind::Type(_) 618 | GenericArgKind::Const(_), 619 _, 620 ) => { 621 // HIR lowering sometimes doesn't catch this in erroneous 622 // programs, so we need to use delay_span_bug here. See #82126. 623 self.infcx.tcx.sess.delay_span_bug( 624 hir_arg.span(), 625 format!("unmatched subst and hir arg: found {kind:?} vs {hir_arg:?}"), 626 ); 627 } 628 } 629 } 630 631 None 632 } 633 634 /// Finds a closure upvar that contains `fr` and label it with a 635 /// fully elaborated type, returning something like `'1`. Result 636 /// looks like: 637 /// 638 /// ```text 639 /// | let x = Some(&22); 640 /// - fully elaborated type of `x` is `Option<&'1 u32>` 641 /// ``` 642 #[instrument(level = "trace", skip(self))] give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName>643 fn give_name_if_anonymous_region_appears_in_upvars(&self, fr: RegionVid) -> Option<RegionName> { 644 let upvar_index = self.regioncx.get_upvar_index_for_region(self.infcx.tcx, fr)?; 645 let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( 646 self.infcx.tcx, 647 &self.upvars, 648 upvar_index, 649 ); 650 let region_name = self.synthesize_region_name(); 651 652 Some(RegionName { 653 name: region_name, 654 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), 655 }) 656 } 657 658 /// Checks for arguments appearing in the (closure) return type. It 659 /// must be a closure since, in a free fn, such an argument would 660 /// have to either also appear in an argument (if using elision) 661 /// or be early bound (named, not in argument). 662 #[instrument(level = "trace", skip(self))] give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName>663 fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Option<RegionName> { 664 let tcx = self.infcx.tcx; 665 let hir = tcx.hir(); 666 667 let return_ty = self.regioncx.universal_regions().unnormalized_output_ty; 668 debug!("give_name_if_anonymous_region_appears_in_output: return_ty = {:?}", return_ty); 669 if !tcx.any_free_region_meets(&return_ty, |r| r.as_var() == fr) { 670 return None; 671 } 672 673 let mir_hir_id = self.mir_hir_id(); 674 675 let (return_span, mir_description, hir_ty) = match hir.get(mir_hir_id) { 676 hir::Node::Expr(hir::Expr { 677 kind: hir::ExprKind::Closure(&hir::Closure { fn_decl, body, fn_decl_span, .. }), 678 .. 679 }) => { 680 let (mut span, mut hir_ty) = match fn_decl.output { 681 hir::FnRetTy::DefaultReturn(_) => { 682 (tcx.sess.source_map().end_point(fn_decl_span), None) 683 } 684 hir::FnRetTy::Return(hir_ty) => (fn_decl.output.span(), Some(hir_ty)), 685 }; 686 let mir_description = match hir.body(body).generator_kind { 687 Some(hir::GeneratorKind::Async(gen)) => match gen { 688 hir::AsyncGeneratorKind::Block => " of async block", 689 hir::AsyncGeneratorKind::Closure => " of async closure", 690 hir::AsyncGeneratorKind::Fn => { 691 let parent_item = 692 hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id); 693 let output = &parent_item 694 .fn_decl() 695 .expect("generator lowered from async fn should be in fn") 696 .output; 697 span = output.span(); 698 if let hir::FnRetTy::Return(ret) = output { 699 hir_ty = Some(self.get_future_inner_return_ty(*ret)); 700 } 701 " of async function" 702 } 703 }, 704 Some(hir::GeneratorKind::Gen) => " of generator", 705 None => " of closure", 706 }; 707 (span, mir_description, hir_ty) 708 } 709 node => match node.fn_decl() { 710 Some(fn_decl) => { 711 let hir_ty = match fn_decl.output { 712 hir::FnRetTy::DefaultReturn(_) => None, 713 hir::FnRetTy::Return(ty) => Some(ty), 714 }; 715 (fn_decl.output.span(), "", hir_ty) 716 } 717 None => (self.body.span, "", None), 718 }, 719 }; 720 721 let highlight = hir_ty 722 .and_then(|hir_ty| self.highlight_if_we_can_match_hir_ty(fr, return_ty, hir_ty)) 723 .unwrap_or_else(|| { 724 // `highlight_if_we_cannot_match_hir_ty` needs to know the number we will give to 725 // the anonymous region. If it succeeds, the `synthesize_region_name` call below 726 // will increment the counter, "reserving" the number we just used. 727 let counter = *self.next_region_name.try_borrow().unwrap(); 728 self.highlight_if_we_cannot_match_hir_ty(fr, return_ty, return_span, counter) 729 }); 730 731 Some(RegionName { 732 name: self.synthesize_region_name(), 733 source: RegionNameSource::AnonRegionFromOutput(highlight, mir_description), 734 }) 735 } 736 737 /// From the [`hir::Ty`] of an async function's lowered return type, 738 /// retrieve the `hir::Ty` representing the type the user originally wrote. 739 /// 740 /// e.g. given the function: 741 /// 742 /// ``` 743 /// async fn foo() -> i32 { 2 } 744 /// ``` 745 /// 746 /// this function, given the lowered return type of `foo`, an [`OpaqueDef`] that implements `Future<Output=i32>`, 747 /// returns the `i32`. 748 /// 749 /// [`OpaqueDef`]: hir::TyKind::OpaqueDef get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx>750 fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> { 751 let hir = self.infcx.tcx.hir(); 752 753 let hir::TyKind::OpaqueDef(id, _, _) = hir_ty.kind else { 754 span_bug!( 755 hir_ty.span, 756 "lowered return type of async fn is not OpaqueDef: {:?}", 757 hir_ty 758 ); 759 }; 760 let opaque_ty = hir.item(id); 761 if let hir::ItemKind::OpaqueTy(hir::OpaqueTy { 762 bounds: 763 [ 764 hir::GenericBound::LangItemTrait( 765 hir::LangItem::Future, 766 _, 767 _, 768 hir::GenericArgs { 769 bindings: 770 [ 771 hir::TypeBinding { 772 ident: Ident { name: sym::Output, .. }, 773 kind: 774 hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) }, 775 .. 776 }, 777 ], 778 .. 779 }, 780 ), 781 ], 782 .. 783 }) = opaque_ty.kind 784 { 785 ty 786 } else { 787 span_bug!( 788 hir_ty.span, 789 "bounds from lowered return type of async fn did not match expected format: {opaque_ty:?}", 790 ); 791 } 792 } 793 794 #[instrument(level = "trace", skip(self))] give_name_if_anonymous_region_appears_in_yield_ty( &self, fr: RegionVid, ) -> Option<RegionName>795 fn give_name_if_anonymous_region_appears_in_yield_ty( 796 &self, 797 fr: RegionVid, 798 ) -> Option<RegionName> { 799 // Note: generators from `async fn` yield `()`, so we don't have to 800 // worry about them here. 801 let yield_ty = self.regioncx.universal_regions().yield_ty?; 802 debug!("give_name_if_anonymous_region_appears_in_yield_ty: yield_ty = {:?}", yield_ty); 803 804 let tcx = self.infcx.tcx; 805 806 if !tcx.any_free_region_meets(&yield_ty, |r| r.as_var() == fr) { 807 return None; 808 } 809 810 let mut highlight = RegionHighlightMode::new(tcx); 811 highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap()); 812 let type_name = 813 self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name; 814 815 let yield_span = match tcx.hir().get(self.mir_hir_id()) { 816 hir::Node::Expr(hir::Expr { 817 kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), 818 .. 819 }) => tcx.sess.source_map().end_point(fn_decl_span), 820 _ => self.body.span, 821 }; 822 823 debug!( 824 "give_name_if_anonymous_region_appears_in_yield_ty: \ 825 type_name = {:?}, yield_span = {:?}", 826 yield_span, type_name, 827 ); 828 829 Some(RegionName { 830 name: self.synthesize_region_name(), 831 source: RegionNameSource::AnonRegionFromYieldTy(yield_span, type_name), 832 }) 833 } 834 give_name_if_anonymous_region_appears_in_impl_signature( &self, fr: RegionVid, ) -> Option<RegionName>835 fn give_name_if_anonymous_region_appears_in_impl_signature( 836 &self, 837 fr: RegionVid, 838 ) -> Option<RegionName> { 839 let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { 840 return None; 841 }; 842 if region.has_name() { 843 return None; 844 }; 845 846 let tcx = self.infcx.tcx; 847 let region_parent = tcx.parent(region.def_id); 848 let DefKind::Impl { .. } = tcx.def_kind(region_parent) else { 849 return None; 850 }; 851 852 let found = tcx.any_free_region_meets(&tcx.type_of(region_parent).subst_identity(), |r| { 853 *r == ty::ReEarlyBound(region) 854 }); 855 856 Some(RegionName { 857 name: self.synthesize_region_name(), 858 source: RegionNameSource::AnonRegionFromImplSignature( 859 tcx.def_span(region.def_id), 860 // FIXME(compiler-errors): Does this ever actually show up 861 // anywhere other than the self type? I couldn't create an 862 // example of a `'_` in the impl's trait being referenceable. 863 if found { "self type" } else { "header" }, 864 ), 865 }) 866 } 867 give_name_if_anonymous_region_appears_in_arg_position_impl_trait( &self, fr: RegionVid, ) -> Option<RegionName>868 fn give_name_if_anonymous_region_appears_in_arg_position_impl_trait( 869 &self, 870 fr: RegionVid, 871 ) -> Option<RegionName> { 872 let ty::ReEarlyBound(region) = *self.to_error_region(fr)? else { 873 return None; 874 }; 875 if region.has_name() { 876 return None; 877 }; 878 879 let predicates = self 880 .infcx 881 .tcx 882 .predicates_of(self.body.source.def_id()) 883 .instantiate_identity(self.infcx.tcx) 884 .predicates; 885 886 if let Some(upvar_index) = self 887 .regioncx 888 .universal_regions() 889 .defining_ty 890 .upvar_tys() 891 .position(|ty| self.any_param_predicate_mentions(&predicates, ty, region)) 892 { 893 let (upvar_name, upvar_span) = self.regioncx.get_upvar_name_and_span_for_region( 894 self.infcx.tcx, 895 &self.upvars, 896 upvar_index, 897 ); 898 let region_name = self.synthesize_region_name(); 899 900 Some(RegionName { 901 name: region_name, 902 source: RegionNameSource::AnonRegionFromUpvar(upvar_span, upvar_name), 903 }) 904 } else if let Some(arg_index) = self 905 .regioncx 906 .universal_regions() 907 .unnormalized_input_tys 908 .iter() 909 .position(|ty| self.any_param_predicate_mentions(&predicates, *ty, region)) 910 { 911 let (arg_name, arg_span) = self.regioncx.get_argument_name_and_span_for_region( 912 self.body, 913 &self.local_names, 914 arg_index, 915 ); 916 let region_name = self.synthesize_region_name(); 917 918 Some(RegionName { 919 name: region_name, 920 source: RegionNameSource::AnonRegionFromArgument( 921 RegionNameHighlight::CannotMatchHirTy(arg_span, arg_name?.to_string()), 922 ), 923 }) 924 } else { 925 None 926 } 927 } 928 any_param_predicate_mentions( &self, clauses: &[ty::Clause<'tcx>], ty: Ty<'tcx>, region: ty::EarlyBoundRegion, ) -> bool929 fn any_param_predicate_mentions( 930 &self, 931 clauses: &[ty::Clause<'tcx>], 932 ty: Ty<'tcx>, 933 region: ty::EarlyBoundRegion, 934 ) -> bool { 935 let tcx = self.infcx.tcx; 936 ty.walk().any(|arg| { 937 if let ty::GenericArgKind::Type(ty) = arg.unpack() 938 && let ty::Param(_) = ty.kind() 939 { 940 clauses.iter().any(|pred| { 941 match pred.kind().skip_binder() { 942 ty::ClauseKind::Trait(data) if data.self_ty() == ty => {} 943 ty::ClauseKind::Projection(data) if data.projection_ty.self_ty() == ty => {} 944 _ => return false, 945 } 946 tcx.any_free_region_meets(pred, |r| { 947 *r == ty::ReEarlyBound(region) 948 }) 949 }) 950 } else { 951 false 952 } 953 }) 954 } 955 } 956