Skip to main content

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_args_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_args_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) => output.write_fmt(format_args!(",{0}>", param.name))write!(output, ",{}>", param.name).unwrap(),
186                    _ => output.write_fmt(format_args!(",{0}>",
        len.try_to_target_usize(tcx).expect("expected monomorphic const in codegen")))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) => output.write_fmt(format_args!("; {0}]", param.name))write!(output, "; {}]", param.name).unwrap(),
199                    _ => output.write_fmt(format_args!("; {0}]",
        len.try_to_target_usize(tcx).expect("expected monomorphic const in codegen")))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                output.write_fmt(format_args!(",{0:?}>", pat))write!(output, ",{:?}>", pat).unwrap();
215            } else {
216                output.write_fmt(format_args!("{0:?}", t))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_args_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                        (item_def_id, term)
264                    })
265                    .collect();
266
267                if !projection_bounds.is_empty() {
268                    if principal_has_generic_params {
269                        // push_generic_params_internal() above added a `>` but we actually
270                        // want to add more items to that list, so remove that again...
271                        pop_close_angle_bracket(output);
272                        // .. and add a comma to separate the regular generic args from the
273                        // associated types.
274                        push_arg_separator(cpp_like_debuginfo, output);
275                    } else {
276                        // push_generic_params_internal() did not add `<...>`, so we open
277                        // angle brackets here.
278                        output.push('<');
279                    }
280
281                    for (item_def_id, term) in projection_bounds {
282                        if cpp_like_debuginfo {
283                            output.push_str("assoc$<");
284                            push_item_name(tcx, item_def_id, false, output);
285                            push_arg_separator(cpp_like_debuginfo, output);
286                            push_debuginfo_term_name(tcx, term, true, output, visited);
287                            push_close_angle_bracket(cpp_like_debuginfo, output);
288                        } else {
289                            push_item_name(tcx, item_def_id, false, output);
290                            output.push('=');
291                            push_debuginfo_term_name(tcx, term, true, output, visited);
292                        }
293                        push_arg_separator(cpp_like_debuginfo, output);
294                    }
295
296                    pop_arg_separator(output);
297                    push_close_angle_bracket(cpp_like_debuginfo, output);
298                }
299
300                if auto_traits.len() != 0 {
301                    push_auto_trait_separator(cpp_like_debuginfo, output);
302                }
303            }
304
305            if auto_traits.len() != 0 {
306                let mut auto_traits: SmallVec<[String; 4]> = auto_traits
307                    .into_iter()
308                    .map(|def_id| {
309                        let mut name = String::with_capacity(20);
310                        push_item_name(tcx, def_id, true, &mut name);
311                        name
312                    })
313                    .collect();
314                auto_traits.sort_unstable();
315
316                for auto_trait in auto_traits {
317                    output.push_str(&auto_trait);
318                    push_auto_trait_separator(cpp_like_debuginfo, output);
319                }
320
321                pop_auto_trait_separator(output);
322            }
323
324            if cpp_like_debuginfo {
325                push_close_angle_bracket(cpp_like_debuginfo, output);
326            } else if has_enclosing_parens {
327                output.push(')');
328            }
329        }
330        ty::FnDef(..) | ty::FnPtr(..) => {
331            // We've encountered a weird 'recursive type'
332            // Currently, the only way to generate such a type
333            // is by using 'impl trait':
334            //
335            // fn foo() -> impl Copy { foo }
336            //
337            // There's not really a sensible name we can generate,
338            // since we don't include 'impl trait' types (e.g. ty::Opaque)
339            // in the output
340            //
341            // Since we need to generate *something*, we just
342            // use a dummy string that should make it clear
343            // that something unusual is going on
344            if !visited.insert(t) {
345                output.push_str(if cpp_like_debuginfo {
346                    "recursive_type$"
347                } else {
348                    "<recursive_type>"
349                });
350                return;
351            }
352
353            let sig = tcx.normalize_erasing_late_bound_regions(
354                ty::TypingEnv::fully_monomorphized(),
355                t.fn_sig(tcx),
356            );
357
358            if cpp_like_debuginfo {
359                // Format as a C++ function pointer: return_type (*)(params...)
360                if sig.output().is_unit() {
361                    output.push_str("void");
362                } else {
363                    push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
364                }
365                output.push_str(" (*)(");
366            } else {
367                output.push_str(sig.safety.prefix_str());
368
369                if sig.abi != rustc_abi::ExternAbi::Rust {
370                    let _ = output.write_fmt(format_args!("extern {0} ", sig.abi))write!(output, "extern {} ", sig.abi);
371                }
372
373                output.push_str("fn(");
374            }
375
376            if !sig.inputs().is_empty() {
377                for &parameter_type in sig.inputs() {
378                    push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
379                    push_arg_separator(cpp_like_debuginfo, output);
380                }
381                pop_arg_separator(output);
382            }
383
384            if sig.c_variadic {
385                if !sig.inputs().is_empty() {
386                    output.push_str(", ...");
387                } else {
388                    output.push_str("...");
389                }
390            }
391
392            output.push(')');
393
394            if !cpp_like_debuginfo && !sig.output().is_unit() {
395                output.push_str(" -> ");
396                push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
397            }
398
399            // We only keep the type in 'visited'
400            // for the duration of the body of this method.
401            // It's fine for a particular function type
402            // to show up multiple times in one overall type
403            // (e.g. MyType<fn() -> u8, fn() -> u8>
404            //
405            // We only care about avoiding recursing
406            // directly back to the type we're currently
407            // processing
408            visited.remove(&t);
409        }
410        ty::Closure(def_id, args)
411        | ty::CoroutineClosure(def_id, args)
412        | ty::Coroutine(def_id, args, ..) => {
413            // Name will be "{closure_env#0}<T1, T2, ...>", "{coroutine_env#0}<T1, T2, ...>", or
414            // "{async_fn_env#0}<T1, T2, ...>", etc.
415            // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
416            // an artificial `enum2$<>` type, as defined in msvc_enum_fallback().
417            if cpp_like_debuginfo && t.is_coroutine() {
418                let ty_and_layout =
419                    tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(t)).unwrap();
420                msvc_enum_fallback(
421                    tcx,
422                    ty_and_layout,
423                    &|output, visited| {
424                        push_closure_or_coroutine_name(tcx, def_id, args, true, output, visited);
425                    },
426                    output,
427                    visited,
428                );
429            } else {
430                push_closure_or_coroutine_name(tcx, def_id, args, qualified, output, visited);
431            }
432        }
433        ty::UnsafeBinder(_) => {
    ::core::panicking::panic_fmt(format_args!("not yet implemented: {0}",
            format_args!("FIXME(unsafe_binders)")));
}todo!("FIXME(unsafe_binders)"),
434        ty::Param(_)
435        | ty::Error(_)
436        | ty::Infer(_)
437        | ty::Placeholder(..)
438        | ty::Alias(..)
439        | ty::Bound(..)
440        | ty::CoroutineWitness(..) => {
441            ::rustc_middle::util::bug::bug_fmt(format_args!("debuginfo: Trying to create type name for unexpected type: {0:?}",
        t));bug!(
442                "debuginfo: Trying to create type name for \
443                  unexpected type: {:?}",
444                t
445            );
446        }
447    }
448
449    /// MSVC names enums differently than other platforms so that the debugging visualization
450    // format (natvis) is able to understand enums and render the active variant correctly in the
451    // debugger. For more information, look in
452    // rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
453    fn msvc_enum_fallback<'tcx>(
454        tcx: TyCtxt<'tcx>,
455        ty_and_layout: TyAndLayout<'tcx>,
456        push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
457        output: &mut String,
458        visited: &mut FxHashSet<Ty<'tcx>>,
459    ) {
460        if !!wants_c_like_enum_debuginfo(tcx, ty_and_layout) {
    ::core::panicking::panic("assertion failed: !wants_c_like_enum_debuginfo(tcx, ty_and_layout)")
};assert!(!wants_c_like_enum_debuginfo(tcx, ty_and_layout));
461        output.push_str("enum2$<");
462        push_inner(output, visited);
463        push_close_angle_bracket(true, output);
464    }
465
466    const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
467
468    fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) {
469        if cpp_like_debuginfo {
470            push_arg_separator(cpp_like_debuginfo, output);
471        } else {
472            output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
473        }
474    }
475
476    fn pop_auto_trait_separator(output: &mut String) {
477        if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
478            output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
479        } else {
480            pop_arg_separator(output);
481        }
482    }
483}
484
485pub enum VTableNameKind {
486    // Is the name for the const/static holding the vtable?
487    GlobalVariable,
488    // Is the name for the type of the vtable?
489    Type,
490}
491
492/// Computes a name for the global variable storing a vtable (or the type of that global variable).
493///
494/// The name is of the form:
495///
496/// `<path::to::SomeType as path::to::SomeTrait>::{vtable}`
497///
498/// or, when generating C++-like names:
499///
500/// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
501///
502/// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
503/// `{vtable}`, so that the type and the corresponding global variable get assigned different
504/// names.
505pub fn compute_debuginfo_vtable_name<'tcx>(
506    tcx: TyCtxt<'tcx>,
507    t: Ty<'tcx>,
508    trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
509    kind: VTableNameKind,
510) -> String {
511    let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
512
513    let mut vtable_name = String::with_capacity(64);
514
515    if cpp_like_debuginfo {
516        vtable_name.push_str("impl$<");
517    } else {
518        vtable_name.push('<');
519    }
520
521    let mut visited = FxHashSet::default();
522    push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
523
524    if cpp_like_debuginfo {
525        vtable_name.push_str(", ");
526    } else {
527        vtable_name.push_str(" as ");
528    }
529
530    if let Some(trait_ref) = trait_ref {
531        let trait_ref =
532            tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
533        push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
534        visited.clear();
535        push_generic_args_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
536    } else {
537        vtable_name.push('_');
538    }
539
540    push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
541
542    let suffix = match (cpp_like_debuginfo, kind) {
543        (true, VTableNameKind::GlobalVariable) => "::vtable$",
544        (false, VTableNameKind::GlobalVariable) => "::{vtable}",
545        (true, VTableNameKind::Type) => "::vtable_type$",
546        (false, VTableNameKind::Type) => "::{vtable_type}",
547    };
548
549    vtable_name.reserve_exact(suffix.len());
550    vtable_name.push_str(suffix);
551
552    vtable_name
553}
554
555pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) {
556    let def_key = tcx.def_key(def_id);
557    if qualified && let Some(parent) = def_key.parent {
558        push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
559        output.push_str("::");
560    }
561
562    push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
563}
564
565fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
566    use CoroutineDesugaring::*;
567    use CoroutineKind::*;
568    use CoroutineSource::*;
569    match coroutine_kind {
570        Some(Desugared(Gen, Block)) => "gen_block",
571        Some(Desugared(Gen, Closure)) => "gen_closure",
572        Some(Desugared(Gen, Fn)) => "gen_fn",
573        Some(Desugared(Async, Block)) => "async_block",
574        Some(Desugared(Async, Closure)) => "async_closure",
575        Some(Desugared(Async, Fn)) => "async_fn",
576        Some(Desugared(AsyncGen, Block)) => "async_gen_block",
577        Some(Desugared(AsyncGen, Closure)) => "async_gen_closure",
578        Some(Desugared(AsyncGen, Fn)) => "async_gen_fn",
579        Some(Coroutine(_)) => "coroutine",
580        None => "closure",
581    }
582}
583
584fn push_disambiguated_special_name(
585    label: &str,
586    disambiguator: u32,
587    cpp_like_debuginfo: bool,
588    output: &mut String,
589) {
590    if cpp_like_debuginfo {
591        output.write_fmt(format_args!("{0}${1}", label, disambiguator))write!(output, "{label}${disambiguator}").unwrap();
592    } else {
593        output.write_fmt(format_args!("{{{0}#{1}}}", label, disambiguator))write!(output, "{{{label}#{disambiguator}}}").unwrap();
594    }
595}
596
597fn push_unqualified_item_name(
598    tcx: TyCtxt<'_>,
599    def_id: DefId,
600    disambiguated_data: DisambiguatedDefPathData,
601    output: &mut String,
602) {
603    match disambiguated_data.data {
604        DefPathData::CrateRoot => {
605            output.push_str(tcx.crate_name(def_id.krate).as_str());
606        }
607        DefPathData::Closure => {
608            let label = coroutine_kind_label(tcx.coroutine_kind(def_id));
609
610            push_disambiguated_special_name(
611                label,
612                disambiguated_data.disambiguator,
613                cpp_like_debuginfo(tcx),
614                output,
615            );
616        }
617        _ => match disambiguated_data.data.name() {
618            DefPathDataName::Named(name) => {
619                output.push_str(name.as_str());
620            }
621            DefPathDataName::Anon { namespace } => {
622                push_disambiguated_special_name(
623                    namespace.as_str(),
624                    disambiguated_data.disambiguator,
625                    cpp_like_debuginfo(tcx),
626                    output,
627                );
628            }
629        },
630    };
631}
632
633pub fn push_generic_args<'tcx>(tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, output: &mut String) {
634    let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
635    let mut visited = FxHashSet::default();
636    push_generic_args_internal(tcx, args, output, &mut visited);
637}
638
639fn push_generic_args_internal<'tcx>(
640    tcx: TyCtxt<'tcx>,
641    args: GenericArgsRef<'tcx>,
642    output: &mut String,
643    visited: &mut FxHashSet<Ty<'tcx>>,
644) -> bool {
645    match (&args,
        &tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(),
                args)) {
    (left_val, right_val) => {
        if !(*left_val == *right_val) {
            let kind = ::core::panicking::AssertKind::Eq;
            ::core::panicking::assert_failed(kind, &*left_val, &*right_val,
                ::core::option::Option::None);
        }
    }
};assert_eq!(args, tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), args));
646    let mut args = args.non_erasable_generics().peekable();
647    if args.peek().is_none() {
648        return false;
649    }
650    let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
651
652    output.push('<');
653
654    for arg in args {
655        match arg {
656            GenericArgKind::Type(ty) => push_debuginfo_type_name(tcx, ty, true, output, visited),
657            GenericArgKind::Const(ct) => push_debuginfo_const_name(tcx, ct, output),
658            other => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected non-erasable generic: {0:?}",
        other))bug!("Unexpected non-erasable generic: {:?}", other),
