rustc_sanitizers/cfi/typeid/itanium_cxx_abi/
encode.rs

1//! Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium
2//! C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that
3//! are not used across the FFI boundary.
4//!
5//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
6//! see design document in the tracking issue #89653.
7
8use std::fmt::Write as _;
9
10use rustc_abi::{ExternAbi, Integer};
11use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, CASE_INSENSITIVE, ToBaseN};
12use rustc_data_structures::fx::FxHashMap;
13use rustc_hir as hir;
14use rustc_middle::bug;
15use rustc_middle::ty::layout::IntegerExt;
16use rustc_middle::ty::{
17    self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
18    IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy,
19};
20use rustc_span::def_id::DefId;
21use rustc_span::sym;
22use tracing::instrument;
23
24use crate::cfi::typeid::TypeIdOptions;
25use crate::cfi::typeid::itanium_cxx_abi::transform::{TransformTy, TransformTyOptions};
26
27/// Options for encode_ty.
28pub(crate) type EncodeTyOptions = TypeIdOptions;
29
30/// Substitution dictionary key.
31#[derive(Eq, Hash, PartialEq)]
32pub(crate) enum DictKey<'tcx> {
33    Ty(Ty<'tcx>, TyQ),
34    Region(Region<'tcx>),
35    Const(Const<'tcx>),
36    Predicate(ExistentialPredicate<'tcx>),
37}
38
39/// Type and extended type qualifiers.
40#[derive(Eq, Hash, PartialEq)]
41pub(crate) enum TyQ {
42    None,
43    Const,
44    Mut,
45}
46
47/// Substitutes a component if found in the substitution dictionary (see
48/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>).
49fn compress<'tcx>(
50    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
51    key: DictKey<'tcx>,
52    comp: &mut String,
53) {
54    match dict.get(&key) {
55        Some(num) => {
56            comp.clear();
57            let _ = write!(comp, "S{}_", to_seq_id(*num));
58        }
59        None => {
60            dict.insert(key, dict.len());
61        }
62    }
63}
64
65/// Encodes args using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
66/// types that are not used at the FFI boundary.
67fn encode_args<'tcx>(
68    tcx: TyCtxt<'tcx>,
69    args: GenericArgsRef<'tcx>,
70    for_def: DefId,
71    has_erased_self: bool,
72    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
73    options: EncodeTyOptions,
74) -> String {
75    // [I<subst1..substN>E] as part of vendor extended type
76    let mut s = String::new();
77    let args: Vec<GenericArg<'_>> = args.iter().collect();
78    if !args.is_empty() {
79        s.push('I');
80        let def_generics = tcx.generics_of(for_def);
81        for (n, arg) in args.iter().enumerate() {
82            match arg.kind() {
83                GenericArgKind::Lifetime(region) => {
84                    s.push_str(&encode_region(region, dict));
85                }
86                GenericArgKind::Type(ty) => {
87                    s.push_str(&encode_ty(tcx, ty, dict, options));
88                }
89                GenericArgKind::Const(c) => {
90                    let n = n + (has_erased_self as usize);
91                    let ct_ty =
92                        tcx.type_of(def_generics.param_at(n, tcx).def_id).instantiate_identity();
93                    s.push_str(&encode_const(tcx, c, ct_ty, dict, options));
94                }
95            }
96        }
97        s.push('E');
98    }
99    s
100}
101
102/// Encodes a const using the Itanium C++ ABI as a literal argument (see
103/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
104fn encode_const<'tcx>(
105    tcx: TyCtxt<'tcx>,
106    ct: Const<'tcx>,
107    ct_ty: Ty<'tcx>,
108    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
109    options: EncodeTyOptions,
110) -> String {
111    // L<element-type>[n][<element-value>]E as literal argument
112    let mut s = String::from('L');
113
114    match ct.kind() {
115        // Const parameters
116        ty::ConstKind::Param(..) => {
117            // L<element-type>E as literal argument
118
119            // Element type
120            s.push_str(&encode_ty(tcx, ct_ty, dict, options));
121        }
122
123        // Literal arguments
124        ty::ConstKind::Value(cv) => {
125            // L<element-type>[n]<element-value>E as literal argument
126
127            // Element type
128            s.push_str(&encode_ty(tcx, cv.ty, dict, options));
129
130            // The only allowed types of const values are bool, u8, u16, u32,
131            // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
132            // bool value false is encoded as 0 and true as 1.
133            match cv.ty.kind() {
134                ty::Int(ity) => {
135                    let bits = cv
136                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
137                        .expect("expected monomorphic const in cfi");
138                    let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
139                    if val < 0 {
140                        s.push('n');
141                    }
142                    let _ = write!(s, "{val}");
143                }
144                ty::Uint(_) => {
145                    let val = cv
146                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
147                        .expect("expected monomorphic const in cfi");
148                    let _ = write!(s, "{val}");
149                }
150                ty::Bool => {
151                    let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
152                    let _ = write!(s, "{val}");
153                }
154                _ => {
155                    bug!("encode_const: unexpected type `{:?}`", cv.ty);
156                }
157            }
158        }
159
160        _ => {
161            bug!("encode_const: unexpected kind `{:?}`", ct.kind());
162        }
163    }
164
165    // Close the "L..E" pair
166    s.push('E');
167
168    compress(dict, DictKey::Const(ct), &mut s);
169
170    s
171}
172
173/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
174/// Rust types that are not used at the FFI boundary.
175fn encode_fnsig<'tcx>(
176    tcx: TyCtxt<'tcx>,
177    fn_sig: &FnSig<'tcx>,
178    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
179    options: TypeIdOptions,
180) -> String {
181    // Function types are delimited by an "F..E" pair
182    let mut s = String::from("F");
183
184    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
185        .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
186    match fn_sig.abi {
187        ExternAbi::C { .. } => {
188            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
189        }
190        _ => {
191            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
192        }
193    }
194
195    // Encode the return type
196    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
197        .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
198    let mut type_folder = TransformTy::new(tcx, transform_ty_options);
199    let ty = fn_sig.output().fold_with(&mut type_folder);
200    s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
201
202    // Encode the parameter types
203    let tys = fn_sig.inputs();
204    if !tys.is_empty() {
205        for ty in tys {
206            let ty = ty.fold_with(&mut type_folder);
207            s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
208        }
209
210        if fn_sig.c_variadic {
211            s.push('z');
212        }
213    } else if fn_sig.c_variadic {
214        s.push('z');
215    } else {
216        // Empty parameter lists, whether declared as () or conventionally as (void), are
217        // encoded with a void parameter specifier "v".
218        s.push('v')
219    }
220
221    // Close the "F..E" pair
222    s.push('E');
223
224    s
225}
226
227/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for
228/// Rust types that are not used at the FFI boundary.
229fn encode_predicate<'tcx>(
230    tcx: TyCtxt<'tcx>,
231    predicate: ty::PolyExistentialPredicate<'tcx>,
232    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
233    options: EncodeTyOptions,
234) -> String {
235    // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
236    // extended type.
237    let mut s = String::new();
238    match predicate.as_ref().skip_binder() {
239        ty::ExistentialPredicate::Trait(trait_ref) => {
240            let name = encode_ty_name(tcx, trait_ref.def_id);
241            let _ = write!(s, "u{}{}", name.len(), name);
242            s.push_str(&encode_args(tcx, trait_ref.args, trait_ref.def_id, true, dict, options));
243        }
244        ty::ExistentialPredicate::Projection(projection) => {
245            let name = encode_ty_name(tcx, projection.def_id);
246            let _ = write!(s, "u{}{}", name.len(), name);
247            s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options));
248            match projection.term.kind() {
249                TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)),
250                TermKind::Const(c) => s.push_str(&encode_const(
251                    tcx,
252                    c,
253                    tcx.type_of(projection.def_id).instantiate(tcx, projection.args),
254                    dict,
255                    options,
256                )),
257            }
258        }
259        ty::ExistentialPredicate::AutoTrait(def_id) => {
260            let name = encode_ty_name(tcx, *def_id);
261            let _ = write!(s, "u{}{}", name.len(), name);
262        }
263    };
264    compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
265    s
266}
267
268/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for
269/// Rust types that are not used at the FFI boundary.
270fn encode_predicates<'tcx>(
271    tcx: TyCtxt<'tcx>,
272    predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
273    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
274    options: EncodeTyOptions,
275) -> String {
276    // <predicate1[..predicateN]>E as part of vendor extended type
277    let mut s = String::new();
278    let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates.iter().collect();
279    for predicate in predicates {
280        s.push_str(&encode_predicate(tcx, predicate, dict, options));
281    }
282    s
283}
284
285/// Encodes a region using the Itanium C++ ABI as a vendor extended type.
286fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>, usize>) -> String {
287    // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
288    let mut s = String::new();
289    match region.kind() {
290        RegionKind::ReBound(ty::BoundVarIndexKind::Bound(debruijn), r) => {
291            s.push_str("u6regionI");
292            // Debruijn index, which identifies the binder, as region disambiguator
293            let num = debruijn.index() as u64;
294            if num > 0 {
295                s.push_str(&to_disambiguator(num));
296            }
297            // Index within the binder
298            let _ = write!(s, "{}", r.var.index() as u64);
299            s.push('E');
300            compress(dict, DictKey::Region(region), &mut s);
301        }
302        RegionKind::ReErased => {
303            s.push_str("u6region");
304            compress(dict, DictKey::Region(region), &mut s);
305        }
306        RegionKind::ReBound(ty::BoundVarIndexKind::Canonical, _)
307        | RegionKind::ReEarlyParam(..)
308        | RegionKind::ReLateParam(..)
309        | RegionKind::ReStatic
310        | RegionKind::ReError(_)
311        | RegionKind::ReVar(..)
312        | RegionKind::RePlaceholder(..) => {
313            bug!("encode_region: unexpected `{:?}`", region.kind());
314        }
315    }
316    s
317}
318
319/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
320/// Rust types that are not used at the FFI boundary.
321#[instrument(level = "trace", skip(tcx, dict))]
322pub(crate) fn encode_ty<'tcx>(
323    tcx: TyCtxt<'tcx>,
324    ty: Ty<'tcx>,
325    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
326    options: EncodeTyOptions,
327) -> String {
328    let mut typeid = String::new();
329
330    match ty.kind() {
331        // Primitive types
332
333        // Rust's bool has the same layout as C17's _Bool, that is, its size and alignment are
334        // implementation-defined. Any bool can be cast into an integer, taking on the values 1
335        // (true) or 0 (false).
336        //
337        // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
338        ty::Bool => {
339            typeid.push('b');
340        }
341
342        ty::Int(..) | ty::Uint(..) => {
343            // u<length><type-name> as vendor extended type
344            let mut s = String::from(match ty.kind() {
345                ty::Int(IntTy::I8) => "u2i8",
346                ty::Int(IntTy::I16) => "u3i16",
347                ty::Int(IntTy::I32) => "u3i32",
348                ty::Int(IntTy::I64) => "u3i64",
349                ty::Int(IntTy::I128) => "u4i128",
350                ty::Int(IntTy::Isize) => "u5isize",
351                ty::Uint(UintTy::U8) => "u2u8",
352                ty::Uint(UintTy::U16) => "u3u16",
353                ty::Uint(UintTy::U32) => "u3u32",
354                ty::Uint(UintTy::U64) => "u3u64",
355                ty::Uint(UintTy::U128) => "u4u128",
356                ty::Uint(UintTy::Usize) => "u5usize",
357                _ => bug!("encode_ty: unexpected `{:?}`", ty.kind()),
358            });
359            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
360            typeid.push_str(&s);
361        }
362
363        // Rust's f16, f32, f64, and f126 half (16-bit), single (32-bit), double (64-bit), and
364        // quad (128-bit)  precision floating-point types have IEEE-754 binary16, binary32,
365        // binary64, and binary128 floating-point layouts, respectively.
366        //
367        // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#fixed-width-floating-point-types.)
368        ty::Float(float_ty) => {
369            typeid.push_str(match float_ty {
370                FloatTy::F16 => "Dh",
371                FloatTy::F32 => "f",
372                FloatTy::F64 => "d",
373                FloatTy::F128 => "g",
374            });
375        }
376
377        ty::Char => {
378            // u4char as vendor extended type
379            let mut s = String::from("u4char");
380            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
381            typeid.push_str(&s);
382        }
383
384        ty::Str => {
385            // u3str as vendor extended type
386            let mut s = String::from("u3str");
387            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
388            typeid.push_str(&s);
389        }
390
391        ty::Never => {
392            // u5never as vendor extended type
393            let mut s = String::from("u5never");
394            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
395            typeid.push_str(&s);
396        }
397
398        // Compound types
399        // () in Rust is equivalent to void return type in C
400        _ if ty.is_unit() => {
401            typeid.push('v');
402        }
403
404        // Sequence types
405        ty::Tuple(tys) => {
406            // u5tupleI<element-type1..element-typeN>E as vendor extended type
407            let mut s = String::from("u5tupleI");
408            for ty in tys.iter() {
409                s.push_str(&encode_ty(tcx, ty, dict, options));
410            }
411            s.push('E');
412            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
413            typeid.push_str(&s);
414        }
415
416        ty::Array(ty0, len) => {
417            // A<array-length><element-type>
418            let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi");
419            let mut s = String::from("A");
420            let _ = write!(s, "{len}");
421            s.push_str(&encode_ty(tcx, *ty0, dict, options));
422            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
423            typeid.push_str(&s);
424        }
425
426        ty::Pat(ty0, pat) => {
427            // u3patI<element-type><pattern>E as vendor extended type
428            let mut s = String::from("u3patI");
429            s.push_str(&encode_ty(tcx, *ty0, dict, options));
430            write!(s, "{:?}", **pat).unwrap();
431            s.push('E');
432            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
433            typeid.push_str(&s);
434        }
435
436        ty::Slice(ty0) => {
437            // u5sliceI<element-type>E as vendor extended type
438            let mut s = String::from("u5sliceI");
439            s.push_str(&encode_ty(tcx, *ty0, dict, options));
440            s.push('E');
441            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
442            typeid.push_str(&s);
443        }
444
445        // User-defined types
446        ty::Adt(adt_def, args) => {
447            let mut s = String::new();
448            let def_id = adt_def.did();
449            if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) {
450                // Use user-defined CFI encoding for type
451                if let Some(value_str) = cfi_encoding.value_str() {
452                    let value_str = value_str.as_str().trim();
453                    if !value_str.is_empty() {
454                        s.push_str(value_str);
455                        // Don't compress user-defined builtin types (see
456                        // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and
457                        // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
458                        let builtin_types = [
459                            "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y",
460                            "n", "o", "f", "d", "e", "g", "z", "Dh",
461                        ];
462                        if !builtin_types.contains(&value_str) {
463                            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
464                        }
465                    } else {
466                        #[allow(
467                            rustc::diagnostic_outside_of_impl,
468                            rustc::untranslatable_diagnostic
469                        )]
470                        tcx.dcx()
471                            .struct_span_err(
472                                cfi_encoding.span(),
473                                format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
474                            )
475                            .emit();
476                    }
477                } else {
478                    bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
479                }
480            } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
481                // For cross-language LLVM CFI support, the encoding must be compatible at the FFI
482                // boundary. For instance:
483                //
484                //     struct type1 {};
485                //     void foo(struct type1* bar) {}
486                //
487                // Is encoded as:
488                //
489                //     _ZTSFvP5type1E
490                //
491                // So, encode any repr(C) user-defined type for extern function types with the "C"
492                // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where
493                // <name> is <unscoped-name>.
494                let name = tcx.item_name(def_id).to_string();
495                let _ = write!(s, "{}{}", name.len(), name);
496                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
497            } else {
498                // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is
499                // <subst>, as vendor extended type.
500                let name = encode_ty_name(tcx, def_id);
501                let _ = write!(s, "u{}{}", name.len(), name);
502                s.push_str(&encode_args(tcx, args, def_id, false, dict, options));
503                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
504            }
505            typeid.push_str(&s);
506        }
507
508        ty::Foreign(def_id) => {
509            // <length><name>, where <name> is <unscoped-name>
510            let mut s = String::new();
511            if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) {
512                // Use user-defined CFI encoding for type
513                if let Some(value_str) = cfi_encoding.value_str() {
514                    if !value_str.to_string().trim().is_empty() {
515                        s.push_str(value_str.to_string().trim());
516                    } else {
517                        #[allow(
518                            rustc::diagnostic_outside_of_impl,
519                            rustc::untranslatable_diagnostic
520                        )]
521                        tcx.dcx()
522                            .struct_span_err(
523                                cfi_encoding.span(),
524                                format!("invalid `cfi_encoding` for `{:?}`", ty.kind()),
525                            )
526                            .emit();
527                    }
528                } else {
529                    bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
530                }
531            } else {
532                let name = tcx.item_name(*def_id).to_string();
533                let _ = write!(s, "{}{}", name.len(), name);
534            }
535            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
536            typeid.push_str(&s);
537        }
538
539        // Function types
540        ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
541            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
542            // as vendor extended type.
543            let mut s = String::new();
544            let name = encode_ty_name(tcx, *def_id);
545            let _ = write!(s, "u{}{}", name.len(), name);
546            s.push_str(&encode_args(tcx, args, *def_id, false, dict, options));
547            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
548            typeid.push_str(&s);
549        }
550
551        ty::CoroutineClosure(def_id, args) => {
552            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
553            // as vendor extended type.
554            let mut s = String::new();
555            let name = encode_ty_name(tcx, *def_id);
556            let _ = write!(s, "u{}{}", name.len(), name);
557            let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args());
558            s.push_str(&encode_args(tcx, parent_args, *def_id, false, dict, options));
559            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
560            typeid.push_str(&s);
561        }
562
563        ty::Coroutine(def_id, args, ..) => {
564            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
565            // as vendor extended type.
566            let mut s = String::new();
567            let name = encode_ty_name(tcx, *def_id);
568            let _ = write!(s, "u{}{}", name.len(), name);
569            // Encode parent args only
570            s.push_str(&encode_args(
571                tcx,
572                tcx.mk_args(args.as_coroutine().parent_args()),
573                *def_id,
574                false,
575                dict,
576                options,
577            ));
578            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
579            typeid.push_str(&s);
580        }
581
582        // Pointer types
583        ty::Ref(region, ty0, ..) => {
584            // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
585            let mut s = String::new();
586            s.push_str("u3refI");
587            s.push_str(&encode_ty(tcx, *ty0, dict, options));
588            s.push('E');
589            compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s);
590            if ty.is_mutable_ptr() {
591                s = format!("{}{}", "U3mut", s);
592                compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
593            }
594            typeid.push_str(&s);
595        }
596
597        ty::RawPtr(ptr_ty, _mutbl) => {
598            // FIXME: This can definitely not be so spaghettified.
599            // P[K]<element-type>
600            let mut s = String::new();
601            s.push_str(&encode_ty(tcx, *ptr_ty, dict, options));
602            if !ty.is_mutable_ptr() {
603                s = format!("{}{}", "K", s);
604                compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s);
605            };
606            s = format!("{}{}", "P", s);
607            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
608            typeid.push_str(&s);
609        }
610
611        ty::FnPtr(sig_tys, hdr) => {
612            // PF<return-type><parameter-type1..parameter-typeN>E
613            let mut s = String::from("P");
614            s.push_str(&encode_fnsig(
615                tcx,
616                &sig_tys.with(*hdr).skip_binder(),
617                dict,
618                TypeIdOptions::empty(),
619            ));
620            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
621            typeid.push_str(&s);
622        }
623
624        // FIXME(unsafe_binders): Implement this.
625        ty::UnsafeBinder(_) => {
626            todo!()
627        }
628
629        // Trait types
630        ty::Dynamic(predicates, region) => {
631            // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
632            // vendor extended type.
633            let mut s = String::from("u3dynI");
634            s.push_str(&encode_predicates(tcx, predicates, dict, options));
635            s.push_str(&encode_region(*region, dict));
636            s.push('E');
637            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
638            typeid.push_str(&s);
639        }
640
641        // Type parameters
642        ty::Param(..) => {
643            // u5param as vendor extended type
644            let mut s = String::from("u5param");
645            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
646            typeid.push_str(&s);
647        }
648
649        // Unexpected types
650        ty::Alias(..)
651        | ty::Bound(..)
652        | ty::Error(..)
653        | ty::CoroutineWitness(..)
654        | ty::Infer(..)
655        | ty::Placeholder(..) => {
656            bug!("encode_ty: unexpected `{:?}`", ty.kind());
657        }
658    };
659
660    typeid
661}
662
663/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
664fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
665    // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
666    // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
667    //
668    // N<namespace-tagN>..N<namespace-tag1>
669    // C<crate-disambiguator><crate-name>
670    // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
671    //
672    // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
673    //
674    //     pub type Type1 = impl Send;
675    //     let _: Type1 = <Struct1<i32>>::foo;
676    //     fn foo1(_: Type1) { }
677    //
678    //     pub type Type2 = impl Send;
679    //     let _: Type2 = <Trait1<i32>>::foo;
680    //     fn foo2(_: Type2) { }
681    //
682    //     pub type Type3 = impl Send;
683    //     let _: Type3 = <i32 as Trait1<i32>>::foo;
684    //     fn foo3(_: Type3) { }
685    //
686    //     pub type Type4 = impl Send;
687    //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
688    //     fn foo3(_: Type4) { }
689    //
690    // Are encoded as:
691    //
692    //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
693    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
694    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
695    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
696    //
697    // The reason for not using v0's extended form of paths is to use a consistent and simpler
698    // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e.,
699    // keep symbol names close to how methods are represented in error messages). See
700    // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
701    let mut s = String::new();
702
703    // Start and namespace tags
704    let mut def_path = tcx.def_path(def_id);
705    def_path.data.reverse();
706    for disambiguated_data in &def_path.data {
707        s.push('N');
708        s.push_str(match disambiguated_data.data {
709            hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
710            hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
711            hir::definitions::DefPathData::TypeNs(..) => "t",
712            hir::definitions::DefPathData::ValueNs(..) => "v",
713            hir::definitions::DefPathData::Closure => "C",
714            hir::definitions::DefPathData::Ctor => "c",
715            hir::definitions::DefPathData::AnonConst => "K",
716            hir::definitions::DefPathData::LateAnonConst => "k",
717            hir::definitions::DefPathData::OpaqueTy => "i",
718            hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
719            hir::definitions::DefPathData::NestedStatic => "n",
720            hir::definitions::DefPathData::CrateRoot
721            | hir::definitions::DefPathData::Use
722            | hir::definitions::DefPathData::GlobalAsm
723            | hir::definitions::DefPathData::MacroNs(..)
724            | hir::definitions::DefPathData::OpaqueLifetime(..)
725            | hir::definitions::DefPathData::LifetimeNs(..)
726            | hir::definitions::DefPathData::DesugaredAnonymousLifetime
727            | hir::definitions::DefPathData::AnonAssocTy(..) => {
728                bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
729            }
730        });
731    }
732
733    // Crate disambiguator and name
734    s.push('C');
735    s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64()));
736    let crate_name = tcx.crate_name(def_path.krate).to_string();
737    let _ = write!(s, "{}{}", crate_name.len(), crate_name);
738
739    // Disambiguators and names
740    def_path.data.reverse();
741    for disambiguated_data in &def_path.data {
742        let num = disambiguated_data.disambiguator as u64;
743        if num > 0 {
744            s.push_str(&to_disambiguator(num));
745        }
746
747        let name = disambiguated_data.data.to_string();
748        let _ = write!(s, "{}", name.len());
749
750        // Prepend a '_' if name starts with a digit or '_'
751        if let Some(first) = name.as_bytes().first() {
752            if first.is_ascii_digit() || *first == b'_' {
753                s.push('_');
754            }
755        } else {
756            bug!("encode_ty_name: invalid name `{:?}`", name);
757        }
758
759        s.push_str(&name);
760    }
761
762    s
763}
764
765/// Converts a number to a disambiguator (see
766/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
767fn to_disambiguator(num: u64) -> String {
768    if let Some(num) = num.checked_sub(1) {
769        format!("s{}_", num.to_base(ALPHANUMERIC_ONLY))
770    } else {
771        "s_".to_string()
772    }
773}
774
775/// Converts a number to a sequence number (see
776/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
777fn to_seq_id(num: usize) -> String {
778    if let Some(num) = num.checked_sub(1) {
779        (num as u64).to_base(CASE_INSENSITIVE).to_uppercase()
780    } else {
781        "".to_string()
782    }
783}