• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Type Names for Debug Info.
2 
3 // Notes on targeting MSVC:
4 // In general, MSVC's debugger attempts to parse all arguments as C++ expressions,
5 // even if the argument is explicitly a symbol name.
6 // As such, there are many things that cause parsing issues:
7 // * `#` is treated as a special character for macros.
8 // * `{` or `<` at the beginning of a name is treated as an operator.
9 // * `>>` is always treated as a right-shift.
10 // * `[` in a name is treated like a regex bracket expression (match any char
11 //   within the brackets).
12 // * `"` is treated as the start of a string.
13 
14 use rustc_data_structures::fx::FxHashSet;
15 use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher};
16 use rustc_hir::def_id::DefId;
17 use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
18 use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability};
19 use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
20 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
21 use rustc_middle::ty::{self, ExistentialProjection, ParamEnv, Ty, TyCtxt};
22 use rustc_target::abi::Integer;
23 use smallvec::SmallVec;
24 
25 use std::fmt::Write;
26 
27 use crate::debuginfo::wants_c_like_enum_debuginfo;
28 
29 /// Compute the name of the type as it should be stored in debuginfo. Does not do
30 /// any caching, i.e., calling the function twice with the same type will also do
31 /// the work twice. The `qualified` parameter only affects the first level of the
32 /// type name, further levels (i.e., type parameters) are always fully qualified.
compute_debuginfo_type_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, qualified: bool, ) -> String33 pub fn compute_debuginfo_type_name<'tcx>(
34     tcx: TyCtxt<'tcx>,
35     t: Ty<'tcx>,
36     qualified: bool,
37 ) -> String {
38     let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
39 
40     let mut result = String::with_capacity(64);
41     let mut visited = FxHashSet::default();
42     push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
43     result
44 }
45 
46 // Pushes the name of the type as it should be stored in debuginfo on the
47 // `output` String. See also compute_debuginfo_type_name().
push_debuginfo_type_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, qualified: bool, output: &mut String, visited: &mut FxHashSet<Ty<'tcx>>, )48 fn push_debuginfo_type_name<'tcx>(
49     tcx: TyCtxt<'tcx>,
50     t: Ty<'tcx>,
51     qualified: bool,
52     output: &mut String,
53     visited: &mut FxHashSet<Ty<'tcx>>,
54 ) {
55     // When targeting MSVC, emit C++ style type names for compatibility with
56     // .natvis visualizers (and perhaps other existing native debuggers?)
57     let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
58 
59     match *t.kind() {
60         ty::Bool => output.push_str("bool"),
61         ty::Char => output.push_str("char"),
62         ty::Str => {
63             if cpp_like_debuginfo {
64                 output.push_str("str$")
65             } else {
66                 output.push_str("str")
67             }
68         }
69         ty::Never => {
70             if cpp_like_debuginfo {
71                 output.push_str("never$");
72             } else {
73                 output.push('!');
74             }
75         }
76         ty::Int(int_ty) => output.push_str(int_ty.name_str()),
77         ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
78         ty::Float(float_ty) => output.push_str(float_ty.name_str()),
79         ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
80         ty::Adt(def, substs) => {
81             // `layout_for_cpp_like_fallback` will be `Some` if we want to use the fallback encoding.
82             let layout_for_cpp_like_fallback = if cpp_like_debuginfo && def.is_enum() {
83                 match tcx.layout_of(ParamEnv::reveal_all().and(t)) {
84                     Ok(layout) => {
85                         if !wants_c_like_enum_debuginfo(layout) {
86                             Some(layout)
87                         } else {
88                             // This is a C-like enum so we don't want to use the fallback encoding
89                             // for the name.
90                             None
91                         }
92                     }
93                     Err(e) => {
94                         // Computing the layout can still fail here, e.g. if the target architecture
95                         // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961.
96                         tcx.sess.emit_fatal(e.into_diagnostic());
97                     }
98                 }
99             } else {
100                 // We are not emitting cpp-like debuginfo or this isn't even an enum.
101                 None
102             };
103 
104             if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
105                 msvc_enum_fallback(
106                     ty_and_layout,
107                     &|output, visited| {
108                         push_item_name(tcx, def.did(), true, output);
109                         push_generic_params_internal(tcx, substs, output, visited);
110                     },
111                     output,
112                     visited,
113                 );
114             } else {
115                 push_item_name(tcx, def.did(), qualified, output);
116                 push_generic_params_internal(tcx, substs, output, visited);
117             }
118         }
119         ty::Tuple(component_types) => {
120             if cpp_like_debuginfo {
121                 output.push_str("tuple$<");
122             } else {
123                 output.push('(');
124             }
125 
126             for component_type in component_types {
127                 push_debuginfo_type_name(tcx, component_type, true, output, visited);
128                 push_arg_separator(cpp_like_debuginfo, output);
129             }
130             if !component_types.is_empty() {
131                 pop_arg_separator(output);
132             }
133 
134             if cpp_like_debuginfo {
135                 push_close_angle_bracket(cpp_like_debuginfo, output);
136             } else {
137                 output.push(')');
138             }
139         }
140         ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
141             if cpp_like_debuginfo {
142                 match mutbl {
143                     Mutability::Not => output.push_str("ptr_const$<"),
144                     Mutability::Mut => output.push_str("ptr_mut$<"),
145                 }
146             } else {
147                 output.push('*');
148                 match mutbl {
149                     Mutability::Not => output.push_str("const "),
150                     Mutability::Mut => output.push_str("mut "),
151                 }
152             }
153 
154             push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
155 
156             if cpp_like_debuginfo {
157                 push_close_angle_bracket(cpp_like_debuginfo, output);
158             }
159         }
160         ty::Ref(_, inner_type, mutbl) => {
161             if cpp_like_debuginfo {
162                 match mutbl {
163                     Mutability::Not => output.push_str("ref$<"),
164                     Mutability::Mut => output.push_str("ref_mut$<"),
165                 }
166             } else {
167                 output.push('&');
168                 output.push_str(mutbl.prefix_str());
169             }
170 
171             push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
172 
173             if cpp_like_debuginfo {
174                 push_close_angle_bracket(cpp_like_debuginfo, output);
175             }
176         }
177         ty::Array(inner_type, len) => {
178             if cpp_like_debuginfo {
179                 output.push_str("array$<");
180                 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
181                 match len.kind() {
182                     ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(),
183                     _ => write!(
184                         output,
185                         ",{}>",
186                         len.eval_target_usize(tcx, ty::ParamEnv::reveal_all())
187                     )
188                     .unwrap(),
189                 }
190             } else {
191                 output.push('[');
192                 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
193                 match len.kind() {
194                     ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(),
195                     _ => write!(
196                         output,
197                         "; {}]",
198                         len.eval_target_usize(tcx, ty::ParamEnv::reveal_all())
199                     )
200                     .unwrap(),
201                 }
202             }
203         }
204         ty::Slice(inner_type) => {
205             if cpp_like_debuginfo {
206                 output.push_str("slice2$<");
207             } else {
208                 output.push('[');
209             }
210 
211             push_debuginfo_type_name(tcx, inner_type, true, output, visited);
212 
213             if cpp_like_debuginfo {
214                 push_close_angle_bracket(cpp_like_debuginfo, output);
215             } else {
216                 output.push(']');
217             }
218         }
219         ty::Dynamic(ref trait_data, ..) => {
220             let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
221 
222             let has_enclosing_parens = if cpp_like_debuginfo {
223                 output.push_str("dyn$<");
224                 false
225             } else {
226                 if trait_data.len() > 1 && auto_traits.len() != 0 {
227                     // We need enclosing parens because there is more than one trait
228                     output.push_str("(dyn ");
229                     true
230                 } else {
231                     output.push_str("dyn ");
232                     false
233                 }
234             };
235 
236             if let Some(principal) = trait_data.principal() {
237                 let principal =
238                     tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
239                 push_item_name(tcx, principal.def_id, qualified, output);
240                 let principal_has_generic_params =
241                     push_generic_params_internal(tcx, principal.substs, output, visited);
242 
243                 let projection_bounds: SmallVec<[_; 4]> = trait_data
244                     .projection_bounds()
245                     .map(|bound| {
246                         let ExistentialProjection { def_id: item_def_id, term, .. } =
247                             tcx.erase_late_bound_regions(bound);
248                         // FIXME(associated_const_equality): allow for consts here
249                         (item_def_id, term.ty().unwrap())
250                     })
251                     .collect();
252 
253                 if projection_bounds.len() != 0 {
254                     if principal_has_generic_params {
255                         // push_generic_params_internal() above added a `>` but we actually
256                         // want to add more items to that list, so remove that again...
257                         pop_close_angle_bracket(output);
258                         // .. and add a comma to separate the regular generic args from the
259                         // associated types.
260                         push_arg_separator(cpp_like_debuginfo, output);
261                     } else {
262                         // push_generic_params_internal() did not add `<...>`, so we open
263                         // angle brackets here.
264                         output.push('<');
265                     }
266 
267                     for (item_def_id, ty) in projection_bounds {
268                         if cpp_like_debuginfo {
269                             output.push_str("assoc$<");
270                             push_item_name(tcx, item_def_id, false, output);
271                             push_arg_separator(cpp_like_debuginfo, output);
272                             push_debuginfo_type_name(tcx, ty, true, output, visited);
273                             push_close_angle_bracket(cpp_like_debuginfo, output);
274                         } else {
275                             push_item_name(tcx, item_def_id, false, output);
276                             output.push('=');
277                             push_debuginfo_type_name(tcx, ty, true, output, visited);
278                         }
279                         push_arg_separator(cpp_like_debuginfo, output);
280                     }
281 
282                     pop_arg_separator(output);
283                     push_close_angle_bracket(cpp_like_debuginfo, output);
284                 }
285 
286                 if auto_traits.len() != 0 {
287                     push_auto_trait_separator(cpp_like_debuginfo, output);
288                 }
289             }
290 
291             if auto_traits.len() != 0 {
292                 let mut auto_traits: SmallVec<[String; 4]> = auto_traits
293                     .into_iter()
294                     .map(|def_id| {
295                         let mut name = String::with_capacity(20);
296                         push_item_name(tcx, def_id, true, &mut name);
297                         name
298                     })
299                     .collect();
300                 auto_traits.sort_unstable();
301 
302                 for auto_trait in auto_traits {
303                     output.push_str(&auto_trait);
304                     push_auto_trait_separator(cpp_like_debuginfo, output);
305                 }
306 
307                 pop_auto_trait_separator(output);
308             }
309 
310             if cpp_like_debuginfo {
311                 push_close_angle_bracket(cpp_like_debuginfo, output);
312             } else if has_enclosing_parens {
313                 output.push(')');
314             }
315         }
316         ty::FnDef(..) | ty::FnPtr(_) => {
317             // We've encountered a weird 'recursive type'
318             // Currently, the only way to generate such a type
319             // is by using 'impl trait':
320             //
321             // fn foo() -> impl Copy { foo }
322             //
323             // There's not really a sensible name we can generate,
324             // since we don't include 'impl trait' types (e.g. ty::Opaque)
325             // in the output
326             //
327             // Since we need to generate *something*, we just
328             // use a dummy string that should make it clear
329             // that something unusual is going on
330             if !visited.insert(t) {
331                 output.push_str(if cpp_like_debuginfo {
332                     "recursive_type$"
333                 } else {
334                     "<recursive_type>"
335                 });
336                 return;
337             }
338 
339             let sig =
340                 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx));
341 
342             if cpp_like_debuginfo {
343                 // Format as a C++ function pointer: return_type (*)(params...)
344                 if sig.output().is_unit() {
345                     output.push_str("void");
346                 } else {
347                     push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
348                 }
349                 output.push_str(" (*)(");
350             } else {
351                 output.push_str(sig.unsafety.prefix_str());
352 
353                 if sig.abi != rustc_target::spec::abi::Abi::Rust {
354                     output.push_str("extern \"");
355                     output.push_str(sig.abi.name());
356                     output.push_str("\" ");
357                 }
358 
359                 output.push_str("fn(");
360             }
361 
362             if !sig.inputs().is_empty() {
363                 for &parameter_type in sig.inputs() {
364                     push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
365                     push_arg_separator(cpp_like_debuginfo, output);
366                 }
367                 pop_arg_separator(output);
368             }
369 
370             if sig.c_variadic {
371                 if !sig.inputs().is_empty() {
372                     output.push_str(", ...");
373                 } else {
374                     output.push_str("...");
375                 }
376             }
377 
378             output.push(')');
379 
380             if !cpp_like_debuginfo && !sig.output().is_unit() {
381                 output.push_str(" -> ");
382                 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
383             }
384 
385             // We only keep the type in 'visited'
386             // for the duration of the body of this method.
387             // It's fine for a particular function type
388             // to show up multiple times in one overall type
389             // (e.g. MyType<fn() -> u8, fn() -> u8>
390             //
391             // We only care about avoiding recursing
392             // directly back to the type we're currently
393             // processing
394             visited.remove(&t);
395         }
396         ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
397             // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
398             // "{async_fn_env#0}<T1, T2, ...>", etc.
399             // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
400             // an artificial `enum2$<>` type, as defined in msvc_enum_fallback().
401             if cpp_like_debuginfo && t.is_generator() {
402                 let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
403                 msvc_enum_fallback(
404                     ty_and_layout,
405                     &|output, visited| {
406                         push_closure_or_generator_name(tcx, def_id, substs, true, output, visited);
407                     },
408                     output,
409                     visited,
410                 );
411             } else {
412                 push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited);
413             }
414         }
415         // Type parameters from polymorphized functions.
416         ty::Param(_) => {
417             write!(output, "{:?}", t).unwrap();
418         }
419         ty::Error(_)
420         | ty::Infer(_)
421         | ty::Placeholder(..)
422         | ty::Alias(..)
423         | ty::Bound(..)
424         | ty::GeneratorWitnessMIR(..)
425         | ty::GeneratorWitness(..) => {
426             bug!(
427                 "debuginfo: Trying to create type name for \
428                   unexpected type: {:?}",
429                 t
430             );
431         }
432     }
433 
434     /// MSVC names enums differently than other platforms so that the debugging visualization
435     // format (natvis) is able to understand enums and render the active variant correctly in the
436     // debugger. For more information, look in
437     // rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
438     fn msvc_enum_fallback<'tcx>(
439         ty_and_layout: TyAndLayout<'tcx>,
440         push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
441         output: &mut String,
442         visited: &mut FxHashSet<Ty<'tcx>>,
443     ) {
444         debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
445         output.push_str("enum2$<");
446         push_inner(output, visited);
447         push_close_angle_bracket(true, output);
448     }
449 
450     const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
451 
452     fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) {
453         if cpp_like_debuginfo {
454             push_arg_separator(cpp_like_debuginfo, output);
455         } else {
456             output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
457         }
458     }
459 
460     fn pop_auto_trait_separator(output: &mut String) {
461         if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
462             output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
463         } else {
464             pop_arg_separator(output);
465         }
466     }
467 }
468 
469 pub enum VTableNameKind {
470     // Is the name for the const/static holding the vtable?
471     GlobalVariable,
472     // Is the name for the type of the vtable?
473     Type,
474 }
475 
476 /// Computes a name for the global variable storing a vtable (or the type of that global variable).
477 ///
478 /// The name is of the form:
479 ///
480 /// `<path::to::SomeType as path::to::SomeTrait>::{vtable}`
481 ///
482 /// or, when generating C++-like names:
483 ///
484 /// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
485 ///
486 /// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
487 /// `{vtable}`, so that the type and the corresponding global variable get assigned different
488 /// names.
compute_debuginfo_vtable_name<'tcx>( tcx: TyCtxt<'tcx>, t: Ty<'tcx>, trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, kind: VTableNameKind, ) -> String489 pub fn compute_debuginfo_vtable_name<'tcx>(
490     tcx: TyCtxt<'tcx>,
491     t: Ty<'tcx>,
492     trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
493     kind: VTableNameKind,
494 ) -> String {
495     let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
496 
497     let mut vtable_name = String::with_capacity(64);
498 
499     if cpp_like_debuginfo {
500         vtable_name.push_str("impl$<");
501     } else {
502         vtable_name.push('<');
503     }
504 
505     let mut visited = FxHashSet::default();
506     push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
507 
508     if cpp_like_debuginfo {
509         vtable_name.push_str(", ");
510     } else {
511         vtable_name.push_str(" as ");
512     }
513 
514     if let Some(trait_ref) = trait_ref {
515         let trait_ref =
516             tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref);
517         push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
518         visited.clear();
519         push_generic_params_internal(tcx, trait_ref.substs, &mut vtable_name, &mut visited);
520     } else {
521         vtable_name.push('_');
522     }
523 
524     push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
525 
526     let suffix = match (cpp_like_debuginfo, kind) {
527         (true, VTableNameKind::GlobalVariable) => "::vtable$",
528         (false, VTableNameKind::GlobalVariable) => "::{vtable}",
529         (true, VTableNameKind::Type) => "::vtable_type$",
530         (false, VTableNameKind::Type) => "::{vtable_type}",
531     };
532 
533     vtable_name.reserve_exact(suffix.len());
534     vtable_name.push_str(suffix);
535 
536     vtable_name
537 }
538 
push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String)539 pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) {
540     let def_key = tcx.def_key(def_id);
541     if qualified {
542         if let Some(parent) = def_key.parent {
543             push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
544             output.push_str("::");
545         }
546     }
547 
548     push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
549 }
550 
generator_kind_label(generator_kind: Option<GeneratorKind>) -> &'static str551 fn generator_kind_label(generator_kind: Option<GeneratorKind>) -> &'static str {
552     match generator_kind {
553         Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block",
554         Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure",
555         Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn",
556         Some(GeneratorKind::Gen) => "generator",
557         None => "closure",
558     }
559 }
560 
push_disambiguated_special_name( label: &str, disambiguator: u32, cpp_like_debuginfo: bool, output: &mut String, )561 fn push_disambiguated_special_name(
562     label: &str,
563     disambiguator: u32,
564     cpp_like_debuginfo: bool,
565     output: &mut String,
566 ) {
567     if cpp_like_debuginfo {
568         write!(output, "{}${}", label, disambiguator).unwrap();
569     } else {
570         write!(output, "{{{}#{}}}", label, disambiguator).unwrap();
571     }
572 }
573 
push_unqualified_item_name( tcx: TyCtxt<'_>, def_id: DefId, disambiguated_data: DisambiguatedDefPathData, output: &mut String, )574 fn push_unqualified_item_name(
575     tcx: TyCtxt<'_>,
576     def_id: DefId,
577     disambiguated_data: DisambiguatedDefPathData,
578     output: &mut String,
579 ) {
580     match disambiguated_data.data {
581         DefPathData::CrateRoot => {
582             output.push_str(tcx.crate_name(def_id.krate).as_str());
583         }
584         DefPathData::ClosureExpr => {
585             let label = generator_kind_label(tcx.generator_kind(def_id));
586 
587             push_disambiguated_special_name(
588                 label,
589                 disambiguated_data.disambiguator,
590                 cpp_like_debuginfo(tcx),
591                 output,
592             );
593         }
594         _ => match disambiguated_data.data.name() {
595             DefPathDataName::Named(name) => {
596                 output.push_str(name.as_str());
597             }
598             DefPathDataName::Anon { namespace } => {
599                 push_disambiguated_special_name(
600                     namespace.as_str(),
601                     disambiguated_data.disambiguator,
602                     cpp_like_debuginfo(tcx),
603                     output,
604                 );
605             }
606         },
607     };
608 }
609 
push_generic_params_internal<'tcx>( tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String, visited: &mut FxHashSet<Ty<'tcx>>, ) -> bool610 fn push_generic_params_internal<'tcx>(
611     tcx: TyCtxt<'tcx>,
612     substs: SubstsRef<'tcx>,
613     output: &mut String,
614     visited: &mut FxHashSet<Ty<'tcx>>,
615 ) -> bool {
616     if substs.non_erasable_generics().next().is_none() {
617         return false;
618     }
619 
620     debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs));
621 
622     let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
623 
624     output.push('<');
625 
626     for type_parameter in substs.non_erasable_generics() {
627         match type_parameter {
628             GenericArgKind::Type(type_parameter) => {
629                 push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
630             }
631             GenericArgKind::Const(ct) => {
632                 push_const_param(tcx, ct, output);
633             }
634             other => bug!("Unexpected non-erasable generic: {:?}", other),
635         }
636 
637         push_arg_separator(cpp_like_debuginfo, output);
638     }
639     pop_arg_separator(output);
640     push_close_angle_bracket(cpp_like_debuginfo, output);
641 
642     true
643 }
644 
push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String)645 fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
646     match ct.kind() {
647         ty::ConstKind::Param(param) => {
648             write!(output, "{}", param.name)
649         }
650         _ => match ct.ty().kind() {
651             ty::Int(ity) => {
652                 let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
653                 let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
654                 write!(output, "{}", val)
655             }
656             ty::Uint(_) => {
657                 let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
658                 write!(output, "{}", val)
659             }
660             ty::Bool => {
661                 let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
662                 write!(output, "{}", val)
663             }
664             _ => {
665                 // If we cannot evaluate the constant to a known type, we fall back
666                 // to emitting a stable hash value of the constant. This isn't very pretty
667                 // but we get a deterministic, virtually unique value for the constant.
668                 //
669                 // Let's only emit 64 bits of the hash value. That should be plenty for
670                 // avoiding collisions and will make the emitted type names shorter.
671                 let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
672                     let mut hasher = StableHasher::new();
673                     let ct = ct.eval(tcx, ty::ParamEnv::reveal_all());
674                     hcx.while_hashing_spans(false, |hcx| {
675                         ct.to_valtree().hash_stable(hcx, &mut hasher)
676                     });
677                     hasher.finish::<Hash64>()
678                 });
679 
680                 if cpp_like_debuginfo(tcx) {
681                     write!(output, "CONST${:x}", hash_short)
682                 } else {
683                     write!(output, "{{CONST#{:x}}}", hash_short)
684                 }
685             }
686         },
687     }
688     .unwrap();
689 }
690 
push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String)691 pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) {
692     let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
693     let mut visited = FxHashSet::default();
694     push_generic_params_internal(tcx, substs, output, &mut visited);
695 }
696 
push_closure_or_generator_name<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, substs: SubstsRef<'tcx>, qualified: bool, output: &mut String, visited: &mut FxHashSet<Ty<'tcx>>, )697 fn push_closure_or_generator_name<'tcx>(
698     tcx: TyCtxt<'tcx>,
699     def_id: DefId,
700     substs: SubstsRef<'tcx>,
701     qualified: bool,
702     output: &mut String,
703     visited: &mut FxHashSet<Ty<'tcx>>,
704 ) {
705     // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
706     // "{async_fn_env#0}<T1, T2, ...>", etc.
707     let def_key = tcx.def_key(def_id);
708     let generator_kind = tcx.generator_kind(def_id);
709 
710     if qualified {
711         let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
712         push_item_name(tcx, parent_def_id, true, output);
713         output.push_str("::");
714     }
715 
716     let mut label = String::with_capacity(20);
717     write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap();
718 
719     push_disambiguated_special_name(
720         &label,
721         def_key.disambiguated_data.disambiguator,
722         cpp_like_debuginfo(tcx),
723         output,
724     );
725 
726     // We also need to add the generic arguments of the async fn/generator or
727     // the enclosing function (for closures or async blocks), so that we end
728     // up with a unique name for every instantiation.
729 
730     // Find the generics of the enclosing function, as defined in the source code.
731     let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
732     let generics = tcx.generics_of(enclosing_fn_def_id);
733 
734     // Truncate the substs to the length of the above generics. This will cut off
735     // anything closure- or generator-specific.
736     let substs = substs.truncate_to(tcx, generics);
737     push_generic_params_internal(tcx, substs, output, visited);
738 }
739 
push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String)740 fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
741     // MSVC debugger always treats `>>` as a shift, even when parsing templates,
742     // so add a space to avoid confusion.
743     if cpp_like_debuginfo && output.ends_with('>') {
744         output.push(' ')
745     };
746 
747     output.push('>');
748 }
749 
pop_close_angle_bracket(output: &mut String)750 fn pop_close_angle_bracket(output: &mut String) {
751     assert!(output.ends_with('>'), "'output' does not end with '>': {}", output);
752     output.pop();
753     if output.ends_with(' ') {
754         output.pop();
755     }
756 }
757 
push_arg_separator(cpp_like_debuginfo: bool, output: &mut String)758 fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) {
759     // Natvis does not always like having spaces between parts of the type name
760     // and this causes issues when we need to write a typename in natvis, for example
761     // as part of a cast like the `HashMap` visualizer does.
762     if cpp_like_debuginfo {
763         output.push(',');
764     } else {
765         output.push_str(", ");
766     };
767 }
768 
pop_arg_separator(output: &mut String)769 fn pop_arg_separator(output: &mut String) {
770     if output.ends_with(' ') {
771         output.pop();
772     }
773 
774     assert!(output.ends_with(','));
775 
776     output.pop();
777 }
778 
779 /// Check if we should generate C++ like names and debug information.
cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool780 pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool {
781     tcx.sess.target.is_like_msvc
782 }
783