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, 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;
27 use crate::debuginfo::wants_c_like_enum_debuginfo;
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.
33 pub fn compute_debuginfo_type_name<'tcx>(
38 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
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);
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().
48 fn push_debuginfo_type_name<'tcx>(
53 visited: &mut FxHashSet<Ty<'tcx>>,
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);
60 ty::Bool => output.push_str("bool"),
61 ty::Char => output.push_str("char"),
63 if cpp_like_debuginfo {
64 output.push_str("str$")
66 output.push_str("str")
70 if cpp_like_debuginfo {
71 output.push_str("never$");
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)) {
85 if !wants_c_like_enum_debuginfo(layout) {
88 // This is a C-like enum so we don't want to use the fallback encoding
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 // FIXME: migrate once `rustc_middle::mir::interpret::InterpError` is translatable.
97 tcx.sess.fatal(&format!("{}", e));
101 // We are not emitting cpp-like debuginfo or this isn't even an enum.
105 if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
109 push_item_name(tcx, def.did(), true, output);
110 push_generic_params_internal(tcx, substs, output, visited);
116 push_item_name(tcx, def.did(), qualified, output);
117 push_generic_params_internal(tcx, substs, output, visited);
120 ty::Tuple(component_types) => {
121 if cpp_like_debuginfo {
122 output.push_str("tuple$<");
127 for component_type in component_types {
128 push_debuginfo_type_name(tcx, component_type, true, output, visited);
129 push_arg_separator(cpp_like_debuginfo, output);
131 if !component_types.is_empty() {
132 pop_arg_separator(output);
135 if cpp_like_debuginfo {
136 push_close_angle_bracket(cpp_like_debuginfo, output);
141 ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => {
142 if cpp_like_debuginfo {
144 Mutability::Not => output.push_str("ptr_const$<"),
145 Mutability::Mut => output.push_str("ptr_mut$<"),
150 Mutability::Not => output.push_str("const "),
151 Mutability::Mut => output.push_str("mut "),
155 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
157 if cpp_like_debuginfo {
158 push_close_angle_bracket(cpp_like_debuginfo, output);
161 ty::Ref(_, inner_type, mutbl) => {
162 if cpp_like_debuginfo {
164 Mutability::Not => output.push_str("ref$<"),
165 Mutability::Mut => output.push_str("ref_mut$<"),
169 output.push_str(mutbl.prefix_str());
172 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
174 if cpp_like_debuginfo {
175 push_close_angle_bracket(cpp_like_debuginfo, output);
178 ty::Array(inner_type, len) => {
179 if cpp_like_debuginfo {
180 output.push_str("array$<");
181 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
183 ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(),
184 _ => write!(output, ",{}>", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
189 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
191 ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(),
192 _ => write!(output, "; {}]", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
197 ty::Slice(inner_type) => {
198 if cpp_like_debuginfo {
199 output.push_str("slice2$<");
204 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
206 if cpp_like_debuginfo {
207 push_close_angle_bracket(cpp_like_debuginfo, output);
212 ty::Dynamic(ref trait_data, ..) => {
213 let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
215 let has_enclosing_parens = if cpp_like_debuginfo {
216 output.push_str("dyn$<");
219 if trait_data.len() > 1 && auto_traits.len() != 0 {
220 // We need enclosing parens because there is more than one trait
221 output.push_str("(dyn ");
224 output.push_str("dyn ");
229 if let Some(principal) = trait_data.principal() {
231 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal);
232 push_item_name(tcx, principal.def_id, qualified, output);
233 let principal_has_generic_params =
234 push_generic_params_internal(tcx, principal.substs, output, visited);
236 let projection_bounds: SmallVec<[_; 4]> = trait_data
239 let ExistentialProjection { def_id: item_def_id, term, .. } =
240 tcx.erase_late_bound_regions(bound);
241 // FIXME(associated_const_equality): allow for consts here
242 (item_def_id, term.ty().unwrap())
246 if projection_bounds.len() != 0 {
247 if principal_has_generic_params {
248 // push_generic_params_internal() above added a `>` but we actually
249 // want to add more items to that list, so remove that again...
250 pop_close_angle_bracket(output);
251 // .. and add a comma to separate the regular generic args from the
253 push_arg_separator(cpp_like_debuginfo, output);
255 // push_generic_params_internal() did not add `<...>`, so we open
256 // angle brackets here.
260 for (item_def_id, ty) in projection_bounds {
261 if cpp_like_debuginfo {
262 output.push_str("assoc$<");
263 push_item_name(tcx, item_def_id, false, output);
264 push_arg_separator(cpp_like_debuginfo, output);
265 push_debuginfo_type_name(tcx, ty, true, output, visited);
266 push_close_angle_bracket(cpp_like_debuginfo, output);
268 push_item_name(tcx, item_def_id, false, output);
270 push_debuginfo_type_name(tcx, ty, true, output, visited);
272 push_arg_separator(cpp_like_debuginfo, output);
275 pop_arg_separator(output);
276 push_close_angle_bracket(cpp_like_debuginfo, output);
279 if auto_traits.len() != 0 {
280 push_auto_trait_separator(cpp_like_debuginfo, output);
284 if auto_traits.len() != 0 {
285 let mut auto_traits: SmallVec<[String; 4]> = auto_traits
288 let mut name = String::with_capacity(20);
289 push_item_name(tcx, def_id, true, &mut name);
293 auto_traits.sort_unstable();
295 for auto_trait in auto_traits {
296 output.push_str(&auto_trait);
297 push_auto_trait_separator(cpp_like_debuginfo, output);
300 pop_auto_trait_separator(output);
303 if cpp_like_debuginfo {
304 push_close_angle_bracket(cpp_like_debuginfo, output);
305 } else if has_enclosing_parens {
309 ty::FnDef(..) | ty::FnPtr(_) => {
310 // We've encountered a weird 'recursive type'
311 // Currently, the only way to generate such a type
312 // is by using 'impl trait':
314 // fn foo() -> impl Copy { foo }
316 // There's not really a sensible name we can generate,
317 // since we don't include 'impl trait' types (e.g. ty::Opaque)
320 // Since we need to generate *something*, we just
321 // use a dummy string that should make it clear
322 // that something unusual is going on
323 if !visited.insert(t) {
324 output.push_str(if cpp_like_debuginfo {
333 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx));
335 if cpp_like_debuginfo {
336 // Format as a C++ function pointer: return_type (*)(params...)
337 if sig.output().is_unit() {
338 output.push_str("void");
340 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
342 output.push_str(" (*)(");
344 output.push_str(sig.unsafety.prefix_str());
346 if sig.abi != rustc_target::spec::abi::Abi::Rust {
347 output.push_str("extern \"");
348 output.push_str(sig.abi.name());
349 output.push_str("\" ");
352 output.push_str("fn(");
355 if !sig.inputs().is_empty() {
356 for ¶meter_type in sig.inputs() {
357 push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
358 push_arg_separator(cpp_like_debuginfo, output);
360 pop_arg_separator(output);
364 if !sig.inputs().is_empty() {
365 output.push_str(", ...");
367 output.push_str("...");
373 if !cpp_like_debuginfo && !sig.output().is_unit() {
374 output.push_str(" -> ");
375 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
378 // We only keep the type in 'visited'
379 // for the duration of the body of this method.
380 // It's fine for a particular function type
381 // to show up multiple times in one overall type
382 // (e.g. MyType<fn() -> u8, fn() -> u8>
384 // We only care about avoiding recursing
385 // directly back to the type we're currently
389 ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
390 // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
391 // "{async_fn_env#0}<T1, T2, ...>", etc.
392 // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
393 // an artificial `enum2$<>` type, as defined in msvc_enum_fallback().
394 if cpp_like_debuginfo && t.is_generator() {
395 let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap();
399 push_closure_or_generator_name(tcx, def_id, substs, true, output, visited);
405 push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited);
408 // Type parameters from polymorphized functions.
410 write!(output, "{:?}", t).unwrap();
414 | ty::Placeholder(..)
417 | ty::GeneratorWitnessMIR(..)
418 | ty::GeneratorWitness(..) => {
420 "debuginfo: Trying to create type name for \
421 unexpected type: {:?}",
427 /// MSVC names enums differently than other platforms so that the debugging visualization
428 // format (natvis) is able to understand enums and render the active variant correctly in the
429 // debugger. For more information, look in
430 // rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs.
431 fn msvc_enum_fallback<'tcx>(
432 ty_and_layout: TyAndLayout<'tcx>,
433 push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet<Ty<'tcx>>),
435 visited: &mut FxHashSet<Ty<'tcx>>,
437 debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout));
438 output.push_str("enum2$<");
439 push_inner(output, visited);
440 push_close_angle_bracket(true, output);
443 const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
445 fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) {
446 if cpp_like_debuginfo {
447 push_arg_separator(cpp_like_debuginfo, output);
449 output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
453 fn pop_auto_trait_separator(output: &mut String) {
454 if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
455 output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
457 pop_arg_separator(output);
462 pub enum VTableNameKind {
463 // Is the name for the const/static holding the vtable?
465 // Is the name for the type of the vtable?
469 /// Computes a name for the global variable storing a vtable (or the type of that global variable).
471 /// The name is of the form:
473 /// `<path::to::SomeType as path::to::SomeTrait>::{vtable}`
475 /// or, when generating C++-like names:
477 /// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
479 /// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
480 /// `{vtable}`, so that the type and the corresponding global variable get assigned different
482 pub fn compute_debuginfo_vtable_name<'tcx>(
485 trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
486 kind: VTableNameKind,
488 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
490 let mut vtable_name = String::with_capacity(64);
492 if cpp_like_debuginfo {
493 vtable_name.push_str("impl$<");
495 vtable_name.push('<');
498 let mut visited = FxHashSet::default();
499 push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
501 if cpp_like_debuginfo {
502 vtable_name.push_str(", ");
504 vtable_name.push_str(" as ");
507 if let Some(trait_ref) = trait_ref {
509 tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref);
510 push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
512 push_generic_params_internal(tcx, trait_ref.substs, &mut vtable_name, &mut visited);
514 vtable_name.push('_');
517 push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
519 let suffix = match (cpp_like_debuginfo, kind) {
520 (true, VTableNameKind::GlobalVariable) => "::vtable$",
521 (false, VTableNameKind::GlobalVariable) => "::{vtable}",
522 (true, VTableNameKind::Type) => "::vtable_type$",
523 (false, VTableNameKind::Type) => "::{vtable_type}",
526 vtable_name.reserve_exact(suffix.len());
527 vtable_name.push_str(suffix);
532 pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) {
533 let def_key = tcx.def_key(def_id);
535 if let Some(parent) = def_key.parent {
536 push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
537 output.push_str("::");
541 push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
544 fn generator_kind_label(generator_kind: Option<GeneratorKind>) -> &'static str {
545 match generator_kind {
546 Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block",
547 Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure",
548 Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn",
549 Some(GeneratorKind::Gen) => "generator",
554 fn push_disambiguated_special_name(
557 cpp_like_debuginfo: bool,
560 if cpp_like_debuginfo {
561 write!(output, "{}${}", label, disambiguator).unwrap();
563 write!(output, "{{{}#{}}}", label, disambiguator).unwrap();
567 fn push_unqualified_item_name(
570 disambiguated_data: DisambiguatedDefPathData,
573 match disambiguated_data.data {
574 DefPathData::CrateRoot => {
575 output.push_str(tcx.crate_name(def_id.krate).as_str());
577 DefPathData::ClosureExpr => {
578 let label = generator_kind_label(tcx.generator_kind(def_id));
580 push_disambiguated_special_name(
582 disambiguated_data.disambiguator,
583 cpp_like_debuginfo(tcx),
587 _ => match disambiguated_data.data.name() {
588 DefPathDataName::Named(name) => {
589 output.push_str(name.as_str());
591 DefPathDataName::Anon { namespace } => {
592 push_disambiguated_special_name(
594 disambiguated_data.disambiguator,
595 cpp_like_debuginfo(tcx),
603 fn push_generic_params_internal<'tcx>(
605 substs: SubstsRef<'tcx>,
607 visited: &mut FxHashSet<Ty<'tcx>>,
609 if substs.non_erasable_generics().next().is_none() {
613 debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs));
615 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
619 for type_parameter in substs.non_erasable_generics() {
620 match type_parameter {
621 GenericArgKind::Type(type_parameter) => {
622 push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
624 GenericArgKind::Const(ct) => {
625 push_const_param(tcx, ct, output);
627 other => bug!("Unexpected non-erasable generic: {:?}", other),
630 push_arg_separator(cpp_like_debuginfo, output);
632 pop_arg_separator(output);
633 push_close_angle_bracket(cpp_like_debuginfo, output);
638 fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
640 ty::ConstKind::Param(param) => {
641 write!(output, "{}", param.name)
643 _ => match ct.ty().kind() {
645 let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
646 let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
647 write!(output, "{}", val)
650 let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
651 write!(output, "{}", val)
654 let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap();
655 write!(output, "{}", val)
658 // If we cannot evaluate the constant to a known type, we fall back
659 // to emitting a stable hash value of the constant. This isn't very pretty
660 // but we get a deterministic, virtually unique value for the constant.
662 // Let's only emit 64 bits of the hash value. That should be plenty for
663 // avoiding collisions and will make the emitted type names shorter.
664 let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
665 let mut hasher = StableHasher::new();
666 let ct = ct.eval(tcx, ty::ParamEnv::reveal_all());
667 hcx.while_hashing_spans(false, |hcx| {
668 ct.to_valtree().hash_stable(hcx, &mut hasher)
670 let hash: u64 = hasher.finish();
674 if cpp_like_debuginfo(tcx) {
675 write!(output, "CONST${:x}", hash_short)
677 write!(output, "{{CONST#{:x}}}", hash_short)
685 pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) {
686 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
687 let mut visited = FxHashSet::default();
688 push_generic_params_internal(tcx, substs, output, &mut visited);
691 fn push_closure_or_generator_name<'tcx>(
694 substs: SubstsRef<'tcx>,
697 visited: &mut FxHashSet<Ty<'tcx>>,
699 // Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
700 // "{async_fn_env#0}<T1, T2, ...>", etc.
701 let def_key = tcx.def_key(def_id);
702 let generator_kind = tcx.generator_kind(def_id);
705 let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
706 push_item_name(tcx, parent_def_id, true, output);
707 output.push_str("::");
710 let mut label = String::with_capacity(20);
711 write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap();
713 push_disambiguated_special_name(
715 def_key.disambiguated_data.disambiguator,
716 cpp_like_debuginfo(tcx),
720 // We also need to add the generic arguments of the async fn/generator or
721 // the enclosing function (for closures or async blocks), so that we end
722 // up with a unique name for every instantiation.
724 // Find the generics of the enclosing function, as defined in the source code.
725 let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
726 let generics = tcx.generics_of(enclosing_fn_def_id);
728 // Truncate the substs to the length of the above generics. This will cut off
729 // anything closure- or generator-specific.
730 let substs = substs.truncate_to(tcx, generics);
731 push_generic_params_internal(tcx, substs, output, visited);
734 fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
735 // MSVC debugger always treats `>>` as a shift, even when parsing templates,
736 // so add a space to avoid confusion.
737 if cpp_like_debuginfo && output.ends_with('>') {
744 fn pop_close_angle_bracket(output: &mut String) {
745 assert!(output.ends_with('>'), "'output' does not end with '>': {}", output);
747 if output.ends_with(' ') {
752 fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) {
753 // Natvis does not always like having spaces between parts of the type name
754 // and this causes issues when we need to write a typename in natvis, for example
755 // as part of a cast like the `HashMap` visualizer does.
756 if cpp_like_debuginfo {
759 output.push_str(", ");
763 fn pop_arg_separator(output: &mut String) {
764 if output.ends_with(' ') {
768 assert!(output.ends_with(','));
773 /// Check if we should generate C++ like names and debug information.
774 pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool {
775 tcx.sess.target.is_like_msvc