about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs10
-rw-r--r--compiler/rustc_codegen_gcc/src/type_.rs13
-rw-r--r--compiler/rustc_codegen_llvm/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs26
-rw-r--r--compiler/rustc_codegen_llvm/src/declare.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs30
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/type_.rs17
-rw-r--r--compiler/rustc_symbol_mangling/Cargo.toml1
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs9
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid.rs18
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs929
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs36
-rw-r--r--src/doc/unstable-book/src/compiler-flags/sanitizer.md114
-rw-r--r--src/test/codegen/sanitizer-cfi-add-canonical-jump-tables-flag.rs (renamed from src/test/codegen/sanitizer_cfi_add_canonical_jump_tables_flag.rs)5
-rw-r--r--src/test/codegen/sanitizer-cfi-emit-type-checks.rs (renamed from src/test/codegen/sanitizer_cfi_emit_type_checks.rs)5
-rw-r--r--src/test/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs575
-rw-r--r--src/test/codegen/sanitizer-cfi-emit-type-metadata-itanium-cxx-abi.rs31
-rw-r--r--src/test/codegen/sanitizer_cfi_emit_type_metadata.rs31
-rw-r--r--src/tools/compiletest/src/header.rs2
-rw-r--r--src/tools/compiletest/src/util.rs17
25 files changed, 1732 insertions, 165 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f67fd15610c..c5224661020 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4519,6 +4519,7 @@ dependencies = [
 name = "rustc_symbol_mangling"
 version = "0.0.0"
 dependencies = [
+ "bitflags",
  "punycode",
  "rustc-demangle",
  "rustc_data_structures",
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index 6221a7f6d93..4d40dd0994d 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -784,16 +784,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         // TODO(antoyo)
     }
 
-    fn type_metadata(&mut self, _function: RValue<'gcc>, _typeid: String) {
-        // Unsupported.
-    }
-
-    fn typeid_metadata(&mut self, _typeid: String) -> RValue<'gcc> {
-        // Unsupported.
-        self.context.new_rvalue_from_int(self.int_type, 0)
-    }
-
-
     fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> {
         self.store_with_flags(val, ptr, align, MemFlags::empty())
     }
diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs
index 002b95db36d..68bdb8d4e55 100644
--- a/compiler/rustc_codegen_gcc/src/type_.rs
+++ b/compiler/rustc_codegen_gcc/src/type_.rs
@@ -1,7 +1,7 @@
 use std::convert::TryInto;
 
 use gccjit::{RValue, Struct, Type};
-use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods};
+use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, TypeMembershipMethods};
 use rustc_codegen_ssa::common::TypeKind;
 use rustc_middle::{bug, ty};
 use rustc_middle::ty::layout::TyAndLayout;
@@ -290,3 +290,14 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
 
     (result, packed)
 }
+
+impl<'gcc, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
+    fn set_type_metadata(&self, _function: RValue<'gcc>, _typeid: String) {
+        // Unsupported.
+    }
+
+    fn typeid_metadata(&self, _typeid: String) -> RValue<'gcc> {
+        // Unsupported.
+        self.context.new_rvalue_from_int(self.int_type, 0)
+    }
+}
diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml
index d8fbd0a84fb..f9a5463efcd 100644
--- a/compiler/rustc_codegen_llvm/Cargo.toml
+++ b/compiler/rustc_codegen_llvm/Cargo.toml
@@ -18,7 +18,6 @@ rustc_middle = { path = "../rustc_middle" }
 rustc-demangle = "0.1.21"
 rustc_attr = { path = "../rustc_attr" }
 rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
-rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fs_util = { path = "../rustc_fs_util" }
@@ -30,6 +29,7 @@ rustc_metadata = { path = "../rustc_metadata" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_session = { path = "../rustc_session" }
 rustc_serialize = { path = "../rustc_serialize" }
+rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
 rustc_target = { path = "../rustc_target" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 4a4cccb490d..d3096c73a8a 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -626,32 +626,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         }
     }
 
-    fn type_metadata(&mut self, function: &'ll Value, typeid: String) {
-        let typeid_metadata = self.typeid_metadata(typeid);
-        let v = [self.const_usize(0), typeid_metadata];
-        unsafe {
-            llvm::LLVMGlobalSetMetadata(
-                function,
-                llvm::MD_type as c_uint,
-                llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
-                    self.cx.llcx,
-                    v.as_ptr(),
-                    v.len() as c_uint,
-                )),
-            )
-        }
-    }
-
-    fn typeid_metadata(&mut self, typeid: String) -> Self::Value {
-        unsafe {
-            llvm::LLVMMDStringInContext(
-                self.cx.llcx,
-                typeid.as_ptr() as *const c_char,
-                typeid.as_bytes().len() as c_uint,
-            )
-        }
-    }
-
     fn store(&mut self, val: &'ll Value, ptr: &'ll Value, align: Align) -> &'ll Value {
         self.store_with_flags(val, ptr, align, MemFlags::empty())
     }
diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs
index 5a5c4f7f860..fa0ecd18fc8 100644
--- a/compiler/rustc_codegen_llvm/src/declare.rs
+++ b/compiler/rustc_codegen_llvm/src/declare.rs
@@ -18,7 +18,9 @@ use crate::llvm;
 use crate::llvm::AttributePlace::Function;
 use crate::type_::Type;
 use crate::value::Value;
+use rustc_codegen_ssa::traits::TypeMembershipMethods;
 use rustc_middle::ty::Ty;
+use rustc_symbol_mangling::typeid::typeid_for_fnabi;
 use smallvec::SmallVec;
 use tracing::debug;
 
@@ -97,6 +99,12 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
             fn_abi.llvm_type(self),
         );
         fn_abi.apply_attrs_llfn(self, llfn);
+
+        if self.tcx.sess.is_sanitizer_cfi_enabled() {
+            let typeid = typeid_for_fnabi(self.tcx, fn_abi);
+            self.set_type_metadata(llfn, typeid);
+        }
+
         llfn
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index cf2d3c423c3..eeb38d4ecf5 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -19,7 +19,7 @@ use rustc_target::abi::{AddressSpace, Align, Integer, Size};
 use std::fmt;
 use std::ptr;
 
-use libc::c_uint;
+use libc::{c_char, c_uint};
 
 impl PartialEq for Type {
     fn eq(&self, other: &Self) -> bool {
@@ -289,3 +289,31 @@ impl<'ll, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         ty.llvm_type(self)
     }
 }
+
+impl<'ll, 'tcx> TypeMembershipMethods<'tcx> for CodegenCx<'ll, 'tcx> {
+    fn set_type_metadata(&self, function: &'ll Value, typeid: String) {
+        let typeid_metadata = self.typeid_metadata(typeid);
+        let v = [self.const_usize(0), typeid_metadata];
+        unsafe {
+            llvm::LLVMGlobalSetMetadata(
+                function,
+                llvm::MD_type as c_uint,
+                llvm::LLVMValueAsMetadata(llvm::LLVMMDNodeInContext(
+                    self.llcx,
+                    v.as_ptr(),
+                    v.len() as c_uint,
+                )),
+            )
+        }
+    }
+
+    fn typeid_metadata(&self, typeid: String) -> &'ll Value {
+        unsafe {
+            llvm::LLVMMDStringInContext(
+                self.llcx,
+                typeid.as_ptr() as *const c_char,
+                typeid.len() as c_uint,
+            )
+        }
+    }
+}
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 773c55cf551..3eee58d9d1c 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -20,7 +20,7 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
 use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};
 use rustc_span::source_map::Span;
 use rustc_span::{sym, Symbol};
-use rustc_symbol_mangling::typeid_for_fnabi;
+use rustc_symbol_mangling::typeid::typeid_for_fnabi;
 use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode};
 use rustc_target::abi::{self, HasDataLayout, WrappingRange};
 use rustc_target::spec::abi::Abi;
@@ -918,7 +918,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             // FIXME(rcvalle): Add support for generalized identifiers.
             // FIXME(rcvalle): Create distinct unnamed MDNodes for internal identifiers.
             let typeid = typeid_for_fnabi(bx.tcx(), fn_abi);
-            let typeid_metadata = bx.typeid_metadata(typeid);
+            let typeid_metadata = self.cx.typeid_metadata(typeid);
 
             // Test whether the function pointer is associated with the type identifier.
             let cond = bx.type_test(fn_ptr, typeid_metadata);
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index ec3f7a2156a..8ee375fa9e3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -3,7 +3,6 @@ use rustc_middle::mir;
 use rustc_middle::mir::interpret::ErrorHandled;
 use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
 use rustc_middle::ty::{self, Instance, Ty, TypeFoldable, TypeVisitable};
-use rustc_symbol_mangling::typeid_for_fnabi;
 use rustc_target::abi::call::{FnAbi, PassMode};
 
 use std::iter;
@@ -247,13 +246,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     for (bb, _) in traversal::reverse_postorder(&mir) {
         fx.codegen_block(bb);
     }
-
-    // For backends that support CFI using type membership (i.e., testing whether a given  pointer
-    // is associated with a type identifier).
-    if cx.tcx().sess.is_sanitizer_cfi_enabled() {
-        let typeid = typeid_for_fnabi(cx.tcx(), fn_abi);
-        bx.type_metadata(llfn, typeid);
-    }
 }
 
 /// Produces, for each argument, a `Value` pointing at the
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index 1bbe10141fc..9f49749bb41 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -160,8 +160,6 @@ pub trait BuilderMethods<'a, 'tcx>:
 
     fn range_metadata(&mut self, load: Self::Value, range: WrappingRange);
     fn nonnull_metadata(&mut self, load: Self::Value);
-    fn type_metadata(&mut self, function: Self::Function, typeid: String);
-    fn typeid_metadata(&mut self, typeid: String) -> Self::Value;
 
     fn store(&mut self, val: Self::Value, ptr: Self::Value, align: Align) -> Self::Value;
     fn store_with_flags(
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 396768e0a42..782fdadbfb8 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -40,7 +40,8 @@ pub use self::intrinsic::IntrinsicCallMethods;
 pub use self::misc::MiscMethods;
 pub use self::statics::{StaticBuilderMethods, StaticMethods};
 pub use self::type_::{
-    ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMethods,
+    ArgAbiMethods, BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods, TypeMembershipMethods,
+    TypeMethods,
 };
 pub use self::write::{ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods};
 
diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs
index 5d3f07317a3..8158e8dd011 100644
--- a/compiler/rustc_codegen_ssa/src/traits/type_.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs
@@ -117,6 +117,13 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
     ) -> Self::Type;
 }
 
+// For backends that support CFI using type membership (i.e., testing whether a given  pointer is
+// associated with a type identifier).
+pub trait TypeMembershipMethods<'tcx>: Backend<'tcx> {
+    fn set_type_metadata(&self, function: Self::Function, typeid: String);
+    fn typeid_metadata(&self, typeid: String) -> Self::Value;
+}
+
 pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
     fn store_fn_arg(
         &mut self,
@@ -133,6 +140,12 @@ pub trait ArgAbiMethods<'tcx>: HasCodegen<'tcx> {
     fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Self::Type;
 }
 
-pub trait TypeMethods<'tcx>: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {}
+pub trait TypeMethods<'tcx>:
+    DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx>
+{
+}
 
-impl<'tcx, T> TypeMethods<'tcx> for T where Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> {}
+impl<'tcx, T> TypeMethods<'tcx> for T where
+    Self: DerivedTypeMethods<'tcx> + LayoutTypeMethods<'tcx> + TypeMembershipMethods<'tcx>
+{
+}
diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml
index d5befa10e23..b104a40c231 100644
--- a/compiler/rustc_symbol_mangling/Cargo.toml
+++ b/compiler/rustc_symbol_mangling/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
 doctest = false
 
 [dependencies]
+bitflags = "1.2.1"
 tracing = "0.1"
 punycode = "0.4.0"
 rustc-demangle = "0.1.21"
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index bed0e81e66e..5fc992023ca 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -102,9 +102,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
 use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
+use rustc_middle::ty::{self, Instance, TyCtxt};
 use rustc_session::config::SymbolManglingVersion;
-use rustc_target::abi::call::FnAbi;
 
 use tracing::debug;
 
@@ -112,6 +111,7 @@ mod legacy;
 mod v0;
 
 pub mod test;
+pub mod typeid;
 
 /// This function computes the symbol name for the given `instance` and the
 /// given instantiating crate. That is, if you know that instance X is
@@ -150,11 +150,6 @@ fn symbol_name_provider<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty
     ty::SymbolName::new(tcx, &symbol_name)
 }
 
-/// This function computes the typeid for the given function ABI.
-pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
-    v0::mangle_typeid_for_fnabi(tcx, fn_abi)
-}
-
 pub fn typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::PolyExistentialTraitRef<'tcx>,
diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs
new file mode 100644
index 00000000000..9228bea43f9
--- /dev/null
+++ b/compiler/rustc_symbol_mangling/src/typeid.rs
@@ -0,0 +1,18 @@
+// For more information about type metadata and type metadata identifiers for cross-language LLVM
+// CFI support, see Type metadata in the design document in the tracking issue #89653.
+
+use rustc_middle::ty::{FnSig, Ty, TyCtxt};
+use rustc_target::abi::call::FnAbi;
+
+mod typeid_itanium_cxx_abi;
+use typeid_itanium_cxx_abi::TypeIdOptions;
+
+/// Returns a type metadata identifier for the specified FnAbi.
+pub fn typeid_for_fnabi<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> String {
+    typeid_itanium_cxx_abi::typeid_for_fnabi(tcx, fn_abi, TypeIdOptions::NO_OPTIONS)
+}
+
+/// Returns a type metadata identifier for the specified FnSig.
+pub fn typeid_for_fnsig<'tcx>(tcx: TyCtxt<'tcx>, fn_sig: &FnSig<'tcx>) -> String {
+    typeid_itanium_cxx_abi::typeid_for_fnsig(tcx, fn_sig, TypeIdOptions::NO_OPTIONS)
+}
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
new file mode 100644
index 00000000000..a09b52fbfdf
--- /dev/null
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -0,0 +1,929 @@
+// For more information about type metadata and type metadata identifiers for cross-language LLVM
+// CFI support, see Type metadata in the design document in the tracking issue #89653.
+
+// FIXME(rcvalle): Identify C char and integer type uses and encode them with their respective
+// builtin type encodings as specified by the Itanium C++ ABI for extern function types with the "C"
+// calling convention to use this encoding for cross-language LLVM CFI.
+
+use bitflags::bitflags;
+use core::fmt::Display;
+use rustc_data_structures::base_n;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
+use rustc_middle::ty::{
+    self, Binder, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind,
+    Term, Ty, TyCtxt, UintTy,
+};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::sym;
+use rustc_target::abi::call::{Conv, FnAbi};
+use rustc_target::spec::abi::Abi;
+use std::fmt::Write as _;
+
+/// Type and extended type qualifiers.
+#[derive(Eq, Hash, PartialEq)]
+enum TyQ {
+    None,
+    Const,
+    Mut,
+}
+
+/// Substitution dictionary key.
+#[derive(Eq, Hash, PartialEq)]
+enum DictKey<'tcx> {
+    Ty(Ty<'tcx>, TyQ),
+    Region(Region<'tcx>),
+    Const(Const<'tcx>),
+    Predicate(ExistentialPredicate<'tcx>),
+}
+
+bitflags! {
+    /// Options for typeid_for_fnabi and typeid_for_fnsig.
+    pub struct TypeIdOptions: u32 {
+        const NO_OPTIONS = 0;
+        const GENERALIZE_POINTERS = 1;
+        const GENERALIZE_REPR_C = 2;
+    }
+}
+
+/// Options for encode_ty.
+type EncodeTyOptions = TypeIdOptions;
+
+/// Options for transform_ty.
+type TransformTyOptions = TypeIdOptions;
+
+/// Converts a number to a disambiguator (see
+/// <https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html>).
+fn to_disambiguator(num: u64) -> String {
+    if let Some(num) = num.checked_sub(1) {
+        format!("s{}_", base_n::encode(num as u128, 62))
+    } else {
+        "s_".to_string()
+    }
+}
+
+/// Converts a number to a sequence number (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.seq-id>).
+fn to_seq_id(num: usize) -> String {
+    if let Some(num) = num.checked_sub(1) {
+        base_n::encode(num as u128, 36).to_uppercase()
+    } else {
+        "".to_string()
+    }
+}
+
+/// Substitutes a component if found in the substitution dictionary (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression>).
+fn compress<'tcx>(
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    key: DictKey<'tcx>,
+    comp: &mut String,
+) {
+    match dict.get(&key) {
+        Some(num) => {
+            comp.clear();
+            let _ = write!(comp, "S{}_", to_seq_id(*num));
+        }
+        None => {
+            dict.insert(key, dict.len());
+        }
+    }
+}
+
+// FIXME(rcvalle): Move to compiler/rustc_middle/src/ty/sty.rs after C types work is done, possibly
+// along with other is_c_type methods.
+/// Returns whether a `ty::Ty` is `c_void`.
+fn is_c_void_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        ty::Adt(adt_def, ..) => {
+            let def_id = adt_def.0.did;
+            let crate_name = tcx.crate_name(def_id.krate);
+            if tcx.item_name(def_id).as_str() == "c_void"
+                && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
+            {
+                true
+            } else {
+                false
+            }
+        }
+        _ => false,
+    }
+}
+
+/// Encodes a const using the Itanium C++ ABI as a literal argument (see
+/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
+fn encode_const<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    c: Const<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // L<element-type>[n]<element-value>E as literal argument
+    let mut s = String::from('L');
+
+    // Element type
+    s.push_str(&encode_ty(tcx, c.ty(), dict, options));
+
+    // The only allowed types of const parameters are bool, u8, u16, u32, u64, u128, usize i8, i16,
+    // i32, i64, i128, isize, and char. The bool value false is encoded as 0 and true as 1.
+    fn push_signed_value<T: Display + PartialOrd>(s: &mut String, value: T, zero: T) {
+        if value < zero {
+            s.push('n')
+        };
+        let _ = write!(s, "{}", value);
+    }
+
+    fn push_unsigned_value<T: Display>(s: &mut String, value: T) {
+        let _ = write!(s, "{}", value);
+    }
+
+    if let Some(scalar_int) = c.kind().try_to_scalar_int() {
+        let signed = c.ty().is_signed();
+        match scalar_int.size().bits() {
+            8 if signed => push_signed_value(&mut s, scalar_int.try_to_i8().unwrap(), 0),
+            16 if signed => push_signed_value(&mut s, scalar_int.try_to_i16().unwrap(), 0),
+            32 if signed => push_signed_value(&mut s, scalar_int.try_to_i32().unwrap(), 0),
+            64 if signed => push_signed_value(&mut s, scalar_int.try_to_i64().unwrap(), 0),
+            128 if signed => push_signed_value(&mut s, scalar_int.try_to_i128().unwrap(), 0),
+            8 => push_unsigned_value(&mut s, scalar_int.try_to_u8().unwrap()),
+            16 => push_unsigned_value(&mut s, scalar_int.try_to_u16().unwrap()),
+            32 => push_unsigned_value(&mut s, scalar_int.try_to_u32().unwrap()),
+            64 => push_unsigned_value(&mut s, scalar_int.try_to_u64().unwrap()),
+            128 => push_unsigned_value(&mut s, scalar_int.try_to_u128().unwrap()),
+            _ => {
+                bug!("encode_const: unexpected size `{:?}`", scalar_int.size().bits());
+            }
+        };
+    } else {
+        bug!("encode_const: unexpected type `{:?}`", c.ty());
+    }
+
+    // Close the "L..E" pair
+    s.push('E');
+
+    compress(dict, DictKey::Const(c), &mut s);
+
+    s
+}
+
+/// Encodes a FnSig using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_fnsig<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_sig: &FnSig<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: TypeIdOptions,
+) -> String {
+    // Function types are delimited by an "F..E" pair
+    let mut s = String::from("F");
+
+    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
+    match fn_sig.abi {
+        Abi::C { .. } => {
+            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+        _ => {
+            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+    }
+
+    // Encode the return type
+    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
+    let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options);
+    s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
+
+    // Encode the parameter types
+    let tys = fn_sig.inputs();
+    if !tys.is_empty() {
+        for ty in tys {
+            let ty = transform_ty(tcx, *ty, transform_ty_options);
+            s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
+        }
+
+        if fn_sig.c_variadic {
+            s.push('z');
+        }
+    } else {
+        if fn_sig.c_variadic {
+            s.push('z');
+        } else {
+            // Empty parameter lists, whether declared as () or conventionally as (void), are
+            // encoded with a void parameter specifier "v".
+            s.push('v')
+        }
+    }
+
+    // Close the "F..E" pair
+    s.push('E');
+
+    s
+}
+
+/// Encodes a predicate using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_predicate<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    predicate: Binder<'tcx, ExistentialPredicate<'tcx>>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>, as vendor
+    // extended type.
+    let mut s = String::new();
+    match predicate.as_ref().skip_binder() {
+        ty::ExistentialPredicate::Trait(trait_ref) => {
+            let name = encode_ty_name(tcx, trait_ref.def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+            s.push_str(&encode_substs(tcx, trait_ref.substs, dict, options));
+        }
+        ty::ExistentialPredicate::Projection(projection) => {
+            let name = encode_ty_name(tcx, projection.item_def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+            s.push_str(&encode_substs(tcx, projection.substs, dict, options));
+            match projection.term {
+                Term::Ty(ty) => {
+                    s.push_str(&encode_ty(tcx, ty, dict, options));
+                }
+                Term::Const(c) => {
+                    s.push_str(&encode_const(tcx, c, dict, options));
+                }
+            }
+        }
+        ty::ExistentialPredicate::AutoTrait(def_id) => {
+            let name = encode_ty_name(tcx, *def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+        }
+    };
+    compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s);
+    s
+}
+
+/// Encodes predicates using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    predicates: &List<Binder<'tcx, ExistentialPredicate<'tcx>>>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // <predicate1[..predicateN]>E as part of vendor extended type
+    let mut s = String::new();
+    let predicates: Vec<Binder<'tcx, ExistentialPredicate<'tcx>>> =
+        predicates.iter().map(|predicate| predicate).collect();
+    for predicate in predicates {
+        s.push_str(&encode_predicate(tcx, predicate, dict, options));
+    }
+    s
+}
+
+/// Encodes a region using the Itanium C++ ABI as a vendor extended type.
+fn encode_region<'tcx>(
+    _tcx: TyCtxt<'tcx>,
+    region: Region<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    _options: EncodeTyOptions,
+) -> String {
+    // u6region[I[<region-disambiguator>][<region-index>]E] as vendor extended type
+    let mut s = String::new();
+    match region.kind() {
+        RegionKind::ReLateBound(debruijn, r) => {
+            s.push_str("u6regionI");
+            // Debruijn index, which identifies the binder, as region disambiguator
+            let num = debruijn.index() as u64;
+            if num > 0 {
+                s.push_str(&to_disambiguator(num));
+            }
+            // Index within the binder
+            let _ = write!(s, "{}", r.var.index() as u64);
+            s.push('E');
+            compress(dict, DictKey::Region(region), &mut s);
+        }
+        RegionKind::ReErased => {
+            s.push_str("u6region");
+            compress(dict, DictKey::Region(region), &mut s);
+        }
+        RegionKind::ReEarlyBound(..)
+        | RegionKind::ReFree(..)
+        | RegionKind::ReStatic
+        | RegionKind::ReVar(..)
+        | RegionKind::RePlaceholder(..)
+        | RegionKind::ReEmpty(..) => {
+            bug!("encode_region: unexpected `{:?}`", region.kind());
+        }
+    }
+    s
+}
+
+/// Encodes substs using the Itanium C++ ABI with vendor extended type qualifiers and types for Rust
+/// types that are not used at the FFI boundary.
+fn encode_substs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    substs: SubstsRef<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    // [I<subst1..substN>E] as part of vendor extended type
+    let mut s = String::new();
+    let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect();
+    if !substs.is_empty() {
+        s.push('I');
+        for subst in substs {
+            match subst.unpack() {
+                GenericArgKind::Lifetime(region) => {
+                    s.push_str(&encode_region(tcx, region, dict, options));
+                }
+                GenericArgKind::Type(ty) => {
+                    s.push_str(&encode_ty(tcx, ty, dict, options));
+                }
+                GenericArgKind::Const(c) => {
+                    s.push_str(&encode_const(tcx, c, dict, options));
+                }
+            }
+        }
+        s.push('E');
+    }
+    s
+}
+
+/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
+fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String {
+    // Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
+    // <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
+    //
+    // N<namespace-tagN>..N<namespace-tag1>
+    // C<crate-disambiguator><crate-name>
+    // <path-disambiguator1><path-name1>..<path-disambiguatorN><path-nameN>
+    //
+    // With additional tags for DefPathData::Impl and DefPathData::ForeignMod. For instance:
+    //
+    //     pub type Type1 = impl Send;
+    //     let _: Type1 = <Struct1<i32>>::foo;
+    //     fn foo1(_: Type1) { }
+    //
+    //     pub type Type2 = impl Send;
+    //     let _: Type2 = <Trait1<i32>>::foo;
+    //     fn foo2(_: Type2) { }
+    //
+    //     pub type Type3 = impl Send;
+    //     let _: Type3 = <i32 as Trait1<i32>>::foo;
+    //     fn foo3(_: Type3) { }
+    //
+    //     pub type Type4 = impl Send;
+    //     let _: Type4 = <Struct1<i32> as Trait1<i32>>::foo;
+    //     fn foo3(_: Type4) { }
+    //
+    // Are encoded as:
+    //
+    //     _ZTSFvu29NvNIC1234_5crate8{{impl}}3fooIu3i32EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3dynIu21NtC1234_5crate6Trait1Iu3i32Eu6regionES_EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu3i32S_EE
+    //     _ZTSFvu27NvNtC1234_5crate6Trait13fooIu22NtC1234_5crate7Struct1Iu3i32ES_EE
+    //
+    // The reason for not using v0's extended form of paths is to use a consistent and simpler
+    // encoding, as the reasoning for using it isn't relevand for type metadata identifiers (i.e.,
+    // keep symbol names close to how methods are represented in error messages). See
+    // https://rust-lang.github.io/rfcs/2603-rust-symbol-name-mangling-v0.html#methods.
+    let mut s = String::new();
+
+    // Start and namespace tags
+    let mut def_path = tcx.def_path(def_id);
+    def_path.data.reverse();
+    for disambiguated_data in &def_path.data {
+        s.push('N');
+        s.push_str(match disambiguated_data.data {
+            hir::definitions::DefPathData::Impl => "I", // Not specified in v0's <namespace>
+            hir::definitions::DefPathData::ForeignMod => "F", // Not specified in v0's <namespace>
+            hir::definitions::DefPathData::TypeNs(..) => "t",
+            hir::definitions::DefPathData::ValueNs(..) => "v",
+            hir::definitions::DefPathData::ClosureExpr => "C",
+            hir::definitions::DefPathData::Ctor => "c",
+            hir::definitions::DefPathData::AnonConst => "k",
+            hir::definitions::DefPathData::ImplTrait => "i",
+            hir::definitions::DefPathData::CrateRoot
+            | hir::definitions::DefPathData::Use
+            | hir::definitions::DefPathData::GlobalAsm
+            | hir::definitions::DefPathData::MacroNs(..)
+            | hir::definitions::DefPathData::LifetimeNs(..) => {
+                bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
+            }
+        });
+    }
+
+    // Crate disambiguator and name
+    s.push('C');
+    s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64()));
+    let crate_name = tcx.crate_name(def_path.krate).to_string();
+    let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
+
+    // Disambiguators and names
+    def_path.data.reverse();
+    for disambiguated_data in &def_path.data {
+        let num = disambiguated_data.disambiguator as u64;
+        if num > 0 {
+            s.push_str(&to_disambiguator(num));
+        }
+
+        let name = disambiguated_data.data.to_string();
+        let _ = write!(s, "{}", name.len());
+
+        // Prepend a '_' if name starts with a digit or '_'
+        if let Some(first) = name.as_bytes().get(0) {
+            if first.is_ascii_digit() || *first == b'_' {
+                s.push('_');
+            }
+        } else {
+            bug!("encode_ty_name: invalid name `{:?}`", name);
+        }
+
+        s.push_str(&name);
+    }
+
+    s
+}
+
+/// Encodes a ty:Ty using the Itanium C++ ABI with vendor extended type qualifiers and types for
+/// Rust types that are not used at the FFI boundary.
+fn encode_ty<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    ty: Ty<'tcx>,
+    dict: &mut FxHashMap<DictKey<'tcx>, usize>,
+    options: EncodeTyOptions,
+) -> String {
+    let mut typeid = String::new();
+
+    match ty.kind() {
+        // Primitive types
+        ty::Bool => {
+            typeid.push('b');
+        }
+
+        ty::Int(..) | ty::Uint(..) | ty::Float(..) => {
+            // u<length><type-name> as vendor extended type
+            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",
+                ty::Float(FloatTy::F32) => "u3f32",
+                ty::Float(FloatTy::F64) => "u3f64",
+                _ => "",
+            });
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Char => {
+            // u4char as vendor extended type
+            let mut s = String::from("u4char");
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Str => {
+            // u3str as vendor extended type
+            let mut s = String::from("u3str");
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Never => {
+            // u5never as vendor extended type
+            let mut s = String::from("u5never");
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Compound types
+        // () in Rust is equivalent to void return type in C
+        _ if ty.is_unit() => {
+            typeid.push('v');
+        }
+
+        // Sequence types
+        ty::Tuple(tys) => {
+            // u5tupleI<element-type1..element-typeN>E as vendor extended type
+            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) => {
+            // A<array-length><element-type>
+            let mut s = String::from("A");
+            let _ = write!(s, "{}", &len.kind().try_to_scalar().unwrap().to_u64().unwrap());
+            s.push_str(&encode_ty(tcx, *ty0, dict, options));
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::Slice(ty0) => {
+            // u5sliceI<element-type>E as vendor extended type
+            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);
+        }
+
+        // User-defined types
+        ty::Adt(adt_def, substs) => {
+            let mut s = String::new();
+            let def_id = adt_def.0.did;
+            if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
+                // For for cross-language CFI support, the encoding must be compatible at the FFI
+                // boundary. For instance:
+                //
+                //     struct type1 {};
+                //     void foo(struct type1* bar) {}
+                //
+                // Is encoded as:
+                //
+                //     _ZTSFvP5type1E
+                //
+                // So, encode any repr(C) user-defined type for extern function types with the "C"
+                // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where
+                // <name> is <unscoped-name>.
+                let name = tcx.item_name(def_id).to_string();
+                let _ = write!(s, "{}{}", name.len(), &name);
+                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            } else {
+                // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is
+                // <subst>, as vendor extended type.
+                let name = encode_ty_name(tcx, def_id);
+                let _ = write!(s, "u{}{}", name.len(), &name);
+                s.push_str(&encode_substs(tcx, substs, dict, options));
+                compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            }
+            typeid.push_str(&s);
+        }
+
+        ty::Foreign(def_id) => {
+            // <length><name>, where <name> is <unscoped-name>
+            let mut s = String::new();
+            let name = tcx.item_name(*def_id).to_string();
+            let _ = write!(s, "{}{}", name.len(), &name);
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Function types
+        ty::FnDef(def_id, substs)
+        | ty::Closure(def_id, substs)
+        | ty::Generator(def_id, substs, ..) => {
+            // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is <subst>,
+            // as vendor extended type.
+            let mut s = String::new();
+            let name = encode_ty_name(tcx, *def_id);
+            let _ = write!(s, "u{}{}", name.len(), &name);
+            s.push_str(&encode_substs(tcx, substs, dict, options));
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Pointer types
+        ty::Ref(region, ty0, ..) => {
+            // [U3mut]u3refI<element-type>E as vendor extended type qualifier and type
+            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(tcx.mk_imm_ref(*region, *ty0), TyQ::None), &mut s);
+            if ty.is_mutable_ptr() {
+                s = format!("{}{}", "U3mut", &s);
+                compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s);
+            }
+            typeid.push_str(&s);
+        }
+
+        ty::RawPtr(tm) => {
+            // P[K]<element-type>
+            let mut s = String::new();
+            s.push_str(&encode_ty(tcx, tm.ty, dict, options));
+            if !ty.is_mutable_ptr() {
+                s = format!("{}{}", "K", &s);
+                compress(dict, DictKey::Ty(tm.ty, TyQ::Const), &mut s);
+            };
+            s = format!("{}{}", "P", &s);
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        ty::FnPtr(fn_sig) => {
+            // PF<return-type><parameter-type1..parameter-typeN>E
+            let mut s = String::from("P");
+            s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::NO_OPTIONS));
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Trait types
+        ty::Dynamic(predicates, region) => {
+            // u3dynI<element-type1[..element-typeN]>E, where <element-type> is <predicate>, as
+            // vendor extended type.
+            let mut s = String::from("u3dynI");
+            s.push_str(&encode_predicates(tcx, predicates, dict, options));
+            s.push_str(&encode_region(tcx, *region, dict, options));
+            s.push('E');
+            compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+            typeid.push_str(&s);
+        }
+
+        // Unexpected types
+        ty::Bound(..)
+        | ty::Error(..)
+        | ty::GeneratorWitness(..)
+        | ty::Infer(..)
+        | ty::Opaque(..)
+        | ty::Param(..)
+        | ty::Placeholder(..)
+        | ty::Projection(..) => {
+            bug!("encode_ty: unexpected `{:?}`", ty.kind());
+        }
+    };
+
+    typeid
+}
+
+// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all
+// c_void types into unit types unconditionally, and generalizes all pointers if
+// TransformTyOptions::GENERALIZE_POINTERS option is set.
+fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> {
+    let mut ty = ty;
+
+    match ty.kind() {
+        ty::Bool
+        | ty::Int(..)
+        | ty::Uint(..)
+        | ty::Float(..)
+        | ty::Char
+        | ty::Str
+        | ty::Never
+        | ty::Foreign(..)
+        | ty::Dynamic(..) => {}
+
+        _ if ty.is_unit() => {}
+
+        ty::Tuple(tys) => {
+            ty = tcx.mk_tup(tys.iter().map(|ty| transform_ty(tcx, ty, options)));
+        }
+
+        ty::Array(ty0, len) => {
+            let len = len.kind().try_to_scalar().unwrap().to_u64().unwrap();
+            ty = tcx.mk_array(transform_ty(tcx, *ty0, options), len);
+        }
+
+        ty::Slice(ty0) => {
+            ty = tcx.mk_slice(transform_ty(tcx, *ty0, options));
+        }
+
+        ty::Adt(adt_def, substs) => {
+            if is_c_void_ty(tcx, ty) {
+                ty = tcx.mk_unit();
+            } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
+            {
+                ty = tcx.mk_adt(*adt_def, ty::List::empty());
+            } else if adt_def.repr().transparent() && adt_def.is_struct() {
+                let variant = adt_def.non_enum_variant();
+                let param_env = tcx.param_env(variant.def_id);
+                let field = variant.fields.iter().find(|field| {
+                    let ty = tcx.type_of(field.did);
+                    let is_zst =
+                        tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
+                    !is_zst
+                });
+                if field.is_none() {
+                    // Transform repr(transparent) types without non-ZST field into ()
+                    ty = tcx.mk_unit();
+                } else {
+                    let ty0 = tcx.type_of(field.unwrap().did);
+                    // Generalize any repr(transparent) user-defined type that is either a pointer
+                    // or reference, and either references itself or any other type that contains or
+                    // references itself, to avoid a reference cycle.
+                    if ty0.is_any_ptr() && ty0.contains(ty) {
+                        ty = transform_ty(
+                            tcx,
+                            ty0,
+                            options | TransformTyOptions::GENERALIZE_POINTERS,
+                        );
+                    } else {
+                        ty = transform_ty(tcx, ty0, options);
+                    }
+                }
+            } else {
+                ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
+            }
+        }
+
+        ty::FnDef(def_id, substs) => {
+            ty = tcx.mk_fn_def(*def_id, transform_substs(tcx, substs, options));
+        }
+
+        ty::Closure(def_id, substs) => {
+            ty = tcx.mk_closure(*def_id, transform_substs(tcx, substs, options));
+        }
+
+        ty::Generator(def_id, substs, movability) => {
+            ty = tcx.mk_generator(*def_id, transform_substs(tcx, substs, options), *movability);
+        }
+
+        ty::Ref(region, ty0, ..) => {
+            if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+                } else {
+                    ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_unit());
+                }
+            } else {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ref(*region, transform_ty(tcx, *ty0, options));
+                } else {
+                    ty = tcx.mk_imm_ref(*region, transform_ty(tcx, *ty0, options));
+                }
+            }
+        }
+
+        ty::RawPtr(tm) => {
+            if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ptr(tcx.mk_unit());
+                } else {
+                    ty = tcx.mk_imm_ptr(tcx.mk_unit());
+                }
+            } else {
+                if ty.is_mutable_ptr() {
+                    ty = tcx.mk_mut_ptr(transform_ty(tcx, tm.ty, options));
+                } else {
+                    ty = tcx.mk_imm_ptr(transform_ty(tcx, tm.ty, options));
+                }
+            }
+        }
+
+        ty::FnPtr(fn_sig) => {
+            if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
+                ty = tcx.mk_imm_ptr(tcx.mk_unit());
+            } else {
+                let parameters: Vec<Ty<'tcx>> = fn_sig
+                    .skip_binder()
+                    .inputs()
+                    .iter()
+                    .map(|ty| transform_ty(tcx, *ty, options))
+                    .collect();
+                let output = transform_ty(tcx, fn_sig.skip_binder().output(), options);
+                ty = tcx.mk_fn_ptr(ty::Binder::bind_with_vars(
+                    tcx.mk_fn_sig(
+                        parameters.iter(),
+                        &output,
+                        fn_sig.c_variadic(),
+                        fn_sig.unsafety(),
+                        fn_sig.abi(),
+                    ),
+                    fn_sig.bound_vars(),
+                ));
+            }
+        }
+
+        ty::Bound(..)
+        | ty::Error(..)
+        | ty::GeneratorWitness(..)
+        | ty::Infer(..)
+        | ty::Opaque(..)
+        | ty::Param(..)
+        | ty::Placeholder(..)
+        | ty::Projection(..) => {
+            bug!("transform_ty: unexpected `{:?}`", ty.kind());
+        }
+    }
+
+    ty
+}
+
+/// Transforms substs for being encoded and used in the substitution dictionary.
+fn transform_substs<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    substs: SubstsRef<'tcx>,
+    options: TransformTyOptions,
+) -> SubstsRef<'tcx> {
+    let substs: Vec<GenericArg<'tcx>> = substs
+        .iter()
+        .map(|subst| {
+            if let GenericArgKind::Type(ty) = subst.unpack() {
+                if is_c_void_ty(tcx, ty) {
+                    tcx.mk_unit().into()
+                } else {
+                    transform_ty(tcx, ty, options).into()
+                }
+            } else {
+                subst
+            }
+        })
+        .collect();
+    tcx.mk_substs(substs.iter())
+}
+
+/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
+/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
+pub fn typeid_for_fnabi<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+    options: TypeIdOptions,
+) -> String {
+    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
+    // its type.
+    let mut typeid = String::from("_Z");
+
+    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
+    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
+    // code (i.e., 'TS') prefixed to the type encoding for the function.
+    typeid.push_str("TS");
+
+    // Function types are delimited by an "F..E" pair
+    typeid.push('F');
+
+    // A dictionary of substitution candidates used for compression (see
+    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
+    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
+
+    let mut encode_ty_options = EncodeTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
+    match fn_abi.conv {
+        Conv::C => {
+            encode_ty_options.insert(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+        _ => {
+            encode_ty_options.remove(EncodeTyOptions::GENERALIZE_REPR_C);
+        }
+    }
+
+    // Encode the return type
+    let transform_ty_options = TransformTyOptions::from_bits(options.bits())
+        .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
+    let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options);
+    typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+
+    // Encode the parameter types
+    if !fn_abi.c_variadic {
+        if !fn_abi.args.is_empty() {
+            for arg in fn_abi.args.iter() {
+                let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options);
+                typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+            }
+        } else {
+            // Empty parameter lists, whether declared as () or conventionally as (void), are
+            // encoded with a void parameter specifier "v".
+            typeid.push('v');
+        }
+    } else {
+        for n in 0..fn_abi.fixed_count {
+            let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options);
+            typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
+        }
+
+        typeid.push('z');
+    }
+
+    // Close the "F..E" pair
+    typeid.push('E');
+
+    typeid
+}
+
+/// Returns a type metadata identifier for the specified FnSig using the Itanium C++ ABI with vendor
+/// extended type qualifiers and types for Rust types that are not used at the FFI boundary.
+pub fn typeid_for_fnsig<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    fn_sig: &FnSig<'tcx>,
+    options: TypeIdOptions,
+) -> String {
+    // A name is mangled by prefixing "_Z" to an encoding of its name, and in the case of functions
+    // its type.
+    let mut typeid = String::from("_Z");
+
+    // Clang uses the Itanium C++ ABI's virtual tables and RTTI typeinfo structure name as type
+    // metadata identifiers for function pointers. The typeinfo name encoding is a two-character
+    // code (i.e., 'TS') prefixed to the type encoding for the function.
+    typeid.push_str("TS");
+
+    // A dictionary of substitution candidates used for compression (see
+    // https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression).
+    let mut dict: FxHashMap<DictKey<'tcx>, usize> = FxHashMap::default();
+
+    // Encode the function signature
+    typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));
+
+    typeid
+}
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 79e3f10526c..71fa5a44887 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -12,7 +12,6 @@ use rustc_middle::ty::{
     self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, UintTy,
 };
 use rustc_span::symbol::kw;
