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 p = LegacySymbolMangler { tcx, path: SymbolPath::new(), keep_within_component: false };
62 p.print_def_path(
63 def_id,
64 if let ty::InstanceKind::DropGlue(_, _)
65 | ty::InstanceKind::AsyncDropGlueCtorShim(_, _)
66 | ty::InstanceKind::FutureDropPollShim(_, _, _) = instance.def
67 {
68 &*instance.args
70 } else if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def {
71 let ty::Coroutine(_, cor_args) = ty.kind() else {
72 bug!();
73 };
74 let drop_ty = cor_args.first().unwrap().expect_ty();
75 tcx.mk_args(&[GenericArg::from(drop_ty)])
76 } else {
77 &[]
78 },
79 )
80 .unwrap();
81
82 match instance.def {
83 ty::InstanceKind::ThreadLocalShim(..) => {
84 p.write_str("{{tls-shim}}").unwrap();
85 }
86 ty::InstanceKind::VTableShim(..) => {
87 p.write_str("{{vtable-shim}}").unwrap();
88 }
89 ty::InstanceKind::ReifyShim(_, reason) => {
90 p.write_str("{{reify-shim").unwrap();
91 match reason {
92 Some(ReifyReason::FnPtr) => p.write_str("-fnptr").unwrap(),
93 Some(ReifyReason::Vtable) => p.write_str("-vtable").unwrap(),
94 None => (),
95 }
96 p.write_str("}}").unwrap();
97 }
98 ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } => {
101 p.write_str(if receiver_by_ref { "{{by-move-shim}}" } else { "{{by-ref-shim}}" })
102 .unwrap();
103 }
104 _ => {}
105 }
106
107 if let ty::InstanceKind::FutureDropPollShim(..) = instance.def {
108 let _ = p.write_str("{{drop-shim}}");
109 }
110
111 p.path.finish(hash)
112}
113
114fn get_symbol_hash<'tcx>(
115 tcx: TyCtxt<'tcx>,
116
117 instance: Instance<'tcx>,
119
120 item_type: Ty<'tcx>,
125
126 instantiating_crate: Option<CrateNum>,
127) -> Hash64 {
128 let def_id = instance.def_id();
129 let args = instance.args;
130 debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, args);
131
132 tcx.with_stable_hashing_context(|mut hcx| {
133 let mut hasher = StableHasher::new();
134
135 tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher);
139
140 assert!(!item_type.has_erasable_regions());
144 hcx.while_hashing_spans(false, |hcx| {
145 item_type.hash_stable(hcx, &mut hasher);
146
147 if let ty::FnDef(..) = item_type.kind() {
151 item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher);
152 }
153
154 args.hash_stable(hcx, &mut hasher);
156
157 if let Some(instantiating_crate) = instantiating_crate {
158 tcx.def_path_hash(instantiating_crate.as_def_id())
159 .stable_crate_id()
160 .hash_stable(hcx, &mut hasher);
161 }
162
163 discriminant(&instance.def).hash_stable(hcx, &mut hasher);
167 });
168
169 hasher.finish::<Hash64>()
171 })
172}
173
174#[derive(Debug)]
188struct SymbolPath {
189 result: String,
190 temp_buf: String,
191}
192
193impl SymbolPath {
194 fn new() -> Self {
195 let mut result =
196 SymbolPath { result: String::with_capacity(64), temp_buf: String::with_capacity(16) };
197 result.result.push_str("_ZN"); result
199 }
200
201 fn finalize_pending_component(&mut self) {
202 if !self.temp_buf.is_empty() {
203 let _ = write!(self.result, "{}{}", self.temp_buf.len(), self.temp_buf);
204 self.temp_buf.clear();
205 }
206 }
207
208 fn finish(mut self, hash: Hash64) -> String {
209 self.finalize_pending_component();
210 let _ = write!(self.result, "17h{hash:016x}E");
212 self.result
213 }
214}
215
216struct LegacySymbolMangler<'tcx> {
217 tcx: TyCtxt<'tcx>,
218 path: SymbolPath,
219
220 keep_within_component: bool,
225}
226
227impl<'tcx> Printer<'tcx> for LegacySymbolMangler<'tcx> {
232 fn tcx(&self) -> TyCtxt<'tcx> {
233 self.tcx
234 }
235
236 fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
237 Ok(())
241 }
242
243 fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
244 match *ty.kind() {
245 ty::FnDef(def_id, args)
247 | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
248 | ty::Closure(def_id, args)
249 | ty::CoroutineClosure(def_id, args)
250 | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args),
251
252 ty::Array(ty, size) => {
255 self.write_str("[")?;
256 self.print_type(ty)?;
257 self.write_str("; ")?;
258 if let Some(size) = size.try_to_target_usize(self.tcx()) {
259 write!(self, "{size}")?
260 } else if let ty::ConstKind::Param(param) = size.kind() {
261 param.print(self)?
262 } else {
263 self.write_str("_")?
264 }
265 self.write_str("]")?;
266 Ok(())
267 }
268
269 ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"),
270
271 _ => self.pretty_print_type(ty),
272 }
273 }
274
275 fn print_dyn_existential(
276 &mut self,
277 predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
278 ) -> Result<(), PrintError> {
279 let mut first = true;
280 for p in predicates {
281 if !first {
282 write!(self, "+")?;
283 }
284 first = false;
285 p.print(self)?;
286 }
287 Ok(())
288 }
289
290 fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
291 match ct.kind() {
293 ty::ConstKind::Value(cv) if cv.ty.is_integral() => {
294 let scalar = cv.valtree.unwrap_leaf();
297 let signed = matches!(cv.ty.kind(), ty::Int(_));
298 write!(
299 self,
300 "{:#?}",
301 ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral())
302 )?;
303 }
304 _ => self.write_str("_")?,
305 }
306 Ok(())
307 }
308
309 fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
310 self.write_str(self.tcx.crate_name(cnum).as_str())?;
311 Ok(())
312 }
313
314 fn print_path_with_qualified(
315 &mut self,
316 self_ty: Ty<'tcx>,
317 trait_ref: Option<ty::TraitRef<'tcx>>,
318 ) -> Result<(), PrintError> {
319 match self_ty.kind() {
322 ty::FnDef(..)
323 | ty::Alias(..)
324 | ty::Closure(..)
325 | ty::CoroutineClosure(..)
326 | ty::Coroutine(..)
327 if trait_ref.is_none() =>
328 {
329 self.print_type(self_ty)
330 }
331
332 _ => self.pretty_print_path_with_qualified(self_ty, trait_ref),
333 }
334 }
335
336 fn print_path_with_impl(
337 &mut self,
338 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
339 self_ty: Ty<'tcx>,
340 trait_ref: Option<ty::TraitRef<'tcx>>,
341 ) -> Result<(), PrintError> {
342 self.pretty_print_path_with_impl(
343 |cx| {
344 print_prefix(cx)?;
345
346 if cx.keep_within_component {
347 cx.write_str("::")?;
349 } else {
350 cx.path.finalize_pending_component();
351 }
352
353 Ok(())
354 },
355 self_ty,
356 trait_ref,
357 )
358 }
359
360 fn print_path_with_simple(
361 &mut self,
362 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
363 disambiguated_data: &DisambiguatedDefPathData,
364 ) -> Result<(), PrintError> {
365 print_prefix(self)?;
366
367 if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
369 return Ok(());
370 }
371
372 if self.keep_within_component {
373 self.write_str("::")?;
375 } else {
376 self.path.finalize_pending_component();
377 }
378
379 write!(self, "{}", disambiguated_data.data)?;
380
381 Ok(())
382 }
383
384 fn print_path_with_generic_args(
385 &mut self,
386 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
387 args: &[GenericArg<'tcx>],
388 ) -> Result<(), PrintError> {
389 print_prefix(self)?;
390
391 let args =
392 args.iter().cloned().filter(|arg| !matches!(arg.kind(), GenericArgKind::Lifetime(_)));
393 if args.clone().next().is_some() {
394 self.generic_delimiters(|cx| cx.comma_sep(args))
395 } else {
396 Ok(())
397 }
398 }
399
400 fn print_impl_path(
401 &mut self,
402 impl_def_id: DefId,
403 args: &'tcx [GenericArg<'tcx>],
404 ) -> Result<(), PrintError> {
405 let self_ty = self.tcx.type_of(impl_def_id);
406 let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id);
407 let generics = self.tcx.generics_of(impl_def_id);
408 let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len()
422 || &args[..generics.count()]
423 == self
424 .tcx
425 .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id))
426 .as_slice()
427 {
428 (
429 ty::TypingEnv::post_analysis(self.tcx, impl_def_id),
430 self_ty.instantiate_identity(),
431 impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()),
432 )
433 } else {
434 assert!(
435 !args.has_non_region_param(),
436 "should not be mangling partially substituted \
437 polymorphic instance: {impl_def_id:?} {args:?}"
438 );
439 (
440 ty::TypingEnv::fully_monomorphized(),
441 self_ty.instantiate(self.tcx, args),
442 impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)),
443 )
444 };
445
446 match &mut impl_trait_ref {
447 Some(impl_trait_ref) => {
448 assert_eq!(impl_trait_ref.self_ty(), self_ty);
449 *impl_trait_ref = self.tcx.normalize_erasing_regions(typing_env, *impl_trait_ref);
450 self_ty = impl_trait_ref.self_ty();
451 }
452 None => {
453 self_ty = self.tcx.normalize_erasing_regions(typing_env, self_ty);
454 }
455 }
456
457 self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref)
458 }
459}
460
461impl<'tcx> PrettyPrinter<'tcx> for LegacySymbolMangler<'tcx> {
462 fn should_print_optional_region(&self, _region: ty::Region<'_>) -> bool {
463 false
464 }
465
466 fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError>
468 where
469 T: Print<'tcx, Self>,
470 {
471 if let Some(first) = elems.next() {
472 first.print(self)?;
473 for elem in elems {
474 self.write_str(",")?;
475 elem.print(self)?;
476 }
477 }
478 Ok(())
479 }
480
481 fn generic_delimiters(
482 &mut self,
483 f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
484 ) -> Result<(), PrintError> {
485 write!(self, "<")?;
486
487 let kept_within_component = mem::replace(&mut self.keep_within_component, true);
488 f(self)?;
489 self.keep_within_component = kept_within_component;
490
491 write!(self, ">")?;
492
493 Ok(())
494 }
495}
496
497impl fmt::Write for LegacySymbolMangler<'_> {
498 fn write_str(&mut self, s: &str) -> fmt::Result {
499 for c in s.chars() {
506 if self.path.temp_buf.is_empty() {
507 match c {
508 'a'..='z' | 'A'..='Z' | '_' => {}
509 _ => {
510 self.path.temp_buf.push('_');
512 }
513 }
514 }
515 match c {
516 '@' => self.path.temp_buf.push_str("$SP$"),
518 '*' => self.path.temp_buf.push_str("$BP$"),
519 '&' => self.path.temp_buf.push_str("$RF$"),
520 '<' => self.path.temp_buf.push_str("$LT$"),
521 '>' => self.path.temp_buf.push_str("$GT$"),
522 '(' => self.path.temp_buf.push_str("$LP$"),
523 ')' => self.path.temp_buf.push_str("$RP$"),
524 ',' => self.path.temp_buf.push_str("$C$"),
525
526 '-' | ':' | '.' if self.tcx.has_strict_asm_symbol_naming() => {
527 self.path.temp_buf.push('$')
529 }
530
531 '-' | ':' => self.path.temp_buf.push('.'),
534
535 'm' if self.path.temp_buf.ends_with(".llv") => self.path.temp_buf.push_str("$u6d$"),
537
538 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.path.temp_buf.push(c),
540
541 _ => {
542 self.path.temp_buf.push('$');
543 for c in c.escape_unicode().skip(1) {
544 match c {
545 '{' => {}
546 '}' => self.path.temp_buf.push('$'),
547 c => self.path.temp_buf.push(c),
548 }
549 }
550 }
551 }
552 }
553
554 Ok(())
555 }
556}