1use std::fmt::Write;
15
16use rustc_abi::Integer;
17use rustc_data_structures::fx::FxHashSet;
18use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
19use rustc_hashes::Hash64;
20use rustc_hir::def_id::DefId;
21use rustc_hir::definitions::{DefPathData, DefPathDataName, DisambiguatedDefPathData};
22use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Mutability};
23use rustc_middle::bug;
24use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
25use rustc_middle::ty::{self, ExistentialProjection, GenericArgKind, GenericArgsRef, Ty, TyCtxt};
26use smallvec::SmallVec;
27
28use crate::debuginfo::wants_c_like_enum_debuginfo;
29
30pub fn compute_debuginfo_type_name<'tcx>(
35 tcx: TyCtxt<'tcx>,
36 t: Ty<'tcx>,
37 qualified: bool,
38) -> String {
39 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
40
41 let mut result = String::with_capacity(64);
42 let mut visited = FxHashSet::default();
43 push_debuginfo_type_name(tcx, t, qualified, &mut result, &mut visited);
44 result
45}
46
47fn push_debuginfo_type_name<'tcx>(
50 tcx: TyCtxt<'tcx>,
51 t: Ty<'tcx>,
52 qualified: bool,
53 output: &mut String,
54 visited: &mut FxHashSet<Ty<'tcx>>,
55) {
56 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
59
60 match *t.kind() {
61 ty::Bool => output.push_str("bool"),
62 ty::Char => output.push_str("char"),
63 ty::Str => {
64 if cpp_like_debuginfo {
65 output.push_str("str$")
66 } else {
67 output.push_str("str")
68 }
69 }
70 ty::Never => {
71 if cpp_like_debuginfo {
72 output.push_str("never$");
73 } else {
74 output.push('!');
75 }
76 }
77 ty::Int(int_ty) => output.push_str(int_ty.name_str()),
78 ty::Uint(uint_ty) => output.push_str(uint_ty.name_str()),
79 ty::Float(float_ty) => output.push_str(float_ty.name_str()),
80 ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
81 ty::Adt(def, args) => {
82 let layout_for_cpp_like_fallback = if cpp_like_debuginfo && def.is_enum() {
84 match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(t)) {
85 Ok(layout) => {
86 if !wants_c_like_enum_debuginfo(tcx, layout) {
87 Some(layout)
88 } else {
89 None
92 }
93 }
94 Err(e) => {
95 tcx.dcx().emit_fatal(e.into_diagnostic());
99 }
100 }
101 } else {
102 None
104 };
105
106 if let Some(ty_and_layout) = layout_for_cpp_like_fallback {
107 msvc_enum_fallback(
108 tcx,
109 ty_and_layout,
110 &|output, visited| {
111 push_item_name(tcx, def.did(), true, output);
112 push_generic_params_internal(tcx, args, output, visited);
113 },
114 output,
115 visited,
116 );
117 } else {
118 push_item_name(tcx, def.did(), qualified, output);
119 push_generic_params_internal(tcx, args, output, visited);
120 }
121 }
122 ty::Tuple(component_types) => {
123 if cpp_like_debuginfo {
124 output.push_str("tuple$<");
125 } else {
126 output.push('(');
127 }
128
129 for component_type in component_types {
130 push_debuginfo_type_name(tcx, component_type, true, output, visited);
131 push_arg_separator(cpp_like_debuginfo, output);
132 }
133 if !component_types.is_empty() {
134 pop_arg_separator(output);
135 }
136
137 if cpp_like_debuginfo {
138 push_close_angle_bracket(cpp_like_debuginfo, output);
139 } else {
140 output.push(')');
141 }
142 }
143 ty::RawPtr(inner_type, mutbl) => {
144 if cpp_like_debuginfo {
145 match mutbl {
146 Mutability::Not => output.push_str("ptr_const$<"),
147 Mutability::Mut => output.push_str("ptr_mut$<"),
148 }
149 } else {
150 output.push('*');
151 match mutbl {
152 Mutability::Not => output.push_str("const "),
153 Mutability::Mut => output.push_str("mut "),
154 }
155 }
156
157 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
158
159 if cpp_like_debuginfo {
160 push_close_angle_bracket(cpp_like_debuginfo, output);
161 }
162 }
163 ty::Ref(_, inner_type, mutbl) => {
164 if cpp_like_debuginfo {
165 match mutbl {
166 Mutability::Not => output.push_str("ref$<"),
167 Mutability::Mut => output.push_str("ref_mut$<"),
168 }
169 } else {
170 output.push('&');
171 output.push_str(mutbl.prefix_str());
172 }
173
174 push_debuginfo_type_name(tcx, inner_type, qualified, output, visited);
175
176 if cpp_like_debuginfo {
177 push_close_angle_bracket(cpp_like_debuginfo, output);
178 }
179 }
180 ty::Array(inner_type, len) => {
181 if cpp_like_debuginfo {
182 output.push_str("array$<");
183 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
184 match len.kind() {
185 ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(),
186 _ => write!(
187 output,
188 ",{}>",
189 len.try_to_target_usize(tcx)
190 .expect("expected monomorphic const in codegen")
191 )
192 .unwrap(),
193 }
194 } else {
195 output.push('[');
196 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
197 match len.kind() {
198 ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(),
199 _ => write!(
200 output,
201 "; {}]",
202 len.try_to_target_usize(tcx)
203 .expect("expected monomorphic const in codegen")
204 )
205 .unwrap(),
206 }
207 }
208 }
209 ty::Pat(inner_type, pat) => {
210 if cpp_like_debuginfo {
211 output.push_str("pat$<");
212 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
213 write!(output, ",{:?}>", pat).unwrap();
215 } else {
216 write!(output, "{:?}", t).unwrap();
217 }
218 }
219 ty::Slice(inner_type) => {
220 if cpp_like_debuginfo {
221 output.push_str("slice2$<");
222 } else {
223 output.push('[');
224 }
225
226 push_debuginfo_type_name(tcx, inner_type, true, output, visited);
227
228 if cpp_like_debuginfo {
229 push_close_angle_bracket(cpp_like_debuginfo, output);
230 } else {
231 output.push(']');
232 }
233 }
234 ty::Dynamic(trait_data, ..) => {
235 let auto_traits: SmallVec<[DefId; 4]> = trait_data.auto_traits().collect();
236
237 let has_enclosing_parens = if cpp_like_debuginfo {
238 output.push_str("dyn$<");
239 false
240 } else if trait_data.len() > 1 && auto_traits.len() != 0 {
241 output.push_str("(dyn ");
243 true
244 } else {
245 output.push_str("dyn ");
246 false
247 };
248
249 if let Some(principal) = trait_data.principal() {
250 let principal = tcx.normalize_erasing_late_bound_regions(
251 ty::TypingEnv::fully_monomorphized(),
252 principal,
253 );
254 push_item_name(tcx, principal.def_id, qualified, output);
255 let principal_has_generic_params =
256 push_generic_params_internal(tcx, principal.args, output, visited);
257
258 let projection_bounds: SmallVec<[_; 4]> = trait_data
259 .projection_bounds()
260 .map(|bound| {
261 let ExistentialProjection { def_id: item_def_id, term, .. } =
262 tcx.instantiate_bound_regions_with_erased(bound);
263 (item_def_id, term.expect_type())
265 })
266 .collect();
267
268 if projection_bounds.len() != 0 {
269 if principal_has_generic_params {
270 pop_close_angle_bracket(output);
273 push_arg_separator(cpp_like_debuginfo, output);
276 } else {
277 output.push('<');
280 }
281
282 for (item_def_id, ty) in projection_bounds {
283 if cpp_like_debuginfo {
284 output.push_str("assoc$<");
285 push_item_name(tcx, item_def_id, false, output);
286 push_arg_separator(cpp_like_debuginfo, output);
287 push_debuginfo_type_name(tcx, ty, true, output, visited);
288 push_close_angle_bracket(cpp_like_debuginfo, output);
289 } else {
290 push_item_name(tcx, item_def_id, false, output);
291 output.push('=');
292 push_debuginfo_type_name(tcx, ty, true, output, visited);
293 }
294 push_arg_separator(cpp_like_debuginfo, output);
295 }
296
297 pop_arg_separator(output);
298 push_close_angle_bracket(cpp_like_debuginfo, output);
299 }
300
301 if auto_traits.len() != 0 {
302 push_auto_trait_separator(cpp_like_debuginfo, output);
303 }
304 }
305
306 if auto_traits.len() != 0 {
307 let mut auto_traits: SmallVec<[String; 4]> = auto_traits
308 .into_iter()
309 .map(|def_id| {
310 let mut name = String::with_capacity(20);
311 push_item_name(tcx, def_id, true, &mut name);
312 name
313 })
314 .collect();
315 auto_traits.sort_unstable();
316
317 for auto_trait in auto_traits {
318 output.push_str(&auto_trait);
319 push_auto_trait_separator(cpp_like_debuginfo, output);
320 }
321
322 pop_auto_trait_separator(output);
323 }
324
325 if cpp_like_debuginfo {
326 push_close_angle_bracket(cpp_like_debuginfo, output);
327 } else if has_enclosing_parens {
328 output.push(')');
329 }
330 }
331 ty::FnDef(..) | ty::FnPtr(..) => {
332 if !visited.insert(t) {
346 output.push_str(if cpp_like_debuginfo {
347 "recursive_type$"
348 } else {
349 "<recursive_type>"
350 });
351 return;
352 }
353
354 let sig = tcx.normalize_erasing_late_bound_regions(
355 ty::TypingEnv::fully_monomorphized(),
356 t.fn_sig(tcx),
357 );
358
359 if cpp_like_debuginfo {
360 if sig.output().is_unit() {
362 output.push_str("void");
363 } else {
364 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
365 }
366 output.push_str(" (*)(");
367 } else {
368 output.push_str(sig.safety.prefix_str());
369
370 if sig.abi != rustc_abi::ExternAbi::Rust {
371 let _ = write!(output, "extern {} ", sig.abi);
372 }
373
374 output.push_str("fn(");
375 }
376
377 if !sig.inputs().is_empty() {
378 for ¶meter_type in sig.inputs() {
379 push_debuginfo_type_name(tcx, parameter_type, true, output, visited);
380 push_arg_separator(cpp_like_debuginfo, output);
381 }
382 pop_arg_separator(output);
383 }
384
385 if sig.c_variadic {
386 if !sig.inputs().is_empty() {
387 output.push_str(", ...");
388 } else {
389 output.push_str("...");
390 }
391 }
392
393 output.push(')');
394
395 if !cpp_like_debuginfo && !sig.output().is_unit() {
396 output.push_str(" -> ");
397 push_debuginfo_type_name(tcx, sig.output(), true, output, visited);
398 }
399
400 visited.remove(&t);
410 }
411 ty::Closure(def_id, args)
412 | ty::CoroutineClosure(def_id, args)
413 | ty::Coroutine(def_id, args, ..) => {
414 if cpp_like_debuginfo && t.is_coroutine() {
419 let ty_and_layout =
420 tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(t)).unwrap();
421 msvc_enum_fallback(
422 tcx,
423 ty_and_layout,
424 &|output, visited| {
425 push_closure_or_coroutine_name(tcx, def_id, args, true, output, visited);
426 },
427 output,
428 visited,
429 );
430 } else {
431 push_closure_or_coroutine_name(tcx, def_id, args, qualified, output, visited);
432 }
433 }
434 ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binders)"),
435 ty::Param(_)
436 | ty::Error(_)
437 | ty::Infer(_)
438 | ty::Placeholder(..)
439 | ty::Alias(..)
440 | ty::Bound(..)
441 | ty::CoroutineWitness(..) => {
442 bug!(
443 "debuginfo: Trying to create type name for \
444 unexpected type: {:?}",
445 t
446 );
447 }
448 }
449
450 fn msvc_enum_fallback<'tcx>(
455 tcx: TyCtxt<'tcx>,
456 ty_and_layout: TyAndLayout<'tcx>,
457 push_inner: &dyn Fn(&mut String, &mut FxHashSet<Ty<'tcx>>),
458 output: &mut String,
459 visited: &mut FxHashSet<Ty<'tcx>>,
460 ) {
461 assert!(!wants_c_like_enum_debuginfo(tcx, ty_and_layout));
462 output.push_str("enum2$<");
463 push_inner(output, visited);
464 push_close_angle_bracket(true, output);
465 }
466
467 const NON_CPP_AUTO_TRAIT_SEPARATOR: &str = " + ";
468
469 fn push_auto_trait_separator(cpp_like_debuginfo: bool, output: &mut String) {
470 if cpp_like_debuginfo {
471 push_arg_separator(cpp_like_debuginfo, output);
472 } else {
473 output.push_str(NON_CPP_AUTO_TRAIT_SEPARATOR);
474 }
475 }
476
477 fn pop_auto_trait_separator(output: &mut String) {
478 if output.ends_with(NON_CPP_AUTO_TRAIT_SEPARATOR) {
479 output.truncate(output.len() - NON_CPP_AUTO_TRAIT_SEPARATOR.len());
480 } else {
481 pop_arg_separator(output);
482 }
483 }
484}
485
486pub enum VTableNameKind {
487 GlobalVariable,
489 Type,
491}
492
493pub fn compute_debuginfo_vtable_name<'tcx>(
507 tcx: TyCtxt<'tcx>,
508 t: Ty<'tcx>,
509 trait_ref: Option<ty::ExistentialTraitRef<'tcx>>,
510 kind: VTableNameKind,
511) -> String {
512 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
513
514 let mut vtable_name = String::with_capacity(64);
515
516 if cpp_like_debuginfo {
517 vtable_name.push_str("impl$<");
518 } else {
519 vtable_name.push('<');
520 }
521
522 let mut visited = FxHashSet::default();
523 push_debuginfo_type_name(tcx, t, true, &mut vtable_name, &mut visited);
524
525 if cpp_like_debuginfo {
526 vtable_name.push_str(", ");
527 } else {
528 vtable_name.push_str(" as ");
529 }
530
531 if let Some(trait_ref) = trait_ref {
532 let trait_ref =
533 tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), trait_ref);
534 push_item_name(tcx, trait_ref.def_id, true, &mut vtable_name);
535 visited.clear();
536 push_generic_params_internal(tcx, trait_ref.args, &mut vtable_name, &mut visited);
537 } else {
538 vtable_name.push('_');
539 }
540
541 push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
542
543 let suffix = match (cpp_like_debuginfo, kind) {
544 (true, VTableNameKind::GlobalVariable) => "::vtable$",
545 (false, VTableNameKind::GlobalVariable) => "::{vtable}",
546 (true, VTableNameKind::Type) => "::vtable_type$",
547 (false, VTableNameKind::Type) => "::{vtable_type}",
548 };
549
550 vtable_name.reserve_exact(suffix.len());
551 vtable_name.push_str(suffix);
552
553 vtable_name
554}
555
556pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &mut String) {
557 let def_key = tcx.def_key(def_id);
558 if qualified {
559 if let Some(parent) = def_key.parent {
560 push_item_name(tcx, DefId { krate: def_id.krate, index: parent }, true, output);
561 output.push_str("::");
562 }
563 }
564
565 push_unqualified_item_name(tcx, def_id, def_key.disambiguated_data, output);
566}
567
568fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
569 use CoroutineDesugaring::*;
570 use CoroutineKind::*;
571 use CoroutineSource::*;
572 match coroutine_kind {
573 Some(Desugared(Gen, Block)) => "gen_block",
574 Some(Desugared(Gen, Closure)) => "gen_closure",
575 Some(Desugared(Gen, Fn)) => "gen_fn",
576 Some(Desugared(Async, Block)) => "async_block",
577 Some(Desugared(Async, Closure)) => "async_closure",
578 Some(Desugared(Async, Fn)) => "async_fn",
579 Some(Desugared(AsyncGen, Block)) => "async_gen_block",
580 Some(Desugared(AsyncGen, Closure)) => "async_gen_closure",
581 Some(Desugared(AsyncGen, Fn)) => "async_gen_fn",
582 Some(Coroutine(_)) => "coroutine",
583 None => "closure",
584 }
585}
586
587fn push_disambiguated_special_name(
588 label: &str,
589 disambiguator: u32,
590 cpp_like_debuginfo: bool,
591 output: &mut String,
592) {
593 if cpp_like_debuginfo {
594 write!(output, "{label}${disambiguator}").unwrap();
595 } else {
596 write!(output, "{{{label}#{disambiguator}}}").unwrap();
597 }
598}
599
600fn push_unqualified_item_name(
601 tcx: TyCtxt<'_>,
602 def_id: DefId,
603 disambiguated_data: DisambiguatedDefPathData,
604 output: &mut String,
605) {
606 match disambiguated_data.data {
607 DefPathData::CrateRoot => {
608 output.push_str(tcx.crate_name(def_id.krate).as_str());
609 }
610 DefPathData::Closure => {
611 let label = coroutine_kind_label(tcx.coroutine_kind(def_id));
612
613 push_disambiguated_special_name(
614 label,
615 disambiguated_data.disambiguator,
616 cpp_like_debuginfo(tcx),
617 output,
618 );
619 }
620 _ => match disambiguated_data.data.name() {
621 DefPathDataName::Named(name) => {
622 output.push_str(name.as_str());
623 }
624 DefPathDataName::Anon { namespace } => {
625 push_disambiguated_special_name(
626 namespace.as_str(),
627 disambiguated_data.disambiguator,
628 cpp_like_debuginfo(tcx),
629 output,
630 );
631 }
632 },
633 };
634}
635
636fn push_generic_params_internal<'tcx>(
637 tcx: TyCtxt<'tcx>,
638 args: GenericArgsRef<'tcx>,
639 output: &mut String,
640 visited: &mut FxHashSet<Ty<'tcx>>,
641) -> bool {
642 assert_eq!(args, tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), args));
643 let mut args = args.non_erasable_generics().peekable();
644 if args.peek().is_none() {
645 return false;
646 }
647 let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
648
649 output.push('<');
650
651 for type_parameter in args {
652 match type_parameter {
653 GenericArgKind::Type(type_parameter) => {
654 push_debuginfo_type_name(tcx, type_parameter, true, output, visited);
655 }
656 GenericArgKind::Const(ct) => {
657 push_const_param(tcx, ct, output);
658 }
659 other => bug!("Unexpected non-erasable generic: {:?}", other),
660 }
661
662 push_arg_separator(cpp_like_debuginfo, output);
663 }
664 pop_arg_separator(output);
665 push_close_angle_bracket(cpp_like_debuginfo, output);
666
667 true
668}
669
670fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
671 match ct.kind() {
672 ty::ConstKind::Param(param) => {
673 write!(output, "{}", param.name)
674 }
675 ty::ConstKind::Value(cv) => {
676 match cv.ty.kind() {
677 ty::Int(ity) => {
678 let bits = cv
679 .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
680 .expect("expected monomorphic const in codegen");
681 let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
682 write!(output, "{val}")
683 }
684 ty::Uint(_) => {
685 let val = cv
686 .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
687 .expect("expected monomorphic const in codegen");
688 write!(output, "{val}")
689 }
690 ty::Bool => {
691 let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
692 write!(output, "{val}")
693 }
694 _ => {
695 let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
702 let mut hasher = StableHasher::new();
703 hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
704 hasher.finish::<Hash64>()
705 });
706
707 if cpp_like_debuginfo(tcx) {
708 write!(output, "CONST${hash_short:x}")
709 } else {
710 write!(output, "{{CONST#{hash_short:x}}}")
711 }
712 }
713 }
714 }
715 _ => bug!("Invalid `Const` during codegen: {:?}", ct),
716 }
717 .unwrap();
718}
719
720pub fn push_generic_params<'tcx>(
721 tcx: TyCtxt<'tcx>,
722 args: GenericArgsRef<'tcx>,
723 output: &mut String,
724) {
725 let _prof = tcx.prof.generic_activity("compute_debuginfo_type_name");
726 let mut visited = FxHashSet::default();
727 push_generic_params_internal(tcx, args, output, &mut visited);
728}
729
730fn push_closure_or_coroutine_name<'tcx>(
731 tcx: TyCtxt<'tcx>,
732 def_id: DefId,
733 args: GenericArgsRef<'tcx>,
734 qualified: bool,
735 output: &mut String,
736 visited: &mut FxHashSet<Ty<'tcx>>,
737) {
738 let def_key = tcx.def_key(def_id);
741 let coroutine_kind = tcx.coroutine_kind(def_id);
742
743 if qualified {
744 let parent_def_id = DefId { index: def_key.parent.unwrap(), ..def_id };
745 push_item_name(tcx, parent_def_id, true, output);
746 output.push_str("::");
747 }
748
749 let mut label = String::with_capacity(20);
750 write!(&mut label, "{}_env", coroutine_kind_label(coroutine_kind)).unwrap();
751
752 push_disambiguated_special_name(
753 &label,
754 def_key.disambiguated_data.disambiguator,
755 cpp_like_debuginfo(tcx),
756 output,
757 );
758
759 let enclosing_fn_def_id = tcx.typeck_root_def_id(def_id);
765 let generics = tcx.generics_of(enclosing_fn_def_id);
766
767 let args = args.truncate_to(tcx, generics);
772 push_generic_params_internal(tcx, args, output, visited);
773}
774
775fn push_close_angle_bracket(cpp_like_debuginfo: bool, output: &mut String) {
776 if cpp_like_debuginfo && output.ends_with('>') {
779 output.push(' ')
780 };
781
782 output.push('>');
783}
784
785fn pop_close_angle_bracket(output: &mut String) {
786 assert!(output.ends_with('>'), "'output' does not end with '>': {output}");
787 output.pop();
788 if output.ends_with(' ') {
789 output.pop();
790 }
791}
792
793fn push_arg_separator(cpp_like_debuginfo: bool, output: &mut String) {
794 if cpp_like_debuginfo {
798 output.push(',');
799 } else {
800 output.push_str(", ");
801 };
802}
803
804fn pop_arg_separator(output: &mut String) {
805 if output.ends_with(' ') {
806 output.pop();
807 }
808
809 assert!(output.ends_with(','));
810
811 output.pop();
812}
813
814pub fn cpp_like_debuginfo(tcx: TyCtxt<'_>) -> bool {
816 tcx.sess.target.is_like_msvc
817}