1use std::fmt::Write as _;
9
10use rustc_abi::{ExternAbi, Integer};
11use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, CASE_INSENSITIVE, ToBaseN};
12use rustc_data_structures::fx::FxHashMap;
13use rustc_hir as hir;
14use rustc_middle::bug;
15use rustc_middle::ty::layout::IntegerExt;
16use rustc_middle::ty::{
17 self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
18 IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy,
19};
20use rustc_span::def_id::DefId;
21use rustc_span::sym;
22use tracing::instrument;
23
24use crate::cfi::typeid::TypeIdOptions;
25use crate::cfi::typeid::itanium_cxx_abi::transform::{TransformTy, TransformTyOptions};
26
27pub(crate) type EncodeTyOptions = TypeIdOptions;
29
30#[derive(Eq, Hash, PartialEq)]
32pub(crate) enum DictKey<'tcx> {
33 Ty(Ty<'tcx>, TyQ),
34 Region(Region<'tcx>),
35 Const(Const<'tcx>),
36 Predicate(ExistentialPredicate<'tcx>),
37}
38
39#[derive(Eq, Hash, PartialEq)]
41pub(crate) enum TyQ {
42 None,
43 Const,
44 Mut,
45}
46
47fn compress<'tcx>(
50 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
51 key: DictKey<'tcx>,
52 comp: &mut String,
53) {
54 match dict.get(&key) {
55 Some(num) => {
56 comp.clear();
57 let _ = write!(comp, "S{}_", to_seq_id(*num));
58 }
59 None => {
60 dict.insert(key, dict.len());
61 }
62 }
63}
64
65fn encode_args<'tcx>(
68 tcx: TyCtxt<'tcx>,
69 args: GenericArgsRef<'tcx>,
70 for_def: DefId,
71 has_erased_self: bool,
72 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
73 options: EncodeTyOptions,
74) -> String {
75 let mut s = String::new();
77 let args: Vec<GenericArg<'_>> = args.iter().collect();
78 if !args.is_empty() {
79 s.push('I');
80 let def_generics = tcx.generics_of(for_def);
81 for (n, arg) in args.iter().enumerate() {
82 match arg.kind() {
83 GenericArgKind::Lifetime(region) => {
84 s.push_str(&encode_region(region, dict));
85 }
86 GenericArgKind::Type(ty) => {
87 s.push_str(&encode_ty(tcx, ty, dict, options));
88 }
89 GenericArgKind::Const(c) => {
90 let n = n + (has_erased_self as usize);
91 let ct_ty =
92 tcx.type_of(def_generics.param_at(n, tcx).def_id).instantiate_identity();
93 s.push_str(&encode_const(tcx, c, ct_ty, dict, options));
94 }
95 }
96 }
97 s.push('E');
98 }
99 s
100}
101
102fn encode_const<'tcx>(
105 tcx: TyCtxt<'tcx>,
106 ct: Const<'tcx>,
107 ct_ty: Ty<'tcx>,
108 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
109 options: EncodeTyOptions,
110) -> String {
111 let mut s = String::from('L');
113
114 match ct.kind() {
115 ty::ConstKind::Param(..) => {
117 s.push_str(&encode_ty(tcx, ct_ty, dict, options));
121 }
122
123 ty::ConstKind::Value(cv) => {
125 s.push_str(&encode_ty(tcx, cv.ty, dict, options));
129
130 match cv.ty.kind() {
134 ty::Int(ity) => {
135 let bits = cv
136 .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
137 .expect("expected monomorphic const in cfi");
138 let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
139 if val < 0 {
140 s.push('n');
141 }
142 let _ = write!(s, "{val}");
143 }
144 ty::Uint(_) => {
145 let val = cv
146 .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
147 .expect("expected monomorphic const in cfi");
148 let _ = write!(s, "{val}");
149 }
150 ty::Bool => {
151 let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
152 let _ = write!(s, "{val}");
153 }
154 _ => {
155 bug!("encode_const: unexpected type `{:?}`", cv.ty);
156 }
157 }
158 }
159
160 _ => {
161 bug!("encode_const: unexpected kind `{:?}`", ct.kind());
162 }
163 }
164
165 s.push('E');
167
168 compress(dict, DictKey::Const(ct), &mut s);
169
170 s
171}
172
173fn encode_fnsig<'tcx>(
176 tcx: TyCtxt<'tcx>,
177 fn_sig: &FnSig<'tcx>,
178 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
179 options: TypeIdOptions,
180) -> String {
181 let mut s = String::from("F");
183
184 let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
185 .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
186 match fn_sig.abi {
187 ExternAbi::C { .. } => {
188 encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
189 }
190 _ => {
191 encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
192 }
193 }
194
195 let transform_ty_options = TransformTyOptions::from_bits(options.bits())
197 .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
198 let mut type_folder = TransformTy::new(tcx, transform_ty_options);
199 let ty = fn_sig.output().fold_with(&mut type_folder);
200 s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
201
202 let tys = fn_sig.inputs();
204 if !tys.is_empty() {
205 for ty in tys {
206 let ty = ty.fold_with(&mut type_folder);
207 s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
208 }
209
210 if fn_sig.c_variadic {
211 s.push('z');
212 }
213 } else if fn_sig.c_variadic {
214 s.push('z');
215 } else {
216 s.push('v')
219 }
220
221 s.push('E');
223
224 s
225}
226
227fn encode_predicate<'tcx>(
230 tcx: TyCtxt<'tcx>,
231 predicate: ty::PolyExistentialPredicate<'tcx>,
232 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
233 options: EncodeTyOptions,
234) -> String {
235 let mut s = String::new();
238 match predicate.as_ref().skip_binder() {
239 ty::ExistentialPredicate::Trait(trait_ref) => {
240 let name = encode_ty_name(tcx, trait_ref.def_id);
241 let _ = write!(s, "u{}{}", name.len(), name);
242 s.push_str(&encode_args(tcx, trait_ref.args, trait_ref.def_id, true, dict, options));
243 }
244 ty::ExistentialPredicate::Projection(projection) => {
245 let name = encode_ty_name(tcx, projection.def_id);
246 let _ = write!(s, "u{}{}", name.len(), name);
247 s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options));
248 match projection.term.kind() {
249 TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)),
250 TermKind::Const(c) => s.push_str(&encode_const(
251 tcx,
252 c,
253 tcx.type_of(projection.def_id).instantiate(tcx, projection.args),
254 dict,
255 options,
256 )),
257 }
258 }
259 ty::ExistentialPredicate::AutoTrait(def_id) => {
260 let name = encode_ty_name(tcx, *def_id);
261 let _ = write!(s, "u{}{}", name.len(), name);
262 }
263 };
264 compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
265 s
266}
267
268fn encode_predicates<'tcx>(
271 tcx: TyCtxt<'tcx>,
272 predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
273 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
274 options: EncodeTyOptions,
275) -> String {
276 let mut s = String::new();
278 let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates.iter().collect();
279 for predicate in predicates {
280 s.push_str(&encode_predicate(tcx, predicate, dict, options));
281 }
282 s
283}
284
285fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>, usize>) -> String {
287 let mut s = String::new();
289 match region.kind() {
290 RegionKind::ReBound(ty::BoundVarIndexKind::Bound(debruijn), r) => {
291 s.push_str("u6regionI");
292 let num = debruijn.index() as u64;
294 if num > 0 {
295 s.push_str(&to_disambiguator(num));
296 }
297 let _ = write!(s, "{}", r.var.index() as u64);
299 s.push('E');
300 compress(dict, DictKey::Region(region), &mut s);
301 }
302 RegionKind::ReErased => {
303 s.push_str("u6region");
304 compress(dict, DictKey::Region(region), &mut s);
305 }
306 RegionKind::ReBound(ty::BoundVarIndexKind::Canonical, _)
307 | RegionKind::ReEarlyParam(..)
308 | RegionKind::ReLateParam(..)
309 | RegionKind::ReStatic
310 | RegionKind::ReError(_)
311 | RegionKind::ReVar(..)
312 | RegionKind::RePlaceholder(..) => {
313 bug!("encode_region: unexpected `{:?}`", region.kind());
314 }
315 }
316 s
317}
318
319#[instrument(level = "trace", skip(tcx, dict))]
322pub(crate) fn encode_ty<'tcx>(
323 tcx: TyCtxt<'tcx>,
324 ty: Ty<'tcx>,
325 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
326 options: EncodeTyOptions,
327) -> String {
328 let mut typeid = String::new();
329
330 match ty.kind() {
331 ty::Bool => {
339 typeid.push('b');
340 }
341
342 ty::Int(..) | ty::Uint(..) => {
343 let mut s = String::from(match ty.kind() {
345 ty::Int(IntTy::I8) => "u2i8",
346 ty::Int(IntTy::I16) => "u3i16",
347 ty::Int(IntTy::I32) => "u3i32",
348 ty::Int(IntTy::I64) => "u3i64",
349 ty::Int(IntTy::I128) => "u4i128",
350 ty::Int(IntTy::Isize) => "u5isize",
351 ty::Uint(UintTy::U8) => "u2u8",
352 ty::Uint(UintTy::U16) => "u3u16",
353 ty::Uint(UintTy::U32) => "u3u32",
354 ty::Uint(UintTy::U64) => "u3u64",
355 ty::Uint(UintTy::U128) => "u4u128",
356 ty::Uint(UintTy::Usize) => "u5usize",
357 _ => bug!("encode_ty: unexpected `{:?}`", ty.kind()),
358 });
359 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
360 typeid.push_str(&s);
361 }
362
363 ty::Float(float_ty) => {
369 typeid.push_str(match float_ty {
370 FloatTy::F16 => "Dh",
371 FloatTy::F32 => "f",
372 FloatTy::F64 => "d",
373 FloatTy::F128 => "g",
374 });
375 }
376
377 ty::Char => {
378 let mut s = String::from("u4char");
380 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
381 typeid.push_str(&s);
382 }
383
384 ty::Str => {
385 let mut s = String::from("u3str");
387 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
388 typeid.push_str(&s);
389 }
390
391 ty::Never => {
392 let mut s = String::from("u5never");
394 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
395 typeid.push_str(&s);
396 }
397
398 _ if ty.is_unit() => {
401 typeid.push('v');
402 }
403
404 ty::Tuple(tys) => {
406 let mut s = String::from("u5tupleI");
408 for ty in tys.iter() {
409 s.push_str(&encode_ty(tcx, ty, dict, options));
410 }
411 s.push('E');
412 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
413 typeid.push_str(&s);
414 }
415
416 ty::Array(ty0, len) => {
417 let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi");
419 let mut s = String::from("A");
420 let _ = write!(s, "{len}");
421 s.push_str(&encode_ty(tcx, *ty0, dict, options));
422 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
423 typeid.push_str(&s);
424 }
425
426 ty::Pat(ty0, pat) => {
427 let mut s = String::from("u3patI");
429 s.push_str(&encode_ty(tcx, *ty0, dict, options));
430 write!(s, "{:?}", **pat).unwrap();
431 s.push('E');
432 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
433 typeid.push_str(&s);
434 }
435
436 ty::Slice(ty0) => {
437 let mut s = String::from("u5sliceI");
439 s.push_str(&encode_ty(tcx, *ty0, dict, options));
440 s.push('E');
441 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
442 typeid.push_str(&s);
443 }
444
445 ty::Adt(adt_def, args) => {
447 let mut s = String::new();
448 let def_id = adt_def.did();
449 if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) {
450 if let Some(value_str) = cfi_encoding.value_str() {
452 let value_str = value_str.as_str().trim();
453 if !value_str.is_empty() {
454 s.push_str(value_str);
455 let builtin_types = [
459 "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y",
460 "n", "o", "f", "d", "e", "g", "z", "Dh",
461 ];
462 if !builtin_types.contains(&value_str) {
463 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
464 }
465 } else {
466 #[allow(
467 rustc::diagnostic_outside_of_impl,
468 rustc::untranslatable_diagnostic
469 )]
470 tcx.dcx()
471 .struct_span_err(
472 cfi_encoding.span(),
473 format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
474 )
475 .emit();
476 }
477 } else {
478 bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
479 }
480 } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
481 let name = tcx.item_name(def_id).to_string();
495 let _ = write!(s, "{}{}", name.len(), name);
496 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
497 } else {
498 let name = encode_ty_name(tcx, def_id);
501 let _ = write!(s, "u{}{}", name.len(), name);
502 s.push_str(&encode_args(tcx, args, def_id, false, dict, options));
503 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
504 }
505 typeid.push_str(&s);
506 }
507
508 ty::Foreign(def_id) => {
509 let mut s = String::new();
511 if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) {
512 if let Some(value_str) = cfi_encoding.value_str() {
514 if !value_str.to_string().trim().is_empty() {
515 s.push_str(value_str.to_string().trim());
516 } else {
517 #[allow(
518 rustc::diagnostic_outside_of_impl,
519 rustc::untranslatable_diagnostic
520 )]
521 tcx.dcx()
522 .struct_span_err(
523 cfi_encoding.span(),
524 format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
525 )
526 .emit();
527 }
528 } else {
529 bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
530 }
531 } else {
532 let name = tcx.item_name(*def_id).to_string();
533 let _ = write!(s, "{}{}", name.len(), name);
534 }
535 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
536 typeid.push_str(&s);
537 }
538
539 ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
541 let mut s = String::new();
544 let name = encode_ty_name(tcx, *def_id);
545 let _ = write!(s, "u{}{}", name.len(), name);
546 s.push_str(&encode_args(tcx, args, *def_id, false, dict, options));
547 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
548 typeid.push_str(&s);
549 }
550
551 ty::CoroutineClosure(def_id, args) => {
552 let mut s = String::new();
555 let name = encode_ty_name(tcx, *def_id);
556 let _ = write!(s, "u{}{}", name.len(), name);
557 let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args());
558 s.push_str(&encode_args(tcx, parent_args, *def_id, false, dict, options));
559 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
560 typeid.push_str(&s);
561 }
562
563 ty::Coroutine(def_id, args, ..) => {
564 let mut s = String::new();
567 let name = encode_ty_name(tcx, *def_id);
568 let _ = write!(s, "u{}{}", name.len(), name);
569 s.push_str(&encode_args(
571 tcx,
572 tcx.mk_args(args.as_coroutine().parent_args()),
573 *def_id,
574 false,
575 dict,
576 options,
577 ));
578 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
579 typeid.push_str(&s);
580 }
581
582 ty::Ref(region, ty0, ..) => {
584 let mut s = String::new();
586 s.push_str("u3refI");
587 s.push_str(&encode_ty(tcx, *ty0, dict, options));
588 s.push('E');
589 compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s);
590 if ty.is_mutable_ptr() {
591 s = format!("{}{}", "U3mut", s);
592 compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
593 }
594 typeid.push_str(&s);
595 }
596
597 ty::RawPtr(ptr_ty, _mutbl) => {
598 let mut s = String::new();
601 s.push_str(&encode_ty(tcx, *ptr_ty, dict, options));
602 if !ty.is_mutable_ptr() {
603 s = format!("{}{}", "K", s);
604 compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s);
605 };
606 s = format!("{}{}", "P", s);
607 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
608 typeid.push_str(&s);
609 }
610
611 ty::FnPtr(sig_tys, hdr) => {
612 let mut s = String::from("P");
614 s.push_str(&encode_fnsig(
615 tcx,
616 &sig_tys.with(*hdr).skip_binder(),
617 dict,
618 TypeIdOptions::empty(),
619 ));
620 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
621 typeid.push_str(&s);
622 }
623
624 ty::UnsafeBinder(_) => {
626 todo!()
627 }
628
629 ty::Dynamic(predicates, region) => {
631 let mut s = String::from("u3dynI");
634 s.push_str(&encode_predicates(tcx, predicates, dict, options));
635 s.push_str(&encode_region(*region, dict));
636 s.push('E');
637 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
638 typeid.push_str(&s);
639 }
640
641 ty::Param(..) => {
643 let mut s = String::from("u5param");
645 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
646 typeid.push_str(&s);
647 }
648
649 ty::Alias(..)
651 | ty::Bound(..)
652 | ty::Error(..)
653 | ty::CoroutineWitness(..)
654 | ty::Infer(..)
655 | ty::Placeholder(..) => {
656 bug!("encode_ty: unexpected `{:?}`", ty.kind());
657 }
658 };
659
660 typeid
661}
662
663fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
665 let mut s = String::new();
702
703 let mut def_path = tcx.def_path(def_id);
705 def_path.data.reverse();
706 for disambiguated_data in &def_path.data {
707 s.push('N');
708 s.push_str(match disambiguated_data.data {
709 hir::definitions::DefPathData::Impl => "I", hir::definitions::DefPathData::ForeignMod => "F", hir::definitions::DefPathData::TypeNs(..) => "t",
712 hir::definitions::DefPathData::ValueNs(..) => "v",
713 hir::definitions::DefPathData::Closure => "C",
714 hir::definitions::DefPathData::Ctor => "c",
715 hir::definitions::DefPathData::AnonConst => "K",
716 hir::definitions::DefPathData::LateAnonConst => "k",
717 hir::definitions::DefPathData::OpaqueTy => "i",
718 hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
719 hir::definitions::DefPathData::NestedStatic => "n",
720 hir::definitions::DefPathData::CrateRoot
721 | hir::definitions::DefPathData::Use
722 | hir::definitions::DefPathData::GlobalAsm
723 | hir::definitions::DefPathData::MacroNs(..)
724 | hir::definitions::DefPathData::OpaqueLifetime(..)
725 | hir::definitions::DefPathData::LifetimeNs(..)
726 | hir::definitions::DefPathData::DesugaredAnonymousLifetime
727 | hir::definitions::DefPathData::AnonAssocTy(..) => {
728 bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
729 }
730 });
731 }
732
733 s.push('C');
735 s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64()));
736 let crate_name = tcx.crate_name(def_path.krate).to_string();
737 let _ = write!(s, "{}{}", crate_name.len(), crate_name);
738
739 def_path.data.reverse();
741 for disambiguated_data in &def_path.data {
742 let num = disambiguated_data.disambiguator as u64;
743 if num > 0 {
744 s.push_str(&to_disambiguator(num));
745 }
746
747 let name = disambiguated_data.data.to_string();
748 let _ = write!(s, "{}", name.len());
749
750 if let Some(first) = name.as_bytes().first() {
752 if first.is_ascii_digit() || *first == b'_' {
753 s.push('_');
754 }
755 } else {
756 bug!("encode_ty_name: invalid name `{:?}`", name);
757 }
758
759 s.push_str(&name);
760 }
761
762 s
763}
764
765fn to_disambiguator(num: u64) -> String {
768 if let Some(num) = num.checked_sub(1) {
769 format!("s{}_", num.to_base(ALPHANUMERIC_ONLY))
770 } else {
771 "s_".to_string()
772 }
773}
774
775fn to_seq_id(num: usize) -> String {
778 if let Some(num) = num.checked_sub(1) {
779 (num as u64).to_base(CASE_INSENSITIVE).to_uppercase()
780 } else {
781 "".to_string()
782 }
783}