1use std::fmt::{self, Write};
2use std::mem::{self, discriminant};
3
4use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
5use rustc_hashes::Hash64;
6use rustc_hir::def_id::{CrateNum, DefId};
7use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
8use rustc_middle::bug;
9use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer};
10use rustc_middle::ty::{
11 self, GenericArg, GenericArgKind, Instance, ReifyReason, Ty, TyCtxt, TypeVisitableExt,
12};
13use tracing::debug;
14
15pub(super) fn mangle<'tcx>(
16 tcx: TyCtxt<'tcx>,
17 instance: Instance<'tcx>,
18 instantiating_crate: Option<CrateNum>,
19) -> String {
20 let def_id = instance.def_id();
21
22 let mut ty_def_id = def_id;
27 let instance_ty;
28 loop {
29 let key = tcx.def_key(ty_def_id);
30 match key.disambiguated_data.data {
31 DefPathData::TypeNs(_)
32 | DefPathData::ValueNs(_)
33 | DefPathData::Closure
34 | DefPathData::SyntheticCoroutineBody => {
35 instance_ty = tcx.type_of(ty_def_id).instantiate_identity();
36 debug!(?instance_ty);
37 break;
38 }
39 _ => {
40 ty_def_id.index = key.parent.unwrap_or_else(|| {
44 bug!(
45 "finding type for {:?}, encountered def-id {:?} with no \
46 parent",
47 def_id,
48 ty_def_id
49 );
50 });
51 }
52 }
53 }
54
55 let instance_ty = tcx.erase_regions(instance_ty);
58
59 let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate);
60
61 let mut printer = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false };
62 printer
63 .print_def_path(
64 def_id,
65 if let ty::InstanceKind::DropGlue(_, _)
66 | ty::InstanceKind::AsyncDropGlueCtorShim(_, _)
67 | ty::InstanceKind::FutureDropPollShim(_, _, _) = instance.def
68 {
69 &*instance.args
71 } else if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def {
72 let ty::Coroutine(_, cor_args) = ty.kind() else {
73 bug!();
74 };
75 let drop_ty = cor_args.first().unwrap().expect_ty();
76 tcx.mk_args(&[GenericArg::from(drop_ty)])
77 } else {
78 &[]
79 },
80 )
81 .unwrap();
82
83 match instance.def {
84 ty::InstanceKind::ThreadLocalShim(..) => {
85 printer.write_str("{{tls-shim}}").unwrap();
86 }
87 ty::InstanceKind::VTableShim(..) => {
88 printer.write_str("{{vtable-shim}}").unwrap();
89 }
90 ty::InstanceKind::ReifyShim(_, reason) => {
91 printer.write_str("{{reify-shim").unwrap();
92 match reason {
93 Some(ReifyReason::FnPtr) => printer.write_str("-fnptr").unwrap(),
94 Some(ReifyReason::Vtable) => printer.write_str("-vtable").unwrap(),
95 None => (),
96 }
97 printer.write_str("}}").unwrap();
98 }
99 ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } => {
102 printer
103 .write_str(if receiver_by_ref { "{{by-move-shim}}" } else { "{{by-ref-shim}}" })
104 .unwrap();
105 }
106 _ => {}
107 }
108
109 if let ty::InstanceKind::FutureDropPollShim(..) = instance.def {
110 let _ = printer.write_str("{{drop-shim}}");
111 }
112
113 printer.path.finish(hash)
114}
115
116fn get_symbol_hash<'tcx>(
117 tcx: TyCtxt<'tcx>,
118
119 instance: Instance<'tcx>,
121
122 item_type: Ty<'tcx>,
127
128 instantiating_crate: Option<CrateNum>,
129) -> Hash64 {
130 let def_id = instance.def_id();
131 let args = instance.args;
132 debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, args);
133
134 tcx.with_stable_hashing_context(|mut hcx| {
135 let mut hasher = StableHasher::new();
136
137 tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher);
141
142 assert!(!item_type.has_erasable_regions());
146 hcx.while_hashing_spans(false, |hcx| {
147 item_type.hash_stable(hcx, &mut hasher);
148
149 if let ty::FnDef(..) = item_type.kind() {
153 item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher);
154 }
155
156 args.hash_stable(hcx, &mut hasher);
158
159 if let Some(instantiating_crate) = instantiating_crate {
160 tcx.def_path_hash(instantiating_crate.as_def_id())
161 .stable_crate_id()
162 .hash_stable(hcx, &mut hasher);
163 }
164
165 discriminant(&instance.def).hash_stable(hcx, &mut hasher);
169 });
170
171 hasher.finish::<Hash64>()
173 })
174}
175
176#[derive(Debug)]
190struct SymbolPath {
191 result: String,
192 temp_buf: String,
193}
194
195impl SymbolPath {
196 fn new() -> Self {
197 let mut result =
198 SymbolPath { result: String::with_capacity(64), temp_buf: String::with_capacity(16) };
199 result.result.push_str("_ZN"); result
201 }
202
203 fn finalize_pending_component(&mut self) {
204 if !self.temp_buf.is_empty() {
205 let _ = write!(self.result, "{}{}", self.temp_buf.len(), self.temp_buf);
206 self.temp_buf.clear();
207 }
208 }
209
210 fn finish(mut self, hash: Hash64) -> String {
211 self.finalize_pending_component();
212 let _ = write!(self.result, "17h{hash:016x}E");
214 self.result
215 }
216}
217
218struct SymbolPrinter<'tcx> {
219 tcx: TyCtxt<'tcx>,
220 path: SymbolPath,
221
222 keep_within_component: bool,
227}
228
229impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
234 fn tcx(&self) -> TyCtxt<'tcx> {
235 self.tcx
236 }
237
238 fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
239 Ok(())
240 }
241
242 fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
243 match *ty.kind() {
244 ty::FnDef(def_id, args)
246 | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
247 | ty::Closure(def_id, args)
248 | ty::CoroutineClosure(def_id, args)
249 | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args),
250
251 ty::Array(ty, size) => {
254 self.write_str("[")?;
255 self.print_type(ty)?;
256 self.write_str("; ")?;
257 if let Some(size) = size.try_to_target_usize(self.tcx()) {
258 write!(self, "{size}")?
259 } else if let ty::ConstKind::Param(param) = size.kind() {
260 param.print(self)?
261 } else {
262 self.write_str("_")?
263 }
264 self.write_str("]")?;
265 Ok(())
266 }
267
268 ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"),
269
270 _ => self.pretty_print_type(ty),
271 }
272 }
273
274 fn print_dyn_existential(
275 &mut self,
276 predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
277 ) -> Result<(), PrintError> {
278 let mut first = true;
279 for p in predicates {
280 if !first {
281 write!(self, "+")?;
282 }
283 first = false;
284 p.print(self)?;
285 }
286 Ok(())
287 }
288
289 fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
290 match ct.kind() {
292 ty::ConstKind::Value(cv) if cv.ty.is_integral() => {
293 let scalar = cv.valtree.unwrap_leaf();
296 let signed = matches!(cv.ty.kind(), ty::Int(_));
297 write!(
298 self,
299 "{:#?}",
300 ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral())
301 )?;
302 }
303 _ => self.write_str("_")?,
304 }
305 Ok(())
306 }
307
308 fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
309 self.write_str(self.tcx.crate_name(cnum).as_str())?;
310 Ok(())
311 }
312 fn path_qualified(
313 &mut self,
314 self_ty: Ty<'tcx>,
315 trait_ref: Option<ty::TraitRef<'tcx>>,
316 ) -> Result<(), PrintError> {
317 match self_ty.kind() {
320 ty::FnDef(..)
321 | ty::Alias(..)
322 | ty::Closure(..)
323 | ty::CoroutineClosure(..)
324 | ty::Coroutine(..)
325 if trait_ref.is_none() =>
326 {
327 self.print_type(self_ty)
328 }
329
330 _ => self.pretty_path_qualified(self_ty, trait_ref),
331 }
332 }
333
334 fn path_append_impl(
335 &mut self,
336 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
337 _disambiguated_data: &DisambiguatedDefPathData,
338 self_ty: Ty<'tcx>,
339 trait_ref: Option<ty::TraitRef<'tcx>>,
340 ) -> Result<(), PrintError> {
341 self.pretty_path_append_impl(
342 |cx| {
343 print_prefix(cx)?;
344
345 if cx.keep_within_component {
346 cx.write_str("::")?;
348 } else {
349 cx.path.finalize_pending_component();
350 }
351
352 Ok(())
353 },
354 self_ty,
355 trait_ref,
356 )
357 }
358 fn path_append(
359 &mut self,
360 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
361 disambiguated_data: &DisambiguatedDefPathData,
362 ) -> Result<(), PrintError> {
363 print_prefix(self)?;
364
365 if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
367 return Ok(());
368 }
369
370 if self.keep_within_component {
371 self.write_str("::")?;
373 } else {
374 self.path.finalize_pending_component();
375 }
376
377 write!(self, "{}", disambiguated_data.data)?;
378
379 Ok(())
380 }
381 fn path_generic_args(
382 &mut self,
383 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
384 args: &[GenericArg<'tcx>],
385 ) -> Result<(), PrintError> {
386 print_prefix(self)?;
387
388 let args =
389 args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_)));
390
391 if args.clone().next().is_some() {
392 self.generic_delimiters(|cx| cx.comma_sep(args))
393 } else {
394 Ok(())
395 }
396 }
397
398 fn print_impl_path(
399 &mut self,
400 impl_def_id: DefId,
401 args: &'tcx [GenericArg<'tcx>],
402 ) -> Result<(), PrintError> {
403 let self_ty = self.tcx.type_of(impl_def_id);
404 let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id);
405 let generics = self.tcx.generics_of(impl_def_id);
406 let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len()
420 || &args[..generics.count()]
421 == self
422 .tcx
423 .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id))
424 .as_slice()
425 {
426 (
427 ty::TypingEnv::post_analysis(self.tcx, impl_def_id),
428 self_ty.instantiate_identity(),
429 impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()),
430 )
431 } else {
432 assert!(
433 !args.has_non_region_param(),
434 "should not be mangling partially substituted \
435 polymorphic instance: {impl_def_id:?} {args:?}"
436 );
437 (
438 ty::TypingEnv::fully_monomorphized(),
439 self_ty.instantiate(self.tcx, args),
440 impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)),
441 )
442 };
443
444 match &mut impl_trait_ref {
445 Some(impl_trait_ref) => {
446 assert_eq!(impl_trait_ref.self_ty(), self_ty);
447 *impl_trait_ref = self.tcx.normalize_erasing_regions(typing_env, *impl_trait_ref);
448 self_ty = impl_trait_ref.self_ty();
449 }
450 None => {
451 self_ty = self.tcx.normalize_erasing_regions(typing_env, self_ty);
452 }
453 }
454
455 self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref)
456 }
457}
458
459impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> {
460 fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
461 false
462 }
463 fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError>
464 where
465 T: Print<'tcx, Self>,
466 {
467 if let Some(first) = elems.next() {
468 first.print(self)?;
469 for elem in elems {
470 self.write_str(",")?;
471 elem.print(self)?;
472 }
473 }
474 Ok(())
475 }
476
477 fn generic_delimiters(
478 &mut self,
479 f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
480 ) -> Result<(), PrintError> {
481 write!(self, "<")?;
482
483 let kept_within_component = mem::replace(&mut self.keep_within_component, true);
484 f(self)?;
485 self.keep_within_component = kept_within_component;
486
487 write!(self, ">")?;
488
489 Ok(())
490 }
491}
492
493impl fmt::Write for SymbolPrinter<'_> {
494 fn write_str(&mut self, s: &str) -> fmt::Result {
495 for c in s.chars() {
502 if self.path.temp_buf.is_empty() {
503 match c {
504 'a'..='z' | 'A'..='Z' | '_' => {}
505 _ => {
506 self.path.temp_buf.push('_');
508 }
509 }
510 }
511 match c {
512 '@' => self.path.temp_buf.push_str("$SP$"),
514 '*' => self.path.temp_buf.push_str("$BP$"),
515 '&' => self.path.temp_buf.push_str("$RF$"),
516 '<' => self.path.temp_buf.push_str("$LT$"),
517 '>' => self.path.temp_buf.push_str("$GT$"),
518 '(' => self.path.temp_buf.push_str("$LP$"),
519 ')' => self.path.temp_buf.push_str("$RP$"),
520 ',' => self.path.temp_buf.push_str("$C$"),
521
522 '-' | ':' | '.' if self.tcx.has_strict_asm_symbol_naming() => {
523 self.path.temp_buf.push('$')
525 }
526
527 '-' | ':' => self.path.temp_buf.push('.'),
530
531 'm' if self.path.temp_buf.ends_with(".llv") => self.path.temp_buf.push_str("$u6d$"),
533
534 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.path.temp_buf.push(c),
536
537 _ => {
538 self.path.temp_buf.push('$');
539 for c in c.escape_unicode().skip(1) {
540 match c {
541 '{' => {}
542 '}' => self.path.temp_buf.push('$'),
543 c => self.path.temp_buf.push(c),
544 }
545 }
546 }
547 }
548 }
549
550 Ok(())
551 }
552}