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(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::ReEarlyParam(..)
307 | RegionKind::ReLateParam(..)
308 | RegionKind::ReStatic
309 | RegionKind::ReError(_)
310 | RegionKind::ReVar(..)
311 | RegionKind::RePlaceholder(..) => {
312 bug!("encode_region: unexpected `{:?}`", region.kind());
313 }
314 }
315 s
316}
317
318#[instrument(level = "trace", skip(tcx, dict))]
321pub(crate) fn encode_ty<'tcx>(
322 tcx: TyCtxt<'tcx>,
323 ty: Ty<'tcx>,
324 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
325 options: EncodeTyOptions,
326) -> String {
327 let mut typeid = String::new();
328
329 match ty.kind() {
330 ty::Bool => {
338 typeid.push('b');
339 }
340
341 ty::Int(..) | ty::Uint(..) => {
342 let mut s = String::from(match ty.kind() {
344 ty::Int(IntTy::I8) => "u2i8",
345 ty::Int(IntTy::I16) => "u3i16",
346 ty::Int(IntTy::I32) => "u3i32",
347 ty::Int(IntTy::I64) => "u3i64",
348 ty::Int(IntTy::I128) => "u4i128",
349 ty::Int(IntTy::Isize) => "u5isize",
350 ty::Uint(UintTy::U8) => "u2u8",
351 ty::Uint(UintTy::U16) => "u3u16",
352 ty::Uint(UintTy::U32) => "u3u32",
353 ty::Uint(UintTy::U64) => "u3u64",
354 ty::Uint(UintTy::U128) => "u4u128",
355 ty::Uint(UintTy::Usize) => "u5usize",
356 _ => bug!("encode_ty: unexpected `{:?}`", ty.kind()),
357 });
358 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
359 typeid.push_str(&s);
360 }
361
362 ty::Float(float_ty) => {
368 typeid.push_str(match float_ty {
369 FloatTy::F16 => "Dh",
370 FloatTy::F32 => "f",
371 FloatTy::F64 => "d",
372 FloatTy::F128 => "g",
373 });
374 }
375
376 ty::Char => {
377 let mut s = String::from("u4char");
379 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
380 typeid.push_str(&s);
381 }
382
383 ty::Str => {
384 let mut s = String::from("u3str");
386 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
387 typeid.push_str(&s);
388 }
389
390 ty::Never => {
391 let mut s = String::from("u5never");
393 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
394 typeid.push_str(&s);
395 }
396
397 _ if ty.is_unit() => {
400 typeid.push('v');
401 }
402
403 ty::Tuple(tys) => {
405 let mut s = String::from("u5tupleI");
407 for ty in tys.iter() {
408 s.push_str(&encode_ty(tcx, ty, dict, options));
409 }
410 s.push('E');
411 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
412 typeid.push_str(&s);
413 }
414
415 ty::Array(ty0, len) => {
416 let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi");
418 let mut s = String::from("A");
419 let _ = write!(s, "{len}");
420 s.push_str(&encode_ty(tcx, *ty0, dict, options));
421 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
422 typeid.push_str(&s);
423 }
424
425 ty::Pat(ty0, pat) => {
426 let mut s = String::from("u3patI");
428 s.push_str(&encode_ty(tcx, *ty0, dict, options));
429 write!(s, "{:?}", **pat).unwrap();
430 s.push('E');
431 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
432 typeid.push_str(&s);
433 }
434
435 ty::Slice(ty0) => {
436 let mut s = String::from("u5sliceI");
438 s.push_str(&encode_ty(tcx, *ty0, dict, options));
439 s.push('E');
440 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
441 typeid.push_str(&s);
442 }
443
444 ty::Adt(adt_def, args) => {
446 let mut s = String::new();
447 let def_id = adt_def.did();
448 if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) {
449 if let Some(value_str) = cfi_encoding.value_str() {
451 let value_str = value_str.as_str().trim();
452 if !value_str.is_empty() {
453 s.push_str(value_str);
454 let builtin_types = [
458 "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y",
459 "n", "o", "f", "d", "e", "g", "z", "Dh",
460 ];
461 if !builtin_types.contains(&value_str) {
462 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
463 }
464 } else {
465 #[allow(
466 rustc::diagnostic_outside_of_impl,
467 rustc::untranslatable_diagnostic
468 )]
469 tcx.dcx()
470 .struct_span_err(
471 cfi_encoding.span(),
472 format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
473 )
474 .emit();
475 }
476 } else {
477 bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
478 }
479 } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
480 let name = tcx.item_name(def_id).to_string();
494 let _ = write!(s, "{}{}", name.len(), name);
495 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
496 } else {
497 let name = encode_ty_name(tcx, def_id);
500 let _ = write!(s, "u{}{}", name.len(), name);
501 s.push_str(&encode_args(tcx, args, def_id, false, dict, options));
502 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
503 }
504 typeid.push_str(&s);
505 }
506
507 ty::Foreign(def_id) => {
508 let mut s = String::new();
510 if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) {
511 if let Some(value_str) = cfi_encoding.value_str() {
513 if !value_str.to_string().trim().is_empty() {
514 s.push_str(value_str.to_string().trim());
515 } else {
516 #[allow(
517 rustc::diagnostic_outside_of_impl,
518 rustc::untranslatable_diagnostic
519 )]
520 tcx.dcx()
521 .struct_span_err(
522 cfi_encoding.span(),
523 format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
524 )
525 .emit();
526 }
527 } else {
528 bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
529 }
530 } else {
531 let name = tcx.item_name(*def_id).to_string();
532 let _ = write!(s, "{}{}", name.len(), name);
533 }
534 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
535 typeid.push_str(&s);
536 }
537
538 ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
540 let mut s = String::new();
543 let name = encode_ty_name(tcx, *def_id);
544 let _ = write!(s, "u{}{}", name.len(), name);
545 s.push_str(&encode_args(tcx, args, *def_id, false, dict, options));
546 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
547 typeid.push_str(&s);
548 }
549
550 ty::CoroutineClosure(def_id, args) => {
551 let mut s = String::new();
554 let name = encode_ty_name(tcx, *def_id);
555 let _ = write!(s, "u{}{}", name.len(), name);
556 let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args());
557 s.push_str(&encode_args(tcx, parent_args, *def_id, false, dict, options));
558 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
559 typeid.push_str(&s);
560 }
561
562 ty::Coroutine(def_id, args, ..) => {
563 let mut s = String::new();
566 let name = encode_ty_name(tcx, *def_id);
567 let _ = write!(s, "u{}{}", name.len(), name);
568 s.push_str(&encode_args(
570 tcx,
571 tcx.mk_args(args.as_coroutine().parent_args()),
572 *def_id,
573 false,
574 dict,
575 options,
576 ));
577 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
578 typeid.push_str(&s);
579 }
580
581 ty::Ref(region, ty0, ..) => {
583 let mut s = String::new();
585 s.push_str("u3refI");
586 s.push_str(&encode_ty(tcx, *ty0, dict, options));
587 s.push('E');
588 compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s);
589 if ty.is_mutable_ptr() {
590 s = format!("{}{}", "U3mut", s);
591 compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
592 }
593 typeid.push_str(&s);
594 }
595
596 ty::RawPtr(ptr_ty, _mutbl) => {
597 let mut s = String::new();
600 s.push_str(&encode_ty(tcx, *ptr_ty, dict, options));
601 if !ty.is_mutable_ptr() {
602 s = format!("{}{}", "K", s);
603 compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s);
604 };
605 s = format!("{}{}", "P", s);
606 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
607 typeid.push_str(&s);
608 }
609
610 ty::FnPtr(sig_tys, hdr) => {
611 let mut s = String::from("P");
613 s.push_str(&encode_fnsig(
614 tcx,
615 &sig_tys.with(*hdr).skip_binder(),
616 dict,
617 TypeIdOptions::empty(),
618 ));
619 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
620 typeid.push_str(&s);
621 }
622
623 ty::UnsafeBinder(_) => {
625 todo!()
626 }
627
628 ty::Dynamic(predicates, region, kind) => {
630 let mut s = String::from(match kind {
633 ty::Dyn => "u3dynI",
634 ty::DynStar => "u7dynstarI",
635 });
636 s.push_str(&encode_predicates(tcx, predicates, dict, options));
637 s.push_str(&encode_region(*region, dict));
638 s.push('E');
639 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
640 typeid.push_str(&s);
641 }
642
643 ty::Param(..) => {
645 let mut s = String::from("u5param");
647 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
648 typeid.push_str(&s);
649 }
650
651 ty::Alias(..)
653 | ty::Bound(..)
654 | ty::Error(..)
655 | ty::CoroutineWitness(..)
656 | ty::Infer(..)
657 | ty::Placeholder(..) => {
658 bug!("encode_ty: unexpected `{:?}`", ty.kind());
659 }
660 };
661
662 typeid
663}
664
665fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
667 let mut s = String::new();
704
705 let mut def_path = tcx.def_path(def_id);
707 def_path.data.reverse();
708 for disambiguated_data in &def_path.data {
709 s.push('N');
710 s.push_str(match disambiguated_data.data {
711 hir::definitions::DefPathData::Impl => "I", hir::definitions::DefPathData::ForeignMod => "F", hir::definitions::DefPathData::TypeNs(..) => "t",
714 hir::definitions::DefPathData::ValueNs(..) => "v",
715 hir::definitions::DefPathData::Closure => "C",
716 hir::definitions::DefPathData::Ctor => "c",
717 hir::definitions::DefPathData::AnonConst => "k",
718 hir::definitions::DefPathData::OpaqueTy => "i",
719 hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
720 hir::definitions::DefPathData::NestedStatic => "n",
721 hir::definitions::DefPathData::CrateRoot
722 | hir::definitions::DefPathData::Use
723 | hir::definitions::DefPathData::GlobalAsm
724 | hir::definitions::DefPathData::MacroNs(..)
725 | hir::definitions::DefPathData::OpaqueLifetime(..)
726 | hir::definitions::DefPathData::LifetimeNs(..)
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}