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.
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.
8 use bitflags::bitflags;
9 use core::fmt::Display;
10 use rustc_data_structures::base_n;
11 use rustc_data_structures::fx::FxHashMap;
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,
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 _;
24 /// Type and extended type qualifiers.
25 #[derive(Eq, Hash, PartialEq)]
32 /// Substitution dictionary key.
33 #[derive(Eq, Hash, PartialEq)]
38 Predicate(ExistentialPredicate<'tcx>),
42 /// Options for typeid_for_fnabi and typeid_for_fnsig.
43 pub struct TypeIdOptions: u32 {
45 const GENERALIZE_POINTERS = 1;
46 const GENERALIZE_REPR_C = 2;
50 /// Options for encode_ty.
51 type EncodeTyOptions = TypeIdOptions;
53 /// Options for transform_ty.
54 type TransformTyOptions = TypeIdOptions;
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))
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()
76 /// Substitutes a component if found in the substitution dictionary (see
77 /// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>).
79 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
83 match dict.get(&key) {
86 let _ = write!(comp, "S{}_", to_seq_id(*num));
89 dict.insert(key, dict.len());
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 {
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)
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>(
114 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
115 options: EncodeTyOptions,
117 // L<element-type>[n]<element-value>E as literal argument
118 let mut s = String::from('L');
121 s.push_str(&encode_ty(tcx, c.ty(), dict, options));
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) {
129 let _ = write!(s, "{value}");
132 fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
133 let _ = write!(s, "{value}");
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()),
150 bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
154 bug!("encode_const: unexpected type `{:?}`", c.ty());
157 // Close the "L..E" pair
160 compress(dict, DictKey::Const(c), &mut s);
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>(
170 fn_sig: &FnSig<'tcx>,
171 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
172 options: TypeIdOptions,
174 // Function types are delimited by an "F..E" pair
175 let mut s = String::from("F");
177 let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
178 .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
181 encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
184 encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
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));
194 // Encode the parameter types
195 let tys = fn_sig.inputs();
198 let ty = transform_ty(tcx, *ty, transform_ty_options);
199 s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
202 if fn_sig.c_variadic {
206 if fn_sig.c_variadic {
209 // Empty parameter lists, whether declared as () or conventionally as (void), are
210 // encoded with a void parameter specifier "v".
215 // Close the "F..E" pair
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>(
225 predicate: ty::PolyExistentialPredicate<'tcx>,
226 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
227 options: EncodeTyOptions,
229 // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
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));
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)),
247 ty::ExistentialPredicate::AutoTrait(def_id) => {
248 let name = encode_ty_name(tcx, *def_id);
249 let _ = write!(s, "u{}{}", name.len(), &name);
252 compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
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>(
260 predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
261 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
262 options: EncodeTyOptions,
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));
273 /// Encodes a region using the Itanium C++ ABI as a vendor extended type.
274 fn encode_region<'tcx>(
276 region: Region<'tcx>,
277 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
278 _options: EncodeTyOptions,
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;
288 s.push_str(&to_disambiguator(num));
290 // Index within the binder
291 let _ = write!(s, "{}", r.var.index() as u64);
293 compress(dict, DictKey::Region(region), &mut s);
295 RegionKind::ReErased => {
296 s.push_str("u6region");
297 compress(dict, DictKey::Region(region), &mut s);
299 RegionKind::ReEarlyBound(..)
300 | RegionKind::ReFree(..)
301 | RegionKind::ReStatic
302 | RegionKind::ReVar(..)
303 | RegionKind::RePlaceholder(..) => {
304 bug!("encode_region: unexpected `{:?}`", region.kind());
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>(
314 substs: SubstsRef<'tcx>,
315 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
316 options: EncodeTyOptions,
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() {
323 for subst in substs {
324 match subst.unpack() {
325 GenericArgKind::Lifetime(region) => {
326 s.push_str(&encode_region(tcx, region, dict, options));
328 GenericArgKind::Type(ty) => {
329 s.push_str(&encode_ty(tcx, ty, dict, options));
331 GenericArgKind::Const(c) => {
332 s.push_str(&encode_const(tcx, c, dict, options));
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:
346 // N<namespace-tagN>..N<namespace-tag1>
347 // C<crate-disambiguator><crate-name>
348 // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
350 // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
352 // pub type Type1 = impl Send;
353 // let _: Type1 = <Struct1<i32>>::foo;
354 // fn foo1(_: Type1) { }
356 // pub type Type2 = impl Send;
357 // let _: Type2 = <Trait1<i32>>::foo;
358 // fn foo2(_: Type2) { }
360 // pub type Type3 = impl Send;
361 // let _: Type3 = <i32 as Trait1<i32>>::foo;
362 // fn foo3(_: Type3) { }
364 // pub type Type4 = impl Send;
365 // let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
366 // fn foo3(_: Type4) { }
370 // _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
371 // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
372 // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
373 // _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
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();
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 {
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);
405 // Crate disambiguator and name
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);
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;
416 s.push_str(&to_disambiguator(num));
419 let name = disambiguated_data.data.to_string();
420 let _ = write!(s, "{}", name.len());
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'_' {
428 bug!("encode_ty_name: invalid name `{:?}`", name);
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.
442 dict: &mut FxHashMap<DictKey<'tcx>, usize>,
443 options: EncodeTyOptions,
445 let mut typeid = String::new();
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",
472 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
477 // u4char as vendor extended type
478 let mut s = String::from("u4char");
479 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
484 // u3str as vendor extended type
485 let mut s = String::from("u3str");
486 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
491 // u5never as vendor extended type
492 let mut s = String::from("u5never");
493 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
498 // () in Rust is equivalent to void return type in C
499 _ if ty.is_unit() => {
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));
511 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
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);
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));
529 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
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:
542 // void foo(struct type1* bar) {}
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);
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);
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);
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);
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));
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);
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);
611 s = format!("{}{}", "P", &s);
612 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
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);
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 {
630 ty::DynStar => "u7dynstarI",
632 s.push_str(&encode_predicates(tcx, predicates, dict, options));
633 s.push_str(&encode_region(tcx, *region, dict, options));
635 compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
642 | ty::GeneratorWitness(..)
643 | ty::GeneratorWitnessMIR(..)
647 | ty::Placeholder(..) => {
648 bug!("encode_ty: unexpected `{:?}`", ty.kind());
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> {
671 | ty::Dynamic(..) => {}
673 _ if ty.is_unit() => {}
676 ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
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);
685 ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
688 ty::Adt(adt_def, substs) => {
689 if is_c_void_ty(tcx, ty) {
691 } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
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);
700 tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
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) {
712 options | TransformTyOptions::GENERALIZE_POINTERS,
715 ty = transform_ty(tcx, ty0, options);
718 // Transform repr(transparent) types without non-ZST field into ()
722 ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
726 ty::FnDef(def_id, substs) => {
727 ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
730 ty::Closure(def_id, substs) => {
731 ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
734 ty::Generator(def_id, substs, movability) => {
735 ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
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());
743 ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
746 if ty.is_mutable_ptr() {
747 ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
749 ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
755 if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
756 if ty.is_mutable_ptr() {
757 ty = tcx.mk_mut_ptr(tcx.mk_unit());
759 ty = tcx.mk_imm_ptr(tcx.mk_unit());
762 if ty.is_mutable_ptr() {
763 ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
765 ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
770 ty::FnPtr(fn_sig) => {
771 if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
772 ty = tcx.mk_imm_ptr(tcx.mk_unit());
774 let parameters: Vec<Ty<'tcx>> = fn_sig
778 .map(|ty| transform_ty(tcx, *ty, options))
780 let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
781 ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
796 | ty::GeneratorWitness(..)
797 | ty::GeneratorWitnessMIR(..)
801 | ty::Placeholder(..) => {
802 bug!("transform_ty: unexpected `{:?}`", ty.kind());
809 /// Transforms substs for being encoded and used in the substitution dictionary.
810 fn transform_substs<'tcx>(
812 substs: SubstsRef<'tcx>,
813 options: TransformTyOptions,
814 ) -> SubstsRef<'tcx> {
815 let substs: Vec<GenericArg<'tcx>> = substs
818 if let GenericArgKind::Type(ty) = subst.unpack() {
819 if is_c_void_ty(tcx, ty) {
822 transform_ty(tcx, ty, options).into()
829 tcx.mk_substs(substs.iter())
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>(
837 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
838 options: TypeIdOptions,
840 // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
842 let mut typeid = String::from("_Z");
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");
849 // Function types are delimited by an "F..E" pair
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();
856 let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
857 .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
860 encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
863 encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
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));
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));
881 // Empty parameter lists, whether declared as () or conventionally as (void), are
882 // encoded with a void parameter specifier "v".
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));
894 // Close the "F..E" pair
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>(
904 fn_sig: &FnSig<'tcx>,
905 options: TypeIdOptions,
907 // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
909 let mut typeid = String::from("_Z");
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");
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();
920 // Encode the function signature
921 typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));