From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- .../rustc_codegen_ssa/src/debuginfo/type_names.rs | 821 +++++++++++++++++++++ 1 file changed, 821 insertions(+) create mode 100644 compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs (limited to 'compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs') diff --git a/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs new file mode 100644 index 000000000..8cd5a0fc2 --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs @@ -0,0 +1,821 @@ +// Type Names for Debug Info. + +// Notes on targeting MSVC: +// In general, MSVC's debugger attempts to parse all arguments as C++ expressions, +// even if the argument is explicitly a symbol name. +// As such, there are many things that cause parsing issues: +// * `#` is treated as a special character for macros. +// * `{` or `<` at the beginning of a name is treated as an operator. +// * `>>` is always treated as a right-shift. +// * `[` in a name is treated like a regex bracket expression (match any char +// within the brackets). +// * `"` is treated as the start of a string. + +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_hir::def_id::DefId; +use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData}; +use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Mutability}; +use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; +use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; +use rustc_middle::ty::{self, ExistentialProjection, GeneratorSubsts, ParamEnv, Ty, TyCtxt}; +use rustc_target::abi::{Integer, TagEncoding, Variants}; +use smallvec::SmallVec; + +use std::borrow::Cow; +use std::fmt::Write; + +use crate::debuginfo::wants_c_like_enum_debuginfo; + +// Compute the name of the type as it should be stored in debuginfo. Does not do +// any caching, i.e., calling the function twice with the same type will also do +// the work twice. The `qualified` parameter only affects the first level of the +// type name, further levels (i.e., type parameters) are always fully qualified. +pub fn compute_debuginfo_type_name<'tcx>( + tcx: TyCtxt<'tcx>, + t: Ty<'tcx>, + qualified: bool, +) -> String { + let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); + + let mut result = String::with_capacity(64); + let mut visited = FxHashSet::default(); + push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited); + result +} + +// Pushes the name of the type as it should be stored in debuginfo on the +// `output` String. See also compute_debuginfo_type_name(). +fn push_debuginfo_type_name<'tcx>( + tcx: TyCtxt<'tcx>, + t: Ty<'tcx>, + qualified: bool, + output: &mut String, + visited: &mut FxHashSet>, +) { + // When targeting MSVC, emit C++ style type names for compatibility with + // .natvis visualizers (and perhaps other existing native debuggers?) + let cpp_like_debuginfo = cpp_like_debuginfo(tcx); + + match *t.kind() { + ty::Bool => output.push_str("bool"), + ty::Char => output.push_str("char"), + ty::Str => output.push_str("str"), + ty::Never => { + if cpp_like_debuginfo { + output.push_str("never$"); + } else { + output.push('!'); + } + } + ty::Int(int_ty) => output.push_str(int_ty.name_str()), + ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()), + ty::Float(float_ty) => output.push_str(float_ty.name_str()), + ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output), + ty::Adt(def, substs) => { + // `layout_for_cpp_like_fallback` will be `Some` if we want to use the fallback encoding. + let layout_for_cpp_like_fallback = if cpp_like_debuginfo && def.is_enum() { + match tcx.layout_of(ParamEnv::reveal_all().and(t)) { + Ok(layout) => { + if !wants_c_like_enum_debuginfo(layout) { + Some(layout) + } else { + // This is a C-like enum so we don't want to use the fallback encoding + // for the name. + None + } + } + Err(e) => { + // Computing the layout can still fail here, e.g. if the target architecture + // cannot represent the type. See https://github.com/rust-lang/rust/issues/94961. + tcx.sess.fatal(&format!("{}", e)); + } + } + } else { + // We are not emitting cpp-like debuginfo or this isn't even an enum. + None + }; + + if let Some(ty_and_layout) = layout_for_cpp_like_fallback { + msvc_enum_fallback( + tcx, + ty_and_layout, + &|output, visited| { + push_item_name(tcx, def.did(), true, output); + push_generic_params_internal(tcx, substs, output, visited); + }, + output, + visited, + ); + } else { + push_item_name(tcx, def.did(), qualified, output); + push_generic_params_internal(tcx, substs, output, visited); + } + } + ty::Tuple(component_types) => { + if cpp_like_debuginfo { + output.push_str("tuple$<"); + } else { + output.push('('); + } + + for component_type in component_types { + push_debuginfo_type_name(tcx, component_type, true, output, visited); + push_arg_separator(cpp_like_debuginfo, output); + } + if !component_types.is_empty() { + pop_arg_separator(output); + } + + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); + } else { + output.push(')'); + } + } + ty::RawPtr(ty::TypeAndMut { ty: inner_type, mutbl }) => { + if cpp_like_debuginfo { + match mutbl { + Mutability::Not => output.push_str("ptr_const$<"), + Mutability::Mut => output.push_str("ptr_mut$<"), + } + } else { + output.push('*'); + match mutbl { + Mutability::Not => output.push_str("const "), + Mutability::Mut => output.push_str("mut "), + } + } + + push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); + + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); + } + } + ty::Ref(_, inner_type, mutbl) => { + // Slices and `&str` are treated like C++ pointers when computing debug + // info for MSVC debugger. However, wrapping these types' names in a synthetic type + // causes the .natvis engine for WinDbg to fail to display their data, so we opt these + // types out to aid debugging in MSVC. + let is_slice_or_str = matches!(*inner_type.kind(), ty::Slice(_) | ty::Str); + + if !cpp_like_debuginfo { + output.push('&'); + output.push_str(mutbl.prefix_str()); + } else if !is_slice_or_str { + match mutbl { + Mutability::Not => output.push_str("ref$<"), + Mutability::Mut => output.push_str("ref_mut$<"), + } + } + + push_debuginfo_type_name(tcx, inner_type, qualified, output, visited); + + if cpp_like_debuginfo && !is_slice_or_str { + push_close_angle_bracket(cpp_like_debuginfo, output); + } + } + ty::Array(inner_type, len) => { + if cpp_like_debuginfo { + output.push_str("array$<"); + push_debuginfo_type_name(tcx, inner_type, true, output, visited); + match len.kind() { + ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(), + _ => write!(output, ",{}>", len.eval_usize(tcx, ty::ParamEnv::reveal_all())) + .unwrap(), + } + } else { + output.push('['); + push_debuginfo_type_name(tcx, inner_type, true, output, visited); + match len.kind() { + ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(), + _ => write!(output, "; {}]", len.eval_usize(tcx, ty::ParamEnv::reveal_all())) + .unwrap(), + } + } + } + ty::Slice(inner_type) => { + if cpp_like_debuginfo { + output.push_str("slice$<"); + } else { + output.push('['); + } + + push_debuginfo_type_name(tcx, inner_type, true, output, visited); + + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); + } else { + output.push(']'); + } + } + ty::Dynamic(ref trait_data, ..) => { + let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect(); + + let has_enclosing_parens = if cpp_like_debuginfo { + output.push_str("dyn$<"); + false + } else { + if trait_data.len() > 1 && auto_traits.len() != 0 { + // We need enclosing parens because there is more than one trait + output.push_str("(dyn "); + true + } else { + output.push_str("dyn "); + false + } + }; + + if let Some(principal) = trait_data.principal() { + let principal = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), principal); + push_item_name(tcx, principal.def_id, qualified, output); + let principal_has_generic_params = + push_generic_params_internal(tcx, principal.substs, output, visited); + + let projection_bounds: SmallVec<[_; 4]> = trait_data + .projection_bounds() + .map(|bound| { + let ExistentialProjection { item_def_id, term, .. } = + tcx.erase_late_bound_regions(bound); + // FIXME(associated_const_equality): allow for consts here + (item_def_id, term.ty().unwrap()) + }) + .collect(); + + if projection_bounds.len() != 0 { + if principal_has_generic_params { + // push_generic_params_internal() above added a `>` but we actually + // want to add more items to that list, so remove that again... + pop_close_angle_bracket(output); + // .. and add a comma to separate the regular generic args from the + // associated types. + push_arg_separator(cpp_like_debuginfo, output); + } else { + // push_generic_params_internal() did not add `<...>`, so we open + // angle brackets here. + output.push('<'); + } + + for (item_def_id, ty) in projection_bounds { + if cpp_like_debuginfo { + output.push_str("assoc$<"); + push_item_name(tcx, item_def_id, false, output); + push_arg_separator(cpp_like_debuginfo, output); + push_debuginfo_type_name(tcx, ty, true, output, visited); + push_close_angle_bracket(cpp_like_debuginfo, output); + } else { + push_item_name(tcx, item_def_id, false, output); + output.push('='); + push_debuginfo_type_name(tcx, ty, true, output, visited); + } + push_arg_separator(cpp_like_debuginfo, output); + } + + pop_arg_separator(output); + push_close_angle_bracket(cpp_like_debuginfo, output); + } + + if auto_traits.len() != 0 { + push_auto_trait_separator(cpp_like_debuginfo, output); + } + } + + if auto_traits.len() != 0 { + let mut auto_traits: SmallVec<[String; 4]> = auto_traits + .into_iter() + .map(|def_id| { + let mut name = String::with_capacity(20); + push_item_name(tcx, def_id, true, &mut name); + name + }) + .collect(); + auto_traits.sort_unstable(); + + for auto_trait in auto_traits { + output.push_str(&auto_trait); + push_auto_trait_separator(cpp_like_debuginfo, output); + } + + pop_auto_trait_separator(output); + } + + if cpp_like_debuginfo { + push_close_angle_bracket(cpp_like_debuginfo, output); + } else if has_enclosing_parens { + output.push(')'); + } + } + ty::FnDef(..) | ty::FnPtr(_) => { + // We've encountered a weird 'recursive type' + // Currently, the only way to generate such a type + // is by using 'impl trait': + // + // fn foo() -> impl Copy { foo } + // + // There's not really a sensible name we can generate, + // since we don't include 'impl trait' types (e.g. ty::Opaque) + // in the output + // + // Since we need to generate *something*, we just + // use a dummy string that should make it clear + // that something unusual is going on + if !visited.insert(t) { + output.push_str(if cpp_like_debuginfo { + "recursive_type$" + } else { + "" + }); + return; + } + + let sig = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), t.fn_sig(tcx)); + + if cpp_like_debuginfo { + // Format as a C++ function pointer: return_type (*)(params...) + if sig.output().is_unit() { + output.push_str("void"); + } else { + push_debuginfo_type_name(tcx, sig.output(), true, output, visited); + } + output.push_str(" (*)("); + } else { + output.push_str(sig.unsafety.prefix_str()); + + if sig.abi != rustc_target::spec::abi::Abi::Rust { + output.push_str("extern \""); + output.push_str(sig.abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + } + + if !sig.inputs().is_empty() { + for ¶meter_type in sig.inputs() { + push_debuginfo_type_name(tcx, parameter_type, true, output, visited); + push_arg_separator(cpp_like_debuginfo, output); + } + pop_arg_separator(output); + } + + if sig.c_variadic { + if !sig.inputs().is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + if !cpp_like_debuginfo && !sig.output().is_unit() { + output.push_str(" -> "); + push_debuginfo_type_name(tcx, sig.output(), true, output, visited); + } + + // We only keep the type in 'visited' + // for the duration of the body of this method. + // It's fine for a particular function type + // to show up multiple times in one overall type + // (e.g. MyType u8, fn() -> u8> + // + // We only care about avoiding recursing + // directly back to the type we're currently + // processing + visited.remove(&t); + } + ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => { + // Name will be "{closure_env#0}", "{generator_env#0}", or + // "{async_fn_env#0}", etc. + // In the case of cpp-like debuginfo, the name additionally gets wrapped inside of + // an artificial `enum$<>` type, as defined in msvc_enum_fallback(). + if cpp_like_debuginfo && t.is_generator() { + let ty_and_layout = tcx.layout_of(ParamEnv::reveal_all().and(t)).unwrap(); + msvc_enum_fallback( + tcx, + ty_and_layout, + &|output, visited| { + push_closure_or_generator_name(tcx, def_id, substs, true, output, visited); + }, + output, + visited, + ); + } else { + push_closure_or_generator_name(tcx, def_id, substs, qualified, output, visited); + } + } + // Type parameters from polymorphized functions. + ty::Param(_) => { + write!(output, "{:?}", t).unwrap(); + } + ty::Error(_) + | ty::Infer(_) + | ty::Placeholder(..) + | ty::Projection(..) + | ty::Bound(..) + | ty::Opaque(..) + | ty::GeneratorWitness(..) => { + bug!( + "debuginfo: Trying to create type name for \ + unexpected type: {:?}", + t + ); + } + } + + /// MSVC names enums differently than other platforms so that the debugging visualization + // format (natvis) is able to understand enums and render the active variant correctly in the + // debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and + // `EnumMemberDescriptionFactor::create_member_descriptions`. + fn msvc_enum_fallback<'tcx>( + tcx: TyCtxt<'tcx>, + ty_and_layout: TyAndLayout<'tcx>, + push_inner: &dyn Fn(/*output*/ &mut String, /*visited*/ &mut FxHashSet>), + output: &mut String, + visited: &mut FxHashSet>, + ) { + debug_assert!(!wants_c_like_enum_debuginfo(ty_and_layout)); + let ty = ty_and_layout.ty; + + output.push_str("enum$<"); + push_inner(output, visited); + + let variant_name = |variant_index| match ty.kind() { + ty::Adt(adt_def, _) => { + debug_assert!(adt_def.is_enum()); + Cow::from(adt_def.variant(variant_index).name.as_str()) + } + ty::Generator(..) => GeneratorSubsts::variant_name(variant_index), + _ => unreachable!(), + }; + + if let Variants::Multiple { + tag_encoding: TagEncoding::Niche { dataful_variant, .. }, + tag, + variants, + .. + } = &ty_and_layout.variants + { + let dataful_variant_layout = &variants[*dataful_variant]; + + // calculate the range of values for the dataful variant + let dataful_discriminant_range = + dataful_variant_layout.largest_niche().unwrap().valid_range; + + let min = dataful_discriminant_range.start; + let min = tag.size(&tcx).truncate(min); + + let max = dataful_discriminant_range.end; + let max = tag.size(&tcx).truncate(max); + + let dataful_variant_name = variant_name(*dataful_variant); + write!(output, ", {}, {}, {}", min, max, dataful_variant_name).unwrap(); + } else if let Variants::Single { index: variant_idx } = &ty_and_layout.variants { + // Uninhabited enums can't be constructed and should never need to be visualized so + // skip this step for them. + if !ty_and_layout.abi.is_uninhabited() { + write!(output, ", {}", variant_name(*variant_idx)).unwrap(); + } + } + push_close_angle_bracket(true, output); + } + + const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + "; + + fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) { + if cpp_like_debuginfo { + push_arg_separator(cpp_like_debuginfo, output); + } else { + output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR); + } + } + + fn pop_auto_trait_separator(output: &mut String) { + if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) { + output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len()); + } else { + pop_arg_separator(output); + } + } +} + +pub enum VTableNameKind { + // Is the name for the const/static holding the vtable? + GlobalVariable, + // Is the name for the type of the vtable? + Type, +} + +/// Computes a name for the global variable storing a vtable (or the type of that global variable). +/// +/// The name is of the form: +/// +/// `::{vtable}` +/// +/// or, when generating C++-like names: +/// +/// `impl$::vtable$` +/// +/// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just +/// `{vtable}`, so that the type and the corresponding global variable get assigned different +/// names. +pub fn compute_debuginfo_vtable_name<'tcx>( + tcx: TyCtxt<'tcx>, + t: Ty<'tcx>, + trait_ref: Option>, + kind: VTableNameKind, +) -> String { + let cpp_like_debuginfo = cpp_like_debuginfo(tcx); + + let mut vtable_name = String::with_capacity(64); + + if cpp_like_debuginfo { + vtable_name.push_str("impl$<"); + } else { + vtable_name.push('<'); + } + + let mut visited = FxHashSet::default(); + push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited); + + if cpp_like_debuginfo { + vtable_name.push_str(", "); + } else { + vtable_name.push_str(" as "); + } + + if let Some(trait_ref) = trait_ref { + let trait_ref = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), trait_ref); + push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name); + visited.clear(); + push_generic_params_internal(tcx, trait_ref.substs, &mut vtable_name, &mut visited); + } else { + vtable_name.push_str("_"); + } + + push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name); + + let suffix = match (cpp_like_debuginfo, kind) { + (true, VTableNameKind::GlobalVariable) => "::vtable$", + (false, VTableNameKind::GlobalVariable) => "::{vtable}", + (true, VTableNameKind::Type) => "::vtable_type$", + (false, VTableNameKind::Type) => "::{vtable_type}", + }; + + vtable_name.reserve_exact(suffix.len()); + vtable_name.push_str(suffix); + + vtable_name +} + +pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) { + let def_key = tcx.def_key(def_id); + if qualified { + if let Some(parent) = def_key.parent { + push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output); + output.push_str("::"); + } + } + + push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output); +} + +fn generator_kind_label(generator_kind: Option) -> &'static str { + match generator_kind { + Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) => "async_block", + Some(GeneratorKind::Async(AsyncGeneratorKind::Closure)) => "async_closure", + Some(GeneratorKind::Async(AsyncGeneratorKind::Fn)) => "async_fn", + Some(GeneratorKind::Gen) => "generator", + None => "closure", + } +} + +fn push_disambiguated_special_name( + label: &str, + disambiguator: u32, + cpp_like_debuginfo: bool, + output: &mut String, +) { + if cpp_like_debuginfo { + write!(output, "{}${}", label, disambiguator).unwrap(); + } else { + write!(output, "{{{}#{}}}", label, disambiguator).unwrap(); + } +} + +fn push_unqualified_item_name( + tcx: TyCtxt<'_>, + def_id: DefId, + disambiguated_data: DisambiguatedDefPathData, + output: &mut String, +) { + match disambiguated_data.data { + DefPathData::CrateRoot => { + output.push_str(tcx.crate_name(def_id.krate).as_str()); + } + DefPathData::ClosureExpr => { + let label = generator_kind_label(tcx.generator_kind(def_id)); + + push_disambiguated_special_name( + label, + disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); + } + _ => match disambiguated_data.data.name() { + DefPathDataName::Named(name) => { + output.push_str(name.as_str()); + } + DefPathDataName::Anon { namespace } => { + push_disambiguated_special_name( + namespace.as_str(), + disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); + } + }, + }; +} + +fn push_generic_params_internal<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, + output: &mut String, + visited: &mut FxHashSet>, +) -> bool { + if substs.non_erasable_generics().next().is_none() { + return false; + } + + debug_assert_eq!(substs, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), substs)); + + let cpp_like_debuginfo = cpp_like_debuginfo(tcx); + + output.push('<'); + + for type_parameter in substs.non_erasable_generics() { + match type_parameter { + GenericArgKind::Type(type_parameter) => { + push_debuginfo_type_name(tcx, type_parameter, true, output, visited); + } + GenericArgKind::Const(ct) => { + push_const_param(tcx, ct, output); + } + other => bug!("Unexpected non-erasable generic: {:?}", other), + } + + push_arg_separator(cpp_like_debuginfo, output); + } + pop_arg_separator(output); + push_close_angle_bracket(cpp_like_debuginfo, output); + + true +} + +fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) { + match ct.kind() { + ty::ConstKind::Param(param) => { + write!(output, "{}", param.name) + } + _ => match ct.ty().kind() { + ty::Int(ity) => { + let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); + let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128; + write!(output, "{}", val) + } + ty::Uint(_) => { + let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty()); + write!(output, "{}", val) + } + ty::Bool => { + let val = ct.try_eval_bool(tcx, ty::ParamEnv::reveal_all()).unwrap(); + write!(output, "{}", val) + } + _ => { + // If we cannot evaluate the constant to a known type, we fall back + // to emitting a stable hash value of the constant. This isn't very pretty + // but we get a deterministic, virtually unique value for the constant. + // + // Let's only emit 64 bits of the hash value. That should be plenty for + // avoiding collisions and will make the emitted type names shorter. + let hash_short = tcx.with_stable_hashing_context(|mut hcx| { + let mut hasher = StableHasher::new(); + let ct = ct.eval(tcx, ty::ParamEnv::reveal_all()); + hcx.while_hashing_spans(false, |hcx| { + ct.to_valtree().hash_stable(hcx, &mut hasher) + }); + // Note: Don't use `StableHashResult` impl of `u64` here directly, since that + // would lead to endianness problems. + let hash: u128 = hasher.finish(); + (hash.to_le() as u64).to_le() + }); + + if cpp_like_debuginfo(tcx) { + write!(output, "CONST${:x}", hash_short) + } else { + write!(output, "{{CONST#{:x}}}", hash_short) + } + } + }, + } + .unwrap(); +} + +pub fn push_generic_params<'tcx>(tcx: TyCtxt<'tcx>, substs: SubstsRef<'tcx>, output: &mut String) { + let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name"); + let mut visited = FxHashSet::default(); + push_generic_params_internal(tcx, substs, output, &mut visited); +} + +fn push_closure_or_generator_name<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + qualified: bool, + output: &mut String, + visited: &mut FxHashSet>, +) { + // Name will be "{closure_env#0}", "{generator_env#0}", or + // "{async_fn_env#0}", etc. + let def_key = tcx.def_key(def_id); + let generator_kind = tcx.generator_kind(def_id); + + if qualified { + let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id }; + push_item_name(tcx, parent_def_id, true, output); + output.push_str("::"); + } + + let mut label = String::with_capacity(20); + write!(&mut label, "{}_env", generator_kind_label(generator_kind)).unwrap(); + + push_disambiguated_special_name( + &label, + def_key.disambiguated_data.disambiguator, + cpp_like_debuginfo(tcx), + output, + ); + + // We also need to add the generic arguments of the async fn/generator or + // the enclosing function (for closures or async blocks), so that we end + // up with a unique name for every instantiation. + + // Find the generics of the enclosing function, as defined in the source code. + let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id); + let generics = tcx.generics_of(enclosing_fn_def_id); + + // Truncate the substs to the length of the above generics. This will cut off + // anything closure- or generator-specific. + let substs = substs.truncate_to(tcx, generics); + push_generic_params_internal(tcx, substs, output, visited); +} + +fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) { + // MSVC debugger always treats `>>` as a shift, even when parsing templates, + // so add a space to avoid confusion. + if cpp_like_debuginfo && output.ends_with('>') { + output.push(' ') + }; + + output.push('>'); +} + +fn pop_close_angle_bracket(output: &mut String) { + assert!(output.ends_with('>'), "'output' does not end with '>': {}", output); + output.pop(); + if output.ends_with(' ') { + output.pop(); + } +} + +fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) { + // Natvis does not always like having spaces between parts of the type name + // and this causes issues when we need to write a typename in natvis, for example + // as part of a cast like the `HashMap` visualizer does. + if cpp_like_debuginfo { + output.push(','); + } else { + output.push_str(", "); + }; +} + +fn pop_arg_separator(output: &mut String) { + if output.ends_with(' ') { + output.pop(); + } + + assert!(output.ends_with(',')); + + output.pop(); +} + +/// Check if we should generate C++ like names and debug information. +pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool { + tcx.sess.target.is_like_msvc +} -- cgit v1.2.3