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().fatal(e.to_string());
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(inner) => {
434            if cpp_like_debuginfo {
435                output.push_str("unsafe$<");
436            } else {
437                output.push_str("unsafe ");
438            }
439
440            push_debuginfo_type_name(tcx, inner.skip_binder(), qualified, output, visited);
441
442            if cpp_like_debuginfo {
443                push_close_angle_bracket(cpp_like_debuginfo, output);
444            }
445        }
446        ty::Param(_)
447        | ty::Error(_)
448        | ty::Infer(_)
449        | ty::Placeholder(..)
450        | ty::Alias(..)
451        | ty::Bound(..)
452        | ty::CoroutineWitness(..) => {
453            ::rustc_middle::util::bug::bug_fmt(format_args!("debuginfo: Trying to create type name for unexpected type: {0:?}",
        t));bug!(
454                "debuginfo: Trying to create type name for \
455                  unexpected type: {:?}",
456                t
457            );
458        }
459    }
460
461    /// MSVC names enums differently than other platforms so that the debugging visualization
462    // format (natvis) is able to understand enums and render the active variant correctly in the
463    // debugger. For more information, look in
464    // rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
465    fn msvc_enum_fallback<'tcx>(
466        tcx: TyCtxt<'tcx>,
467        ty_and_layout: TyAndLayout<'tcx>,
468        push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
469        output: &mut String,
470        visited: &mut FxHashSet<Ty<'tcx>>,
471    ) {
472        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));
473        output.push_str("enum2$<");
474        push_inner(output, visited);
475        push_close_angle_bracket(true, output);
476    }
477
478    const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
479
480    fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) {
481        if cpp_like_debuginfo {
482            push_arg_separator(cpp_like_debuginfo, output);
483        } else {
484            output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
485        }
486    }
487
488    fn pop_auto_trait_separator(output: &mut String) {
489        if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
490            output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
491        } else {
492            pop_arg_separator(output);
493        }
494    }
495}
496
497pub enum VTableNameKind {
498    // Is the name for the const/static holding the vtable?
499    GlobalVariable,
500    // Is the name for the type of the vtable?
501    Type,
502}
503
504/// Computes a name for the global variable storing a vtable (or the type of that global variable).
505///
506/// The name is of the form:
507///
508/// `<path::to::SomeType as path::to::SomeTrait>::{vtable}`
509///
510/// or, when generating C++-like names:
511///
512/// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
513///
514/// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
515/// `{vtable}`, so that the type and the corresponding global variable get assigned different
516/// names.
517pub fn compute_debuginfo_vtable_name<'tcx>(
518    tcx: TyCtxt<'tcx>,
519    t: Ty<'tcx>,
520    trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
521    kind: VTableNameKind,
522) -> String {
523    let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
524
525    let mut vtable_name = String::with_capacity(64);
526
527    if cpp_like_debuginfo {
528        vtable_name.push_str("impl$<");
529    } else {
530        vtable_name.push('<');
531    }
532
533    let mut visited = FxHashSet::default();
534    push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
535
536    if cpp_like_debuginfo {
537        vtable_name.push_str(", ");
538    } else {
539        vtable_name.push_str(" as ");
540    }
541
542    if let Some(trait_ref) = trait_ref {
543        let trait_ref =
544            tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
545        push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
546        visited.clear();
547        push_generic_args_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
548    } else {
549        vtable_name.push('_');
550    }
551
552    push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
553
554    let suffix = match (cpp_like_debuginfo, kind) {
555        (true, VTableNameKind::GlobalVariable) => "::vtable$",
556        (false, VTableNameKind::GlobalVariable) => "::{vtable}",
557        (true, VTableNameKind::Type) => "::vtable_type$",
558        (false, VTableNameKind::Type) => "::{vtable_type}",
559    };
560
561    vtable_name.reserve_exact(suffix.len());
562    vtable_name.push_str(suffix);
563
564    vtable_name
565}
566
567pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) {
568    let def_key = tcx.def_key(def_id);
569    if qualified && let Some(parent) = def_key.parent {
570        push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
571        output.push_str("::");
572    }
573
574    push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
575}
576
577fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
578    use CoroutineDesugaring::*;
579    use CoroutineKind::*;
580    use CoroutineSource::*;
581    match coroutine_kind {
582        Some(Desugared(Gen, Block)) => "gen_block",
583        Some(Desugared(Gen, Closure)) => "gen_closure",
584        Some(Desugared(Gen, Fn)) => "gen_fn",
585        Some(Desugared(Async, Block)) => "async_block",
586        Some(Desugared(Async, Closure)) => "async_closure",
587        Some(Desugared(Async, Fn)) => "async_fn",
588        Some(Desugared(AsyncGen, Block)) => "async_gen_block",
589        Some(Desugared(AsyncGen, Closure)) => "async_gen_closure",
590        Some(Desugared(AsyncGen, Fn)) => "async_gen_fn",
591        Some(Coroutine(_)) => "coroutine",
592        None => "closure",
593    }
594}
595
596fn push_disambiguated_special_name(
597    label: &str,
598    disambiguator: u32,
599    cpp_like_debuginfo: bool,
600    output: &mut String,
601) {
602    if cpp_like_debuginfo {
603        output.write_fmt(format_args!("{0}${1}", label, disambiguator))write!(output, "{label}${disambiguator}").unwrap();
604    } else {
605        output.write_fmt(format_args!("{{{0}#{1}}}", label, disambiguator))write!(output, "{{{label}#{disambiguator}}}").unwrap();
606    }
607}
608
609fn push_unqualified_item_name(
610    tcx: TyCtxt<'_>,
611    def_id: DefId,
612    disambiguated_data: DisambiguatedDefPathData,
613    output: &mut String,
614) {
615    match disambiguated_data.data {
616        DefPathData::CrateRoot => {
617            output.push_str(tcx.crate_name(def_id.krate).as_str());
618        }
619        DefPathData::Closure => {
620            let label = coroutine_kind_label(tcx.coroutine_kind(def_id));
621
622            push_disambiguated_special_name(
623                label,
624                disambiguated_data.disambiguator,
625                cpp_like_debuginfo(tcx),
626                output,
627            );
628        }
629        _ => match disambiguated_data.data.name() {
630            DefPathDataName::Named(name) => {
631                output.push_str(name.as_str());
632            }
633            DefPathDataName::Anon { namespace } => {
634                push_disambiguated_special_name(
635                    namespace.as_str(),
636                    disambiguated_data.disambiguator,
637                    cpp_like_debuginfo(tcx),
638                    output,
639                );
640            }
641        },
642    };
643}
644
645pub fn push_generic_args<'tcx>(tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, output: &mut String) {
646    let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
647    let mut visited = FxHashSet::default();
648    push_generic_args_internal(tcx, args, output, &mut visited);
649}
650
651fn push_generic_args_internal<'tcx>(
652    tcx: TyCtxt<'tcx>,
653    args: GenericArgsRef<'tcx>,
654    output: &mut String,
655    visited: &mut FxHashSet<Ty<'tcx>>,
656) -> bool {
657    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));
658    let mut args = args.non_erasable_generics().peekable();
659    if args.peek().is_none() {
660        return false;
661    }
662    let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
663
664    output.push('<');
665
666    for arg in args {
667        match arg {
668            GenericArgKind::Type(ty) => push_debuginfo_type_name(tcx, ty, true, output, visited),
669            GenericArgKind::Const(ct) => push_debuginfo_const_name(tcx, ct, output),
670            other => ::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected non-erasable generic: {0:?}",
        other))bug!("Unexpected non-erasable generic: {:?}", other),
