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