]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_symbol_mangling / src / typeid / typeid_itanium_cxx_abi.rs
CommitLineData
064997fb
FG
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
8use bitflags::bitflags;
9use core::fmt::Display;
10use rustc_data_structures::base_n;
11use rustc_data_structures::fx::FxHashMap;
12use rustc_hir as hir;
13use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
14use rustc_middle::ty::{
487cf647
FG
15 self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind,
16 Ty, TyCtxt, UintTy,
064997fb
FG
17};
18use rustc_span::def_id::DefId;
19use rustc_span::symbol::sym;
20use rustc_target::abi::call::{Conv, FnAbi};
21use rustc_target::spec::abi::Abi;
22use std::fmt::Write as _;
23
24/// Type and extended type qualifiers.
25#[derive(Eq, Hash, PartialEq)]
26enum TyQ {
27 None,
28 Const,
29 Mut,
30}
31
32/// Substitution dictionary key.
33#[derive(Eq, Hash, PartialEq)]
34enum DictKey<'tcx> {
35 Ty(Ty<'tcx>, TyQ),
36 Region(Region<'tcx>),
37 Const(Const<'tcx>),
38 Predicate(ExistentialPredicate<'tcx>),
39}
40
41bitflags! {
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.
51type EncodeTyOptions = TypeIdOptions;
52
53/// Options for transform_ty.
54type 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>).
58fn 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>).
68fn 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>).
78fn 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`.
97fn 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>).
116fn 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.
172fn 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.
227fn encode_predicate<'tcx>(
228 tcx: TyCtxt<'tcx>,
487cf647 229 predicate: ty::PolyExistentialPredicate<'tcx>,
064997fb
FG
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));
f2b60f7d
FG
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)),
064997fb
FG
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.
262fn encode_predicates<'tcx>(
263 tcx: TyCtxt<'tcx>,
487cf647 264 predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
064997fb
FG
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();
487cf647 270 let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> =
064997fb
FG
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.
279fn 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(..)
f2b60f7d 308 | RegionKind::RePlaceholder(..) => {
064997fb
FG
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.
317fn 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.
347fn 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.
444fn 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() {
2b03887a 543 // For cross-language CFI support, the encoding must be compatible at the FFI
064997fb
FG
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
f2b60f7d 630 ty::Dynamic(predicates, region, kind) => {
064997fb
FG
631 // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
632 // vendor extended type.
f2b60f7d
FG
633 let mut s = String::from(match kind {
634 ty::Dyn => "u3dynI",
635 ty::DynStar => "u7dynstarI",
636 });
064997fb
FG
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.
663fn 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.
814fn 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.
838pub 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 {
f2b60f7d 889 for n in 0..fn_abi.fixed_count as usize {
064997fb
FG
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.
905pub 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}