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