1 // Type Names for Debug Info.
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.
14 use rustc_data_structures::fx::FxHashSet;
15 use rustc_data_structures::stable_hasher::{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;
20 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
21 use rustc_middle::ty::{self, AdtDef, ExistentialProjection, Ty, TyCtxt};
22 use rustc_query_system::ich::NodeIdHashingMode;
23 use rustc_target::abi::{Integer, TagEncoding, Variants};
24 use smallvec::SmallVec;
28 // Compute the name of the type as it should be stored in debuginfo. Does not do
29 // any caching, i.e., calling the function twice with the same type will also do
30 // the work twice. The `qualified` parameter only affects the first level of the
31 // type name, further levels (i.e., type parameters) are always fully qualified.
32 pub fn compute_debuginfo_type_name<'tcx>(
37 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
39 let mut result = String::with_capacity(64);
40 let mut visited = FxHashSet::default();
41 push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
45 // Pushes the name of the type as it should be stored in debuginfo on the
46 // `output` String. See also compute_debuginfo_type_name().
47 fn push_debuginfo_type_name<'tcx>(
52 visited: &mut FxHashSet<Ty<'tcx>>,
54 // When targeting MSVC, emit C++ style type names for compatibility with
55 // .natvis visualizers (and perhaps other existing native debuggers?)
56 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
59 ty::Bool => output.push_str("bool"),
60 ty::Char => output.push_str("char"),
61 ty::Str => output.push_str("str"),
63 if cpp_like_debuginfo {
64 output.push_str("never$");
69 ty::Int(int_ty) => output.push_str(int_ty.name_str()),
70 ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
71 ty::Float(float_ty) => output.push_str(float_ty.name_str()),
72 ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
73 ty::Adt(def, substs) => {
74 if def.is_enum() && cpp_like_debuginfo {
75 msvc_enum_fallback(tcx, t, def, substs, output, visited);
77 push_item_name(tcx, def.did, qualified, output);
78 push_generic_params_internal(tcx, substs, output, visited);
81 ty::Tuple(component_types) => {
82 if cpp_like_debuginfo {
83 output.push_str("tuple$<");
88 for component_type in component_types {
89 push_debuginfo_type_name(tcx, component_type, true, output, visited);
90 push_arg_separator(cpp_like_debuginfo, output);
92 if !component_types.is_empty() {
93 pop_arg_separator(output);
96 if cpp_like_debuginfo {
97 push_close_angle_bracket(cpp_like_debuginfo, output);
102 ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
103 if cpp_like_debuginfo {
105 Mutability::Not => output.push_str("ptr_const$<"),
106 Mutability::Mut => output.push_str("ptr_mut$<"),
111 Mutability::Not => output.push_str("const "),
112 Mutability::Mut => output.push_str("mut "),
116 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
118 if cpp_like_debuginfo {
119 push_close_angle_bracket(cpp_like_debuginfo, output);
122 ty::Ref(_, inner_type, mutbl) => {
123 // Slices and `&str` are treated like C++ pointers when computing debug
124 // info for MSVC debugger. However, wrapping these types' names in a synthetic type
125 // causes the .natvis engine for WinDbg to fail to display their data, so we opt these
126 // types out to aid debugging in MSVC.
127 let is_slice_or_str = matches!(*inner_type.kind(), ty::Slice(_) | ty::Str);
129 if !cpp_like_debuginfo {
131 output.push_str(mutbl.prefix_str());
132 } else if !is_slice_or_str {
134 Mutability::Not => output.push_str("ref$<"),
135 Mutability::Mut => output.push_str("ref_mut$<"),
139 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
141 if cpp_like_debuginfo && !is_slice_or_str {
142 push_close_angle_bracket(cpp_like_debuginfo, output);
145 ty::Array(inner_type, len) => {
146 if cpp_like_debuginfo {
147 output.push_str("array$<");
148 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
150 ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(),
151 _ => write!(output, ",{}>", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
156 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
158 ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(),
159 _ => write!(output, "; {}]", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
164 ty::Slice(inner_type) => {
165 if cpp_like_debuginfo {
166 output.push_str("slice$<");
171 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
173 if cpp_like_debuginfo {
174 push_close_angle_bracket(cpp_like_debuginfo, output);
179 ty::Dynamic(ref trait_data, ..) => {
180 let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
182 let has_enclosing_parens = if cpp_like_debuginfo {
183 output.push_str("dyn$<");
186 if trait_data.len() > 1 && auto_traits.len() != 0 {
187 // We need enclosing parens because there is more than one trait
188 output.push_str("(dyn ");
191 output.push_str("dyn ");
196 if let Some(principal) = trait_data.principal() {
198 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
199 push_item_name(tcx, principal.def_id, qualified, output);
200 let principal_has_generic_params =
201 push_generic_params_internal(tcx, principal.substs, output, visited);
203 let projection_bounds: SmallVec<[_; 4]> = trait_data
206 let ExistentialProjection { item_def_id, term, .. } = bound.skip_binder();
207 // FIXME(associated_const_equality): allow for consts here
208 (item_def_id, term.ty().unwrap())
212 if projection_bounds.len() != 0 {
213 if principal_has_generic_params {
214 // push_generic_params_internal() above added a `>` but we actually
215 // want to add more items to that list, so remove that again.
216 pop_close_angle_bracket(output);
219 for (item_def_id, ty) in projection_bounds {
220 push_arg_separator(cpp_like_debuginfo, output);
222 if cpp_like_debuginfo {
223 output.push_str("assoc$<");
224 push_item_name(tcx, item_def_id, false, output);
225 push_arg_separator(cpp_like_debuginfo, output);
226 push_debuginfo_type_name(tcx, ty, true, output, visited);
227 push_close_angle_bracket(cpp_like_debuginfo, output);
229 push_item_name(tcx, item_def_id, false, output);
231 push_debuginfo_type_name(tcx, ty, true, output, visited);
235 push_close_angle_bracket(cpp_like_debuginfo, output);
238 if auto_traits.len() != 0 {
239 push_auto_trait_separator(cpp_like_debuginfo, output);
243 if auto_traits.len() != 0 {
244 let mut auto_traits: SmallVec<[String; 4]> = auto_traits
247 let mut name = String::with_capacity(20);
248 push_item_name(tcx, def_id, true, &mut name);
252 auto_traits.sort_unstable();
254 for auto_trait in auto_traits {
255 output.push_str(&auto_trait);
256 push_auto_trait_separator(cpp_like_debuginfo, output);
259 pop_auto_trait_separator(output);
262 if cpp_like_debuginfo {
263 push_close_angle_bracket(cpp_like_debuginfo, output);
264 } else if has_enclosing_parens {
268 ty::FnDef(..) | ty::FnPtr(_) => {
269 // We've encountered a weird 'recursive type'
270 // Currently, the only way to generate such a type
271 // is by using 'impl trait':
273 // fn foo() -> impl Copy { foo }
275 // There's not really a sensible name we can generate,
276 // since we don't include 'impl trait' types (e.g. ty::Opaque)
279 // Since we need to generate *something*, we just
280 // use a dummy string that should make it clear
281 // that something unusual is going on
282 if !visited.insert(t) {
283 output.push_str(if cpp_like_debuginfo {
292 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx));
294 if cpp_like_debuginfo {
295 // Format as a C++ function pointer: return_type (*)(params...)
296 if sig.output().is_unit() {
297 output.push_str("void");
299 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
301 output.push_str(" (*)(");
303 output.push_str(sig.unsafety.prefix_str());
305 if sig.abi != rustc_target::spec::abi::Abi::Rust {
306 output.push_str("extern \"");
307 output.push_str(sig.abi.name());
308 output.push_str("\" ");
311 output.push_str("fn(");
314 if !sig.inputs().is_empty() {
315 for ¶meter_type in sig.inputs() {
316 push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
317 push_arg_separator(cpp_like_debuginfo, output);
319 pop_arg_separator(output);
323 if !sig.inputs().is_empty() {
324 output.push_str(", ...");
326 output.push_str("...");
332 if !cpp_like_debuginfo && !sig.output().is_unit() {
333 output.push_str(" -> ");
334 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
337 // We only keep the type in 'visited'
338 // for the duration of the body of this method.
339 // It's fine for a particular function type
340 // to show up multiple times in one overall type
341 // (e.g. MyType<fn() -> u8, fn() -> u8>
343 // We only care about avoiding recursing
344 // directly back to the type we're currently
348 ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
349 // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
350 // "{async_fn_env#0}<T1, T2, ...>", etc.
351 let def_key = tcx.def_key(def_id);
354 let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
355 push_item_name(tcx, parent_def_id, true, output);
356 output.push_str("::");
359 let mut label = String::with_capacity(20);
360 write!(&mut label, "{}_env", generator_kind_label(tcx.generator_kind(def_id))).unwrap();
362 push_disambiguated_special_name(
364 def_key.disambiguated_data.disambiguator,
369 // We also need to add the generic arguments of the async fn/generator or
370 // the enclosing function (for closures or async blocks), so that we end
371 // up with a unique name for every instantiation.
373 // Find the generics of the enclosing function, as defined in the source code.
374 let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
375 let generics = tcx.generics_of(enclosing_fn_def_id);
377 // Truncate the substs to the length of the above generics. This will cut off
378 // anything closure- or generator-specific.
379 let substs = substs.truncate_to(tcx, generics);
380 push_generic_params_internal(tcx, substs, output, visited);
382 // Type parameters from polymorphized functions.
384 output.push_str(&format!("{:?}", t));
388 | ty::Placeholder(..)
392 | ty::GeneratorWitness(..) => {
394 "debuginfo: Trying to create type name for \
395 unexpected type: {:?}",
401 /// MSVC names enums differently than other platforms so that the debugging visualization
402 // format (natvis) is able to understand enums and render the active variant correctly in the
403 // debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
404 // `EnumMemberDescriptionFactor::create_member_descriptions`.
405 fn msvc_enum_fallback<'tcx>(
409 substs: SubstsRef<'tcx>,
411 visited: &mut FxHashSet<Ty<'tcx>>,
413 let layout = tcx.layout_of(tcx.param_env(def.did).and(ty)).expect("layout error");
415 output.push_str("enum$<");
416 push_item_name(tcx, def.did, true, output);
417 push_generic_params_internal(tcx, substs, output, visited);
419 if let Variants::Multiple {
420 tag_encoding: TagEncoding::Niche { dataful_variant, .. },
426 let dataful_variant_layout = &variants[*dataful_variant];
428 // calculate the range of values for the dataful variant
429 let dataful_discriminant_range =
430 dataful_variant_layout.largest_niche().unwrap().scalar.valid_range;
432 let min = dataful_discriminant_range.start;
433 let min = tag.value.size(&tcx).truncate(min);
435 let max = dataful_discriminant_range.end;
436 let max = tag.value.size(&tcx).truncate(max);
438 let dataful_variant_name = def.variants[*dataful_variant].name.as_str();
440 output.push_str(&format!(", {}, {}, {}", min, max, dataful_variant_name));
441 } else if let Variants::Single { index: variant_idx } = &layout.variants {
442 // Uninhabited enums can't be constructed and should never need to be visualized so
443 // skip this step for them.
444 if def.variants.len() != 0 {
445 let variant = def.variants[*variant_idx].name.as_str();
447 output.push_str(&format!(", {}", variant));
450 push_close_angle_bracket(true, output);
453 const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
455 fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) {
456 if cpp_like_debuginfo {
457 push_arg_separator(cpp_like_debuginfo, output);
459 output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
463 fn pop_auto_trait_separator(output: &mut String) {
464 if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
465 output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
467 pop_arg_separator(output);
472 pub enum VTableNameKind {
473 // Is the name for the const/static holding the vtable?
475 // Is the name for the type of the vtable?
479 /// Computes a name for the global variable storing a vtable (or the type of that global variable).
481 /// The name is of the form:
483 /// `<path::to::SomeType as path::to::SomeTrait>::{vtable}`
485 /// or, when generating C++-like names:
487 /// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
489 /// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
490 /// `{vtable}`, so that the type and the corresponding global variable get assigned different
492 pub fn compute_debuginfo_vtable_name<'tcx>(
495 trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
496 kind: VTableNameKind,
498 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
500 let mut vtable_name = String::with_capacity(64);
502 if cpp_like_debuginfo {
503 vtable_name.push_str("impl$<");
505 vtable_name.push('<');
508 let mut visited = FxHashSet::default();
509 push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
511 if cpp_like_debuginfo {
512 vtable_name.push_str(", ");
514 vtable_name.push_str(" as ");
517 if let Some(trait_ref) = trait_ref {
519 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref);
520 push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
522 push_generic_params_internal(tcx, trait_ref.substs, &mut vtable_name, &mut visited);
524 vtable_name.push_str("_");
527 push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
529 let suffix = match (cpp_like_debuginfo, kind) {
530 (true, VTableNameKind::GlobalVariable) => "::vtable$",
531 (false, VTableNameKind::GlobalVariable) => "::{vtable}",
532 (true, VTableNameKind::Type) => "::vtable_type$",
533 (false, VTableNameKind::Type) => "::{vtable_type}",
536 vtable_name.reserve_exact(suffix.len());
537 vtable_name.push_str(suffix);
542 pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) {
543 let def_key = tcx.def_key(def_id);
545 if let Some(parent) = def_key.parent {
546 push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
547 output.push_str("::");
551 push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
554 fn generator_kind_label(generator_kind: Option<GeneratorKind>) -> &'static str {
555 match generator_kind {
556 Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block",
557 Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure",
558 Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn",
559 Some(GeneratorKind::Gen) => "generator",
564 fn push_disambiguated_special_name(
567 cpp_like_debuginfo: bool,
570 if cpp_like_debuginfo {
571 write!(output, "{}${}", label, disambiguator).unwrap();
573 write!(output, "{{{}#{}}}", label, disambiguator).unwrap();
577 fn push_unqualified_item_name(
580 disambiguated_data: DisambiguatedDefPathData,
583 match disambiguated_data.data {
584 DefPathData::CrateRoot => {
585 output.push_str(tcx.crate_name(def_id.krate).as_str());
587 DefPathData::ClosureExpr => {
588 let label = generator_kind_label(tcx.generator_kind(def_id));
590 push_disambiguated_special_name(
592 disambiguated_data.disambiguator,
593 cpp_like_debuginfo(tcx),
597 _ => match disambiguated_data.data.name() {
598 DefPathDataName::Named(name) => {
599 output.push_str(name.as_str());
601 DefPathDataName::Anon { namespace } => {
602 push_disambiguated_special_name(
604 disambiguated_data.disambiguator,
605 cpp_like_debuginfo(tcx),
613 fn push_generic_params_internal<'tcx>(
615 substs: SubstsRef<'tcx>,
617 visited: &mut FxHashSet<Ty<'tcx>>,
619 if substs.non_erasable_generics().next().is_none() {
623 debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs));
625 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
629 for type_parameter in substs.non_erasable_generics() {
630 match type_parameter {
631 GenericArgKind::Type(type_parameter) => {
632 push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
634 GenericArgKind::Const(ct) => {
635 push_const_param(tcx, ct, output);
637 other => bug!("Unexpected non-erasable generic: {:?}", other),
640 push_arg_separator(cpp_like_debuginfo, output);
642 pop_arg_separator(output);
643 push_close_angle_bracket(cpp_like_debuginfo, output);
648 fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
650 ty::ConstKind::Param(param) => {
651 write!(output, "{}", param.name)
653 _ => match ct.ty().kind() {
655 let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
656 let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
657 write!(output, "{}", val)
660 let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
661 write!(output, "{}", val)
664 let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
665 write!(output, "{}", val)
668 // If we cannot evaluate the constant to a known type, we fall back
669 // to emitting a stable hash value of the constant. This isn't very pretty
670 // but we get a deterministic, virtually unique value for the constant.
671 let hcx = &mut tcx.create_stable_hashing_context();
672 let mut hasher = StableHasher::new();
673 hcx.while_hashing_spans(false, |hcx| {
674 hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
675 ct.val().hash_stable(hcx, &mut hasher);
678 // Let's only emit 64 bits of the hash value. That should be plenty for
679 // avoiding collisions and will make the emitted type names shorter.
680 let hash: u64 = hasher.finish();
682 if cpp_like_debuginfo(tcx) {
683 write!(output, "CONST${:x}", hash)
685 write!(output, "{{CONST#{:x}}}", hash)
693 pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) {
694 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
695 let mut visited = FxHashSet::default();
696 push_generic_params_internal(tcx, substs, output, &mut visited);
699 fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
700 // MSVC debugger always treats `>>` as a shift, even when parsing templates,
701 // so add a space to avoid confusion.
702 if cpp_like_debuginfo && output.ends_with('>') {
709 fn pop_close_angle_bracket(output: &mut String) {
710 assert!(output.ends_with('>'), "'output' does not end with '>': {}", output);
712 if output.ends_with(' ') {
717 fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) {
718 // Natvis does not always like having spaces between parts of the type name
719 // and this causes issues when we need to write a typename in natvis, for example
720 // as part of a cast like the `HashMap` visualizer does.
721 if cpp_like_debuginfo {
724 output.push_str(", ");
728 fn pop_arg_separator(output: &mut String) {
729 if output.ends_with(' ') {
733 assert!(output.ends_with(','));
738 /// Check if we should generate C++ like names and debug information.
739 pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool {
740 tcx.sess.target.is_like_msvc