-use rustc_target::abi::call::FnAbi;
 use rustc_target::abi::Integer;
 use rustc_target::spec::abi::Abi;
 
@@ -59,41 +58,6 @@ pub(super) fn mangle<'tcx>(
     std::mem::take(&mut cx.out)
 }
 
-pub(super) fn mangle_typeid_for_fnabi<'tcx>(
-    _tcx: TyCtxt<'tcx>,
-    fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
-) -> String {
-    // LLVM uses type metadata to allow IR modules to aggregate pointers by their types.[1] This
-    // type metadata is used by LLVM Control Flow Integrity to test whether a given pointer is
-    // associated with a type identifier (i.e., test type membership).
-    //
-    // Clang uses the Itanium C++ ABI's[2] virtual tables and RTTI typeinfo structure name[3] as
-    // type metadata identifiers for function pointers. The typeinfo name encoding is a
-    // two-character code (i.e., “TS”) prefixed to the type encoding for the function.
-    //
-    // For cross-language LLVM CFI support, a compatible encoding must be used by either
-    //
-    //  a. Using a superset of types that encompasses types used by Clang (i.e., Itanium C++ ABI's
-    //     type encodings[4]), or at least types used at the FFI boundary.
-    //  b. Reducing the types to the least common denominator between types used by Clang (or at
-    //     least types used at the FFI boundary) and Rust compilers (if even possible).
-    //  c. Creating a new ABI for cross-language CFI and using it for Clang and Rust compilers (and
-    //     possibly other compilers).
-    //
-    // Option (b) may weaken the protection for Rust-compiled only code, so it should be provided
-    // as an alternative to a Rust-specific encoding for when mixing Rust and C and C++ -compiled
-    // code. Option (c) would require changes to Clang to use the new ABI.
-    //
-    // [1] https://llvm.org/docs/TypeMetadata.html
-    // [2] https://itanium-cxx-abi.github.io/cxx-abi/abi.html
-    // [3] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-special-vtables
-    // [4] https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-type
-    //
-    // FIXME(rcvalle): See comment above.
-    let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
-    format!("typeid{}", arg_count)
-}
-
 pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::PolyExistentialTraitRef<'tcx>,
diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
index 9e11a154db9..7f7549aaf5a 100644
--- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md
+++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
@@ -193,7 +193,8 @@ Shadow byte legend (one shadow byte represents 8 application bytes):
 
 The LLVM Control Flow Integrity (CFI) support in the Rust compiler initially
 provides forward-edge control flow protection for Rust-compiled code only by
-aggregating function pointers in groups identified by their number of arguments.
+aggregating function pointers in groups identified by their return and parameter
+types.
 
 Forward-edge control flow protection for C or C++ and Rust -compiled code "mixed
 binaries" (i.e., for when C or C++ and Rust -compiled code share the same
@@ -245,7 +246,7 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
 fn main() {
     let answer = do_twice(add_one, 5);
 
-    println!("The answer is: {answer}");
+    println!("The answer is: {}", answer);
 
     println!("With CFI enabled, you should not see the next answer");
     let f: fn(i32) -> i32 = unsafe {
@@ -255,18 +256,18 @@ fn main() {
     };
     let next_answer = do_twice(f, 5);
 
-    println!("The next answer is: {next_answer}");
+    println!("The next answer is: {}", next_answer);
 }
 ```
 Fig. 1. Modified example from the [Advanced Functions and
 Closures][rust-book-ch19-05] chapter of the [The Rust Programming
 Language][rust-book] book.
 
-[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
-
 ```shell
-$ rustc rust_cfi.rs -o rust_cfi
-$ ./rust_cfi
+$ cargo run --release
+   Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
+    Finished release [optimized] target(s) in 0.76s
+     Running `target/release/rust-cfi-1`
 The answer is: 12
 With CFI enabled, you should not see the next answer
 The next answer is: 14
@@ -274,11 +275,11 @@ $
 ```
 Fig. 2. Build and execution of the modified example with LLVM CFI disabled.
 
-[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
-
 ```shell
-$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
-$ ./rust_cfi
+$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
+   Compiling rust-cfi-1 v0.1.0 (/home/rcvalle/rust-cfi-1)
+    Finished release [optimized] target(s) in 3.39s
+     Running `target/release/rust-cfi-1`
 The answer is: 12
 With CFI enabled, you should not see the next answer
 Illegal instruction
@@ -308,25 +309,25 @@ fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
 fn main() {
     let answer = do_twice(add_one, 5);
 
-    println!("The answer is: {answer}");
+    println!("The answer is: {}", answer);
 
     println!("With CFI enabled, you should not see the next answer");
     let f: fn(i32) -> i32 =
         unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
     let next_answer = do_twice(f, 5);
 
-    println!("The next answer is: {next_answer}");
+    println!("The next answer is: {}", next_answer);
 }
 ```
 Fig. 4. Another modified example from the [Advanced Functions and
 Closures][rust-book-ch19-05] chapter of the [The Rust Programming
 Language][rust-book] book.
 
-[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
-
 ```shell
-$ rustc rust_cfi.rs -o rust_cfi
-$ ./rust_cfi
+$ cargo run --release
+   Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
+    Finished release [optimized] target(s) in 0.76s
+     Running `target/release/rust-cfi-2`
 The answer is: 12
 With CFI enabled, you should not see the next answer
 The next answer is: 14
@@ -334,11 +335,11 @@ $
 ```
 Fig. 5. Build and execution of the modified example with LLVM CFI disabled.
 
-[//]: # (FIXME: Replace with output from cargo using nightly when #89652 is merged)
-
 ```shell
-$ rustc -Clto -Zsanitizer=cfi rust_cfi.rs -o rust_cfi
-$ ./rust_cfi
+$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
+   Compiling rust-cfi-2 v0.1.0 (/home/rcvalle/rust-cfi-2)
+    Finished release [optimized] target(s) in 3.38s
+     Running `target/release/rust-cfi-2`
 The answer is: 12
 With CFI enabled, you should not see the next answer
 Illegal instruction
@@ -348,14 +349,69 @@ Fig. 6. Build and execution of the modified example with LLVM CFI enabled.
 
 When LLVM CFI is enabled, if there are any attempts to change/hijack control
 flow using an indirect branch/call to a function with different number of
-arguments than intended/passed in the call/branch site, the execution is also
-terminated (see Fig. 6).
-
-Forward-edge control flow protection not only by aggregating function pointers
-in groups identified by their number of arguments, but also their argument
-types, will also be provided in later work by defining and using compatible type
-identifiers (see Type metadata in the design document in the tracking
-issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
+parameters than arguments intended/passed in the call/branch site, the
+execution is also terminated (see Fig. 6).
+
+```rust
+use std::mem;
+
+fn add_one(x: i32) -> i32 {
+    x + 1
+}
+
+fn add_two(x: i64) -> i64 {
+    x + 2
+}
+
+fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
+    f(arg) + f(arg)
+}
+
+fn main() {
+    let answer = do_twice(add_one, 5);
+
+    println!("The answer is: {}", answer);
+
+    println!("With CFI enabled, you should not see the next answer");
+    let f: fn(i32) -> i32 =
+        unsafe { mem::transmute::<*const u8, fn(i32) -> i32>(add_two as *const u8) };
+    let next_answer = do_twice(f, 5);
+
+    println!("The next answer is: {}", next_answer);
+}
+```
+Fig. 7. Another modified example from the [Advanced Functions and
+Closures][rust-book-ch19-05] chapter of the [The Rust Programming
+Language][rust-book] book.
+
+```shell
+ cargo run --release
+   Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
+    Finished release [optimized] target(s) in 0.74s
+     Running `target/release/rust-cfi-3`
+The answer is: 12
+With CFI enabled, you should not see the next answer
+The next answer is: 14
+$
+```
+Fig. 8. Build and execution of the modified example with LLVM CFI disabled.
+
+```shell
+$ RUSTFLAGS="-Zsanitizer=cfi -Cembed-bitcode=yes -Clto" cargo run --release
+   Compiling rust-cfi-3 v0.1.0 (/home/rcvalle/rust-cfi-3)
+    Finished release [optimized] target(s) in 3.40s
+     Running `target/release/rust-cfi-3`
+The answer is: 12
+With CFI enabled, you should not see the next answer
+Illegal instruction
+$
+```
+Fig. 9. Build and execution of the modified example with LLVM CFI enabled.
+
+When LLVM CFI is enabled, if there are any attempts to change/hijack control
+flow using an indirect branch/call to a function with different return and
+parameter types than the return type expected and arguments intended/passed in
+the call/branch site, the execution is also terminated (see Fig. 9).
 
 [rust-book-ch19-05]: https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html
 [rust-book]: https://doc.rust-lang.org/book/title-page.html
diff --git a/src/test/codegen/sanitizer_cfi_add_canonical_jump_tables_flag.rs b/src/test/codegen/sanitizer-cfi-add-canonical-jump-tables-flag.rs
index 68f81808861..c42fbba7425 100644
--- a/src/test/codegen/sanitizer_cfi_add_canonical_jump_tables_flag.rs
+++ b/src/test/codegen/sanitizer-cfi-add-canonical-jump-tables-flag.rs
@@ -1,10 +1,7 @@
 // Verifies that "CFI Canonical Jump Tables" module flag is added.
 //
-// ignore-windows
 // needs-sanitizer-cfi
-// only-aarch64
-// only-x86_64
-// compile-flags: -Clto -Zsanitizer=cfi
+// compile-flags: -Clto -Ctarget-feature=-crt-static -Zsanitizer=cfi
 
 #![crate_type="lib"]
 
diff --git a/src/test/codegen/sanitizer_cfi_emit_type_checks.rs b/src/test/codegen/sanitizer-cfi-emit-type-checks.rs
index 9ed0422ceff..082f0f121ff 100644
--- a/src/test/codegen/sanitizer_cfi_emit_type_checks.rs
+++ b/src/test/codegen/sanitizer-cfi-emit-type-checks.rs
@@ -1,10 +1,7 @@
 // Verifies that pointer type membership tests for indirect calls are emitted.
 //
-// ignore-windows
 // needs-sanitizer-cfi
-// only-aarch64
-// only-x86_64
-// compile-flags: -Clto -Cno-prepopulate-passes -Zsanitizer=cfi
+// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
 
 #![crate_type="lib"]
 
diff --git a/src/test/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs b/src/test/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs
new file mode 100644
index 00000000000..ece2adbdf43
--- /dev/null
+++ b/src/test/codegen/sanitizer-cfi-emit-type-metadata-id-itanium-cxx-abi.rs
@@ -0,0 +1,575 @@
+// Verifies that type metadata identifiers for functions are emitted correctly.
+//
+// needs-sanitizer-cfi
+// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
+
+#![crate_type="lib"]
+#![allow(dead_code)]
+#![allow(incomplete_features)]
+#![allow(unused_must_use)]
+#![feature(adt_const_params, extern_types, inline_const, type_alias_impl_trait)]
+
+extern crate core;
+use core::ffi::c_void;
+use std::marker::PhantomData;
+
+// User-defined type (structure)
+pub struct Struct1<T> {
+    member1: T,
+}
+
+// User-defined type (enum)
+pub enum Enum1<T> {
+    Variant1(T),
+}
+
+// User-defined type (union)
+pub union Union1<T> {
+    member1: std::mem::ManuallyDrop<T>,
+}
+
+// Extern type
+extern {
+    pub type type1;
+}
+
+// Trait
+pub trait Trait1<T> {
+    fn foo(&self) { }
+}
+
+// Trait implementation
+impl<T> Trait1<T> for i32 {
+    fn foo(&self) { }
+}
+
+// Trait implementation
+impl<T> Trait1<T> for Struct1<T> {
+    fn foo(&self) { }
+}
+
+// impl Trait type aliases for helping with defining other types (see below)
+pub type Type1 = impl Send;
+pub type Type2 = impl Send;
+pub type Type3 = impl Send;
+pub type Type4 = impl Send;
+pub type Type5 = impl Send;
+pub type Type6 = impl Send;
+pub type Type7 = impl Send;
+pub type Type8 = impl Send;
+pub type Type9 = impl Send;
+pub type Type10 = impl Send;
+pub type Type11 = impl Send;
+
+pub fn fn1<'a>() {
+    // Closure
+    let closure1 = || { };
+    let _: Type1 = closure1;
+
+    // Constructor
+    pub struct Foo(i32);
+    let _: Type2 = Foo;
+
+    // Type in extern path
+    extern {
+        fn foo();
+    }
+    let _: Type3 = foo;
+
+    // Type in closure path
+    || {
+        pub struct Foo;
+        let _: Type4 = Foo;
+    };
+
+    // Type in const path
+    const {
+        pub struct Foo;
+        fn foo() -> Type5 { Foo }
+    };
+
+    // Type in impl path
+    impl<T> Struct1<T> {
+        fn foo(&self) { }
+    }
+    let _: Type6 = <Struct1<i32>>::foo;
+
+    // Trait method
+    let _: Type7 = <dyn Trait1<i32>>::foo;
+
+    // Trait method
+    let _: Type8 = <i32 as Trait1<i32>>::foo;
+
+    // Trait method
+    let _: Type9 = <Struct1<i32> as Trait1<i32>>::foo;
+
+    // Const generics
+    pub struct Qux<T, const N: usize>([T; N]);
+    let _: Type10 = Qux([0; 32]);
+
+    // Lifetimes/regions
+    pub struct Quux<'a>(&'a i32);
+    pub struct Quuux<'a, 'b>(&'a i32, &'b Quux<'b>);
+    let _: Type11 = Quuux;
+}
+
+// repr(transparent) user-defined type
+struct Foo(i32);
+
+#[repr(transparent)]
+pub struct Type12 {
+    member1: (),
+    member2: PhantomData<i32>,
+    member3: Foo,
+}
+
+// Self-referencing repr(transparent) user-defined type
+#[repr(transparent)]
+pub struct Type13<'a> {
+    member1: (),
+    member2: PhantomData<i32>,
+    member3: &'a Type13<'a>,
+}
+
+pub fn foo0(_: ()) { }
+// CHECK: define{{.*}}foo0{{.*}}!type ![[TYPE0:[0-9]+]]
+pub fn foo1(_: c_void, _: ()) { }
+// CHECK: define{{.*}}foo1{{.*}}!type ![[TYPE1:[0-9]+]]
+pub fn foo2(_: (), _: c_void, _: c_void) { }
+// CHECK: define{{.*}}foo2{{.*}}!type ![[TYPE2:[0-9]+]]
+pub fn foo3(_: *mut c_void) { }
+// CHECK: define{{.*}}foo3{{.*}}!type ![[TYPE3:[0-9]+]]
+pub fn foo4(_: *mut c_void, _: *mut ()) { }
+// CHECK: define{{.*}}foo4{{.*}}!type ![[TYPE4:[0-9]+]]
+pub fn foo5(_: *mut (), _: *mut c_void, _: *mut c_void) { }
+// CHECK: define{{.*}}foo5{{.*}}!type ![[TYPE5:[0-9]+]]
+pub fn foo6(_: *const c_void) { }
+// CHECK: define{{.*}}foo6{{.*}}!type ![[TYPE6:[0-9]+]]
+pub fn foo7(_: *const c_void, _: *const ()) { }
+// CHECK: define{{.*}}foo7{{.*}}!type ![[TYPE7:[0-9]+]]
+pub fn foo8(_: *const (), _: *const c_void, _: *const c_void) { }
+// CHECK: define{{.*}}foo8{{.*}}!type ![[TYPE8:[0-9]+]]
+pub fn foo9(_: bool) { }
+// CHECK: define{{.*}}foo9{{.*}}!type ![[TYPE9:[0-9]+]]
+pub fn foo10(_: bool, _: bool) { }
+// CHECK: define{{.*}}foo10{{.*}}!type ![[TYPE10:[0-9]+]]
+pub fn foo11(_: bool, _: bool, _: bool) { }
+// CHECK: define{{.*}}foo11{{.*}}!type ![[TYPE11:[0-9]+]]
+pub fn foo12(_: i8) { }
+// CHECK: define{{.*}}foo12{{.*}}!type ![[TYPE12:[0-9]+]]
+pub fn foo13(_: i8, _: i8) { }
+// CHECK: define{{.*}}foo13{{.*}}!type ![[TYPE13:[0-9]+]]
+pub fn foo14(_: i8, _: i8, _: i8) { }
+// CHECK: define{{.*}}foo14{{.*}}!type ![[TYPE14:[0-9]+]]
+pub fn foo15(_: i16) { }
+// CHECK: define{{.*}}foo15{{.*}}!type ![[TYPE15:[0-9]+]]
+pub fn foo16(_: i16, _: i16) { }
+// CHECK: define{{.*}}foo16{{.*}}!type ![[TYPE16:[0-9]+]]
+pub fn foo17(_: i16, _: i16, _: i16) { }
+// CHECK: define{{.*}}foo17{{.*}}!type ![[TYPE17:[0-9]+]]
+pub fn foo18(_: i32) { }
+// CHECK: define{{.*}}foo18{{.*}}!type ![[TYPE18:[0-9]+]]
+pub fn foo19(_: i32, _: i32) { }
+// CHECK: define{{.*}}foo19{{.*}}!type ![[TYPE19:[0-9]+]]
+pub fn foo20(_: i32, _: i32, _: i32) { }
+// CHECK: define{{.*}}foo20{{.*}}!type ![[TYPE20:[0-9]+]]
+pub fn foo21(_: i64) { }
+// CHECK: define{{.*}}foo21{{.*}}!type ![[TYPE21:[0-9]+]]
+pub fn foo22(_: i64, _: i64) { }
+// CHECK: define{{.*}}foo22{{.*}}!type ![[TYPE22:[0-9]+]]
+pub fn foo23(_: i64, _: i64, _: i64) { }
+// CHECK: define{{.*}}foo23{{.*}}!type ![[TYPE23:[0-9]+]]
+pub fn foo24(_: i128) { }
+// CHECK: define{{.*}}foo24{{.*}}!type ![[TYPE24:[0-9]+]]
+pub fn foo25(_: i128, _: i128) { }
+// CHECK: define{{.*}}foo25{{.*}}!type ![[TYPE25:[0-9]+]]
+pub fn foo26(_: i128, _: i128, _: i128) { }
+// CHECK: define{{.*}}foo26{{.*}}!type ![[TYPE26:[0-9]+]]
+pub fn foo27(_: isize) { }
+// CHECK: define{{.*}}foo27{{.*}}!type ![[TYPE27:[0-9]+]]
+pub fn foo28(_: isize, _: isize) { }
+// CHECK: define{{.*}}foo28{{.*}}!type ![[TYPE28:[0-9]+]]
+pub fn foo29(_: isize, _: isize, _: isize) { }
+// CHECK: define{{.*}}foo29{{.*}}!type ![[TYPE29:[0-9]+]]
+pub fn foo30(_: u8) { }
+// CHECK: define{{.*}}foo30{{.*}}!type ![[TYPE30:[0-9]+]]
+pub fn foo31(_: u8, _: u8) { }
+// CHECK: define{{.*}}foo31{{.*}}!type ![[TYPE31:[0-9]+]]
+pub fn foo32(_: u8, _: u8, _: u8) { }
+// CHECK: define{{.*}}foo32{{.*}}!type ![[TYPE32:[0-9]+]]
+pub fn foo33(_: u16) { }
+// CHECK: define{{.*}}foo33{{.*}}!type ![[TYPE33:[0-9]+]]
+pub fn foo34(_: u16, _: u16) { }
+// CHECK: define{{.*}}foo34{{.*}}!type ![[TYPE34:[0-9]+]]
+pub fn foo35(_: u16, _: u16, _: u16) { }
+// CHECK: define{{.*}}foo35{{.*}}!type ![[TYPE35:[0-9]+]]
+pub fn foo36(_: u32) { }
+// CHECK: define{{.*}}foo36{{.*}}!type ![[TYPE36:[0-9]+]]
+pub fn foo37(_: u32, _: u32) { }
+// CHECK: define{{.*}}foo37{{.*}}!type ![[TYPE37:[0-9]+]]
+pub fn foo38(_: u32, _: u32, _: u32) { }
+// CHECK: define{{.*}}foo38{{.*}}!type ![[TYPE38:[0-9]+]]
+pub fn foo39(_: u64) { }
+// CHECK: define{{.*}}foo39{{.*}}!type ![[TYPE39:[0-9]+]]
+pub fn foo40(_: u64, _: u64) { }
+// CHECK: define{{.*}}foo40{{.*}}!type ![[TYPE40:[0-9]+]]
+pub fn foo41(_: u64, _: u64, _: u64) { }
+// CHECK: define{{.*}}foo41{{.*}}!type ![[TYPE41:[0-9]+]]
+pub fn foo42(_: u128) { }
+// CHECK: define{{.*}}foo42{{.*}}!type ![[TYPE42:[0-9]+]]
+pub fn foo43(_: u128, _: u128) { }
+// CHECK: define{{.*}}foo43{{.*}}!type ![[TYPE43:[0-9]+]]
+pub fn foo44(_: u128, _: u128, _: u128) { }
+// CHECK: define{{.*}}foo44{{.*}}!type ![[TYPE44:[0-9]+]]
+pub fn foo45(_: usize) { }
+// CHECK: define{{.*}}foo45{{.*}}!type ![[TYPE45:[0-9]+]]
+pub fn foo46(_: usize, _: usize) { }
+// CHECK: define{{.*}}foo46{{.*}}!type ![[TYPE46:[0-9]+]]
+pub fn foo47(_: usize, _: usize, _: usize) { }
+// CHECK: define{{.*}}foo47{{.*}}!type ![[TYPE47:[0-9]+]]
+pub fn foo48(_: f32) { }
+// CHECK: define{{.*}}foo48{{.*}}!type ![[TYPE48:[0-9]+]]
+pub fn foo49(_: f32, _: f32) { }
+// CHECK: define{{.*}}foo49{{.*}}!type ![[TYPE49:[0-9]+]]
+pub fn foo50(_: f32, _: f32, _: f32) { }
+// CHECK: define{{.*}}foo50{{.*}}!type ![[TYPE50:[0-9]+]]
+pub fn foo51(_: f64) { }
+// CHECK: define{{.*}}foo51{{.*}}!type ![[TYPE51:[0-9]+]]
+pub fn foo52(_: f64, _: f64) { }
+// CHECK: define{{.*}}foo52{{.*}}!type ![[TYPE52:[0-9]+]]
+pub fn foo53(_: f64, _: f64, _: f64) { }
+// CHECK: define{{.*}}foo53{{.*}}!type ![[TYPE53:[0-9]+]]
+pub fn foo54(_: char) { }
+// CHECK: define{{.*}}foo54{{.*}}!type ![[TYPE54:[0-9]+]]
+pub fn foo55(_: char, _: char) { }
+// CHECK: define{{.*}}foo55{{.*}}!type ![[TYPE55:[0-9]+]]
+pub fn foo56(_: char, _: char, _: char) { }
+// CHECK: define{{.*}}foo56{{.*}}!type ![[TYPE56:[0-9]+]]
+pub fn foo57(_: &str) { }
+// CHECK: define{{.*}}foo57{{.*}}!type ![[TYPE57:[0-9]+]]
+pub fn foo58(_: &str, _: &str) { }
+// CHECK: define{{.*}}foo58{{.*}}!type ![[TYPE58:[0-9]+]]
+pub fn foo59(_: &str, _: &str, _: &str) { }
+// CHECK: define{{.*}}foo59{{.*}}!type ![[TYPE59:[0-9]+]]
+pub fn foo60(_: (i32, i32)) { }
+// CHECK: define{{.*}}foo60{{.*}}!type ![[TYPE60:[0-9]+]]
+pub fn foo61(_: (i32, i32), _: (i32, i32)) { }
+// CHECK: define{{.*}}foo61{{.*}}!type ![[TYPE61:[0-9]+]]
+pub fn foo62(_: (i32, i32), _: (i32, i32), _: (i32, i32)) { }
+// CHECK: define{{.*}}foo62{{.*}}!type ![[TYPE62:[0-9]+]]
+pub fn foo63(_: [i32; 32]) { }
+// CHECK: define{{.*}}foo63{{.*}}!type ![[TYPE63:[0-9]+]]
+pub fn foo64(_: [i32; 32], _: [i32; 32]) { }
+// CHECK: define{{.*}}foo64{{.*}}!type ![[TYPE64:[0-9]+]]
+pub fn foo65(_: [i32; 32], _: [i32; 32], _: [i32; 32]) { }
+// CHECK: define{{.*}}foo65{{.*}}!type ![[TYPE65:[0-9]+]]
+pub fn foo66(_: &[i32]) { }
+// CHECK: define{{.*}}foo66{{.*}}!type ![[TYPE66:[0-9]+]]
+pub fn foo67(_: &[i32], _: &[i32]) { }
+// CHECK: define{{.*}}foo67{{.*}}!type ![[TYPE67:[0-9]+]]
+pub fn foo68(_: &[i32], _: &[i32], _: &[i32]) { }
+// CHECK: define{{.*}}foo68{{.*}}!type ![[TYPE68:[0-9]+]]
+pub fn foo69(_: &Struct1::<i32>) { }
+// CHECK: define{{.*}}foo69{{.*}}!type ![[TYPE69:[0-9]+]]
+pub fn foo70(_: &Struct1::<i32>, _: &Struct1::<i32>) { }
+// CHECK: define{{.*}}foo70{{.*}}!type ![[TYPE70:[0-9]+]]
+pub fn foo71(_: &Struct1::<i32>, _: &Struct1::<i32>, _: &Struct1::<i32>) { }
+// CHECK: define{{.*}}foo71{{.*}}!type ![[TYPE71:[0-9]+]]
+pub fn foo72(_: &Enum1::<i32>) { }
+// CHECK: define{{.*}}foo72{{.*}}!type ![[TYPE72:[0-9]+]]
+pub fn foo73(_: &Enum1::<i32>, _: &Enum1::<i32>) { }
+// CHECK: define{{.*}}foo73{{.*}}!type ![[TYPE73:[0-9]+]]
+pub fn foo74(_: &Enum1::<i32>, _: &Enum1::<i32>, _: &Enum1::<i32>) { }
+// CHECK: define{{.*}}foo74{{.*}}!type ![[TYPE74:[0-9]+]]
+pub fn foo75(_: &Union1::<i32>) { }
+// CHECK: define{{.*}}foo75{{.*}}!type ![[TYPE75:[0-9]+]]
+pub fn foo76(_: &Union1::<i32>, _: &Union1::<i32>) { }
+// CHECK: define{{.*}}foo76{{.*}}!type ![[TYPE76:[0-9]+]]
+pub fn foo77(_: &Union1::<i32>, _: &Union1::<i32>, _: &Union1::<i32>) { }
+// CHECK: define{{.*}}foo77{{.*}}!type ![[TYPE77:[0-9]+]]
+pub fn foo78(_: *mut type1) { }
+// CHECK: define{{.*}}foo78{{.*}}!type ![[TYPE78:[0-9]+]]
+pub fn foo79(_: *mut type1, _: *mut type1) { }
+// CHECK: define{{.*}}foo79{{.*}}!type ![[TYPE79:[0-9]+]]
+pub fn foo80(_: *mut type1, _: *mut type1, _: *mut type1) { }
+// CHECK: define{{.*}}foo80{{.*}}!type ![[TYPE80:[0-9]+]]
+pub fn foo81(_: &mut i32) { }
+// CHECK: define{{.*}}foo81{{.*}}!type ![[TYPE81:[0-9]+]]
+pub fn foo82(_: &mut i32, _: &i32) { }
+// CHECK: define{{.*}}foo82{{.*}}!type ![[TYPE82:[0-9]+]]
+pub fn foo83(_: &mut i32, _: &i32, _: &i32) { }
+// CHECK: define{{.*}}foo83{{.*}}!type ![[TYPE83:[0-9]+]]
+pub fn foo84(_: &i32) { }
+// CHECK: define{{.*}}foo84{{.*}}!type ![[TYPE84:[0-9]+]]
+pub fn foo85(_: &i32, _: &mut i32) { }
+// CHECK: define{{.*}}foo85{{.*}}!type ![[TYPE85:[0-9]+]]
+pub fn foo86(_: &i32, _: &mut i32, _: &mut i32) { }
+// CHECK: define{{.*}}foo86{{.*}}!type ![[TYPE86:[0-9]+]]
+pub fn foo87(_: *mut i32) { }
+// CHECK: define{{.*}}foo87{{.*}}!type ![[TYPE87:[0-9]+]]
+pub fn foo88(_: *mut i32, _: *const i32) { }
+// CHECK: define{{.*}}foo88{{.*}}!type ![[TYPE88:[0-9]+]]
+pub fn foo89(_: *mut i32, _: *const i32, _: *const i32) { }
+// CHECK: define{{.*}}foo89{{.*}}!type ![[TYPE89:[0-9]+]]
+pub fn foo90(_: *const i32) { }
+// CHECK: define{{.*}}foo90{{.*}}!type ![[TYPE90:[0-9]+]]
+pub fn foo91(_: *const i32, _: *mut i32) { }
+// CHECK: define{{.*}}foo91{{.*}}!type ![[TYPE91:[0-9]+]]
+pub fn foo92(_: *const i32, _: *mut i32, _: *mut i32) { }
+// CHECK: define{{.*}}foo92{{.*}}!type ![[TYPE92:[0-9]+]]
+pub fn foo93(_: fn(i32) -> i32) { }
+// CHECK: define{{.*}}foo93{{.*}}!type ![[TYPE93:[0-9]+]]
+pub fn foo94(_: fn(i32) -> i32, _: fn(i32) -> i32) { }
+// CHECK: define{{.*}}foo94{{.*}}!type ![[TYPE94:[0-9]+]]
+pub fn foo95(_: fn(i32) -> i32, _: fn(i32) -> i32, _: fn(i32) -> i32) { }
+// CHECK: define{{.*}}foo95{{.*}}!type ![[TYPE95:[0-9]+]]
+pub fn foo96(_: &dyn Fn(i32) -> i32) { }
+// CHECK: define{{.*}}foo96{{.*}}!type ![[TYPE96:[0-9]+]]
+pub fn foo97(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { }
+// CHECK: define{{.*}}foo97{{.*}}!type ![[TYPE97:[0-9]+]]
+pub fn foo98(_: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32, _: &dyn Fn(i32) -> i32) { }
+// CHECK: define{{.*}}foo98{{.*}}!type ![[TYPE98:[0-9]+]]
+pub fn foo99(_: &dyn FnMut(i32) -> i32) { }
+// CHECK: define{{.*}}foo99{{.*}}!type ![[TYPE99:[0-9]+]]
+pub fn foo100(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { }
+// CHECK: define{{.*}}foo100{{.*}}!type ![[TYPE100:[0-9]+]]
+pub fn foo101(_: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32, _: &dyn FnMut(i32) -> i32) { }
+// CHECK: define{{.*}}foo101{{.*}}!type ![[TYPE101:[0-9]+]]
+pub fn foo102(_: &dyn FnOnce(i32) -> i32) { }
+// CHECK: define{{.*}}foo102{{.*}}!type ![[TYPE102:[0-9]+]]
+pub fn foo103(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) { }
+// CHECK: define{{.*}}foo103{{.*}}!type ![[TYPE103:[0-9]+]]
+pub fn foo104(_: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32, _: &dyn FnOnce(i32) -> i32) {}
+// CHECK: define{{.*}}foo104{{.*}}!type ![[TYPE104:[0-9]+]]
+pub fn foo105(_: &dyn Send) { }
+// CHECK: define{{.*}}foo105{{.*}}!type ![[TYPE105:[0-9]+]]
+pub fn foo106(_: &dyn Send, _: &dyn Send) { }
+// CHECK: define{{.*}}foo106{{.*}}!type ![[TYPE106:[0-9]+]]
+pub fn foo107(_: &dyn Send, _: &dyn Send, _: &dyn Send) { }
+// CHECK: define{{.*}}foo107{{.*}}!type ![[TYPE107:[0-9]+]]
+pub fn foo108(_: Type1) { }
+// CHECK: define{{.*}}foo108{{.*}}!type ![[TYPE108:[0-9]+]]
+pub fn foo109(_: Type1, _: Type1) { }
+// CHECK: define{{.*}}foo109{{.*}}!type ![[TYPE109:[0-9]+]]
+pub fn foo110(_: Type1, _: Type1, _: Type1) { }
+// CHECK: define{{.*}}foo110{{.*}}!type ![[TYPE110:[0-9]+]]
+pub fn foo111(_: Type2) { }
+// CHECK: define{{.*}}foo111{{.*}}!type ![[TYPE111:[0-9]+]]
+pub fn foo112(_: Type2, _: Type2) { }
+// CHECK: define{{.*}}foo112{{.*}}!type ![[TYPE112:[0-9]+]]
+pub fn foo113(_: Type2, _: Type2, _: Type2) { }
+// CHECK: define{{.*}}foo113{{.*}}!type ![[TYPE113:[0-9]+]]
+pub fn foo114(_: Type3) { }
+// CHECK: define{{.*}}foo114{{.*}}!type ![[TYPE114:[0-9]+]]
+pub fn foo115(_: Type3, _: Type3) { }
+// CHECK: define{{.*}}foo115{{.*}}!type ![[TYPE115:[0-9]+]]
+pub fn foo116(_: Type3, _: Type3, _: Type3) { }
+// CHECK: define{{.*}}foo116{{.*}}!type ![[TYPE116:[0-9]+]]
+pub fn foo117(_: Type4) { }
+// CHECK: define{{.*}}foo117{{.*}}!type ![[TYPE117:[0-9]+]]
+pub fn foo118(_: Type4, _: Type4) { }
+// CHECK: define{{.*}}foo118{{.*}}!type ![[TYPE118:[0-9]+]]
+pub fn foo119(_: Type4, _: Type4, _: Type4) { }
+// CHECK: define{{.*}}foo119{{.*}}!type ![[TYPE119:[0-9]+]]
+pub fn foo120(_: Type5) { }
+// CHECK: define{{.*}}foo120{{.*}}!type ![[TYPE120:[0-9]+]]
+pub fn foo121(_: Type5, _: Type5) { }
+// CHECK: define{{.*}}foo121{{.*}}!type ![[TYPE121:[0-9]+]]
+pub fn foo122(_: Type5, _: Type5, _: Type5) { }
+// CHECK: define{{.*}}foo122{{.*}}!type ![[TYPE122:[0-9]+]]
+pub fn foo123(_: Type6) { }
+// CHECK: define{{.*}}foo123{{.*}}!type ![[TYPE123:[0-9]+]]
+pub fn foo124(_: Type6, _: Type6) { }
+// CHECK: define{{.*}}foo124{{.*}}!type ![[TYPE124:[0-9]+]]
+pub fn foo125(_: Type6, _: Type6, _: Type6) { }
+// CHECK: define{{.*}}foo125{{.*}}!type ![[TYPE125:[0-9]+]]
+pub fn foo126(_: Type7) { }
+// CHECK: define{{.*}}foo126{{.*}}!type ![[TYPE126:[0-9]+]]
+pub fn foo127(_: Type7, _: Type7) { }
+// CHECK: define{{.*}}foo127{{.*}}!type ![[TYPE127:[0-9]+]]
+pub fn foo128(_: Type7, _: Type7, _: Type7) { }
+// CHECK: define{{.*}}foo128{{.*}}!type ![[TYPE128:[0-9]+]]
+pub fn foo129(_: Type8) { }
+// CHECK: define{{.*}}foo129{{.*}}!type ![[TYPE129:[0-9]+]]
+pub fn foo130(_: Type8, _: Type8) { }
+// CHECK: define{{.*}}foo130{{.*}}!type ![[TYPE130:[0-9]+]]
+pub fn foo131(_: Type8, _: Type8, _: Type8) { }
+// CHECK: define{{.*}}foo131{{.*}}!type ![[TYPE131:[0-9]+]]
+pub fn foo132(_: Type9) { }
+// CHECK: define{{.*}}foo132{{.*}}!type ![[TYPE132:[0-9]+]]
+pub fn foo133(_: Type9, _: Type9) { }
+// CHECK: define{{.*}}foo133{{.*}}!type ![[TYPE133:[0-9]+]]
+pub fn foo134(_: Type9, _: Type9, _: Type9) { }
+// CHECK: define{{.*}}foo134{{.*}}!type ![[TYPE134:[0-9]+]]
+pub fn foo135(_: Type10) { }
+// CHECK: define{{.*}}foo135{{.*}}!type ![[TYPE135:[0-9]+]]
+pub fn foo136(_: Type10, _: Type10) { }
+// CHECK: define{{.*}}foo136{{.*}}!type ![[TYPE136:[0-9]+]]
+pub fn foo137(_: Type10, _: Type10, _: Type10) { }
+// CHECK: define{{.*}}foo137{{.*}}!type ![[TYPE137:[0-9]+]]
+pub fn foo138(_: Type11) { }
+// CHECK: define{{.*}}foo138{{.*}}!type ![[TYPE138:[0-9]+]]
+pub fn foo139(_: Type11, _: Type11) { }
+// CHECK: define{{.*}}foo139{{.*}}!type ![[TYPE139:[0-9]+]]
+pub fn foo140(_: Type11, _: Type11, _: Type11) { }
+// CHECK: define{{.*}}foo140{{.*}}!type ![[TYPE140:[0-9]+]]
+pub fn foo141(_: Type12) { }
+// CHECK: define{{.*}}foo141{{.*}}!type ![[TYPE141:[0-9]+]]
+pub fn foo142(_: Type12, _: Type12) { }
+// CHECK: define{{.*}}foo142{{.*}}!type ![[TYPE142:[0-9]+]]
+pub fn foo143(_: Type12, _: Type12, _: Type12) { }
+// CHECK: define{{.*}}foo143{{.*}}!type ![[TYPE143:[0-9]+]]
+pub fn foo144(_: Type13) { }
+// CHECK: define{{.*}}foo144{{.*}}!type ![[TYPE144:[0-9]+]]
+pub fn foo145(_: Type13, _: Type13) { }
+// CHECK: define{{.*}}foo145{{.*}}!type ![[TYPE145:[0-9]+]]
+pub fn foo146(_: Type13, _: Type13, _: Type13) { }
+// CHECK: define{{.*}}foo146{{.*}}!type ![[TYPE146:[0-9]+]]
+
+// CHECK: ![[TYPE0]] = !{i64 0, !"_ZTSFvvE"}
+// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvvvE"}
+// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvvvvE"}
+// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFvPvE"}
+// CHECK: ![[TYPE4]] = !{i64 0, !"_ZTSFvPvS_E"}
+// CHECK: ![[TYPE5]] = !{i64 0, !"_ZTSFvPvS_S_E"}
+// CHECK: ![[TYPE6]] = !{i64 0, !"_ZTSFvPKvE"}
+// CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvPKvS0_E"}
+// CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvPKvS0_S0_E"}
+// CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvbE"}
+// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvbbE"}
+// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvbbbE"}
+// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvu2i8E"}
+// CHECK: ![[TYPE13]] = !{i64 0, !"_ZTSFvu2i8S_E"}
+// CHECK: ![[TYPE14]] = !{i64 0, !"_ZTSFvu2i8S_S_E"}
+// CHECK: ![[TYPE15]] = !{i64 0, !"_ZTSFvu3i16E"}
+// CHECK: ![[TYPE16]] = !{i64 0, !"_ZTSFvu3i16S_E"}
+// CHECK: ![[TYPE17]] = !{i64 0, !"_ZTSFvu3i16S_S_E"}
+// CHECK: ![[TYPE18]] = !{i64 0, !"_ZTSFvu3i32E"}
+// CHECK: ![[TYPE19]] = !{i64 0, !"_ZTSFvu3i32S_E"}
+// CHECK: ![[TYPE20]] = !{i64 0, !"_ZTSFvu3i32S_S_E"}
+// CHECK: ![[TYPE21]] = !{i64 0, !"_ZTSFvu3i64E"}
+// CHECK: ![[TYPE22]] = !{i64 0, !"_ZTSFvu3i64S_E"}
+// CHECK: ![[TYPE23]] = !{i64 0, !"_ZTSFvu3i64S_S_E"}
+// CHECK: ![[TYPE24]] = !{i64 0, !"_ZTSFvu4i128E"}
+// CHECK: ![[TYPE25]] = !{i64 0, !"_ZTSFvu4i128S_E"}
+// CHECK: ![[TYPE26]] = !{i64 0, !"_ZTSFvu4i128S_S_E"}
+// CHECK: ![[TYPE27]] = !{i64 0, !"_ZTSFvu5isizeE"}
+// CHECK: ![[TYPE28]] = !{i64 0, !"_ZTSFvu5isizeS_E"}
+// CHECK: ![[TYPE29]] = !{i64 0, !"_ZTSFvu5isizeS_S_E"}
+// CHECK: ![[TYPE30]] = !{i64 0, !"_ZTSFvu2u8E"}
+// CHECK: ![[TYPE31]] = !{i64 0, !"_ZTSFvu2u8S_E"}
+// CHECK: ![[TYPE32]] = !{i64 0, !"_ZTSFvu2u8S_S_E"}
+// CHECK: ![[TYPE33]] = !{i64 0, !"_ZTSFvu3u16E"}
+// CHECK: ![[TYPE34]] = !{i64 0, !"_ZTSFvu3u16S_E"}
+// CHECK: ![[TYPE35]] = !{i64 0, !"_ZTSFvu3u16S_S_E"}
+// CHECK: ![[TYPE36]] = !{i64 0, !"_ZTSFvu3u32E"}
+// CHECK: ![[TYPE37]] = !{i64 0, !"_ZTSFvu3u32S_E"}
+// CHECK: ![[TYPE38]] = !{i64 0, !"_ZTSFvu3u32S_S_E"}
+// CHECK: ![[TYPE39]] = !{i64 0, !"_ZTSFvu3u64E"}
+// CHECK: ![[TYPE40]] = !{i64 0, !"_ZTSFvu3u64S_E"}
+// CHECK: ![[TYPE41]] = !{i64 0, !"_ZTSFvu3u64S_S_E"}
+// CHECK: ![[TYPE42]] = !{i64 0, !"_ZTSFvu4u128E"}
+// CHECK: ![[TYPE43]] = !{i64 0, !"_ZTSFvu4u128S_E"}
+// CHECK: ![[TYPE44]] = !{i64 0, !"_ZTSFvu4u128S_S_E"}
+// CHECK: ![[TYPE45]] = !{i64 0, !"_ZTSFvu5usizeE"}
+// CHECK: ![[TYPE46]] = !{i64 0, !"_ZTSFvu5usizeS_E"}
+// CHECK: ![[TYPE47]] = !{i64 0, !"_ZTSFvu5usizeS_S_E"}
+// CHECK: ![[TYPE48]] = !{i64 0, !"_ZTSFvu3f32E"}
+// CHECK: ![[TYPE49]] = !{i64 0, !"_ZTSFvu3f32S_E"}
+// CHECK: ![[TYPE50]] = !{i64 0, !"_ZTSFvu3f32S_S_E"}
+// CHECK: ![[TYPE51]] = !{i64 0, !"_ZTSFvu3f64E"}
+// CHECK: ![[TYPE52]] = !{i64 0, !"_ZTSFvu3f64S_E"}
+// CHECK: ![[TYPE53]] = !{i64 0, !"_ZTSFvu3f64S_S_E"}
+// CHECK: ![[TYPE54]] = !{i64 0, !"_ZTSFvu4charE"}
+// CHECK: ![[TYPE55]] = !{i64 0, !"_ZTSFvu4charS_E"}
+// CHECK: ![[TYPE56]] = !{i64 0, !"_ZTSFvu4charS_S_E"}
+// CHECK: ![[TYPE57]] = !{i64 0, !"_ZTSFvu3refIu3strEE"}
+// CHECK: ![[TYPE58]] = !{i64 0, !"_ZTSFvu3refIu3strES0_E"}
+// CHECK: ![[TYPE59]] = !{i64 0, !"_ZTSFvu3refIu3strES0_S0_E"}
+// CHECK: ![[TYPE60]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_EE"}
+// CHECK: ![[TYPE61]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_ES0_E"}
+// CHECK: ![[TYPE62]] = !{i64 0, !"_ZTSFvu5tupleIu3i32S_ES0_S0_E"}
+// CHECK: ![[TYPE63]] = !{i64 0, !"_ZTSFvA32u3i32E"}
+// CHECK: ![[TYPE64]] = !{i64 0, !"_ZTSFvA32u3i32S0_E"}
+// CHECK: ![[TYPE65]] = !{i64 0, !"_ZTSFvA32u3i32S0_S0_E"}
+// CHECK: ![[TYPE66]] = !{i64 0, !"_ZTSFvu3refIu5sliceIu3i32EEE"}
+// CHECK: ![[TYPE67]] = !{i64 0, !"_ZTSFvu3refIu5sliceIu3i32EES1_E"}
+// CHECK: ![[TYPE68]] = !{i64 0, !"_ZTSFvu3refIu5sliceIu3i32EES1_S1_E"}
+// CHECK: ![[TYPE69]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32EEE"}
+// CHECK: ![[TYPE70]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32EES1_E"}
+// CHECK: ![[TYPE71]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32EES1_S1_E"}
+// CHECK: ![[TYPE72]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi5Enum1Iu3i32EEE"}
+// CHECK: ![[TYPE73]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi5Enum1Iu3i32EES1_E"}
+// CHECK: ![[TYPE74]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi5Enum1Iu3i32EES1_S1_E"}
+// CHECK: ![[TYPE75]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Union1Iu3i32EEE"}
+// CHECK: ![[TYPE76]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Union1Iu3i32EES1_E"}
+// CHECK: ![[TYPE77]] = !{i64 0, !"_ZTSFvu3refIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Union1Iu3i32EES1_S1_E"}
+// CHECK: ![[TYPE78]] = !{i64 0, !"_ZTSFvP5type1E"}
+// CHECK: ![[TYPE79]] = !{i64 0, !"_ZTSFvP5type1S0_E"}
+// CHECK: ![[TYPE80]] = !{i64 0, !"_ZTSFvP5type1S0_S0_E"}
+// CHECK: ![[TYPE81]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32EE"}
+// CHECK: ![[TYPE82]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32ES0_E"}
+// CHECK: ![[TYPE83]] = !{i64 0, !"_ZTSFvU3mutu3refIu3i32ES0_S0_E"}
+// CHECK: ![[TYPE84]] = !{i64 0, !"_ZTSFvu3refIu3i32EE"}
+// CHECK: ![[TYPE85]] = !{i64 0, !"_ZTSFvu3refIu3i32EU3mutS0_E"}
+// CHECK: ![[TYPE86]] = !{i64 0, !"_ZTSFvu3refIu3i32EU3mutS0_S1_E"}
+// CHECK: ![[TYPE87]] = !{i64 0, !"_ZTSFvPu3i32E"}
+// CHECK: ![[TYPE88]] = !{i64 0, !"_ZTSFvPu3i32PKS_E"}
+// CHECK: ![[TYPE89]] = !{i64 0, !"_ZTSFvPu3i32PKS_S2_E"}
+// CHECK: ![[TYPE90]] = !{i64 0, !"_ZTSFvPKu3i32E"}
+// CHECK: ![[TYPE91]] = !{i64 0, !"_ZTSFvPKu3i32PS_E"}
+// CHECK: ![[TYPE92]] = !{i64 0, !"_ZTSFvPKu3i32PS_S2_E"}
+// CHECK: ![[TYPE93]] = !{i64 0, !"_ZTSFvPFu3i32S_EE"}
+// CHECK: ![[TYPE94]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_E"}
+// CHECK: ![[TYPE95]] = !{i64 0, !"_ZTSFvPFu3i32S_ES0_S0_E"}
+// CHECK: ![[TYPE96]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"}
+// CHECK: ![[TYPE97]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"}
+// CHECK: ![[TYPE98]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function2FnIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"}
+// CHECK: ![[TYPE99]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"}
+// CHECK: ![[TYPE100]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"}
+// CHECK: ![[TYPE101]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function5FnMutIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"}
+// CHECK: ![[TYPE102]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEEE"}
+// CHECK: ![[TYPE103]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_E"}
+// CHECK: ![[TYPE104]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnceIu5tupleIu3i32EEu{{[0-9]+}}NtNtNtNtC{{[[:print:]]+}}_4core3ops8function6FnOnce6OutputIS0_ES_u6regionEES5_S5_E"}
+// CHECK: ![[TYPE105]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEEE"}
+// CHECK: ![[TYPE106]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_E"}
+// CHECK: ![[TYPE107]] = !{i64 0, !"_ZTSFvu3refIu3dynIu{{[0-9]+}}NtNtC{{[[:print:]]+}}_4core6marker4Sendu6regionEES2_S2_E"}
+// CHECK: ![[TYPE108]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn111{{[{}][{}]}}closure{{[}][}]}}Iu2i8PFvvEvEE"}
+// CHECK: ![[TYPE109]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn111{{[{}][{}]}}closure{{[}][}]}}Iu2i8PFvvEvES1_E"}
+// CHECK: ![[TYPE110]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn111{{[{}][{}]}}closure{{[}][}]}}Iu2i8PFvvEvES1_S1_E"}
+// CHECK: ![[TYPE111]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13Foo15{{[{}][{}]}}constructor{{[}][}]}}E"}
+// CHECK: ![[TYPE112]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13Foo15{{[{}][{}]}}constructor{{[}][}]}}S_E"}
+// CHECK: ![[TYPE113]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13Foo15{{[{}][{}]}}constructor{{[}][}]}}S_S_E"}
+// CHECK: ![[TYPE114]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn110{{[{}][{}]}}extern{{[}][}]}}3fooE"}
+// CHECK: ![[TYPE115]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn110{{[{}][{}]}}extern{{[}][}]}}3fooS_E"}
+// CHECK: ![[TYPE116]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNFNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn110{{[{}][{}]}}extern{{[}][}]}}3fooS_S_E"}
+// CHECK: ![[TYPE117]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn1s0_11{{[{}][{}]}}closure{{[}][}]}}3FooE"}
+// CHECK: ![[TYPE118]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn1s0_11{{[{}][{}]}}closure{{[}][}]}}3FooS_E"}
+// CHECK: ![[TYPE119]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNCNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn1s0_11{{[{}][{}]}}closure{{[}][}]}}3FooS_S_E"}
+// CHECK: ![[TYPE120]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn112{{[{}][{}]}}constant{{[}][}]}}3FooE"}
+// CHECK: ![[TYPE121]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn112{{[{}][{}]}}constant{{[}][}]}}3FooS_E"}
+// CHECK: ![[TYPE122]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNkNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn112{{[{}][{}]}}constant{{[}][}]}}3FooS_S_E"}
+// CHECK: ![[TYPE123]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32EE"}
+// CHECK: ![[TYPE124]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32ES0_E"}
+// CHECK: ![[TYPE125]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNINvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn18{{[{}][{}]}}impl{{[}][}]}}3fooIu3i32ES0_S0_E"}
+// CHECK: ![[TYPE126]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_EE"}
+// CHECK: ![[TYPE127]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_ES3_E"}
+// CHECK: ![[TYPE128]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3dynIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait1Iu3i32Eu6regionES_ES3_S3_E"}
+// CHECK: ![[TYPE129]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_EE"}
+// CHECK: ![[TYPE130]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_ES0_E"}
+// CHECK: ![[TYPE131]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu3i32S_ES0_S0_E"}
+// CHECK: ![[TYPE132]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32ES_EE"}
+// CHECK: ![[TYPE133]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32ES_ES1_E"}
+// CHECK: ![[TYPE134]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NvNtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi6Trait13fooIu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi7Struct1Iu3i32ES_ES1_S1_E"}
+// CHECK: ![[TYPE135]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13QuxIu3i32Lu5usize32EEE"}
+// CHECK: ![[TYPE136]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13QuxIu3i32Lu5usize32EES2_E"}
+// CHECK: ![[TYPE137]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn13QuxIu3i32Lu5usize32EES2_S2_E"}
+// CHECK: ![[TYPE138]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn15Quuux15{{[{}][{}]}}constructor{{[}][}]}}Iu6regionS_EE"}
+// CHECK: ![[TYPE139]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn15Quuux15{{[{}][{}]}}constructor{{[}][}]}}Iu6regionS_ES0_E"}
+// CHECK: ![[TYPE140]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NcNtNvC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3fn15Quuux15{{[{}][{}]}}constructor{{[}][}]}}Iu6regionS_ES0_S0_E"}
+// CHECK: ![[TYPE141]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooE"}
+// CHECK: ![[TYPE142]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_E"}
+// CHECK: ![[TYPE143]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_51sanitizer_cfi_emit_type_metadata_id_itanium_cxx_abi3FooS_S_E"}
+// CHECK: ![[TYPE144]] = !{i64 0, !"_ZTSFvu3refIu3refIvEEE"}
+// CHECK: ![[TYPE145]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_E"}
+// CHECK: ![[TYPE146]] = !{i64 0, !"_ZTSFvu3refIu3refIvEES0_S0_E"}
diff --git a/src/test/codegen/sanitizer-cfi-emit-type-metadata-itanium-cxx-abi.rs b/src/test/codegen/sanitizer-cfi-emit-type-metadata-itanium-cxx-abi.rs
new file mode 100644
index 00000000000..09310ba9f60
--- /dev/null
+++ b/src/test/codegen/sanitizer-cfi-emit-type-metadata-itanium-cxx-abi.rs
@@ -0,0 +1,31 @@
+// Verifies that type metadata for functions are emitted.
+//
+// needs-sanitizer-cfi
+// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
+
+#![crate_type="lib"]
+
+pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
+    // CHECK-LABEL: define{{.*}}foo
+    // CHECK-SAME: {{.*}}!type ![[TYPE1:[0-9]+]]
+    // CHECK:       %1 = call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFu3i32S_E")
+    f(arg)
+}
+
+pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
+    // CHECK-LABEL: define{{.*}}bar
+    // CHECK-SAME: {{.*}}!type ![[TYPE2:[0-9]+]]
+    // CHECK:       %1 = call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFu3i32S_S_E")
+    f(arg1, arg2)
+}
+
+pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
+    // CHECK-LABEL: define{{.*}}baz
+    // CHECK-SAME: {{.*}}!type ![[TYPE3:[0-9]+]]
+    // CHECK:       %1 = call i1 @llvm.type.test(i8* %0, metadata !"_ZTSFu3i32S_S_S_E")
+    f(arg1, arg2, arg3)
+}
+
+// CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFu3i32PFS_S_ES_E"}
+// CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFu3i32PFS_S_S_ES_S_E"}
+// CHECK: ![[TYPE3]] = !{i64 0, !"_ZTSFu3i32PFS_S_S_S_ES_S_S_E"}
diff --git a/src/test/codegen/sanitizer_cfi_emit_type_metadata.rs b/src/test/codegen/sanitizer_cfi_emit_type_metadata.rs
deleted file mode 100644
index 96fced47e78..00000000000
--- a/src/test/codegen/sanitizer_cfi_emit_type_metadata.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Verifies that type metadata for functions are emitted.
-//
-// ignore-windows
-// needs-sanitizer-cfi
-// only-aarch64
-// only-x86_64
-// compile-flags: -Clto -Cno-prepopulate-passes -Zsanitizer=cfi
-
-#![crate_type="lib"]
-
-pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
-    // CHECK-LABEL: define{{.*}}foo{{.*}}!type !{{[0-9]+}}
-    // CHECK:       %1 = call i1 @llvm.type.test(i8* %0, metadata !"typeid1")
-    f(arg)
-}
-
-pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
-    // CHECK-LABEL: define{{.*}}bar{{.*}}!type !{{[0-9]+}}
-    // CHECK:       %1 = call i1 @llvm.type.test(i8* %0, metadata !"typeid2")
-    f(arg1, arg2)
-}
-
-pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
-    // CHECK-LABEL: define{{.*}}baz{{.*}}!type !{{[0-9]+}}
-    // CHECK:       %1 = call i1 @llvm.type.test(i8* %0, metadata !"typeid3")
-    f(arg1, arg2, arg3)
-}
-
-// CHECK: !{{[0-9]+}} = !{i64 0, !"typeid2"}
-// CHECK: !{{[0-9]+}} = !{i64 0, !"typeid3"}
-// CHECK: !{{[0-9]+}} = !{i64 0, !"typeid4"}
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 15d82e6d596..f8f193ddf83 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -871,6 +871,7 @@ pub fn make_test_description<R: Read>(
     let rustc_has_sanitizer_support = env::var_os("RUSTC_SANITIZER_SUPPORT").is_some();
     let has_asm_support = util::has_asm_support(&config.target);
     let has_asan = util::ASAN_SUPPORTED_TARGETS.contains(&&*config.target);
+    let has_cfi = util::CFI_SUPPORTED_TARGETS.contains(&&*config.target);
     let has_lsan = util::LSAN_SUPPORTED_TARGETS.contains(&&*config.target);
     let has_msan = util::MSAN_SUPPORTED_TARGETS.contains(&&*config.target);
     let has_tsan = util::TSAN_SUPPORTED_TARGETS.contains(&&*config.target);
@@ -909,6 +910,7 @@ pub fn make_test_description<R: Read>(
         ignore |= !rustc_has_sanitizer_support
             && config.parse_name_directive(ln, "needs-sanitizer-support");
         ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
+        ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi");
         ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak");
         ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
         ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 102b9308dc9..22df18ee9fb 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -96,6 +96,23 @@ pub const ASAN_SUPPORTED_TARGETS: &[&str] = &[
     "x86_64-unknown-linux-gnu",
 ];
 
+// FIXME(rcvalle): More targets are likely supported.
+pub const CFI_SUPPORTED_TARGETS: &[&str] = &[
+    "aarch64-apple-darwin",
+    "aarch64-fuchsia",
+    "aarch64-linux-android",
+    "aarch64-unknown-freebsd",
+    "aarch64-unknown-linux-gnu",
+    "x86_64-apple-darwin",
+    "x86_64-fuchsia",
+    "x86_64-pc-solaris",
+    "x86_64-unknown-freebsd",
+    "x86_64-unknown-illumos",
+    "x86_64-unknown-linux-gnu",
+    "x86_64-unknown-linux-musl",
+    "x86_64-unknown-netbsd",
+];
+
 pub const LSAN_SUPPORTED_TARGETS: &[&str] = &[
     // FIXME: currently broken, see #88132
     // "aarch64-apple-darwin",