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