CFI: Change type transformation to use TypeFolder

Change type transformation to use TypeFolder.
This commit is contained in:
Ramon de C Valle 2024-03-29 17:13:19 -07:00
parent b38b6caa3e
commit bc8c3eff6b

View File

@ -11,13 +11,14 @@
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind, self, Const, ExistentialPredicate, FloatTy, FnSig, Instance, IntTy, List, Region, RegionKind,
TermKind, Ty, TyCtxt, UintTy, TermKind, Ty, TyCtxt, UintTy,
}; };
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef}; use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgsRef};
use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
use rustc_span::def_id::DefId; use rustc_span::def_id::DefId;
use rustc_span::sym; use rustc_span::sym;
use rustc_target::abi::call::{Conv, FnAbi, PassMode}; use rustc_target::abi::call::{Conv, FnAbi, PassMode};
@ -182,14 +183,15 @@ fn encode_fnsig<'tcx>(
// Encode the return type // Encode the return type
let transform_ty_options = TransformTyOptions::from_bits(options.bits()) let transform_ty_options = TransformTyOptions::from_bits(options.bits())
.unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits()));
let ty = transform_ty(tcx, fn_sig.output(), &mut Vec::new(), transform_ty_options); let mut type_folder = TransformTy::new(tcx, transform_ty_options);
let ty = fn_sig.output().fold_with(&mut type_folder);
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
// Encode the parameter types // Encode the parameter types
let tys = fn_sig.inputs(); let tys = fn_sig.inputs();
if !tys.is_empty() { if !tys.is_empty() {
for ty in tys { for ty in tys {
let ty = transform_ty(tcx, *ty, &mut Vec::new(), transform_ty_options); let ty = ty.fold_with(&mut type_folder);
s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options));
} }
@ -523,15 +525,9 @@ fn encode_ty<'tcx>(
ty::Array(ty0, len) => { ty::Array(ty0, len) => {
// A<array-length><element-type> // A<array-length><element-type>
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
let mut s = String::from("A"); let mut s = String::from("A");
let _ = write!( let _ = write!(s, "{}", &len);
s,
"{}",
&len.try_to_scalar()
.unwrap()
.to_target_usize(&tcx.data_layout)
.expect("Array lens are defined in usize")
);
s.push_str(&encode_ty(tcx, *ty0, dict, options)); s.push_str(&encode_ty(tcx, *ty0, dict, options));
compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s);
typeid.push_str(&s); typeid.push_str(&s);
@ -756,278 +752,208 @@ fn encode_ty<'tcx>(
typeid typeid
} }
/// Transforms predicates for being encoded and used in the substitution dictionary. struct TransformTy<'tcx> {
fn transform_predicates<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
predicates: &List<ty::PolyExistentialPredicate<'tcx>>,
) -> &'tcx List<ty::PolyExistentialPredicate<'tcx>> {
tcx.mk_poly_existential_predicates_from_iter(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),
}
}))
}
/// Transforms args for being encoded and used in the substitution dictionary.
fn transform_args<'tcx>(
tcx: TyCtxt<'tcx>,
args: GenericArgsRef<'tcx>,
parents: &mut Vec<Ty<'tcx>>,
options: TransformTyOptions, options: TransformTyOptions,
) -> GenericArgsRef<'tcx> { parents: Vec<Ty<'tcx>>,
let args = args.iter().map(|arg| match arg.unpack() {
GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(),
GenericArgKind::Type(ty) => transform_ty(tcx, ty, parents, options).into(),
_ => arg,
});
tcx.mk_args_from_iter(args)
} }
// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms all impl<'tcx> TransformTy<'tcx> {
// c_void types into unit types unconditionally, generalizes pointers if fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
TransformTy { tcx, options, parents: Vec::new() }
}
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
// Transforms a ty:Ty for being encoded and used in the substitution dictionary. It transforms
// all c_void types into unit types unconditionally, generalizes pointers if
// TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if
// TransformTyOptions::NORMALIZE_INTEGERS option is set. // TransformTyOptions::NORMALIZE_INTEGERS option is set.
fn transform_ty<'tcx>( fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
tcx: TyCtxt<'tcx>, match t.kind() {
mut ty: Ty<'tcx>, ty::Array(..)
parents: &mut Vec<Ty<'tcx>>, | ty::Closure(..)
options: TransformTyOptions, | ty::Coroutine(..)
) -> Ty<'tcx> { | ty::CoroutineClosure(..)
match ty.kind() { | ty::CoroutineWitness(..)
ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {} | ty::Float(..)
| ty::FnDef(..)
| ty::Foreign(..)
| ty::Never
| ty::Slice(..)
| ty::Str
| ty::Tuple(..) => t.super_fold_with(self),
ty::Bool => { ty::Bool => {
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
// Note: on all platforms that Rust's currently supports, its size and alignment are // Note: on all platforms that Rust's currently supports, its size and alignment
// 1, and its ABI class is INTEGER - see Rust Layout and ABIs. // 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.) // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
// //
// Clang represents bool as an 8-bit unsigned integer. // Clang represents bool as an 8-bit unsigned integer.
ty = tcx.types.u8; self.tcx.types.u8
} else {
t
} }
} }
ty::Char => { ty::Char => {
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
// Since #118032, char is guaranteed to have the same size, alignment, and function // Since #118032, char is guaranteed to have the same size, alignment, and
// call ABI as u32 on all platforms. // function call ABI as u32 on all platforms.
ty = tcx.types.u32; self.tcx.types.u32
} else {
t
} }
} }
ty::Int(..) | ty::Uint(..) => { ty::Int(..) | ty::Uint(..) => {
if options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) { if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
// Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit wide. // Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit
// All platforms we currently support have a C platform, and as a consequence, // wide. All platforms we currently support have a C platform, and as a
// isize/usize are at least 16-bit wide for all of them. // 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.) // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
match ty.kind() { match t.kind() {
ty::Int(IntTy::Isize) => match tcx.sess.target.pointer_width { ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
16 => ty = tcx.types.i16, 16 => self.tcx.types.i16,
32 => ty = tcx.types.i32, 32 => self.tcx.types.i32,
64 => ty = tcx.types.i64, 64 => self.tcx.types.i64,
128 => ty = tcx.types.i128, 128 => self.tcx.types.i128,
_ => bug!( _ => bug!(
"transform_ty: unexpected pointer width `{}`", "fold_ty: unexpected pointer width `{}`",
tcx.sess.target.pointer_width self.tcx.sess.target.pointer_width
), ),
}, },
ty::Uint(UintTy::Usize) => match tcx.sess.target.pointer_width { ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
16 => ty = tcx.types.u16, 16 => self.tcx.types.u16,
32 => ty = tcx.types.u32, 32 => self.tcx.types.u32,
64 => ty = tcx.types.u64, 64 => self.tcx.types.u64,
128 => ty = tcx.types.u128, 128 => self.tcx.types.u128,
_ => bug!( _ => bug!(
"transform_ty: unexpected pointer width `{}`", "fold_ty: unexpected pointer width `{}`",
tcx.sess.target.pointer_width self.tcx.sess.target.pointer_width
), ),
}, },
_ => (), _ => t,
} }
} else {
t
} }
} }
_ if ty.is_unit() => {} ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
ty::Tuple(tys) => {
ty = Ty::new_tup_from_iter(
tcx,
tys.iter().map(|ty| transform_ty(tcx, ty, parents, options)),
);
}
ty::Array(ty0, len) => {
let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all());
ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, parents, options), len);
}
ty::Slice(ty0) => {
ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, parents, options));
}
ty::Adt(adt_def, args) => { ty::Adt(adt_def, args) => {
if ty.is_c_void(tcx) { if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t)
ty = Ty::new_unit(tcx);
} else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c()
{
ty = Ty::new_adt(tcx, *adt_def, ty::List::empty());
} else if adt_def.repr().transparent() && adt_def.is_struct() && !parents.contains(&ty)
{ {
// Don't transform repr(transparent) types with an user-defined CFI encoding to // Don't transform repr(transparent) types with an user-defined CFI encoding to
// preserve the user-defined CFI encoding. // preserve the user-defined CFI encoding.
if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) { if let Some(_) = self.tcx.get_attr(adt_def.did(), sym::cfi_encoding) {
return ty; return t;
} }
let variant = adt_def.non_enum_variant(); let variant = adt_def.non_enum_variant();
let param_env = tcx.param_env(variant.def_id); let param_env = self.tcx.param_env(variant.def_id);
let field = variant.fields.iter().find(|field| { let field = variant.fields.iter().find(|field| {
let ty = tcx.type_of(field.did).instantiate_identity(); let ty = self.tcx.type_of(field.did).instantiate_identity();
let is_zst = let is_zst = self
tcx.layout_of(param_env.and(ty)).is_ok_and(|layout| layout.is_zst()); .tcx
.layout_of(param_env.and(ty))
.is_ok_and(|layout| layout.is_zst());
!is_zst !is_zst
}); });
if let Some(field) = field { if let Some(field) = field {
let ty0 = tcx.type_of(field.did).instantiate(tcx, args); let ty0 = self.tcx.type_of(field.did).instantiate(self.tcx, args);
// Generalize any repr(transparent) user-defined type that is either a pointer // Generalize any repr(transparent) user-defined type that is either a
// or reference, and either references itself or any other type that contains or // pointer or reference, and either references itself or any other type that
// references itself, to avoid a reference cycle. // contains or references itself, to avoid a reference cycle.
// If the self reference is not through a pointer, for example, due // If the self reference is not through a pointer, for example, due
// to using `PhantomData`, need to skip normalizing it if we hit it again. // to using `PhantomData`, need to skip normalizing it if we hit it again.
parents.push(ty); self.parents.push(t);
if ty0.is_any_ptr() && ty0.contains(ty) { let ty = if ty0.is_any_ptr() && ty0.contains(t) {
ty = transform_ty( let options = self.options;
tcx, self.options |= TransformTyOptions::GENERALIZE_POINTERS;
ty0, let ty = ty0.fold_with(self);
parents, self.options = options;
options | TransformTyOptions::GENERALIZE_POINTERS, ty
);
} else { } else {
ty = transform_ty(tcx, ty0, parents, options); ty0.fold_with(self)
} };
parents.pop(); self.parents.pop();
ty
} else { } else {
// Transform repr(transparent) types without non-ZST field into () // Transform repr(transparent) types without non-ZST field into ()
ty = Ty::new_unit(tcx); self.tcx.types.unit
} }
} else { } else {
ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, parents, options)); t.super_fold_with(self)
} }
} }
ty::FnDef(def_id, args) => { ty::Ref(..) => {
ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, parents, options)); if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
} if t.is_mutable_ptr() {
Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
ty::Closure(def_id, args) => {
ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, parents, options));
}
ty::CoroutineClosure(def_id, args) => {
ty = Ty::new_coroutine_closure(
tcx,
*def_id,
transform_args(tcx, args, parents, options),
);
}
ty::Coroutine(def_id, args) => {
ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, parents, options));
}
ty::Ref(region, ty0, ..) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
if ty.is_mutable_ptr() {
ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx));
} else { } else {
ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, Ty::new_unit(tcx)); Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
} }
} else { } else {
if ty.is_mutable_ptr() { t.super_fold_with(self)
ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options));
} else {
ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options));
}
} }
} }
ty::RawPtr(ptr_ty, _) => { ty::RawPtr(..) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
if ty.is_mutable_ptr() { if t.is_mutable_ptr() {
ty = Ty::new_mut_ptr(tcx, Ty::new_unit(tcx)); Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
} else { } else {
ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx)); Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
} }
} else { } else {
if ty.is_mutable_ptr() { t.super_fold_with(self)
ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options));
} else {
ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options));
}
} }
} }
ty::FnPtr(fn_sig) => { ty::FnPtr(..) => {
if options.contains(TransformTyOptions::GENERALIZE_POINTERS) { if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
ty = Ty::new_imm_ptr(tcx, Ty::new_unit(tcx)); Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
} else { } else {
let parameters: Vec<Ty<'tcx>> = fn_sig t.super_fold_with(self)
.skip_binder()
.inputs()
.iter()
.map(|ty| transform_ty(tcx, *ty, parents, options))
.collect();
let output = transform_ty(tcx, fn_sig.skip_binder().output(), parents, options);
ty = Ty::new_fn_ptr(
tcx,
ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
parameters,
output,
fn_sig.c_variadic(),
fn_sig.unsafety(),
fn_sig.abi(),
),
fn_sig.bound_vars(),
),
);
} }
} }
ty::Dynamic(predicates, _region, kind) => { ty::Dynamic(predicates, _region, kind) => {
ty = Ty::new_dynamic( let predicates = self.tcx.mk_poly_existential_predicates_from_iter(
tcx, predicates.iter().filter_map(|predicate| match predicate.skip_binder() {
transform_predicates(tcx, predicates), ty::ExistentialPredicate::Trait(trait_ref) => {
tcx.lifetimes.re_erased, let trait_ref = ty::TraitRef::identity(self.tcx, trait_ref.def_id);
*kind, Some(ty::Binder::dummy(ty::ExistentialPredicate::Trait(
ty::ExistentialTraitRef::erase_self_ty(self.tcx, trait_ref),
)))
}
ty::ExistentialPredicate::Projection(..) => None,
ty::ExistentialPredicate::AutoTrait(..) => Some(predicate),
}),
); );
Ty::new_dynamic(self.tcx, predicates, self.tcx.lifetimes.re_erased, *kind)
} }
ty::Alias(..) => { ty::Alias(..) => {
ty = transform_ty( self.fold_ty(self.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), t))
tcx,
tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty),
parents,
options,
);
} }
ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => { ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => {
bug!("transform_ty: unexpected `{:?}`", ty.kind()); bug!("fold_ty: unexpected `{:?}`", t.kind());
}
} }
} }
ty fn interner(&self) -> TyCtxt<'tcx> {
self.tcx
}
} }
/// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor /// Returns a type metadata identifier for the specified FnAbi using the Itanium C++ ABI with vendor
@ -1068,7 +994,8 @@ pub fn typeid_for_fnabi<'tcx>(
// Encode the return type // Encode the return type
let transform_ty_options = TransformTyOptions::from_bits(options.bits()) let transform_ty_options = TransformTyOptions::from_bits(options.bits())
.unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits()));
let ty = transform_ty(tcx, fn_abi.ret.layout.ty, &mut Vec::new(), transform_ty_options); let mut type_folder = TransformTy::new(tcx, transform_ty_options);
let ty = fn_abi.ret.layout.ty.fold_with(&mut type_folder);
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
// Encode the parameter types // Encode the parameter types
@ -1080,7 +1007,7 @@ pub fn typeid_for_fnabi<'tcx>(
let mut pushed_arg = false; let mut pushed_arg = false;
for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) {
pushed_arg = true; pushed_arg = true;
let ty = transform_ty(tcx, arg.layout.ty, &mut Vec::new(), transform_ty_options); let ty = arg.layout.ty.fold_with(&mut type_folder);
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
} }
if !pushed_arg { if !pushed_arg {
@ -1093,8 +1020,7 @@ pub fn typeid_for_fnabi<'tcx>(
if fn_abi.args[n].mode == PassMode::Ignore { if fn_abi.args[n].mode == PassMode::Ignore {
continue; continue;
} }
let ty = let ty = fn_abi.args[n].layout.ty.fold_with(&mut type_folder);
transform_ty(tcx, fn_abi.args[n].layout.ty, &mut Vec::new(), transform_ty_options);
typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options));
} }