659        }
660
661        push_arg_separator(cpp_like_debuginfo, output);
662    }
663    pop_arg_separator(output);
664    push_close_angle_bracket(cpp_like_debuginfo, output);
665
666    true
667}
668
669fn push_debuginfo_term_name<'tcx>(
670    tcx: TyCtxt<'tcx>,
671    term: ty::Term<'tcx>,
672    qualified: bool,
673    output: &mut String,
674    visited: &mut FxHashSet<Ty<'tcx>>,
675) {
676    match term.kind() {
677        ty::TermKind::Ty(ty) => push_debuginfo_type_name(tcx, ty, qualified, output, visited),
678        ty::TermKind::Const(ct) => push_debuginfo_const_name(tcx, ct, output),
679    }
680}
681
682fn push_debuginfo_const_name<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
683    match ct.kind() {
684        ty::ConstKind::Param(param) => {
685            output.write_fmt(format_args!("{0}", param.name))write!(output, "{}", param.name)
686        }
687        ty::ConstKind::Value(cv) => {
688            match cv.ty.kind() {
689                ty::Int(ity) => {
690                    let bits = cv
691                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
692                        .expect("expected monomorphic const in codegen");
693                    let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
694                    output.write_fmt(format_args!("{0}", val))write!(output, "{val}")
695                }
696                ty::Uint(_) => {
697                    let val = cv
698                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
699                        .expect("expected monomorphic const in codegen");
700                    output.write_fmt(format_args!("{0}", val))write!(output, "{val}")
701                }
702                ty::Bool => {
703                    let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
704                    output.write_fmt(format_args!("{0}", val))write!(output, "{val}")
705                }
706                _ => {
707                    // If we cannot evaluate the constant to a known type, we fall back
708                    // to emitting a stable hash value of the constant. This isn't very pretty
709                    // but we get a deterministic, virtually unique value for the constant.
710                    //
711                    // Let's only emit 64 bits of the hash value. That should be plenty for
712                    // avoiding collisions and will make the emitted type names shorter.
713                    let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
714                        let mut hasher = StableHasher::new();
715                        hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
716                        hasher.finish::<Hash64>()
717                    });
718
719                    if cpp_like_debuginfo(tcx) {
720                        output.write_fmt(format_args!("CONST${0:x}", hash_short))write!(output, "CONST${hash_short:x}")
721                    } else {
722                        output.write_fmt(format_args!("{{CONST#{0:x}}}", hash_short))write!(output, "{{CONST#{hash_short:x}}}")
723                    }
724                }
725            }
726        }
727        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("Invalid `Const` during codegen: {0:?}",
        ct))bug!("Invalid `Const` during codegen: {:?}", ct),
