Skip to main content

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_hir::find_attr;
15use rustc_middle::bug;
16use rustc_middle::ty::layout::IntegerExt;
17use rustc_middle::ty::{
18    self, Const, ExistentialPredicate, FloatTy, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
19    IntTy, List, Region, RegionKind, TermKind, Ty, TyCtxt, TypeFoldable, UintTy,
20};
21use rustc_span::def_id::DefId;
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(#[automatically_derived]
impl<'tcx> ::core::cmp::Eq for DictKey<'tcx> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Ty<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<TyQ>;
        let _: ::core::cmp::AssertParamIsEq<Region<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<Const<'tcx>>;
        let _: ::core::cmp::AssertParamIsEq<ExistentialPredicate<'tcx>>;
    }
}Eq, #[automatically_derived]
impl<'tcx> ::core::hash::Hash for DictKey<'tcx> {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state);
        match self {
            DictKey::Ty(__self_0, __self_1) => {
                ::core::hash::Hash::hash(__self_0, state);
                ::core::hash::Hash::hash(__self_1, state)
            }
            DictKey::Region(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            DictKey::Const(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
            DictKey::Predicate(__self_0) =>
                ::core::hash::Hash::hash(__self_0, state),
        }
    }
}Hash, #[automatically_derived]
impl<'tcx> ::core::cmp::PartialEq for DictKey<'tcx> {
    #[inline]
    fn eq(&self, other: &DictKey<'tcx>) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (DictKey::Ty(__self_0, __self_1),
                    DictKey::Ty(__arg1_0, __arg1_1)) =>
                    __self_0 == __arg1_0 && __self_1 == __arg1_1,
                (DictKey::Region(__self_0), DictKey::Region(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (DictKey::Const(__self_0), DictKey::Const(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (DictKey::Predicate(__self_0), DictKey::Predicate(__arg1_0))
                    => __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}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(#[automatically_derived]
impl ::core::cmp::Eq for TyQ {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for TyQ {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash, #[automatically_derived]
impl ::core::cmp::PartialEq for TyQ {
    #[inline]
    fn eq(&self, other: &TyQ) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}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 _ = comp.write_fmt(format_args!("S{0}_", to_seq_id(*num)))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 = tcx
92                        .type_of(def_generics.param_at(n, tcx).def_id)
93                        .instantiate_identity()
94                        .skip_norm_wip();
95                    s.push_str(&encode_const(tcx, c, ct_ty, dict, options));
96                }
97            }
98        }
99        s.push('E');
100    }
101    s
102}
103
104/// Encodes a const using the Itanium C++ ABI as a literal argument (see
105/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
106fn encode_const<'tcx>(
107    tcx: TyCtxt<'tcx>,
108    ct: Const<'tcx>,
109    ct_ty: Ty<'tcx>,
110    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
111    options: EncodeTyOptions,
112) -> String {
113    // L<element-type>[n][<element-value>]E as literal argument
114    let mut s = String::from('L');
115
116    match ct.kind() {
117        // Const parameters
118        ty::ConstKind::Param(..) => {
119            // L<element-type>E as literal argument
120
121            // Element type
122            s.push_str(&encode_ty(tcx, ct_ty, dict, options));
123        }
124
125        // Literal arguments
126        ty::ConstKind::Value(cv) => {
127            // L<element-type>[n]<element-value>E as literal argument
128
129            // Element type
130            s.push_str(&encode_ty(tcx, cv.ty, dict, options));
131
132            // The only allowed types of const values are bool, u8, u16, u32,
133            // u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
134            // bool value false is encoded as 0 and true as 1.
135            match cv.ty.kind() {
136                ty::Int(ity) => {
137                    let bits = cv
138                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
139                        .expect("expected monomorphic const in cfi");
140                    let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
141                    if val < 0 {
142                        s.push('n');
143                    }
144                    let _ = s.write_fmt(format_args!("{0}", val))write!(s, "{val}");
145                }
146                ty::Uint(_) => {
147                    let val = cv
148                        .try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
149                        .expect("expected monomorphic const in cfi");
150                    let _ = s.write_fmt(format_args!("{0}", val))write!(s, "{val}");
151                }
152                ty::Bool => {
153                    let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
154                    let _ = s.write_fmt(format_args!("{0}", val))write!(s, "{val}");
155                }
156                _ => {
157                    ::rustc_middle::util::bug::bug_fmt(format_args!("encode_const: unexpected type `{0:?}`",
        cv.ty));bug!("encode_const: unexpected type `{:?}`", cv.ty);
158                }
159            }
160        }
161
162        _ => {
163            ::rustc_middle::util::bug::bug_fmt(format_args!("encode_const: unexpected kind `{0:?}`",
        ct.kind()));bug!("encode_const: unexpected kind `{:?}`", ct.kind());
164        }
165    }
166
167    // Close the "L..E" pair
168    s.push('E');
169
170    compress(dict, DictKey::Const(ct), &mut s);
171
172    s
173}
174
175/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
176/// Rust types that are not used at the FFI boundary.
177fn encode_fnsig<'tcx>(
178    tcx: TyCtxt<'tcx>,
179    fn_sig: &FnSig<'tcx>,
180    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
181    options: TypeIdOptions,
182) -> String {
183    // Function types are delimited by an "F..E" pair
184    let mut s = String::from("F");
185
186    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
187        .unwrap_or_else(|| ::rustc_middle::util::bug::bug_fmt(format_args!("encode_fnsig: invalid option(s) `{0:?}`",
        options.bits()))bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
188    match fn_sig.abi() {
189        ExternAbi::C { .. } => {
190            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
191        }
192        _ => {
193            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
194        }
195    }
196
197    // Encode the return type
198    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
199        .unwrap_or_else(|| ::rustc_middle::util::bug::bug_fmt(format_args!("encode_fnsig: invalid option(s) `{0:?}`",
        options.bits()))bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
200    let mut type_folder = TransformTy::new(tcx, transform_ty_options);
201    let ty = fn_sig.output().fold_with(&mut type_folder);
202    s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
203
204    // Encode the parameter types
205    let tys = fn_sig.inputs();
206    if !tys.is_empty() {
207        for ty in tys {
208            let ty = ty.fold_with(&mut type_folder);
209            s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
210        }
211
212        if fn_sig.c_variadic() {
213            s.push('z');
214        }
215    } else if fn_sig.c_variadic() {
216        s.push('z');
217    } else {
218        // Empty parameter lists, whether declared as () or conventionally as (void), are
219        // encoded with a void parameter specifier "v".
220        s.push('v')
221    }
222
223    // Close the "F..E" pair
224    s.push('E');
225
226    s
227}
228
229/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for
230/// Rust types that are not used at the FFI boundary.
231fn encode_predicate<'tcx>(
232    tcx: TyCtxt<'tcx>,
233    predicate: ty::PolyExistentialPredicate<'tcx>,
234    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
235    options: EncodeTyOptions,
236) -> String {
237    // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
238    // extended type.
239    let mut s = String::new();
240    match predicate.as_ref().skip_binder() {
241        ty::ExistentialPredicate::Trait(trait_ref) => {
242            let name = encode_ty_name(tcx, trait_ref.def_id);
243            let _ = s.write_fmt(format_args!("u{0}{1}", name.len(), name))write!(s, "u{}{}", name.len(), name);
244            s.push_str(&encode_args(tcx, trait_ref.args, trait_ref.def_id, true, dict, options));
245        }
246        ty::ExistentialPredicate::Projection(projection) => {
247            let name = encode_ty_name(tcx, projection.def_id);
248            let _ = s.write_fmt(format_args!("u{0}{1}", name.len(), name))write!(s, "u{}{}", name.len(), name);
249            s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options));
250            match projection.term.kind() {
251                TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)),
252                TermKind::Const(c) => s.push_str(&encode_const(
253                    tcx,
254                    c,
255                    tcx.type_of(projection.def_id)
256                        .instantiate(tcx, projection.args)
257                        .skip_norm_wip(),
258                    dict,
259                    options,
260                )),
261            }
262        }
263        ty::ExistentialPredicate::AutoTrait(def_id) => {
264            let name = encode_ty_name(tcx, *def_id);
265            let _ = s.write_fmt(format_args!("u{0}{1}", name.len(), name))write!(s, "u{}{}", name.len(), name);
266        }
267    };
268    compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
269    s
270}
271
272/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for
273/// Rust types that are not used at the FFI boundary.
274fn encode_predicates<'tcx>(
275    tcx: TyCtxt<'tcx>,
276    predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
277    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
278    options: EncodeTyOptions,
279) -> String {
280    // <predicate1[..predicateN]>E as part of vendor extended type
281    let mut s = String::new();
282    let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates.iter().collect();
283    for predicate in predicates {
284        s.push_str(&encode_predicate(tcx, predicate, dict, options));
285    }
286    s
287}
288
289/// Encodes a region using the Itanium C++ ABI as a vendor extended type.
290fn encode_region<'tcx>(region: Region<'tcx>, dict: &mut FxHashMap<DictKey<'tcx>, usize>) -> String {
291    // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
292    let mut s = String::new();
293    match region.kind() {
294        RegionKind::ReBound(ty::BoundVarIndexKind::Bound(debruijn), r) => {
295            s.push_str("u6regionI");
296            // Debruijn index, which identifies the binder, as region disambiguator
297            let num = debruijn.index() as u64;
298            if num > 0 {
299                s.push_str(&to_disambiguator(num));
300            }
301            // Index within the binder
302            let _ = s.write_fmt(format_args!("{0}", r.var.index() as u64))write!(s, "{}", r.var.index() as u64);
303            s.push('E');
304            compress(dict, DictKey::Region(region), &mut s);
305        }
306        RegionKind::ReErased => {
307            s.push_str("u6region");
308            compress(dict, DictKey::Region(region), &mut s);
309        }
310        RegionKind::ReBound(ty::BoundVarIndexKind::Canonical, _)
311        | RegionKind::ReEarlyParam(..)
312        | RegionKind::ReLateParam(..)
313        | RegionKind::ReStatic
314        | RegionKind::ReError(_)
315        | RegionKind::ReVar(..)
316        | RegionKind::RePlaceholder(..) => {
317            ::rustc_middle::util::bug::bug_fmt(format_args!("encode_region: unexpected `{0:?}`",
        region.kind()));bug!("encode_region: unexpected `{:?}`", region.kind());
318        }
319    }
320    s
321}
322
323/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
324/// Rust types that are not used at the FFI boundary.
325#[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::TRACE <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("encode_ty",
                                    "rustc_sanitizers::cfi::typeid::itanium_cxx_abi::encode",
                                    ::tracing::Level::TRACE,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs"),
                                    ::tracing_core::__macro_support::Option::Some(325u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_sanitizers::cfi::typeid::itanium_cxx_abi::encode"),
                                    ::tracing_core::field::FieldSet::new(&["ty", "options"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::TRACE <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&ty)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&options)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: String = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let mut typeid = String::new();
            match ty.kind() {
                ty::Bool => { typeid.push('b'); }
                ty::Int(..) | ty::Uint(..) => {
                    let mut s =
                        String::from(match ty.kind() {
                                ty::Int(IntTy::I8) => "u2i8",
                                ty::Int(IntTy::I16) => "u3i16",
                                ty::Int(IntTy::I32) => "u3i32",
                                ty::Int(IntTy::I64) => "u3i64",
                                ty::Int(IntTy::I128) => "u4i128",
                                ty::Int(IntTy::Isize) => "u5isize",
                                ty::Uint(UintTy::U8) => "u2u8",
                                ty::Uint(UintTy::U16) => "u3u16",
                                ty::Uint(UintTy::U32) => "u3u32",
                                ty::Uint(UintTy::U64) => "u3u64",
                                ty::Uint(UintTy::U128) => "u4u128",
                                ty::Uint(UintTy::Usize) => "u5usize",
                                _ =>
                                    ::rustc_middle::util::bug::bug_fmt(format_args!("encode_ty: unexpected `{0:?}`",
                                            ty.kind())),
                            });
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Float(float_ty) => {
                    typeid.push_str(match float_ty {
                            FloatTy::F16 => "Dh",
                            FloatTy::F32 => "f",
                            FloatTy::F64 => "d",
                            FloatTy::F128 => "g",
                        });
                }
                ty::Char => {
                    let mut s = String::from("u4char");
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Str => {
                    let mut s = String::from("u3str");
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Never => {
                    let mut s = String::from("u5never");
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                _ if ty.is_unit() => { typeid.push('v'); }
                ty::Tuple(tys) => {
                    let mut s = String::from("u5tupleI");
                    for ty in tys.iter() {
                        s.push_str(&encode_ty(tcx, ty, dict, options));
                    }
                    s.push('E');
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Array(ty0, len) => {
                    let len =
                        len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi");
                    let mut s = String::from("A");
                    let _ = s.write_fmt(format_args!("{0}", len));
                    s.push_str(&encode_ty(tcx, *ty0, dict, options));
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Pat(ty0, pat) => {
                    let mut s = String::from("u3patI");
                    s.push_str(&encode_ty(tcx, *ty0, dict, options));
                    s.write_fmt(format_args!("{0:?}", **pat)).unwrap();
                    s.push('E');
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Slice(ty0) => {
                    let mut s = String::from("u5sliceI");
                    s.push_str(&encode_ty(tcx, *ty0, dict, options));
                    s.push('E');
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Adt(adt_def, args) => {
                    let mut s = String::new();
                    let def_id = adt_def.did();
                    if let Some(encoding) =
                            {
                                {
                                    'done:
                                        {
                                        for i in
                                            ::rustc_hir::attrs::HasAttrs::get_attrs(def_id, &tcx) {
                                            #[allow(unused_imports)]
                                            use rustc_hir::attrs::AttributeKind::*;
                                            let i: &rustc_hir::Attribute = i;
                                            match i {
                                                rustc_hir::Attribute::Parsed(CfiEncoding { encoding }) => {
                                                    break 'done Some(encoding);
                                                }
                                                rustc_hir::Attribute::Unparsed(..) =>
                                                    {}
                                                    #[deny(unreachable_patterns)]
                                                    _ => {}
                                            }
                                        }
                                        None
                                    }
                                }
                            } {
                        let encoding = encoding.as_str().trim();
                        s.push_str(&encoding);
                        let builtin_types =
                            ["v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m",
                                    "x", "y", "n", "o", "f", "d", "e", "g", "z", "Dh"];
                        if !builtin_types.contains(&encoding) {
                            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                        }
                    } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C)
                            && adt_def.repr().c() {
                        let name = tcx.item_name(def_id).to_string();
                        let _ =
                            s.write_fmt(format_args!("{0}{1}", name.len(), name));
                        compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    } else {
                        let name = encode_ty_name(tcx, def_id);
                        let _ =
                            s.write_fmt(format_args!("u{0}{1}", name.len(), name));
                        s.push_str(&encode_args(tcx, args, def_id, false, dict,
                                    options));
                        compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    }
                    typeid.push_str(&s);
                }
                ty::Foreign(def_id) => {
                    let mut s = String::new();
                    if let Some(encoding) =
                            {
                                {
                                    'done:
                                        {
                                        for i in
                                            ::rustc_hir::attrs::HasAttrs::get_attrs(*def_id, &tcx) {
                                            #[allow(unused_imports)]
                                            use rustc_hir::attrs::AttributeKind::*;
                                            let i: &rustc_hir::Attribute = i;
                                            match i {
                                                rustc_hir::Attribute::Parsed(CfiEncoding { encoding }) => {
                                                    break 'done Some(encoding);
                                                }
                                                rustc_hir::Attribute::Unparsed(..) =>
                                                    {}
                                                    #[deny(unreachable_patterns)]
                                                    _ => {}
                                            }
                                        }
                                        None
                                    }
                                }
                            } {
                        s.push_str(encoding.as_str().trim());
                    } else {
                        let name = tcx.item_name(*def_id).to_string();
                        let _ =
                            s.write_fmt(format_args!("{0}{1}", name.len(), name));
                    }
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
                    let mut s = String::new();
                    let name = encode_ty_name(tcx, *def_id);
                    let _ =
                        s.write_fmt(format_args!("u{0}{1}", name.len(), name));
                    s.push_str(&encode_args(tcx, args, *def_id, false, dict,
                                options));
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::CoroutineClosure(def_id, args) => {
                    let mut s = String::new();
                    let name = encode_ty_name(tcx, *def_id);
                    let _ =
                        s.write_fmt(format_args!("u{0}{1}", name.len(), name));
                    let parent_args =
                        tcx.mk_args(args.as_coroutine_closure().parent_args());
                    s.push_str(&encode_args(tcx, parent_args, *def_id, false,
                                dict, options));
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Coroutine(def_id, args, ..) => {
                    let mut s = String::new();
                    let name = encode_ty_name(tcx, *def_id);
                    let _ =
                        s.write_fmt(format_args!("u{0}{1}", name.len(), name));
                    s.push_str(&encode_args(tcx,
                                tcx.mk_args(args.as_coroutine().parent_args()), *def_id,
                                false, dict, options));
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Ref(region, ty0, ..) => {
                    let mut s = String::new();
                    s.push_str("u3refI");
                    s.push_str(&encode_ty(tcx, *ty0, dict, options));
                    s.push('E');
                    compress(dict,
                        DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None),
                        &mut s);
                    if ty.is_mutable_ptr() {
                        s =
                            ::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("{0}{1}", "U3mut", s))
                                });
                        compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
                    }
                    typeid.push_str(&s);
                }
                ty::RawPtr(ptr_ty, _mutbl) => {
                    let mut s = String::new();
                    s.push_str(&encode_ty(tcx, *ptr_ty, dict, options));
                    if !ty.is_mutable_ptr() {
                        s =
                            ::alloc::__export::must_use({
                                    ::alloc::fmt::format(format_args!("{0}{1}", "K", s))
                                });
                        compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s);
                    };
                    s =
                        ::alloc::__export::must_use({
                                ::alloc::fmt::format(format_args!("{0}{1}", "P", s))
                            });
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::FnPtr(sig_tys, hdr) => {
                    let mut s = String::from("P");
                    s.push_str(&encode_fnsig(tcx,
                                &sig_tys.with(*hdr).skip_binder(), dict,
                                TypeIdOptions::empty()));
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::UnsafeBinder(_) => {
                    ::core::panicking::panic("not yet implemented")
                }
                ty::Dynamic(predicates, region) => {
                    let mut s = String::from("u3dynI");
                    s.push_str(&encode_predicates(tcx, predicates, dict,
                                options));
                    s.push_str(&encode_region(*region, dict));
                    s.push('E');
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Param(..) => {
                    let mut s = String::from("u5param");
                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
                    typeid.push_str(&s);
                }
                ty::Alias(..) | ty::Bound(..) | ty::Error(..) |
                    ty::CoroutineWitness(..) | ty::Infer(..) |
                    ty::Placeholder(..) => {
                    ::rustc_middle::util::bug::bug_fmt(format_args!("encode_ty: unexpected `{0:?}`",
                            ty.kind()));
                }
            };
            typeid
        }
    }
}#[instrument(level = "trace", skip(tcx, dict))]
326pub(crate) fn encode_ty<'tcx>(
327    tcx: TyCtxt<'tcx>,
328    ty: Ty<'tcx>,
329    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
330    options: EncodeTyOptions,
331) -> String {
332    let mut typeid = String::new();
333
334    match ty.kind() {
335        // Primitive types
336
337        // Rust's bool has the same layout as C17's _Bool, that is, its size and alignment are
338        // implementation-defined. Any bool can be cast into an integer, taking on the values 1
339        // (true) or 0 (false).
340        //
341        // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
342        ty::Bool => {
343            typeid.push('b');
344        }
345
346        ty::Int(..) | ty::Uint(..) => {
347            // u<length><type-name> as vendor extended type
348            let mut s = String::from(match ty.kind() {
349                ty::Int(IntTy::I8) => "u2i8",
350                ty::Int(IntTy::I16) => "u3i16",
351                ty::Int(IntTy::I32) => "u3i32",
352                ty::Int(IntTy::I64) => "u3i64",
353                ty::Int(IntTy::I128) => "u4i128",
354                ty::Int(IntTy::Isize) => "u5isize",
355                ty::Uint(UintTy::U8) => "u2u8",
356                ty::Uint(UintTy::U16) => "u3u16",
357                ty::Uint(UintTy::U32) => "u3u32",
358                ty::Uint(UintTy::U64) => "u3u64",
359                ty::Uint(UintTy::U128) => "u4u128",
360                ty::Uint(UintTy::Usize) => "u5usize",
361                _ => bug!("encode_ty: unexpected `{:?}`", ty.kind()),
362            });
363            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
364            typeid.push_str(&s);
365        }
366
367        // Rust's f16, f32, f64, and f126 half (16-bit), single (32-bit), double (64-bit), and
368        // quad (128-bit)  precision floating-point types have IEEE-754 binary16, binary32,
369        // binary64, and binary128 floating-point layouts, respectively.
370        //
371        // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#fixed-width-floating-point-types.)
372        ty::Float(float_ty) => {
373            typeid.push_str(match float_ty {
374                FloatTy::F16 => "Dh",
375                FloatTy::F32 => "f",
376                FloatTy::F64 => "d",
377                FloatTy::F128 => "g",
378            });
379        }
380
381        ty::Char => {
382            // u4char as vendor extended type
383            let mut s = String::from("u4char");
384            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
385            typeid.push_str(&s);
386        }
387
388        ty::Str => {
389            // u3str as vendor extended type
390            let mut s = String::from("u3str");
391            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
392            typeid.push_str(&s);
393        }
394
395        ty::Never => {
396            // u5never as vendor extended type
397            let mut s = String::from("u5never");
398            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
399            typeid.push_str(&s);
400        }
401
402        // Compound types
403        // () in Rust is equivalent to void return type in C
404        _ if ty.is_unit() => {
405            typeid.push('v');
406        }
407
408        // Sequence types
409        ty::Tuple(tys) => {
410            // u5tupleI<element-type1..element-typeN>E as vendor extended type
411            let mut s = String::from("u5tupleI");
412            for ty in tys.iter() {
413                s.push_str(&encode_ty(tcx, ty, dict, options));
414            }
415            s.push('E');
416            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
417            typeid.push_str(&s);
418        }
419
420        ty::Array(ty0, len) => {
421            // A<array-length><element-type>
422            let len = len.try_to_target_usize(tcx).expect("expected monomorphic const in cfi");
423            let mut s = String::from("A");
424            let _ = write!(s, "{len}");
425            s.push_str(&encode_ty(tcx, *ty0, dict, options));
426            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
427            typeid.push_str(&s);
428        }
429
430        ty::Pat(ty0, pat) => {
431            // u3patI<element-type><pattern>E as vendor extended type
432            let mut s = String::from("u3patI");
433            s.push_str(&encode_ty(tcx, *ty0, dict, options));
434            write!(s, "{:?}", **pat).unwrap();
435            s.push('E');
436            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
437            typeid.push_str(&s);
438        }
439
440        ty::Slice(ty0) => {
441            // u5sliceI<element-type>E as vendor extended type
442            let mut s = String::from("u5sliceI");
443            s.push_str(&encode_ty(tcx, *ty0, dict, options));
444            s.push('E');
445            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
446            typeid.push_str(&s);
447        }
448
449        // User-defined types
450        ty::Adt(adt_def, args) => {
451            let mut s = String::new();
452            let def_id = adt_def.did();
453            if let Some(encoding) = find_attr!(tcx, def_id, CfiEncoding { encoding } => encoding) {
454                let encoding = encoding.as_str().trim();
455                // Use user-defined CFI encoding for type
456                s.push_str(&encoding);
457                // Don't compress user-defined builtin types (see
458                // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-builtin and
459                // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
460                let builtin_types = [
461                    "v", "w", "b", "c", "a", "h", "s", "t", "i", "j", "l", "m", "x", "y", "n", "o",
462                    "f", "d", "e", "g", "z", "Dh",
463                ];
464                if !builtin_types.contains(&encoding) {
465                    compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
466                }
467            } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
468                // For cross-language LLVM CFI support, the encoding must be compatible at the FFI
469                // boundary. For instance:
470                //
471                //     struct type1 {};
472                //     void foo(struct type1* bar) {}
473                //
474                // Is encoded as:
475                //
476                //     _ZTSFvP5type1E
477                //
478                // So, encode any repr(C) user-defined type for extern function types with the "C"
479                // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where
480                // <name> is <unscoped-name>.
481                let name = tcx.item_name(def_id).to_string();
482                let _ = write!(s, "{}{}", name.len(), name);
483                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
484            } else {
485                // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is
486                // <subst>, as vendor extended type.
487                let name = encode_ty_name(tcx, def_id);
488                let _ = write!(s, "u{}{}", name.len(), name);
489                s.push_str(&encode_args(tcx, args, def_id, false, dict, options));
490                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
491            }
492            typeid.push_str(&s);
493        }
494
495        ty::Foreign(def_id) => {
496            // <length><name>, where <name> is <unscoped-name>
497            let mut s = String::new();
498
499            if let Some(encoding) = find_attr!(tcx, *def_id, CfiEncoding {encoding} => encoding) {
500                // Use user-defined CFI encoding for type
501                s.push_str(encoding.as_str().trim());
502            } else {
503                let name = tcx.item_name(*def_id).to_string();
504                let _ = write!(s, "{}{}", name.len(), name);
505            }
506            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
507            typeid.push_str(&s);
508        }
509
510        // Function types
511        ty::FnDef(def_id, args) | ty::Closure(def_id, args) => {
512            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
513            // as vendor extended type.
514            let mut s = String::new();
515            let name = encode_ty_name(tcx, *def_id);
516            let _ = write!(s, "u{}{}", name.len(), name);
517            s.push_str(&encode_args(tcx, args, *def_id, false, dict, options));
518            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
519            typeid.push_str(&s);
520        }
521
522        ty::CoroutineClosure(def_id, args) => {
523            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
524            // as vendor extended type.
525            let mut s = String::new();
526            let name = encode_ty_name(tcx, *def_id);
527            let _ = write!(s, "u{}{}", name.len(), name);
528            let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args());
529            s.push_str(&encode_args(tcx, parent_args, *def_id, false, dict, options));
530            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
531            typeid.push_str(&s);
532        }
533
534        ty::Coroutine(def_id, args, ..) => {
535            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
536            // as vendor extended type.
537            let mut s = String::new();
538            let name = encode_ty_name(tcx, *def_id);
539            let _ = write!(s, "u{}{}", name.len(), name);
540            // Encode parent args only
541            s.push_str(&encode_args(
542                tcx,
543                tcx.mk_args(args.as_coroutine().parent_args()),
544                *def_id,
545                false,
546                dict,
547                options,
548            ));
549            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
550            typeid.push_str(&s);
551        }
552
553        // Pointer types
554        ty::Ref(region, ty0, ..) => {
555            // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
556            let mut s = String::new();
557            s.push_str("u3refI");
558            s.push_str(&encode_ty(tcx, *ty0, dict, options));
559            s.push('E');
560            compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s);
561            if ty.is_mutable_ptr() {
562                s = format!("{}{}", "U3mut", s);
563                compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
564            }
565            typeid.push_str(&s);
566        }
567
568        ty::RawPtr(ptr_ty, _mutbl) => {
569            // FIXME: This can definitely not be so spaghettified.
570            // P[K]<element-type>
571            let mut s = String::new();
572            s.push_str(&encode_ty(tcx, *ptr_ty, dict, options));
573            if !ty.is_mutable_ptr() {
574                s = format!("{}{}", "K", s);
575                compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s);
576            };
577            s = format!("{}{}", "P", s);
578            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
579            typeid.push_str(&s);
580        }
581
582        ty::FnPtr(sig_tys, hdr) => {
583            // PF<return-type><parameter-type1..parameter-typeN>E
584            let mut s = String::from("P");
585            s.push_str(&encode_fnsig(
586                tcx,
587                &sig_tys.with(*hdr).skip_binder(),
588                dict,
589                TypeIdOptions::empty(),
590            ));
591            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
592            typeid.push_str(&s);
593        }
594
595        // FIXME(unsafe_binders): Implement this.
596        ty::UnsafeBinder(_) => {
597            todo!()
598        }
599
600        // Trait types
601        ty::Dynamic(predicates, region) => {
602            // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
603            // vendor extended type.
604            let mut s = String::from("u3dynI");
605            s.push_str(&encode_predicates(tcx, predicates, dict, options));
606            s.push_str(&encode_region(*region, dict));
607            s.push('E');
608            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
609            typeid.push_str(&s);
610        }
611
612        // Type parameters
613        ty::Param(..) => {
614            // u5param as vendor extended type
615            let mut s = String::from("u5param");
616            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
617            typeid.push_str(&s);
618        }
619
620        // Unexpected types
621        ty::Alias(..)
622        | ty::Bound(..)
623        | ty::Error(..)
624        | ty::CoroutineWitness(..)
625        | ty::Infer(..)
626        | ty::Placeholder(..) => {
627            bug!("encode_ty: unexpected `{:?}`", ty.kind());
628        }
629    };
630
631    typeid
632}
633
634/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
635fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
636    // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
637    // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
638    //
639    // N<namespace-tagN>..N<namespace-tag1>
640    // C<crate-disambiguator><crate-name>
641    // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
642    //
643    // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
644    //
645    //     pub type Type1 = impl Send;
646    //     let _: Type1 = <Struct1<i32>>::foo;
647    //     fn foo1(_: Type1) { }
648    //
649    //     pub type Type2 = impl Send;
650    //     let _: Type2 = <Trait1<i32>>::foo;
651    //     fn foo2(_: Type2) { }
652    //
653    //     pub type Type3 = impl Send;
654    //     let _: Type3 = <i32 as Trait1<i32>>::foo;
655    //     fn foo3(_: Type3) { }
656    //
657    //     pub type Type4 = impl Send;
658    //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
659    //     fn foo3(_: Type4) { }
660    //
661    // Are encoded as:
662    //
663    //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
664    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
665    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
666    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
667    //
668    // The reason for not using v0's extended form of paths is to use a consistent and simpler
669    // encoding, as the reasoning for using it isn't relevant for type metadata identifiers (i.e.,
670    // keep symbol names close to how methods are represented in error messages). See
671    // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
672    let mut s = String::new();
673
674    // Start and namespace tags
675    let mut def_path = tcx.def_path(def_id);
676    def_path.data.reverse();
677    for disambiguated_data in &def_path.data {
678        s.push('N');
679        s.push_str(match disambiguated_data.data {
680            hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
681            hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
682            hir::definitions::DefPathData::TypeNs(..) => "t",
683            hir::definitions::DefPathData::ValueNs(..) => "v",
684            hir::definitions::DefPathData::Closure => "C",
685            hir::definitions::DefPathData::Ctor => "c",
686            hir::definitions::DefPathData::AnonConst => "K",
687            hir::definitions::DefPathData::OpaqueTy => "i",
688            hir::definitions::DefPathData::SyntheticCoroutineBody => "s",
689            hir::definitions::DefPathData::NestedStatic => "n",
690            hir::definitions::DefPathData::CrateRoot
691            | hir::definitions::DefPathData::Use
692            | hir::definitions::DefPathData::GlobalAsm
693            | hir::definitions::DefPathData::MacroNs(..)
694            | hir::definitions::DefPathData::OpaqueLifetime(..)
695            | hir::definitions::DefPathData::LifetimeNs(..)
696            | hir::definitions::DefPathData::AnonAssocTy(..) => {
697                ::rustc_middle::util::bug::bug_fmt(format_args!("encode_ty_name: unexpected `{0:?}`",
        disambiguated_data.data));bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
698            }
699        });
700    }
701
702    // Crate disambiguator and name
703    s.push('C');
704    s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64()));
705    let crate_name = tcx.crate_name(def_path.krate).to_string();
706    let _ = s.write_fmt(format_args!("{0}{1}", crate_name.len(), crate_name))write!(s, "{}{}", crate_name.len(), crate_name);
707
708    // Disambiguators and names
709    def_path.data.reverse();
710    for disambiguated_data in &def_path.data {
711        let num = disambiguated_data.disambiguator as u64;
712        if num > 0 {
713            s.push_str(&to_disambiguator(num));
714        }
715
716        let name = disambiguated_data.data.to_string();
717        let _ = s.write_fmt(format_args!("{0}", name.len()))write!(s, "{}", name.len());
718
719        // Prepend a '_' if name starts with a digit or '_'
720        if let Some(first) = name.as_bytes().first() {
721            if first.is_ascii_digit() || *first == b'_' {
722                s.push('_');
723            }
724        } else {
725            ::rustc_middle::util::bug::bug_fmt(format_args!("encode_ty_name: invalid name `{0:?}`",
        name));bug!("encode_ty_name: invalid name `{:?}`", name);
726        }
727
728        s.push_str(&name);
729    }
730
731    s
732}
733
734/// Converts a number to a disambiguator (see
735/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
736fn to_disambiguator(num: u64) -> String {
737    if let Some(num) = num.checked_sub(1) {
738        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("s{0}_",
                num.to_base(ALPHANUMERIC_ONLY)))
    })format!("s{}_", num.to_base(ALPHANUMERIC_ONLY))
739    } else {
740        "s_".to_string()
741    }
742}
743
744/// Converts a number to a sequence number (see
745/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
746fn to_seq_id(num: usize) -> String {
747    if let Some(num) = num.checked_sub(1) {
748        (num as u64).to_base(CASE_INSENSITIVE).to_uppercase()
749    } else {
750        "".to_string()
751    }
752}