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