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(_) | DefPathData::ValueNs(_) | DefPathData::Closure => {
32 instance_ty = tcx.type_of(ty_def_id).instantiate_identity();
33 debug!(?instance_ty);
34 break;
35 }
36 _ => {
37 ty_def_id.index = key.parent.unwrap_or_else(|| {
41 bug!(
42 "finding type for {:?}, encountered def-id {:?} with no \
43 parent",
44 def_id,
45 ty_def_id
46 );
47 });
48 }
49 }
50 }
51
52 let instance_ty = tcx.erase_regions(instance_ty);
55
56 let hash = get_symbol_hash(tcx, instance, instance_ty, instantiating_crate);
57
58 let mut printer = SymbolPrinter { tcx, path: SymbolPath::new(), keep_within_component: false };
59 printer
60 .print_def_path(
61 def_id,
62 if let ty::InstanceKind::DropGlue(_, _)
63 | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) = instance.def
64 {
65 &*instance.args
67 } else {
68 &[]
69 },
70 )
71 .unwrap();
72
73 match instance.def {
74 ty::InstanceKind::ThreadLocalShim(..) => {
75 printer.write_str("{{tls-shim}}").unwrap();
76 }
77 ty::InstanceKind::VTableShim(..) => {
78 printer.write_str("{{vtable-shim}}").unwrap();
79 }
80 ty::InstanceKind::ReifyShim(_, reason) => {
81 printer.write_str("{{reify-shim").unwrap();
82 match reason {
83 Some(ReifyReason::FnPtr) => printer.write_str("-fnptr").unwrap(),
84 Some(ReifyReason::Vtable) => printer.write_str("-vtable").unwrap(),
85 None => (),
86 }
87 printer.write_str("}}").unwrap();
88 }
89 ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } => {
92 printer
93 .write_str(if receiver_by_ref { "{{by-move-shim}}" } else { "{{by-ref-shim}}" })
94 .unwrap();
95 }
96 _ => {}
97 }
98
99 printer.path.finish(hash)
100}
101
102fn get_symbol_hash<'tcx>(
103 tcx: TyCtxt<'tcx>,
104
105 instance: Instance<'tcx>,
107
108 item_type: Ty<'tcx>,
113
114 instantiating_crate: Option<CrateNum>,
115) -> Hash64 {
116 let def_id = instance.def_id();
117 let args = instance.args;
118 debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, args);
119
120 tcx.with_stable_hashing_context(|mut hcx| {
121 let mut hasher = StableHasher::new();
122
123 tcx.def_path_hash(def_id).hash_stable(&mut hcx, &mut hasher);
127
128 assert!(!item_type.has_erasable_regions());
132 hcx.while_hashing_spans(false, |hcx| {
133 item_type.hash_stable(hcx, &mut hasher);
134
135 if let ty::FnDef(..) = item_type.kind() {
139 item_type.fn_sig(tcx).hash_stable(hcx, &mut hasher);
140 }
141
142 args.hash_stable(hcx, &mut hasher);
144
145 if let Some(instantiating_crate) = instantiating_crate {
146 tcx.def_path_hash(instantiating_crate.as_def_id())
147 .stable_crate_id()
148 .hash_stable(hcx, &mut hasher);
149 }
150
151 discriminant(&instance.def).hash_stable(hcx, &mut hasher);
155 });
156
157 hasher.finish::<Hash64>()
159 })
160}
161
162#[derive(Debug)]
176struct SymbolPath {
177 result: String,
178 temp_buf: String,
179}
180
181impl SymbolPath {
182 fn new() -> Self {
183 let mut result =
184 SymbolPath { result: String::with_capacity(64), temp_buf: String::with_capacity(16) };
185 result.result.push_str("_ZN"); result
187 }
188
189 fn finalize_pending_component(&mut self) {
190 if !self.temp_buf.is_empty() {
191 let _ = write!(self.result, "{}{}", self.temp_buf.len(), self.temp_buf);
192 self.temp_buf.clear();
193 }
194 }
195
196 fn finish(mut self, hash: Hash64) -> String {
197 self.finalize_pending_component();
198 let _ = write!(self.result, "17h{hash:016x}E");
200 self.result
201 }
202}
203
204struct SymbolPrinter<'tcx> {
205 tcx: TyCtxt<'tcx>,
206 path: SymbolPath,
207
208 keep_within_component: bool,
213}
214
215impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
220 fn tcx(&self) -> TyCtxt<'tcx> {
221 self.tcx
222 }
223
224 fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
225 Ok(())
226 }
227
228 fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
229 match *ty.kind() {
230 ty::FnDef(def_id, args)
232 | ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
233 | ty::Closure(def_id, args)
234 | ty::CoroutineClosure(def_id, args)
235 | ty::Coroutine(def_id, args) => self.print_def_path(def_id, args),
236
237 ty::Array(ty, size) => {
240 self.write_str("[")?;
241 self.print_type(ty)?;
242 self.write_str("; ")?;
243 if let Some(size) = size.try_to_target_usize(self.tcx()) {
244 write!(self, "{size}")?
245 } else if let ty::ConstKind::Param(param) = size.kind() {
246 param.print(self)?
247 } else {
248 self.write_str("_")?
249 }
250 self.write_str("]")?;
251 Ok(())
252 }
253
254 ty::Alias(ty::Inherent, _) => panic!("unexpected inherent projection"),
255
256 _ => self.pretty_print_type(ty),
257 }
258 }
259
260 fn print_dyn_existential(
261 &mut self,
262 predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
263 ) -> Result<(), PrintError> {
264 let mut first = true;
265 for p in predicates {
266 if !first {
267 write!(self, "+")?;
268 }
269 first = false;
270 p.print(self)?;
271 }
272 Ok(())
273 }
274
275 fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
276 match ct.kind() {
278 ty::ConstKind::Value(cv) if cv.ty.is_integral() => {
279 let scalar = cv.valtree.unwrap_leaf();
282 let signed = matches!(cv.ty.kind(), ty::Int(_));
283 write!(
284 self,
285 "{:#?}",
286 ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral())
287 )?;
288 }
289 _ => self.write_str("_")?,
290 }
291 Ok(())
292 }
293
294 fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
295 self.write_str(self.tcx.crate_name(cnum).as_str())?;
296 Ok(())
297 }
298 fn path_qualified(
299 &mut self,
300 self_ty: Ty<'tcx>,
301 trait_ref: Option<ty::TraitRef<'tcx>>,
302 ) -> Result<(), PrintError> {
303 match self_ty.kind() {
306 ty::FnDef(..)
307 | ty::Alias(..)
308 | ty::Closure(..)
309 | ty::CoroutineClosure(..)
310 | ty::Coroutine(..)
311 if trait_ref.is_none() =>
312 {
313 self.print_type(self_ty)
314 }
315
316 _ => self.pretty_path_qualified(self_ty, trait_ref),
317 }
318 }
319
320 fn path_append_impl(
321 &mut self,
322 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
323 _disambiguated_data: &DisambiguatedDefPathData,
324 self_ty: Ty<'tcx>,
325 trait_ref: Option<ty::TraitRef<'tcx>>,
326 ) -> Result<(), PrintError> {
327 self.pretty_path_append_impl(
328 |cx| {
329 print_prefix(cx)?;
330
331 if cx.keep_within_component {
332 cx.write_str("::")?;
334 } else {
335 cx.path.finalize_pending_component();
336 }
337
338 Ok(())
339 },
340 self_ty,
341 trait_ref,
342 )
343 }
344 fn path_append(
345 &mut self,
346 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
347 disambiguated_data: &DisambiguatedDefPathData,
348 ) -> Result<(), PrintError> {
349 print_prefix(self)?;
350
351 if let DefPathData::ForeignMod | DefPathData::Ctor = disambiguated_data.data {
353 return Ok(());
354 }
355
356 if self.keep_within_component {
357 self.write_str("::")?;
359 } else {
360 self.path.finalize_pending_component();
361 }
362
363 write!(self, "{}", disambiguated_data.data)?;
364
365 Ok(())
366 }
367 fn path_generic_args(
368 &mut self,
369 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
370 args: &[GenericArg<'tcx>],
371 ) -> Result<(), PrintError> {
372 print_prefix(self)?;
373
374 let args =
375 args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
376
377 if args.clone().next().is_some() {
378 self.generic_delimiters(|cx| cx.comma_sep(args))
379 } else {
380 Ok(())
381 }
382 }
383
384 fn print_impl_path(
385 &mut self,
386 impl_def_id: DefId,
387 args: &'tcx [GenericArg<'tcx>],
388 ) -> Result<(), PrintError> {
389 let self_ty = self.tcx.type_of(impl_def_id);
390 let impl_trait_ref = self.tcx.impl_trait_ref(impl_def_id);
391 let generics = self.tcx.generics_of(impl_def_id);
392 let (typing_env, mut self_ty, mut impl_trait_ref) = if generics.count() > args.len()
406 || &args[..generics.count()]
407 == self
408 .tcx
409 .erase_regions(ty::GenericArgs::identity_for_item(self.tcx, impl_def_id))
410 .as_slice()
411 {
412 (
413 ty::TypingEnv::post_analysis(self.tcx, impl_def_id),
414 self_ty.instantiate_identity(),
415 impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate_identity()),
416 )
417 } else {
418 assert!(
419 !args.has_non_region_param(),
420 "should not be mangling partially substituted \
421 polymorphic instance: {impl_def_id:?} {args:?}"
422 );
423 (
424 ty::TypingEnv::fully_monomorphized(),
425 self_ty.instantiate(self.tcx, args),
426 impl_trait_ref.map(|impl_trait_ref| impl_trait_ref.instantiate(self.tcx, args)),
427 )
428 };
429
430 match &mut impl_trait_ref {
431 Some(impl_trait_ref) => {
432 assert_eq!(impl_trait_ref.self_ty(), self_ty);
433 *impl_trait_ref = self.tcx.normalize_erasing_regions(typing_env, *impl_trait_ref);
434 self_ty = impl_trait_ref.self_ty();
435 }
436 None => {
437 self_ty = self.tcx.normalize_erasing_regions(typing_env, self_ty);
438 }
439 }
440
441 self.default_print_impl_path(impl_def_id, self_ty, impl_trait_ref)
442 }
443}
444
445impl<'tcx> PrettyPrinter<'tcx> for SymbolPrinter<'tcx> {
446 fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
447 false
448 }
449 fn comma_sep<T>(&mut self, mut elems: impl Iterator<Item = T>) -> Result<(), PrintError>
450 where
451 T: Print<'tcx, Self>,
452 {
453 if let Some(first) = elems.next() {
454 first.print(self)?;
455 for elem in elems {
456 self.write_str(",")?;
457 elem.print(self)?;
458 }
459 }
460 Ok(())
461 }
462
463 fn generic_delimiters(
464 &mut self,
465 f: impl FnOnce(&mut Self) -> Result<(), PrintError>,
466 ) -> Result<(), PrintError> {
467 write!(self, "<")?;
468
469 let kept_within_component = mem::replace(&mut self.keep_within_component, true);
470 f(self)?;
471 self.keep_within_component = kept_within_component;
472
473 write!(self, ">")?;
474
475 Ok(())
476 }
477}
478
479impl fmt::Write for SymbolPrinter<'_> {
480 fn write_str(&mut self, s: &str) -> fmt::Result {
481 for c in s.chars() {
488 if self.path.temp_buf.is_empty() {
489 match c {
490 'a'..='z' | 'A'..='Z' | '_' => {}
491 _ => {
492 self.path.temp_buf.push('_');
494 }
495 }
496 }
497 match c {
498 '@' => self.path.temp_buf.push_str("$SP$"),
500 '*' => self.path.temp_buf.push_str("$BP$"),
501 '&' => self.path.temp_buf.push_str("$RF$"),
502 '<' => self.path.temp_buf.push_str("$LT$"),
503 '>' => self.path.temp_buf.push_str("$GT$"),
504 '(' => self.path.temp_buf.push_str("$LP$"),
505 ')' => self.path.temp_buf.push_str("$RP$"),
506 ',' => self.path.temp_buf.push_str("$C$"),
507
508 '-' | ':' | '.' if self.tcx.has_strict_asm_symbol_naming() => {
509 self.path.temp_buf.push('$')
511 }
512
513 '-' | ':' => self.path.temp_buf.push('.'),
516
517 'm' if self.path.temp_buf.ends_with(".llv") => self.path.temp_buf.push_str("$u6d$"),
519
520 'a'..='z' | 'A'..='Z' | '0'..='9' | '_' | '.' | '$' => self.path.temp_buf.push(c),
522
523 _ => {
524 self.path.temp_buf.push('$');
525 for c in c.escape_unicode().skip(1) {
526 match c {
527 '{' => {}
528 '}' => self.path.temp_buf.push('$'),
529 c => self.path.temp_buf.push(c),
530 }
531 }
532 }
533 }
534 }
535
536 Ok(())
537 }
538}