671        }
672
673        push_arg_separator(cpp_like_debuginfo, output);
674    }
675    pop_arg_separator(output);
676    push_close_angle_bracket(cpp_like_debuginfo, output);
677
678    true
679}
680
681fn push_debuginfo_term_name<'tcx>(
682    tcx: TyCtxt<'tcx>,
683    term: ty::Term<'tcx>,
684    qualified: bool,
685    output: &mut String,
686    visited: &mut FxHashSet<Ty<'tcx>>,
687) {
688    match term.kind() {
689        ty::TermKind::Ty(ty) => push_debuginfo_type_name(tcx, ty, qualified, output, visited),
690        ty::TermKind::Const(ct) => push_debuginfo_const_name(tcx, ct, output),
691    }
692}
693
694fn push_debuginfo_const_name<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
695    match ct.kind() {
696        ty::ConstKind::Param(param) => {
697            output.write_fmt(format_args!("{0}", param.name))write!(output, "{}", param.name)
698        }
699        ty::ConstKind::Value(cv) => {
700            match cv.ty.kind() {
701                ty::Int(ity) => {
702                    let bits = cv
703                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
704                        .expect("expected monomorphic const in codegen");
705                    let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
706                    output.write_fmt(format_args!("{0}", val))write!(output, "{val}")
707                }
708                ty::Uint(_) => {
709                    let val = cv
710                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
711                        .expect("expected monomorphic const in codegen");
712                    output.write_fmt(format_args!("{0}", val))write!(output, "{val}")
713                }
714                ty::Bool => {
715                    let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
716                    output.write_fmt(format_args!("{0}", val))write!(output, "{val}")
717                }
718                _ => {
719                    // If we cannot evaluate the constant to a known type, we fall back
720                    // to emitting a stable hash value of the constant. This isn't very pretty
721                    // but we get a deterministic, virtually unique value for the constant.
722                    //
723                    // Let's only emit 64 bits of the hash value. That should be plenty for
724                    // avoiding collisions and will make the emitted type names shorter.
725                    let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
726                        let mut hasher = StableHasher::new();
727                        hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
728                        hasher.finish::<Hash64>()
729                    });
730
731                    if cpp_like_debuginfo(tcx) {
732                        output.write_fmt(format_args!("CONST${0:x}", hash_short))write!(output, "CONST${hash_short:x}")
733                    } else {
734                        output.write_fmt(format_args!("{{CONST#{0:x}}}", hash_short))write!(output, "{{CONST#{hash_short:x}}}")
735                    }
736                }
737            }
738        }
739        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("Invalid `Const` during codegen: {0:?}",
        ct))bug!("Invalid `Const` during codegen: {:?}", ct),
