summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs')
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs338
1 files changed, 258 insertions, 80 deletions
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
index 1a679f32c..9fa49123a 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -1,26 +1,30 @@
-// 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;
+/// Type metadata identifiers (using Itanium C++ ABI mangling for encoding) for LLVM Control Flow
+/// Integrity (CFI) and cross-language LLVM CFI support.
+///
+/// Encodes type metadata identifiers for LLVM CFI and cross-language LLVM CFI support using Itanium
+/// C++ ABI mangling for encoding with vendor extended type qualifiers and types for Rust types that
+/// are not used across the FFI boundary.
+///
+/// For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
+/// see design document in the tracking issue #89653.
use core::fmt::Display;
use rustc_data_structures::base_n;
use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::DiagnosticMessage;
use rustc_hir as hir;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{
- self, Const, ExistentialPredicate, FloatTy, FnSig, IntTy, List, Region, RegionKind, TermKind,
- Ty, TyCtxt, UintTy,
+ self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
+ TermKind, Ty, TyCtxt, UintTy,
};
use rustc_span::def_id::DefId;
-use rustc_span::symbol::sym;
+use rustc_span::sym;
use rustc_target::abi::call::{Conv, FnAbi};
use rustc_target::spec::abi::Abi;
use std::fmt::Write as _;
+use crate::typeid::TypeIdOptions;
+
/// Type and extended type qualifiers.
#[derive(Eq, Hash, PartialEq)]
enum TyQ {
@@ -38,15 +42,6 @@ enum DictKey<'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;
@@ -91,21 +86,6 @@ fn compress<'tcx>(
}
}
-// 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);
- tcx.item_name(def_id).as_str() == "c_void"
- && (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
- }
- _ => 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>(
@@ -292,12 +272,11 @@ fn encode_region<'tcx>(
s.push('E');
compress(dict, DictKey::Region(region), &mut s);
}
- RegionKind::ReErased => {
+ RegionKind::ReEarlyBound(..) | RegionKind::ReErased => {
s.push_str("u6region");
compress(dict, DictKey::Region(region), &mut s);
}
- RegionKind::ReEarlyBound(..)
- | RegionKind::ReFree(..)
+ RegionKind::ReFree(..)
| RegionKind::ReStatic
| RegionKind::ReError(_)
| RegionKind::ReVar(..)
@@ -406,7 +385,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
// Crate disambiguator and name
s.push('C');
- s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).to_u64()));
+ s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64()));
let crate_name = tcx.crate_name(def_path.krate).to_string();
let _ = write!(s, "{}{}", crate_name.len(), &crate_name);
@@ -448,6 +427,12 @@ fn encode_ty<'tcx>(
match ty.kind() {
// Primitive types
+
+ // Rust's bool has the same layout as C17's _Bool, that is, its size and alignment are
+ // implementation-defined. Any bool can be cast into an integer, taking on the values 1
+ // (true) or 0 (false).
+ //
+ // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
ty::Bool => {
typeid.push('b');
}
@@ -535,9 +520,33 @@ fn encode_ty<'tcx>(
// 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 cross-language CFI support, the encoding must be compatible at the FFI
+ let def_id = adt_def.did();
+ if let Some(cfi_encoding) = tcx.get_attr(def_id, sym::cfi_encoding) {
+ // Use user-defined CFI encoding for type
+ if let Some(value_str) = cfi_encoding.value_str() {
+ if !value_str.to_string().trim().is_empty() {
+ s.push_str(&value_str.to_string().trim());
+ } else {
+ #[allow(
+ rustc::diagnostic_outside_of_impl,
+ rustc::untranslatable_diagnostic
+ )]
+ tcx.sess
+ .struct_span_err(
+ cfi_encoding.span,
+ DiagnosticMessage::Str(format!(
+ "invalid `cfi_encoding` for `{:?}`",
+ ty.kind()
+ )),
+ )
+ .emit();
+ }
+ } else {
+ bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
+ }
+ compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+ } else if options.contains(EncodeTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() {
+ // For cross-language LLVM CFI support, the encoding must be compatible at the FFI
// boundary. For instance:
//
// struct type1 {};
@@ -567,8 +576,33 @@ fn encode_ty<'tcx>(
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);
+ if let Some(cfi_encoding) = tcx.get_attr(*def_id, sym::cfi_encoding) {
+ // Use user-defined CFI encoding for type
+ if let Some(value_str) = cfi_encoding.value_str() {
+ if !value_str.to_string().trim().is_empty() {
+ s.push_str(&value_str.to_string().trim());
+ } else {
+ #[allow(
+ rustc::diagnostic_outside_of_impl,
+ rustc::untranslatable_diagnostic
+ )]
+ tcx.sess
+ .struct_span_err(
+ cfi_encoding.span,
+ DiagnosticMessage::Str(format!(
+ "invalid `cfi_encoding` for `{:?}`",
+ ty.kind()
+ )),
+ )
+ .emit();
+ }
+ } else {
+ bug!("encode_ty: invalid `cfi_encoding` for `{:?}`", ty.kind());
+ }
+ } else {
+ 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);
}
@@ -618,7 +652,7 @@ fn encode_ty<'tcx>(
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));
+ s.push_str(&encode_fnsig(tcx, &fn_sig.skip_binder(), dict, TypeIdOptions::empty()));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s);
}
@@ -638,6 +672,14 @@ fn encode_ty<'tcx>(
typeid.push_str(&s);
}
+ // Type parameters
+ ty::Param(..) => {
+ // u5param as vendor extended type
+ let mut s = String::from("u5param");
+ compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
+ typeid.push_str(&s);
+ }
+
// Unexpected types
ty::Bound(..)
| ty::Error(..)
@@ -645,7 +687,6 @@ fn encode_ty<'tcx>(
| ty::GeneratorWitnessMIR(..)
| ty::Infer(..)
| ty::Alias(..)
- | ty::Param(..)
| ty::Placeholder(..) => {
bug!("encode_ty: unexpected `{:?}`", ty.kind());
}
@@ -654,23 +695,96 @@ fn encode_ty<'tcx>(
typeid
}
+/// Transforms predicates for being encoded and used in the substitution dictionary.
+fn transform_predicates<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
+ _options: EncodeTyOptions,
+) -> &'tcx List<ty::PolyExistentialPredicate<'tcx>> {
+ let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates
+ .iter()
+ .filter_map(|predicate| match predicate.skip_binder() {
+ ty::ExistentialPredicate::Trait(trait_ref) => {
+ let trait_ref = ty::TraitRef::identity(tcx, trait_ref.def_id);
+ Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait(
+ ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref),
+ )))
+ }
+ ty::ExistentialPredicate::Projection(..) => None,
+ ty::ExistentialPredicate::AutoTrait(..) => Some(predicate),
+ })
+ .collect();
+ tcx.mk_poly_existential_predicates(&predicates)
+}
+
+/// 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 = substs.iter().map(|subst| match subst.unpack() {
+ GenericArgKind::Type(ty) if ty.is_c_void(tcx) => tcx.mk_unit().into(),
+ GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(),
+ _ => subst,
+ });
+ tcx.mk_substs_from_iter(substs)
+}
+
// 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.
-#[instrument(level = "trace", skip(tcx))]
+// c_void types into unit types unconditionally, generalizes pointers if
+// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
+// TransformTyOptions::NORMALIZE_INTEGERS 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(..) => {}
+ ty::Float(..) | ty::Char | ty::Str | ty::Never | ty::Foreign(..) => {}
+
+ ty::Bool => {
+ if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+ // Note: on all platforms that Rust's currently supports, its size and alignment are
+ // 1, and its ABI class is INTEGER - see Rust Layout and ABIs.
+ //
+ // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
+ //
+ // Clang represents bool as an 8-bit unsigned integer.
+ ty = tcx.types.u8;
+ }
+ }
+
+ ty::Int(..) | ty::Uint(..) => {
+ if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+ // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit wide.
+ // All platforms we currently support have a C platform, and as a consequence,
+ // isize/usize are at least 16-bit wide for all of them.
+ //
+ // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
+ match ty.kind() {
+ ty::Int(IntTy::Isize) => match tcx.sess.target.pointer_width {
+ 16 => ty = tcx.types.i16,
+ 32 => ty = tcx.types.i32,
+ 64 => ty = tcx.types.i64,
+ 128 => ty = tcx.types.i128,
+ _ => bug!(
+ "transform_ty: unexpected pointer width `{}`",
+ tcx.sess.target.pointer_width
+ ),
+ },
+ ty::Uint(UintTy::Usize) => match tcx.sess.target.pointer_width {
+ 16 => ty = tcx.types.u16,
+ 32 => ty = tcx.types.u32,
+ 64 => ty = tcx.types.u64,
+ 128 => ty = tcx.types.u128,
+ _ => bug!(
+ "transform_ty: unexpected pointer width `{}`",
+ tcx.sess.target.pointer_width
+ ),
+ },
+ _ => (),
+ }
+ }
+ }
_ if ty.is_unit() => {}
@@ -688,18 +802,23 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
}
ty::Adt(adt_def, substs) => {
- if is_c_void_ty(tcx, ty) {
+ if ty.is_c_void(tcx) {
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() {
+ // Don't transform repr(transparent) types with an user-defined CFI encoding to
+ // preserve the user-defined CFI encoding.
+ if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
+ return ty;
+ }
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).subst_identity();
let is_zst =
- tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
+ tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| layout.is_zst());
!is_zst
});
if let Some(field) = field {
@@ -793,6 +912,14 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
}
}
+ ty::Dynamic(predicates, _region, kind) => {
+ ty = tcx.mk_dynamic(
+ transform_predicates(tcx, predicates, options),
+ tcx.lifetimes.re_erased,
+ *kind,
+ );
+ }
+
ty::Bound(..)
| ty::Error(..)
| ty::GeneratorWitness(..)
@@ -808,26 +935,6 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio
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 = 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
- }
- });
- tcx.mk_substs_from_iter(substs)
-}
-
/// 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.
#[instrument(level = "trace", skip(tcx))]
@@ -893,6 +1000,15 @@ pub fn typeid_for_fnabi<'tcx>(
// Close the "F..E" pair
typeid.push('E');
+ // Add encoding suffixes
+ if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+ typeid.push_str(".normalized");
+ }
+
+ if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
+ typeid.push_str(".generalized");
+ }
+
typeid
}
@@ -919,5 +1035,67 @@ pub fn typeid_for_fnsig<'tcx>(
// Encode the function signature
typeid.push_str(&encode_fnsig(tcx, fn_sig, &mut dict, options));
+ // Add encoding suffixes
+ if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
+ typeid.push_str(".normalized");
+ }
+
+ if options.contains(EncodeTyOptions::GENERALIZE_POINTERS) {
+ typeid.push_str(".generalized");
+ }
+
typeid
}
+
+/// Returns a type metadata identifier for the specified Instance 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_instance<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ instance: &Instance<'tcx>,
+ options: TypeIdOptions,
+) -> String {
+ let fn_abi = tcx
+ .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty())))
+ .unwrap_or_else(|instance| {
+ bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance)
+ });
+
+ // If this instance is a method and self is a reference, get the impl it belongs to
+ let impl_def_id = tcx.impl_of_method(instance.def_id());
+ if impl_def_id.is_some() && !fn_abi.args.is_empty() && fn_abi.args[0].layout.ty.is_ref() {
+ // If this impl is not an inherent impl, get the trait it implements
+ if let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id.unwrap()) {
+ // Transform the concrete self into a reference to a trait object
+ let existential_predicate = trait_ref.map_bound(|trait_ref| {
+ ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(
+ tcx, trait_ref,
+ ))
+ });
+ let existential_predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(
+ existential_predicate.skip_binder(),
+ )]);
+ // Is the concrete self mutable?
+ let self_ty = if fn_abi.args[0].layout.ty.is_mutable_ptr() {
+ tcx.mk_mut_ref(
+ tcx.lifetimes.re_erased,
+ tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
+ )
+ } else {
+ tcx.mk_imm_ref(
+ tcx.lifetimes.re_erased,
+ tcx.mk_dynamic(existential_predicates, tcx.lifetimes.re_erased, ty::Dyn),
+ )
+ };
+
+ // Replace the concrete self in an fn_abi clone by the reference to a trait object
+ let mut fn_abi = fn_abi.clone();
+ // HACK(rcvalle): It is okay to not replace or update the entire ArgAbi here because the
+ // other fields are never used.
+ fn_abi.args[0].layout.ty = self_ty;
+
+ return typeid_for_fnabi(tcx, &fn_abi, options);
+ }
+ }
+
+ typeid_for_fnabi(tcx, &fn_abi, options)
+}