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