740    }
741    .unwrap();
742}
743
744fn push_closure_or_coroutine_name<'tcx>(
745    tcx: TyCtxt<'tcx>,
746    def_id: DefId,
747    args: GenericArgsRef<'tcx>,
748    qualified: bool,
749    output: &mut String,
750    visited: &mut FxHashSet<Ty<'tcx>>,
751) {
752    // Name will be "{closure_env#0}<T1, T2, ...>", "{coroutine_env#0}<T1, T2, ...>", or
753    // "{async_fn_env#0}<T1, T2, ...>", etc.
754    let def_key = tcx.def_key(def_id);
755    let coroutine_kind = tcx.coroutine_kind(def_id);
756
757    if qualified {
758        let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
759        push_item_name(tcx, parent_def_id, true, output);
760        output.push_str("::");
761    }
762
763    let mut label = String::with_capacity(20);
764    (&mut label).write_fmt(format_args!("{0}_env",
        coroutine_kind_label(coroutine_kind)))write!(&mut label, "{}_env", coroutine_kind_label(coroutine_kind)).unwrap();
765
766    push_disambiguated_special_name(
767        &label,
768        def_key.disambiguated_data.disambiguator,
769        cpp_like_debuginfo(tcx),
770        output,
771    );
772
773    // We also need to add the generic arguments of the async fn/coroutine or
774    // the enclosing function (for closures or async blocks), so that we end
775    // up with a unique name for every instantiation.
776
777    // Find the generics of the enclosing function, as defined in the source code.
778    let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
779    let generics = tcx.generics_of(enclosing_fn_def_id);
780
781    // Truncate the args to the length of the above generics. This will cut off
782    // anything closure- or coroutine-specific.
783    // FIXME(async_closures): This is probably not going to be correct w.r.t.
784    // multiple coroutine flavors. Maybe truncate to (parent + 1)?
785    let args = args.truncate_to(tcx, generics);
786    push_generic_args_internal(tcx, args, output, visited);
787}
788
789fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
790    // MSVC debugger always treats `>>` as a shift, even when parsing templates,
791    // so add a space to avoid confusion.
792    if cpp_like_debuginfo && output.ends_with('>') {
793        output.push(' ')
794    };
795
796    output.push('>');
797}
798
799fn pop_close_angle_bracket(output: &mut String) {
800    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}");
801    output.pop();
802    if output.ends_with(' ') {
803        output.pop();
804    }
805}
806
807fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) {
808    // Natvis does not always like having spaces between parts of the type name
809    // and this causes issues when we need to write a typename in natvis, for example
810    // as part of a cast like the `HashMap` visualizer does.
811    if cpp_like_debuginfo {
812        output.push(',');
813    } else {
814        output.push_str(", ");
815    };
816}
817
818fn pop_arg_separator(output: &mut String) {
819    if output.ends_with(' ') {
820        output.pop();
821    }
822
823    if !output.ends_with(',') {
    ::core::panicking::panic("assertion failed: output.ends_with(\',\')")
};assert!(output.ends_with(','));
824
825    output.pop();
826}
827
828/// Check if we should generate C++ like names and debug information.
829pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool {
830    tcx.sess.target.is_like_msvc
831}