]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / compiler / rustc_symbol_mangling / src / typeid / typeid_itanium_cxx_abi.rs
1 // For more information about type metadata and type metadata identifiers for cross-language LLVM
2 // CFI support, see Type metadata in the design document in the tracking issue #89653.
3
4 // FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective
5 // builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C"
6 // calling convention to use this encoding for cross-language LLVM CFI.
7
8 use bitflags::bitflags;
9 use core::fmt::Display;
10 use rustc_data_structures::base_n;
11 use rustc_data_structures::fx::FxHashMap;
12 use rustc_hir as hir;
13 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
14 use rustc_middle::ty::{
15     self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind,
16     Ty, TyCtxt, UintTy,
17 };
18 use rustc_span::def_id::DefId;
19 use rustc_span::symbol::sym;
20 use rustc_target::abi::call::{Conv, FnAbi};
21 use rustc_target::spec::abi::Abi;
22 use std::fmt::Write as _;
23
24 /// Type and extended type qualifiers.
25 #[derive(Eq, Hash, PartialEq)]
26 enum TyQ {
27     None,
28     Const,
29     Mut,
30 }
31
32 /// Substitution dictionary key.
33 #[derive(Eq, Hash, PartialEq)]
34 enum DictKey<'tcx> {
35     Ty(Ty<'tcx>, TyQ),
36     Region(Region<'tcx>),
37     Const(Const<'tcx>),
38     Predicate(ExistentialPredicate<'tcx>),
39 }
40
41 bitflags! {
42     /// Options for typeid_for_fnabi and typeid_for_fnsig.
43     pub struct TypeIdOptions: u32 {
44         const NO_OPTIONS = 0;
45         const GENERALIZE_POINTERS = 1;
46         const GENERALIZE_REPR_C = 2;
47     }
48 }
49
50 /// Options for encode_ty.
51 type EncodeTyOptions = TypeIdOptions;
52
53 /// Options for transform_ty.
54 type TransformTyOptions = TypeIdOptions;
55
56 /// Converts a number to a disambiguator (see
57 /// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
58 fn to_disambiguator(num: u64) -> String {
59     if let Some(num) = num.checked_sub(1) {
60         format!("s{}_", base_n::encode(num as u128, 62))
61     } else {
62         "s_".to_string()
63     }
64 }
65
66 /// Converts a number to a sequence number (see
67 /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
68 fn to_seq_id(num: usize) -> String {
69     if let Some(num) = num.checked_sub(1) {
70         base_n::encode(num as u128, 36).to_uppercase()
71     } else {
72         "".to_string()
73     }
74 }
75
76 /// Substitutes a component if found in the substitution dictionary (see
77 /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>).
78 fn compress<'tcx>(
79     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
80     key: DictKey<'tcx>,
81     comp: &mut String,
82 ) {
83     match dict.get(&key) {
84         Some(num) => {
85             comp.clear();
86             let _ = write!(comp, "S{}_", to_seq_id(*num));
87         }
88         None => {
89             dict.insert(key, dict.len());
90         }
91     }
92 }
93
94 // FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly
95 // along with other is_c_type methods.
96 /// Returns whether a `ty::Ty` is `c_void`.
97 fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
98     match ty.kind() {
99         ty::Adt(adt_def, ..) => {
100             let def_id = adt_def.0.did;
101             let crate_name = tcx.crate_name(def_id.krate);
102             tcx.item_name(def_id).as_str() == "c_void"
103                 && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
104         }
105         _ => false,
106     }
107 }
108
109 /// Encodes a const using the Itanium C++ ABI as a literal argument (see
110 /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
111 fn encode_const<'tcx>(
112     tcx: TyCtxt<'tcx>,
113     c: Const<'tcx>,
114     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
115     options: EncodeTyOptions,
116 ) -> String {
117     // L<element-type>[n]<element-value>E as literal argument
118     let mut s = String::from('L');
119
120     // Element type
121     s.push_str(&encode_ty(tcx, c.ty(), dict, options));
122
123     // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16,
124     // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1.
125     fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
126         if value < zero {
127             s.push('n')
128         };
129         let _ = write!(s, "{value}");
130     }
131
132     fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
133         let _ = write!(s, "{value}");
134     }
135
136     if let Some(scalar_int) = c.kind().try_to_scalar_int() {
137         let signed = c.ty().is_signed();
138         match scalar_int.size().bits() {
139             8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
140             16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
141             32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
142             64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
143             128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
144             8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
145             16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
146             32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
147             64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
148             128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
149             _ => {
150                 bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
151             }
152         };
153     } else {
154         bug!("encode_const: unexpected type `{:?}`", c.ty());
155     }
156
157     // Close the "L..E" pair
158     s.push('E');
159
160     compress(dict, DictKey::Const(c), &mut s);
161
162     s
163 }
164
165 /// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
166 /// Rust types that are not used at the FFI boundary.
167 #[instrument(level = "trace", skip(tcx, dict))]
168 fn encode_fnsig<'tcx>(
169     tcx: TyCtxt<'tcx>,
170     fn_sig: &FnSig<'tcx>,
171     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
172     options: TypeIdOptions,
173 ) -> String {
174     // Function types are delimited by an "F..E" pair
175     let mut s = String::from("F");
176
177     let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
178         .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
179     match fn_sig.abi {
180         Abi::C { .. } => {
181             encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
182         }
183         _ => {
184             encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
185         }
186     }
187
188     // Encode the return type
189     let transform_ty_options = TransformTyOptions::from_bits(options.bits())
190         .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
191     let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options);
192     s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
193
194     // Encode the parameter types
195     let tys = fn_sig.inputs();
196     if !tys.is_empty() {
197         for ty in tys {
198             let ty = transform_ty(tcx, *ty, transform_ty_options);
199             s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
200         }
201
202         if fn_sig.c_variadic {
203             s.push('z');
204         }
205     } else {
206         if fn_sig.c_variadic {
207             s.push('z');
208         } else {
209             // Empty parameter lists, whether declared as () or conventionally as (void), are
210             // encoded with a void parameter specifier "v".
211             s.push('v')
212         }
213     }
214
215     // Close the "F..E" pair
216     s.push('E');
217
218     s
219 }
220
221 /// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for
222 /// Rust types that are not used at the FFI boundary.
223 fn encode_predicate<'tcx>(
224     tcx: TyCtxt<'tcx>,
225     predicate: ty::PolyExistentialPredicate<'tcx>,
226     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
227     options: EncodeTyOptions,
228 ) -> String {
229     // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
230     // extended type.
231     let mut s = String::new();
232     match predicate.as_ref().skip_binder() {
233         ty::ExistentialPredicate::Trait(trait_ref) => {
234             let name = encode_ty_name(tcx, trait_ref.def_id);
235             let _ = write!(s, "u{}{}", name.len(), &name);
236             s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options));
237         }
238         ty::ExistentialPredicate::Projection(projection) => {
239             let name = encode_ty_name(tcx, projection.def_id);
240             let _ = write!(s, "u{}{}", name.len(), &name);
241             s.push_str(&encode_substs(tcx, projection.substs, dict, options));
242             match projection.term.unpack() {
243                 TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)),
244                 TermKind::Const(c) => s.push_str(&encode_const(tcx, c, dict, options)),
245             }
246         }
247         ty::ExistentialPredicate::AutoTrait(def_id) => {
248             let name = encode_ty_name(tcx, *def_id);
249             let _ = write!(s, "u{}{}", name.len(), &name);
250         }
251     };
252     compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
253     s
254 }
255
256 /// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for
257 /// Rust types that are not used at the FFI boundary.
258 fn encode_predicates<'tcx>(
259     tcx: TyCtxt<'tcx>,
260     predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
261     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
262     options: EncodeTyOptions,
263 ) -> String {
264     // <predicate1[..predicateN]>E as part of vendor extended type
265     let mut s = String::new();
266     let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates.iter().collect();
267     for predicate in predicates {
268         s.push_str(&encode_predicate(tcx, predicate, dict, options));
269     }
270     s
271 }
272
273 /// Encodes a region using the Itanium C++ ABI as a vendor extended type.
274 fn encode_region<'tcx>(
275     _tcx: TyCtxt<'tcx>,
276     region: Region<'tcx>,
277     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
278     _options: EncodeTyOptions,
279 ) -> String {
280     // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
281     let mut s = String::new();
282     match region.kind() {
283         RegionKind::ReLateBound(debruijn, r) => {
284             s.push_str("u6regionI");
285             // Debruijn index, which identifies the binder, as region disambiguator
286             let num = debruijn.index() as u64;
287             if num > 0 {
288                 s.push_str(&to_disambiguator(num));
289             }
290             // Index within the binder
291             let _ = write!(s, "{}", r.var.index() as u64);
292             s.push('E');
293             compress(dict, DictKey::Region(region), &mut s);
294         }
295         RegionKind::ReErased => {
296             s.push_str("u6region");
297             compress(dict, DictKey::Region(region), &mut s);
298         }
299         RegionKind::ReEarlyBound(..)
300         | RegionKind::ReFree(..)
301         | RegionKind::ReStatic
302         | RegionKind::ReVar(..)
303         | RegionKind::RePlaceholder(..) => {
304             bug!("encode_region: unexpected `{:?}`", region.kind());
305         }
306     }
307     s
308 }
309
310 /// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
311 /// types that are not used at the FFI boundary.
312 fn encode_substs<'tcx>(
313     tcx: TyCtxt<'tcx>,
314     substs: SubstsRef<'tcx>,
315     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
316     options: EncodeTyOptions,
317 ) -> String {
318     // [I<subst1..substN>E] as part of vendor extended type
319     let mut s = String::new();
320     let substs: Vec<GenericArg<'_>> = substs.iter().collect();
321     if !substs.is_empty() {
322         s.push('I');
323         for subst in substs {
324             match subst.unpack() {
325                 GenericArgKind::Lifetime(region) => {
326                     s.push_str(&encode_region(tcx, region, dict, options));
327                 }
328                 GenericArgKind::Type(ty) => {
329                     s.push_str(&encode_ty(tcx, ty, dict, options));
330                 }
331                 GenericArgKind::Const(c) => {
332                     s.push_str(&encode_const(tcx, c, dict, options));
333                 }
334             }
335         }
336         s.push('E');
337     }
338     s
339 }
340
341 /// Encodes a ty:Ty name, including its crate and path disambiguators and names.
342 fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
343     // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
344     // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
345     //
346     // N<namespace-tagN>..N<namespace-tag1>
347     // C<crate-disambiguator><crate-name>
348     // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
349     //
350     // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
351     //
352     //     pub type Type1 = impl Send;
353     //     let _: Type1 = <Struct1<i32>>::foo;
354     //     fn foo1(_: Type1) { }
355     //
356     //     pub type Type2 = impl Send;
357     //     let _: Type2 = <Trait1<i32>>::foo;
358     //     fn foo2(_: Type2) { }
359     //
360     //     pub type Type3 = impl Send;
361     //     let _: Type3 = <i32 as Trait1<i32>>::foo;
362     //     fn foo3(_: Type3) { }
363     //
364     //     pub type Type4 = impl Send;
365     //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
366     //     fn foo3(_: Type4) { }
367     //
368     // Are encoded as:
369     //
370     //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
371     //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
372     //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
373     //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
374     //
375     // The reason for not using v0's extended form of paths is to use a consistent and simpler
376     // encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e.,
377     // keep symbol names close to how methods are represented in error messages). See
378     // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
379     let mut s = String::new();
380
381     // Start and namespace tags
382     let mut def_path = tcx.def_path(def_id);
383     def_path.data.reverse();
384     for disambiguated_data in &def_path.data {
385         s.push('N');
386         s.push_str(match disambiguated_data.data {
387             hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
388             hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
389             hir::definitions::DefPathData::TypeNs(..) => "t",
390             hir::definitions::DefPathData::ValueNs(..) => "v",
391             hir::definitions::DefPathData::ClosureExpr => "C",
392             hir::definitions::DefPathData::Ctor => "c",
393             hir::definitions::DefPathData::AnonConst => "k",
394             hir::definitions::DefPathData::ImplTrait => "i",
395             hir::definitions::DefPathData::CrateRoot
396             | hir::definitions::DefPathData::Use
397             | hir::definitions::DefPathData::GlobalAsm
398             | hir::definitions::DefPathData::MacroNs(..)
399             | hir::definitions::DefPathData::LifetimeNs(..) => {
400                 bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
401             }
402         });
403     }
404
405     // Crate disambiguator and name
406     s.push('C');
407     s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64()));
408     let crate_name = tcx.crate_name(def_path.krate).to_string();
409     let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
410
411     // Disambiguators and names
412     def_path.data.reverse();
413     for disambiguated_data in &def_path.data {
414         let num = disambiguated_data.disambiguator as u64;
415         if num > 0 {
416             s.push_str(&to_disambiguator(num));
417         }
418
419         let name = disambiguated_data.data.to_string();
420         let _ = write!(s, "{}", name.len());
421
422         // Prepend a '_' if name starts with a digit or '_'
423         if let Some(first) = name.as_bytes().get(0) {
424             if first.is_ascii_digit() || *first == b'_' {
425                 s.push('_');
426             }
427         } else {
428             bug!("encode_ty_name: invalid name `{:?}`", name);
429         }
430
431         s.push_str(&name);
432     }
433
434     s
435 }
436
437 /// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
438 /// Rust types that are not used at the FFI boundary.
439 fn encode_ty<'tcx>(
440     tcx: TyCtxt<'tcx>,
441     ty: Ty<'tcx>,
442     dict: &mut FxHashMap<DictKey<'tcx>, usize>,
443     options: EncodeTyOptions,
444 ) -> String {
445     let mut typeid = String::new();
446
447     match ty.kind() {
448         // Primitive types
449         ty::Bool => {
450             typeid.push('b');
451         }
452
453         ty::Int(..) | ty::Uint(..) | ty::Float(..) => {
454             // u<length><type-name> as vendor extended type
455             let mut s = String::from(match ty.kind() {
456                 ty::Int(IntTy::I8) => "u2i8",
457                 ty::Int(IntTy::I16) => "u3i16",
458                 ty::Int(IntTy::I32) => "u3i32",
459                 ty::Int(IntTy::I64) => "u3i64",
460                 ty::Int(IntTy::I128) => "u4i128",
461                 ty::Int(IntTy::Isize) => "u5isize",
462                 ty::Uint(UintTy::U8) => "u2u8",
463                 ty::Uint(UintTy::U16) => "u3u16",
464                 ty::Uint(UintTy::U32) => "u3u32",
465                 ty::Uint(UintTy::U64) => "u3u64",
466                 ty::Uint(UintTy::U128) => "u4u128",
467                 ty::Uint(UintTy::Usize) => "u5usize",
468                 ty::Float(FloatTy::F32) => "u3f32",
469                 ty::Float(FloatTy::F64) => "u3f64",
470                 _ => "",
471             });
472             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
473             typeid.push_str(&s);
474         }
475
476         ty::Char => {
477             // u4char as vendor extended type
478             let mut s = String::from("u4char");
479             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
480             typeid.push_str(&s);
481         }
482
483         ty::Str => {
484             // u3str as vendor extended type
485             let mut s = String::from("u3str");
486             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
487             typeid.push_str(&s);
488         }
489
490         ty::Never => {
491             // u5never as vendor extended type
492             let mut s = String::from("u5never");
493             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
494             typeid.push_str(&s);
495         }
496
497         // Compound types
498         // () in Rust is equivalent to void return type in C
499         _ if ty.is_unit() => {
500             typeid.push('v');
501         }
502
503         // Sequence types
504         ty::Tuple(tys) => {
505             // u5tupleI<element-type1..element-typeN>E as vendor extended type
506             let mut s = String::from("u5tupleI");
507             for ty in tys.iter() {
508                 s.push_str(&encode_ty(tcx, ty, dict, options));
509             }
510             s.push('E');
511             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
512             typeid.push_str(&s);
513         }
514
515         ty::Array(ty0, len) => {
516             // A<array-length><element-type>
517             let mut s = String::from("A");
518             let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap());
519             s.push_str(&encode_ty(tcx, *ty0, dict, options));
520             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
521             typeid.push_str(&s);
522         }
523
524         ty::Slice(ty0) => {
525             // u5sliceI<element-type>E as vendor extended type
526             let mut s = String::from("u5sliceI");
527             s.push_str(&encode_ty(tcx, *ty0, dict, options));
528             s.push('E');
529             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
530             typeid.push_str(&s);
531         }
532
533         // User-defined types
534         ty::Adt(adt_def, substs) => {
535             let mut s = String::new();
536             let def_id = adt_def.0.did;
537             if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
538                 // For cross-language CFI support, the encoding must be compatible at the FFI
539                 // boundary. For instance:
540                 //
541                 //     struct type1 {};
542                 //     void foo(struct type1* bar) {}
543                 //
544                 // Is encoded as:
545                 //
546                 //     _ZTSFvP5type1E
547                 //
548                 // So, encode any repr(C) user-defined type for extern function types with the "C"
549                 // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where
550                 // <name> is <unscoped-name>.
551                 let name = tcx.item_name(def_id).to_string();
552                 let _ = write!(s, "{}{}", name.len(), &name);
553                 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
554             } else {
555                 // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is
556                 // <subst>, as vendor extended type.
557                 let name = encode_ty_name(tcx, def_id);
558                 let _ = write!(s, "u{}{}", name.len(), &name);
559                 s.push_str(&encode_substs(tcx, substs, dict, options));
560                 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
561             }
562             typeid.push_str(&s);
563         }
564
565         ty::Foreign(def_id) => {
566             // <length><name>, where <name> is <unscoped-name>
567             let mut s = String::new();
568             let name = tcx.item_name(*def_id).to_string();
569             let _ = write!(s, "{}{}", name.len(), &name);
570             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
571             typeid.push_str(&s);
572         }
573
574         // Function types
575         ty::FnDef(def_id, substs)
576         | ty::Closure(def_id, substs)
577         | ty::Generator(def_id, substs, ..) => {
578             // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
579             // as vendor extended type.
580             let mut s = String::new();
581             let name = encode_ty_name(tcx, *def_id);
582             let _ = write!(s, "u{}{}", name.len(), &name);
583             s.push_str(&encode_substs(tcx, substs, dict, options));
584             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
585             typeid.push_str(&s);
586         }
587
588         // Pointer types
589         ty::Ref(region, ty0, ..) => {
590             // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
591             let mut s = String::new();
592             s.push_str("u3refI");
593             s.push_str(&encode_ty(tcx, *ty0, dict, options));
594             s.push('E');
595             compress(dict, DictKey::Ty(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s);
596             if ty.is_mutable_ptr() {
597                 s = format!("{}{}", "U3mut", &s);
598                 compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
599             }
600             typeid.push_str(&s);
601         }
602
603         ty::RawPtr(tm) => {
604             // P[K]<element-type>
605             let mut s = String::new();
606             s.push_str(&encode_ty(tcx, tm.ty, dict, options));
607             if !ty.is_mutable_ptr() {
608                 s = format!("{}{}", "K", &s);
609                 compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s);
610             };
611             s = format!("{}{}", "P", &s);
612             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
613             typeid.push_str(&s);
614         }
615
616         ty::FnPtr(fn_sig) => {
617             // PF<return-type><parameter-type1..parameter-typeN>E
618             let mut s = String::from("P");
619             s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS));
620             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
621             typeid.push_str(&s);
622         }
623
624         // Trait types
625         ty::Dynamic(predicates, region, kind) => {
626             // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
627             // vendor extended type.
628             let mut s = String::from(match kind {
629                 ty::Dyn => "u3dynI",
630                 ty::DynStar => "u7dynstarI",
631             });
632             s.push_str(&encode_predicates(tcx, predicates, dict, options));
633             s.push_str(&encode_region(tcx, *region, dict, options));
634             s.push('E');
635             compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
636             typeid.push_str(&s);
637         }
638
639         // Unexpected types
640         ty::Bound(..)
641         | ty::Error(..)
642         | ty::GeneratorWitness(..)
643         | ty::GeneratorWitnessMIR(..)
644         | ty::Infer(..)
645         | ty::Alias(..)
646         | ty::Param(..)
647         | ty::Placeholder(..) => {
648             bug!("encode_ty: unexpected `{:?}`", ty.kind());
649         }
650     };
651
652     typeid
653 }
654
655 // Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
656 // c_void types into unit types unconditionally, and generalizes all pointers if
657 // TransformTyOptions::GENERALIZE_POINTERS option is set.
658 #[instrument(level = "trace", skip(tcx))]
659 fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
660     let mut ty = ty;
661
662     match ty.kind() {
663         ty::Bool
664         | ty::Int(..)
665         | ty::Uint(..)
666         | ty::Float(..)
667         | ty::Char
668         | ty::Str
669         | ty::Never
670         | ty::Foreign(..)
671         | ty::Dynamic(..) => {}
672
673         _ if ty.is_unit() => {}
674
675         ty::Tuple(tys) => {
676             ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
677         }
678
679         ty::Array(ty0, len) => {
680             let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap();
681             ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
682         }
683
684         ty::Slice(ty0) => {
685             ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
686         }
687
688         ty::Adt(adt_def, substs) => {
689             if is_c_void_ty(tcx, ty) {
690                 ty = tcx.mk_unit();
691             } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
692             {
693                 ty = tcx.mk_adt(*adt_def, ty::List::empty());
694             } else if adt_def.repr().transparent() && adt_def.is_struct() {
695                 let variant = adt_def.non_enum_variant();
696                 let param_env = tcx.param_env(variant.def_id);
697                 let field = variant.fields.iter().find(|field| {
698                     let ty = tcx.type_of(field.did);
699                     let is_zst =
700                         tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
701                     !is_zst
702                 });
703                 if let Some(field) = field {
704                     let ty0 = tcx.bound_type_of(field.did).subst(tcx, substs);
705                     // Generalize any repr(transparent) user-defined type that is either a pointer
706                     // or reference, and either references itself or any other type that contains or
707                     // references itself, to avoid a reference cycle.
708                     if ty0.is_any_ptr() && ty0.contains(ty) {
709                         ty = transform_ty(
710                             tcx,
711                             ty0,
712                             options | TransformTyOptions::GENERALIZE_POINTERS,
713                         );
714                     } else {
715                         ty = transform_ty(tcx, ty0, options);
716                     }
717                 } else {
718                     // Transform repr(transparent) types without non-ZST field into ()
719                     ty = tcx.mk_unit();
720                 }
721             } else {
722                 ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
723             }
724         }
725
726         ty::FnDef(def_id, substs) => {
727             ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
728         }
729
730         ty::Closure(def_id, substs) => {
731             ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
732         }
733
734         ty::Generator(def_id, substs, movability) => {
735             ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
736         }
737
738         ty::Ref(region, ty0, ..) => {
739             if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
740                 if ty.is_mutable_ptr() {
741                     ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit());
742                 } else {
743                     ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
744                 }
745             } else {
746                 if ty.is_mutable_ptr() {
747                     ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
748                 } else {
749                     ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
750                 }
751             }
752         }
753
754         ty::RawPtr(tm) => {
755             if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
756                 if ty.is_mutable_ptr() {
757                     ty = tcx.mk_mut_ptr(tcx.mk_unit());
758                 } else {
759                     ty = tcx.mk_imm_ptr(tcx.mk_unit());
760                 }
761             } else {
762                 if ty.is_mutable_ptr() {
763                     ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
764                 } else {
765                     ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
766                 }
767             }
768         }
769
770         ty::FnPtr(fn_sig) => {
771             if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
772                 ty = tcx.mk_imm_ptr(tcx.mk_unit());
773             } else {
774                 let parameters: Vec<Ty<'tcx>> = fn_sig
775                     .skip_binder()
776                     .inputs()
777                     .iter()
778                     .map(|ty| transform_ty(tcx, *ty, options))
779                     .collect();
780                 let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
781                 ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
782                     tcx.mk_fn_sig(
783                         parameters.iter(),
784                         &output,
785                         fn_sig.c_variadic(),
786                         fn_sig.unsafety(),
787                         fn_sig.abi(),
788                     ),
789                     fn_sig.bound_vars(),
790                 ));
791             }
792         }
793
794         ty::Bound(..)
795         | ty::Error(..)
796         | ty::GeneratorWitness(..)
797         | ty::GeneratorWitnessMIR(..)
798         | ty::Infer(..)
799         | ty::Alias(..)
800         | ty::Param(..)
801         | ty::Placeholder(..) => {
802             bug!("transform_ty: unexpected `{:?}`", ty.kind());
803         }
804     }
805
806     ty
807 }
808
809 /// Transforms substs for being encoded and used in the substitution dictionary.
810 fn transform_substs<'tcx>(
811     tcx: TyCtxt<'tcx>,
812     substs: SubstsRef<'tcx>,
813     options: TransformTyOptions,
814 ) -> SubstsRef<'tcx> {
815     let substs: Vec<GenericArg<'tcx>> = substs
816         .iter()
817         .map(|subst| {
818             if let GenericArgKind::Type(ty) = subst.unpack() {
819                 if is_c_void_ty(tcx, ty) {
820                     tcx.mk_unit().into()
821                 } else {
822                     transform_ty(tcx, ty, options).into()
823                 }
824             } else {
825                 subst
826             }
827         })
828         .collect();
829     tcx.mk_substs(substs.iter())
830 }
831
832 /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
833 /// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
834 #[instrument(level = "trace", skip(tcx))]
835 pub fn typeid_for_fnabi<'tcx>(
836     tcx: TyCtxt<'tcx>,
837     fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
838     options: TypeIdOptions,
839 ) -> String {
840     // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
841     // its type.
842     let mut typeid = String::from("_Z");
843
844     // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
845     // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
846     // code (i.e., 'TS') prefixed to the type encoding for the function.
847     typeid.push_str("TS");
848
849     // Function types are delimited by an "F..E" pair
850     typeid.push('F');
851
852     // A dictionary of substitution candidates used for compression (see
853     // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
854     let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
855
856     let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
857         .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
858     match fn_abi.conv {
859         Conv::C => {
860             encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
861         }
862         _ => {
863             encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
864         }
865     }
866
867     // Encode the return type
868     let transform_ty_options = TransformTyOptions::from_bits(options.bits())
869         .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
870     let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
871     typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
872
873     // Encode the parameter types
874     if !fn_abi.c_variadic {
875         if !fn_abi.args.is_empty() {
876             for arg in fn_abi.args.iter() {
877                 let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options);
878                 typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
879             }
880         } else {
881             // Empty parameter lists, whether declared as () or conventionally as (void), are
882             // encoded with a void parameter specifier "v".
883             typeid.push('v');
884         }
885     } else {
886         for n in 0..fn_abi.fixed_count as usize {
887             let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options);
888             typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
889         }
890
891         typeid.push('z');
892     }
893
894     // Close the "F..E" pair
895     typeid.push('E');
896
897     typeid
898 }
899
900 /// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor
901 /// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
902 pub fn typeid_for_fnsig<'tcx>(
903     tcx: TyCtxt<'tcx>,
904     fn_sig: &FnSig<'tcx>,
905     options: TypeIdOptions,
906 ) -> String {
907     // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
908     // its type.
909     let mut typeid = String::from("_Z");
910
911     // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
912     // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
913     // code (i.e., 'TS') prefixed to the type encoding for the function.
914     typeid.push_str("TS");
915
916     // A dictionary of substitution candidates used for compression (see
917     // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
918     let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
919
920     // Encode the function signature
921     typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));
922
923     typeid
924 }