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