1010
1111use borrow_check:: nll:: region_infer:: RegionInferenceContext ;
1212use borrow_check:: nll:: ToRegionVid ;
13+ use rustc:: hir;
1314use rustc:: hir:: def_id:: DefId ;
1415use 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 } ;
1618use rustc_data_structures:: indexed_vec:: Idx ;
1719use rustc_errors:: DiagnosticBuilder ;
1820use syntax:: ast:: Name ;
@@ -60,7 +62,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
6062
6163 self . give_name_from_error_region ( tcx, mir_def_id, fr, counter, diag)
6264 . 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+ )
6468 } )
6569 . or_else ( || {
6670 self . give_name_if_anonymous_region_appears_in_upvars ( tcx, mir, fr, counter, diag)
@@ -129,30 +133,48 @@ impl<'tcx> RegionInferenceContext<'tcx> {
129133 & self ,
130134 tcx : TyCtxt < ' _ , ' _ , ' tcx > ,
131135 mir : & Mir < ' tcx > ,
136+ mir_def_id : DefId ,
132137 fr : RegionVid ,
133138 counter : & mut usize ,
134139 diag : & mut DiagnosticBuilder < ' _ > ,
135140 ) -> Option < InternedString > {
136141 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
138144 . unnormalized_input_tys
139145 . iter ( )
140146 . skip ( implicit_inputs)
141147 . 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+ ) ;
143152 tcx. any_free_region_meets ( arg_ty, |r| r. to_region_vid ( ) == fr)
144- } ) ?
145- + implicit_inputs;
153+ } ) ?;
146154
147155 debug ! (
148156 "give_name_if_anonymous_region_appears_in_arguments: \
149157 found {:?} in argument {} which has type {:?}",
150158 fr, argument_index, self . universal_regions. unnormalized_input_tys[ argument_index] ,
151159 ) ;
152160
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+
153175 let region_name = self . synthesize_region_name ( counter) ;
154176
155- let argument_local = Local :: new ( argument_index + 1 ) ;
177+ let argument_local = Local :: new ( argument_index + implicit_inputs + 1 ) ;
156178 let argument_span = mir. local_decls [ argument_local] . source_info . span ;
157179 diag. span_label (
158180 argument_span,
@@ -162,6 +184,240 @@ impl<'tcx> RegionInferenceContext<'tcx> {
162184 Some ( region_name)
163185 }
164186
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+
165421 /// Find a closure upvar that contains `fr` and label it with a
166422 /// fully elaborated type, returning something like `'1`. Result
167423 /// looks like:
@@ -178,7 +434,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
178434 counter : & mut usize ,
179435 diag : & mut DiagnosticBuilder < ' _ > ,
180436 ) -> Option < InternedString > {
181- let upvar_index = self . universal_regions
437+ let upvar_index = self
438+ . universal_regions
182439 . defining_ty
183440 . upvar_tys ( tcx)
184441 . position ( |upvar_ty| {
@@ -189,15 +446,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
189446 tcx. any_free_region_meets ( & upvar_ty, |r| r. to_region_vid ( ) == fr)
190447 } ) ?;
191448
449+ let upvar_ty = self
450+ . universal_regions
451+ . defining_ty
452+ . upvar_tys ( tcx)
453+ . nth ( upvar_index) ;
454+
192455 debug ! (
193456 "give_name_if_anonymous_region_appears_in_upvars: \
194457 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,
201459 ) ;
202460
203461 let region_name = self . synthesize_region_name ( counter) ;
@@ -229,9 +487,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
229487 counter : & mut usize ,
230488 diag : & mut DiagnosticBuilder < ' _ > ,
231489 ) -> 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+ ) ;
235495 if !tcx. any_free_region_meets ( & return_ty, |r| r. to_region_vid ( ) == fr) {
236496 return None ;
237497 }
0 commit comments