728    }
729    .unwrap();
730}
731
732fn push_closure_or_coroutine_name<'tcx>(
733    tcx: TyCtxt<'tcx>,
734    def_id: DefId,
735    args: GenericArgsRef<'tcx>,
736    qualified: bool,
737    output: &mut String,
738    visited: &mut FxHashSet<Ty<'tcx>>,
739) {
740    // Name will be "{closure_env#0}<T1, T2, ...>", "{coroutine_env#0}<T1, T2, ...>", or
741    // "{async_fn_env#0}<T1, T2, ...>", etc.
742    let def_key = tcx.def_key(def_id);
743    let coroutine_kind = tcx.coroutine_kind(def_id);
744
745    if qualified {
746        let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
747        push_item_name(tcx, parent_def_id, true, output);
748        output.push_str("::");
749    }
750
751    let mut label = String::with_capacity(20);
752    (&mut label).write_fmt(format_args!("{0}_env",
        coroutine_kind_label(coroutine_kind)))write!(&mut label, "{}_env", coroutine_kind_label(coroutine_kind)).unwrap();
753
754    push_disambiguated_special_name(
755        &label,
756        def_key.disambiguated_data.disambiguator,
757        cpp_like_debuginfo(tcx),
758        output,
759    );
760
761    // We also need to add the generic arguments of the async fn/coroutine or
762    // the enclosing function (for closures or async blocks), so that we end
763    // up with a unique name for every instantiation.
764
765    // Find the generics of the enclosing function, as defined in the source code.
766    let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
767    let generics = tcx.generics_of(enclosing_fn_def_id);
768
769    // Truncate the args to the length of the above generics. This will cut off
770    // anything closure- or coroutine-specific.
771    // FIXME(async_closures): This is probably not going to be correct w.r.t.
772    // multiple coroutine flavors. Maybe truncate to (parent + 1)?
773    let args = args.truncate_to(tcx, generics);
774    push_generic_args_internal(tcx, args, output, visited);
775}
776
777fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
778    // MSVC debugger always treats `>>` as a shift, even when parsing templates,
779    // so add a space to avoid confusion.
780    if cpp_like_debuginfo && output.ends_with('>') {
781        output.push(' ')
782    };
783
784    output.push('>');
785}
786
787fn pop_close_angle_bracket(output: &mut String) {
788    if !output.ends_with('>') {
    {
        ::core::panicking::panic_fmt(format_args!("\'output\' does not end with \'>\': {0}",
                output));
    }
};assert!(output.ends_with('>'), "'output' does not end with '>': {output}");
789    output.pop();
790    if output.ends_with(' ') {
791        output.pop();
792    }
793}
794
795fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) {
796    // Natvis does not always like having spaces between parts of the type name
797    // and this causes issues when we need to write a typename in natvis, for example
798    // as part of a cast like the `HashMap` visualizer does.
799    if cpp_like_debuginfo {
800        output.push(',');
801    } else {
802        output.push_str(", ");
803    };
804}
805
806fn pop_arg_separator(output: &mut String) {
807    if output.ends_with(' ') {
808        output.pop();
809    }
810
811    if !output.ends_with(',') {
    ::core::panicking::panic("assertion failed: output.ends_with(\',\')")
};assert!(output.ends_with(','));
812
813    output.pop();
814}
815
816/// Check if we should generate C++ like names and debug information.
817pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool {
818    tcx.sess.target.is_like_msvc
819}