10
10
11
11
use borrow_check:: nll:: region_infer:: RegionInferenceContext ;
12
12
use borrow_check:: nll:: ToRegionVid ;
13
+ use rustc:: hir;
13
14
use rustc:: hir:: def_id:: DefId ;
14
15
use rustc:: mir:: { Local , Mir } ;
15
- use rustc:: ty:: { self , RegionVid , TyCtxt } ;
16
+ use rustc:: ty:: subst:: { Substs , UnpackedKind } ;
17
+ use rustc:: ty:: { self , RegionVid , Ty , TyCtxt } ;
16
18
use rustc_data_structures:: indexed_vec:: Idx ;
17
19
use rustc_errors:: DiagnosticBuilder ;
18
20
use syntax:: ast:: Name ;
@@ -60,7 +62,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
60
62
61
63
self . give_name_from_error_region ( tcx, mir_def_id, fr, counter, diag)
62
64
. or_else ( || {
63
- self . give_name_if_anonymous_region_appears_in_arguments ( tcx, mir, fr, counter, diag)
65
+ self . give_name_if_anonymous_region_appears_in_arguments (
66
+ tcx, mir, mir_def_id, fr, counter, diag,
67
+ )
64
68
} )
65
69
. or_else ( || {
66
70
self . give_name_if_anonymous_region_appears_in_upvars ( tcx, mir, fr, counter, diag)
@@ -129,30 +133,48 @@ impl<'tcx> RegionInferenceContext<'tcx> {
129
133
& self ,
130
134
tcx : TyCtxt < ' _ , ' _ , ' tcx > ,
131
135
mir : & Mir < ' tcx > ,
136
+ mir_def_id : DefId ,
132
137
fr : RegionVid ,
133
138
counter : & mut usize ,
134
139
diag : & mut DiagnosticBuilder < ' _ > ,
135
140
) -> Option < InternedString > {
136
141
let implicit_inputs = self . universal_regions . defining_ty . implicit_inputs ( ) ;
137
- let argument_index = self . universal_regions
142
+ let argument_index = self
143
+ . universal_regions
138
144
. unnormalized_input_tys
139
145
. iter ( )
140
146
. skip ( implicit_inputs)
141
147
. position ( |arg_ty| {
142
- debug ! ( "give_name_if_anonymous_region_appears_in_arguments: arg_ty = {:?}" , arg_ty) ;
148
+ debug ! (
149
+ "give_name_if_anonymous_region_appears_in_arguments: arg_ty = {:?}" ,
150
+ arg_ty
151
+ ) ;
143
152
tcx. any_free_region_meets ( arg_ty, |r| r. to_region_vid ( ) == fr)
144
- } ) ?
145
- + implicit_inputs;
153
+ } ) ?;
146
154
147
155
debug ! (
148
156
"give_name_if_anonymous_region_appears_in_arguments: \
149
157
found {:?} in argument {} which has type {:?}",
150
158
fr, argument_index, self . universal_regions. unnormalized_input_tys[ argument_index] ,
151
159
) ;
152
160
161
+ let arg_ty =
162
+ self . universal_regions . unnormalized_input_tys [ implicit_inputs + argument_index] ;
163
+ if let Some ( region_name) = self . give_name_if_we_can_match_hir_ty_from_argument (
164
+ tcx,
165
+ mir_def_id,
166
+ fr,
167
+ arg_ty,
168
+ argument_index,
169
+ counter,
170
+ diag,
171
+ ) {
172
+ return Some ( region_name) ;
173
+ }
174
+
153
175
let region_name = self . synthesize_region_name ( counter) ;
154
176
155
- let argument_local = Local :: new ( argument_index + 1 ) ;
177
+ let argument_local = Local :: new ( argument_index + implicit_inputs + 1 ) ;
156
178
let argument_span = mir. local_decls [ argument_local] . source_info . span ;
157
179
diag. span_label (
158
180
argument_span,
@@ -162,6 +184,240 @@ impl<'tcx> RegionInferenceContext<'tcx> {
162
184
Some ( region_name)
163
185
}
164
186
187
+ fn give_name_if_we_can_match_hir_ty_from_argument (
188
+ & self ,
189
+ tcx : TyCtxt < ' _ , ' _ , ' tcx > ,
190
+ mir_def_id : DefId ,
191
+ needle_fr : RegionVid ,
192
+ argument_ty : Ty < ' tcx > ,
193
+ argument_index : usize ,
194
+ counter : & mut usize ,
195
+ diag : & mut DiagnosticBuilder < ' _ > ,
196
+ ) -> Option < InternedString > {
197
+ let mir_node_id = tcx. hir . as_local_node_id ( mir_def_id) ?;
198
+ let fn_decl = tcx. hir . fn_decl ( mir_node_id) ?;
199
+ let argument_hir_ty: & hir:: Ty = & fn_decl. inputs [ argument_index] ;
200
+ match argument_hir_ty. node {
201
+ // This indicates a variable with no type annotation, like
202
+ // `|x|`... in that case, we can't highlight the type but
203
+ // must highlight the variable.
204
+ hir:: TyInfer => None ,
205
+
206
+ _ => self . give_name_if_we_can_match_hir_ty (
207
+ tcx,
208
+ needle_fr,
209
+ argument_ty,
210
+ argument_hir_ty,
211
+ counter,
212
+ diag,
213
+ ) ,
214
+ }
215
+ }
216
+
217
+ /// Attempts to highlight the specific part of a type annotation
218
+ /// that contains the anonymous reference we want to give a name
219
+ /// to. For example, we might produce an annotation like this:
220
+ ///
221
+ /// ```
222
+ /// | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T>> {
223
+ /// | - let's call the lifetime of this reference `'1`
224
+ /// ```
225
+ ///
226
+ /// the way this works is that we match up `argument_ty`, which is
227
+ /// a `Ty<'tcx>` (the internal form of the type) with
228
+ /// `argument_hir_ty`, a `hir::Ty` (the syntax of the type
229
+ /// annotation). We are descending through the types stepwise,
230
+ /// looking in to find the region `needle_fr` in the internal
231
+ /// type. Once we find that, we can use the span of the `hir::Ty`
232
+ /// to add the highlight.
233
+ ///
234
+ /// This is a somewhat imperfect process, so long the way we also
235
+ /// keep track of the **closest** type we've found. If we fail to
236
+ /// find the exact `&` or `'_` to highlight, then we may fall back
237
+ /// to highlighting that closest type instead.
238
+ fn give_name_if_we_can_match_hir_ty (
239
+ & self ,
240
+ tcx : TyCtxt < ' _ , ' _ , ' tcx > ,
241
+ needle_fr : RegionVid ,
242
+ argument_ty : Ty < ' tcx > ,
243
+ argument_hir_ty : & hir:: Ty ,
244
+ counter : & mut usize ,
245
+ diag : & mut DiagnosticBuilder < ' _ > ,
246
+ ) -> Option < InternedString > {
247
+ let search_stack: & mut Vec < ( Ty < ' tcx > , & hir:: Ty ) > = & mut Vec :: new ( ) ;
248
+
249
+ search_stack. push ( ( argument_ty, argument_hir_ty) ) ;
250
+
251
+ let mut closest_match: & hir:: Ty = argument_hir_ty;
252
+
253
+ while let Some ( ( ty, hir_ty) ) = search_stack. pop ( ) {
254
+ // While we search, also track the closet match.
255
+ if tcx. any_free_region_meets ( & ty, |r| r. to_region_vid ( ) == needle_fr) {
256
+ closest_match = hir_ty;
257
+ }
258
+
259
+ match ( & ty. sty , & hir_ty. node ) {
260
+ // Check if the `argument_ty` is `&'X ..` where `'X`
261
+ // is the region we are looking for -- if so, and we have a `&T`
262
+ // on the RHS, then we want to highlight the `&` like so:
263
+ //
264
+ // &
265
+ // - let's call the lifetime of this reference `'1`
266
+ ( ty:: TyRef ( region, referent_ty, _) , hir:: TyRptr ( _lifetime, referent_hir_ty) ) => {
267
+ if region. to_region_vid ( ) == needle_fr {
268
+ let region_name = self . synthesize_region_name ( counter) ;
269
+
270
+ // Just grab the first character, the `&`.
271
+ let codemap = tcx. sess . codemap ( ) ;
272
+ let ampersand_span = codemap. start_point ( hir_ty. span ) ;
273
+
274
+ diag. span_label (
275
+ ampersand_span,
276
+ format ! (
277
+ "let's call the lifetime of this reference `{}`" ,
278
+ region_name
279
+ ) ,
280
+ ) ;
281
+
282
+ return Some ( region_name) ;
283
+ }
284
+
285
+ // Otherwise, let's descend into the referent types.
286
+ search_stack. push ( ( referent_ty, & referent_hir_ty. ty ) ) ;
287
+ }
288
+
289
+ // Match up something like `Foo<'1>`
290
+ ( ty:: TyAdt ( _adt_def, substs) , hir:: TyPath ( hir:: QPath :: Resolved ( None , path) ) ) => {
291
+ if let Some ( last_segment) = path. segments . last ( ) {
292
+ if let Some ( name) = self . match_adt_and_segment (
293
+ substs,
294
+ needle_fr,
295
+ last_segment,
296
+ counter,
297
+ diag,
298
+ search_stack,
299
+ ) {
300
+ return Some ( name) ;
301
+ }
302
+ }
303
+ }
304
+
305
+ // The following cases don't have lifetimes, so we
306
+ // just worry about trying to match up the rustc type
307
+ // with the HIR types:
308
+ ( ty:: TyTuple ( elem_tys) , hir:: TyTup ( elem_hir_tys) ) => {
309
+ search_stack. extend ( elem_tys. iter ( ) . cloned ( ) . zip ( elem_hir_tys) ) ;
310
+ }
311
+
312
+ ( ty:: TySlice ( elem_ty) , hir:: TySlice ( elem_hir_ty) )
313
+ | ( ty:: TyArray ( elem_ty, _) , hir:: TyArray ( elem_hir_ty, _) ) => {
314
+ search_stack. push ( ( elem_ty, elem_hir_ty) ) ;
315
+ }
316
+
317
+ ( ty:: TyRawPtr ( mut_ty) , hir:: TyPtr ( mut_hir_ty) ) => {
318
+ search_stack. push ( ( mut_ty. ty , & mut_hir_ty. ty ) ) ;
319
+ }
320
+
321
+ _ => {
322
+ // FIXME there are other cases that we could trace
323
+ }
324
+ }
325
+ }
326
+
327
+ let region_name = self . synthesize_region_name ( counter) ;
328
+ diag. span_label (
329
+ closest_match. span ,
330
+ format ! ( "lifetime `{}` appears in this type" , region_name) ,
331
+ ) ;
332
+
333
+ return Some ( region_name) ;
334
+ }
335
+
336
+ /// We've found an enum/struct/union type with the substitutions
337
+ /// `substs` and -- in the HIR -- a path type with the final
338
+ /// segment `last_segment`. Try to find a `'_` to highlight in
339
+ /// the generic args (or, if not, to produce new zipped pairs of
340
+ /// types+hir to search through).
341
+ fn match_adt_and_segment < ' hir > (
342
+ & self ,
343
+ substs : & ' tcx Substs < ' tcx > ,
344
+ needle_fr : RegionVid ,
345
+ last_segment : & ' hir hir:: PathSegment ,
346
+ counter : & mut usize ,
347
+ diag : & mut DiagnosticBuilder < ' _ > ,
348
+ search_stack : & mut Vec < ( Ty < ' tcx > , & ' hir hir:: Ty ) > ,
349
+ ) -> Option < InternedString > {
350
+ // Did the user give explicit arguments? (e.g., `Foo<..>`)
351
+ let args = last_segment. args . as_ref ( ) ?;
352
+ let lifetime = self . try_match_adt_and_generic_args ( substs, needle_fr, args, search_stack) ?;
353
+ match lifetime. name {
354
+ hir:: LifetimeName :: Param ( _)
355
+ | hir:: LifetimeName :: Static
356
+ | hir:: LifetimeName :: Underscore => {
357
+ let region_name = self . synthesize_region_name ( counter) ;
358
+ let ampersand_span = lifetime. span ;
359
+ diag. span_label ( ampersand_span, format ! ( "let's call this `{}`" , region_name) ) ;
360
+ return Some ( region_name) ;
361
+ }
362
+
363
+ hir:: LifetimeName :: Implicit => {
364
+ // In this case, the user left off the lifetime; so
365
+ // they wrote something like:
366
+ //
367
+ // ```
368
+ // x: Foo<T>
369
+ // ```
370
+ //
371
+ // where the fully elaborated form is `Foo<'_, '1,
372
+ // T>`. We don't consider this a match; instead we let
373
+ // the "fully elaborated" type fallback above handle
374
+ // it.
375
+ return None ;
376
+ }
377
+ }
378
+ }
379
+
380
+ /// We've found an enum/struct/union type with the substitutions
381
+ /// `substs` and -- in the HIR -- a path with the generic
382
+ /// arguments `args`. If `needle_fr` appears in the args, return
383
+ /// the `hir::Lifetime` that corresponds to it. If not, push onto
384
+ /// `search_stack` the types+hir to search through.
385
+ fn try_match_adt_and_generic_args < ' hir > (
386
+ & self ,
387
+ substs : & ' tcx Substs < ' tcx > ,
388
+ needle_fr : RegionVid ,
389
+ args : & ' hir hir:: GenericArgs ,
390
+ search_stack : & mut Vec < ( Ty < ' tcx > , & ' hir hir:: Ty ) > ,
391
+ ) -> Option < & ' hir hir:: Lifetime > {
392
+ for ( kind, hir_arg) in substs. iter ( ) . zip ( & args. args ) {
393
+ match ( kind. unpack ( ) , hir_arg) {
394
+ ( UnpackedKind :: Lifetime ( r) , hir:: GenericArg :: Lifetime ( lt) ) => {
395
+ if r. to_region_vid ( ) == needle_fr {
396
+ return Some ( lt) ;
397
+ }
398
+ }
399
+
400
+ ( UnpackedKind :: Type ( ty) , hir:: GenericArg :: Type ( hir_ty) ) => {
401
+ search_stack. push ( ( ty, hir_ty) ) ;
402
+ }
403
+
404
+ ( UnpackedKind :: Lifetime ( _) , _) | ( UnpackedKind :: Type ( _) , _) => {
405
+ // I *think* that HIR lowering should ensure this
406
+ // doesn't happen, even in erroneous
407
+ // programs. Else we should use delay-span-bug.
408
+ span_bug ! (
409
+ hir_arg. span( ) ,
410
+ "unmatched subst and hir arg: found {:?} vs {:?}" ,
411
+ kind,
412
+ hir_arg,
413
+ ) ;
414
+ }
415
+ }
416
+ }
417
+
418
+ None
419
+ }
420
+
165
421
/// Find a closure upvar that contains `fr` and label it with a
166
422
/// fully elaborated type, returning something like `'1`. Result
167
423
/// looks like:
@@ -178,7 +434,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
178
434
counter : & mut usize ,
179
435
diag : & mut DiagnosticBuilder < ' _ > ,
180
436
) -> Option < InternedString > {
181
- let upvar_index = self . universal_regions
437
+ let upvar_index = self
438
+ . universal_regions
182
439
. defining_ty
183
440
. upvar_tys ( tcx)
184
441
. position ( |upvar_ty| {
@@ -189,15 +446,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
189
446
tcx. any_free_region_meets ( & upvar_ty, |r| r. to_region_vid ( ) == fr)
190
447
} ) ?;
191
448
449
+ let upvar_ty = self
450
+ . universal_regions
451
+ . defining_ty
452
+ . upvar_tys ( tcx)
453
+ . nth ( upvar_index) ;
454
+
192
455
debug ! (
193
456
"give_name_if_anonymous_region_appears_in_upvars: \
194
457
found {:?} in upvar {} which has type {:?}",
195
- fr,
196
- upvar_index,
197
- self . universal_regions
198
- . defining_ty
199
- . upvar_tys( tcx)
200
- . nth( upvar_index) ,
458
+ fr, upvar_index, upvar_ty,
201
459
) ;
202
460
203
461
let region_name = self . synthesize_region_name ( counter) ;
@@ -229,9 +487,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
229
487
counter : & mut usize ,
230
488
diag : & mut DiagnosticBuilder < ' _ > ,
231
489
) -> Option < InternedString > {
232
- let return_ty = self . universal_regions
233
- . unnormalized_output_ty ;
234
- debug ! ( "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}" , return_ty) ;
490
+ let return_ty = self . universal_regions . unnormalized_output_ty ;
491
+ debug ! (
492
+ "give_name_if_anonymous_region_appears_in_output: return_ty = {:?}" ,
493
+ return_ty
494
+ ) ;
235
495
if !tcx. any_free_region_meets ( & return_ty, |r| r. to_region_vid ( ) == fr) {
236
496
return None ;
237
497
}
0 commit comments