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