1//! Transforms instances and types for LLVM CFI and cross-language LLVM CFI support using Itanium
2//! C++ ABI mangling.
3//!
4//! For more information about LLVM CFI and cross-language LLVM CFI support for the Rust compiler,
5//! see design document in the tracking issue #89653.
67use std::iter;
89use rustc_hir::{selfas hir, LangItem, find_attr};
10use rustc_middle::bug;
11use rustc_middle::ty::{
12self, AssocContainer, ExistentialPredicateStableCmpExtas _, Instance, IntTy, List, TraitRef,
13Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, UintTy,
14};
15use rustc_span::DUMMY_SP;
16use rustc_span::def_id::DefId;
17use rustc_trait_selection::traits;
18use tracing::{debug, instrument};
1920use crate::cfi::typeid::TypeIdOptions;
21use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions;
2223/// Options for transform_ty.
24pub(crate) type TransformTyOptions = TypeIdOptions;
2526pub(crate) struct TransformTy<'tcx> {
27 tcx: TyCtxt<'tcx>,
28 options: TransformTyOptions,
29 parents: Vec<Ty<'tcx>>,
30}
3132impl<'tcx> TransformTy<'tcx> {
33pub(crate) fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
34TransformTy { tcx, options, parents: Vec::new() }
35 }
36}
3738/// Transforms a ty:Ty for being encoded and used in the substitution dictionary.
39///
40/// * Transforms all c_void types into unit types.
41/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set.
42/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set.
43/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and
44/// either references itself or any other type that contains or references itself, to avoid a
45/// reference cycle.
46/// * Transforms repr(transparent) types without non-ZST field into ().
47///
48impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
49// Transforms a ty:Ty for being encoded and used in the substitution dictionary.
50fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
51match t.kind() {
52 ty::Closure(..)
53 | ty::Coroutine(..)
54 | ty::CoroutineClosure(..)
55 | ty::CoroutineWitness(..)
56 | ty::Dynamic(..)
57 | ty::Float(..)
58 | ty::FnDef(..)
59 | ty::Foreign(..)
60 | ty::Never61 | ty::Pat(..)
62 | ty::Slice(..)
63 | ty::Str64 | ty::Tuple(..)
65 | ty::UnsafeBinder(_) => t.super_fold_with(self),
6667// Don't transform the type of the array length and keep it as `usize`.
68 // This is required for `try_to_target_usize` to work correctly.
69&ty::Array(inner, len) => {
70let inner = self.fold_ty(inner);
71Ty::new_array_with_const_len(self.tcx, inner, len)
72 }
7374 ty::Bool => {
75if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
76// Note: on all platforms that Rust's currently supports, its size and alignment
77 // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs.
78 //
79 // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
80 //
81 // Clang represents bool as an 8-bit unsigned integer.
82self.tcx.types.u8
83 } else {
84t85 }
86 }
8788 ty::Char => {
89if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
90// Since #118032, char is guaranteed to have the same size, alignment, and
91 // function call ABI as u32 on all platforms.
92self.tcx.types.u32
93 } else {
94t95 }
96 }
9798 ty::Int(..) | ty::Uint(..) => {
99if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
100// Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit
101 // wide. All platforms we currently support have a C platform, and as a
102 // consequence, isize/usize are at least 16-bit wide for all of them.
103 //
104 // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
105match t.kind() {
106 ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
10716 => self.tcx.types.i16,
10832 => self.tcx.types.i32,
10964 => self.tcx.types.i64,
110128 => self.tcx.types.i128,
111_ => ::rustc_middle::util::bug::bug_fmt(format_args!("fold_ty: unexpected pointer width `{0}`",
self.tcx.sess.target.pointer_width))bug!(
112"fold_ty: unexpected pointer width `{}`",
113self.tcx.sess.target.pointer_width
114 ),
115 },
116 ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
11716 => self.tcx.types.u16,
11832 => self.tcx.types.u32,
11964 => self.tcx.types.u64,
120128 => self.tcx.types.u128,
121_ => ::rustc_middle::util::bug::bug_fmt(format_args!("fold_ty: unexpected pointer width `{0}`",
self.tcx.sess.target.pointer_width))bug!(
122"fold_ty: unexpected pointer width `{}`",
123self.tcx.sess.target.pointer_width
124 ),
125 },
126_ => t,
127 }
128 } else {
129t130 }
131 }
132133 ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
134135 ty::Adt(adt_def, args) => {
136if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t)
137 {
138// Don't transform repr(transparent) types with an user-defined CFI encoding to
139 // preserve the user-defined CFI encoding.
140if {
{
'done:
{
for i in
::rustc_hir::attrs::HasAttrs::get_attrs(adt_def.did(),
&self.tcx) {
#[allow(unused_imports)]
use rustc_hir::attrs::AttributeKind::*;
let i: &rustc_hir::Attribute = i;
match i {
rustc_hir::Attribute::Parsed(CfiEncoding { .. }) => {
break 'done Some(());
}
rustc_hir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}.is_some()find_attr!(self.tcx, adt_def.did(), CfiEncoding { .. }) {
141return t;
142 }
143let variant = adt_def.non_enum_variant();
144let typing_env = ty::TypingEnv::post_analysis(self.tcx, variant.def_id);
145let field = variant.fields.iter().find(|field| {
146let ty = self.tcx.type_of(field.did).instantiate_identity();
147let is_zst = self148 .tcx
149 .layout_of(typing_env.as_query_input(ty))
150 .is_ok_and(|layout| layout.is_zst());
151 !is_zst152 });
153if let Some(field) = field {
154let ty0 = self.tcx.normalize_erasing_regions(
155 ty::TypingEnv::fully_monomorphized(),
156field.ty(self.tcx, args),
157 );
158// Generalize any repr(transparent) user-defined type that is either a
159 // pointer or reference, and either references itself or any other type that
160 // contains or references itself, to avoid a reference cycle.
161162 // If the self reference is not through a pointer, for example, due
163 // to using `PhantomData`, need to skip normalizing it if we hit it again.
164self.parents.push(t);
165let ty = if ty0.is_any_ptr() && ty0.contains(t) {
166let options = self.options;
167self.options |= TransformTyOptions::GENERALIZE_POINTERS;
168let ty = ty0.fold_with(self);
169self.options = options;
170ty171 } else {
172ty0.fold_with(self)
173 };
174self.parents.pop();
175ty176 } else {
177// Transform repr(transparent) types without non-ZST field into ()
178self.tcx.types.unit
179 }
180 } else {
181t.super_fold_with(self)
182 }
183 }
184185 ty::Ref(..) => {
186if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
187if t.is_mutable_ptr() {
188Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
189 } else {
190Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
191 }
192 } else {
193t.super_fold_with(self)
194 }
195 }
196197 ty::RawPtr(..) => {
198if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
199if t.is_mutable_ptr() {
200Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
201 } else {
202Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
203 }
204 } else {
205t.super_fold_with(self)
206 }
207 }
208209 ty::FnPtr(..) => {
210if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
211Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
212 } else {
213t.super_fold_with(self)
214 }
215 }
216217 ty::Alias(..) => self.fold_ty(
218self.tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), t),
219 ),
220221 ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => {
222::rustc_middle::util::bug::bug_fmt(format_args!("fold_ty: unexpected `{0:?}`",
t.kind()));bug!("fold_ty: unexpected `{:?}`", t.kind());
223 }
224 }
225 }
226227fn cx(&self) -> TyCtxt<'tcx> {
228self.tcx
229 }
230}
231232x;#[instrument(skip(tcx), ret)]233fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
234assert!(!poly_trait_ref.has_non_region_param());
235let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
236 ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
237 });
238let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref)
239 .flat_map(|super_poly_trait_ref| {
240 tcx.associated_items(super_poly_trait_ref.def_id())
241 .in_definition_order()
242 .filter(|item| item.is_type() || item.is_type_const())
243 .filter(|item| !tcx.generics_require_sized_self(item.def_id))
244 .map(move |assoc_item| {
245 super_poly_trait_ref.map_bound(|super_trait_ref| {
246let projection_term = ty::AliasTerm::new_from_args(
247 tcx,
248 assoc_item.def_id,
249 super_trait_ref.args,
250 );
251let term = tcx.normalize_erasing_regions(
252 ty::TypingEnv::fully_monomorphized(),
253 projection_term.to_term(tcx),
254 );
255debug!("Projection {:?} -> {term}", projection_term.to_term(tcx),);
256 ty::ExistentialPredicate::Projection(
257 ty::ExistentialProjection::erase_self_ty(
258 tcx,
259 ty::ProjectionPredicate { projection_term, term },
260 ),
261 )
262 })
263 })
264 })
265 .collect();
266 assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
267let preds = tcx.mk_poly_existential_predicates_from_iter(
268 iter::once(principal_pred).chain(assoc_preds.into_iter()),
269 );
270 Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased)
271}
272273/// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI
274/// mangling.
275///
276/// typeid_for_instance is called at two locations, initially when declaring/defining functions and
277/// methods, and later during code generation at call sites, after type erasure might have occurred.
278///
279/// In the first call (i.e., when declaring/defining functions and methods), it encodes type ids for
280/// an FnAbi or Instance, and these type ids are attached to functions and methods. (These type ids
281/// are used later by the LowerTypeTests LLVM pass to aggregate functions in groups derived from
282/// these type ids.)
283///
284/// In the second call (i.e., during code generation at call sites), it encodes a type id for an
285/// FnAbi or Instance, after type erasure might have occurred, and this type id is used for testing
286/// if a function is member of the group derived from this type id. Therefore, in the first call to
287/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at
288/// most as much information that would be available in the second call (i.e., during code
289/// generation at call sites); otherwise, the type ids would not match.
290///
291/// For this, it:
292///
293/// * Adjust the type ids of DropGlues (see below).
294/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the
295/// entry in the vtable (i.e., by using the signature of the closure passed as an argument to the
296/// shim, or by just removing self).
297/// * Performs type erasure for calls on trait objects by transforming self into a trait object of
298/// the trait that defines the method.
299/// * Performs type erasure for closures call methods by transforming self into a trait object of
300/// the Fn trait that defines the method (for being attached as a secondary type id).
301///
302#[allow(clippy :: suspicious_else_formatting)]
{
let __tracing_attr_span;
let __tracing_attr_guard;
if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() ||
{ false } {
__tracing_attr_span =
{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("transform_instance",
"rustc_sanitizers::cfi::typeid::itanium_cxx_abi::transform",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs"),
::tracing_core::__macro_support::Option::Some(302u32),
::tracing_core::__macro_support::Option::Some("rustc_sanitizers::cfi::typeid::itanium_cxx_abi::transform"),
::tracing_core::field::FieldSet::new(&["instance",
"options"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::SPAN)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let mut interest = ::tracing::subscriber::Interest::never();
if ::tracing::Level::TRACE <=
::tracing::level_filters::STATIC_MAX_LEVEL &&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{ interest = __CALLSITE.interest(); !interest.is_never() }
&&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest) {
let meta = __CALLSITE.metadata();
::tracing::Span::new(meta,
&{
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = meta.fields().iter();
meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&instance)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&::tracing::field::debug(&options)
as &dyn Value))])
})
} else {
let span =
::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
{};
span
}
};
__tracing_attr_guard = __tracing_attr_span.enter();
}
#[warn(clippy :: suspicious_else_formatting)]
{
#[allow(unknown_lints, unreachable_code, clippy ::
diverging_sub_expression, clippy :: empty_loop, clippy ::
let_unit_value, clippy :: let_with_type_underscore, clippy ::
needless_return, clippy :: unreachable)]
if false {
let __tracing_attr_fake_return: Instance<'tcx> = loop {};
return __tracing_attr_fake_return;
}
{
if (#[allow(non_exhaustive_omitted_patterns)] match instance.def {
ty::InstanceKind::Virtual(..) => true,
_ => false,
} &&
tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace))
||
#[allow(non_exhaustive_omitted_patterns)] match instance.def
{
ty::InstanceKind::DropGlue(..) => true,
_ => false,
} {
let def_id =
tcx.lang_items().drop_trait().unwrap_or_else(||
::rustc_middle::util::bug::bug_fmt(format_args!("typeid_for_instance: couldn\'t get drop_trait lang item")));
let predicate =
ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(tcx,
def_id, ty::List::empty()));
let predicates =
tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
let self_ty =
Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased);
instance.args = tcx.mk_args_trait(self_ty, List::empty());
} else if let ty::InstanceKind::Virtual(def_id, _) = instance.def
{
let upcast_ty =
match tcx.trait_of_assoc(def_id) {
Some(trait_id) =>
trait_object_ty(tcx,
ty::Binder::dummy(ty::TraitRef::from_assoc(tcx, trait_id,
instance.args))),
None => instance.args.type_at(0),
};
let ty::Dynamic(preds, lifetime) =
upcast_ty.kind() else {
::rustc_middle::util::bug::bug_fmt(format_args!("Tried to remove autotraits from non-dynamic type {0}",
upcast_ty));
};
let self_ty =
if preds.principal().is_some() {
let filtered_preds =
tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred|
{
!#[allow(non_exhaustive_omitted_patterns)] match pred.skip_binder()
{
ty::ExistentialPredicate::AutoTrait(..) => true,
_ => false,
}
}));
Ty::new_dynamic(tcx, filtered_preds, *lifetime)
} else { tcx.types.unit };
instance.args =
tcx.mk_args_trait(self_ty,
instance.args.into_iter().skip(1));
} else if let ty::InstanceKind::VTableShim(def_id) = instance.def
&& let Some(trait_id) = tcx.trait_of_assoc(def_id) {
let trait_ref =
ty::TraitRef::new_from_args(tcx, trait_id, instance.args);
let invoke_ty =
trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
instance.args =
tcx.mk_args_trait(invoke_ty,
trait_ref.args.into_iter().skip(1));
}
if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
if let Some((trait_ref, method_id, ancestor)) =
implemented_method(tcx, instance) {
let trait_ref =
tcx.instantiate_and_normalize_erasing_regions(instance.args,
ty::TypingEnv::fully_monomorphized(), trait_ref);
let invoke_ty =
trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
instance.def = ty::InstanceKind::Virtual(method_id, 0);
let abstract_trait_args =
tcx.mk_args_trait(invoke_ty,
trait_ref.args.into_iter().skip(1));
instance.args =
instance.args.rebase_onto(tcx, ancestor,
abstract_trait_args);
} else if tcx.is_closure_like(instance.def_id()) {
let closure_ty =
instance.ty(tcx, ty::TypingEnv::fully_monomorphized());
let (trait_id, inputs) =
match closure_ty.kind() {
ty::Closure(..) => {
let closure_args = instance.args.as_closure();
let trait_id =
tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
let tuple_args =
tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
(trait_id, Some(tuple_args))
}
ty::Coroutine(..) =>
match tcx.coroutine_kind(instance.def_id()).unwrap() {
hir::CoroutineKind::Coroutine(..) =>
(tcx.require_lang_item(LangItem::Coroutine, DUMMY_SP),
Some(instance.args.as_coroutine().resume_ty())),
hir::CoroutineKind::Desugared(desugaring, _) => {
let lang_item =
match desugaring {
hir::CoroutineDesugaring::Async => LangItem::Future,
hir::CoroutineDesugaring::AsyncGen =>
LangItem::AsyncIterator,
hir::CoroutineDesugaring::Gen => LangItem::Iterator,
};
(tcx.require_lang_item(lang_item, DUMMY_SP), None)
}
},
ty::CoroutineClosure(..) =>
(tcx.require_lang_item(LangItem::FnOnce, DUMMY_SP),
Some(tcx.instantiate_bound_regions_with_erased(instance.args.as_coroutine_closure().coroutine_closure_sig()).tupled_inputs_ty)),
x =>
::rustc_middle::util::bug::bug_fmt(format_args!("Unexpected type kind for closure-like: {0:?}",
x)),
};
let concrete_args =
tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
let trait_ref =
ty::TraitRef::new_from_args(tcx, trait_id, concrete_args);
let invoke_ty =
trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
let abstract_args =
tcx.mk_args_trait(invoke_ty,
trait_ref.args.into_iter().skip(1));
let call =
tcx.associated_items(trait_id).in_definition_order().find(|it|
it.is_fn()).expect("No call-family function on closure-like Fn trait?").def_id;
instance.def = ty::InstanceKind::Virtual(call, 0);
instance.args = abstract_args;
}
}
instance
}
}
}#[instrument(level = "trace", skip(tcx))]303pub(crate) fn transform_instance<'tcx>(
304 tcx: TyCtxt<'tcx>,
305mut instance: Instance<'tcx>,
306 options: TransformTyOptions,
307) -> Instance<'tcx> {
308// FIXME: account for async-drop-glue
309if (matches!(instance.def, ty::InstanceKind::Virtual(..))
310 && tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace))
311 || matches!(instance.def, ty::InstanceKind::DropGlue(..))
312 {
313// Adjust the type ids of DropGlues
314 //
315 // DropGlues may have indirect calls to one or more given types drop function. Rust allows
316 // for types to be erased to any trait object and retains the drop function for the original
317 // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is
318 // called a second time, it only has information after type erasure and it could be a call
319 // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on
320 // declaration/definition, and during code generation at call sites so they have the same
321 // type id and match.
322 //
323 // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of
324 // any other type.
325 //
326let def_id = tcx
327 .lang_items()
328 .drop_trait()
329 .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
330let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(
331 tcx,
332 def_id,
333 ty::List::empty(),
334 ));
335let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
336let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased);
337 instance.args = tcx.mk_args_trait(self_ty, List::empty());
338 } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def {
339// Transform self into a trait object of the trait that defines the method for virtual
340 // functions to match the type erasure done below.
341let upcast_ty = match tcx.trait_of_assoc(def_id) {
342Some(trait_id) => trait_object_ty(
343 tcx,
344 ty::Binder::dummy(ty::TraitRef::from_assoc(tcx, trait_id, instance.args)),
345 ),
346// drop_in_place won't have a defining trait, skip the upcast
347None => instance.args.type_at(0),
348 };
349let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else {
350bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}");
351 };
352let self_ty = if preds.principal().is_some() {
353let filtered_preds =
354 tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
355 !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
356 }));
357 Ty::new_dynamic(tcx, filtered_preds, *lifetime)
358 } else {
359// If there's no principal type, re-encode it as a unit, since we don't know anything
360 // about it. This technically discards the knowledge that it was a type that was made
361 // into a trait object at some point, but that's not a lot.
362tcx.types.unit
363 };
364 instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1));
365 } else if let ty::InstanceKind::VTableShim(def_id) = instance.def
366 && let Some(trait_id) = tcx.trait_of_assoc(def_id)
367 {
368// Adjust the type ids of VTableShims to the type id expected in the call sites for the
369 // entry in the vtable (i.e., by using the signature of the closure passed as an argument
370 // to the shim, or by just removing self).
371let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, instance.args);
372let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
373 instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
374 }
375376if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
377// Perform type erasure for calls on trait objects by transforming self into a trait object
378 // of the trait that defines the method.
379if let Some((trait_ref, method_id, ancestor)) = implemented_method(tcx, instance) {
380// Trait methods will have a Self polymorphic parameter, where the concreteized
381 // implementation will not. We need to walk back to the more general trait method
382let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
383 instance.args,
384 ty::TypingEnv::fully_monomorphized(),
385 trait_ref,
386 );
387let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
388389// At the call site, any call to this concrete function through a vtable will be
390 // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
391 // original method id, and we've recovered the trait arguments, we can make the callee
392 // instance we're computing the alias set for match the caller instance.
393 //
394 // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
395 // If we ever *do* start encoding the vtable index, we will need to generate an alias set
396 // based on which vtables we are putting this method into, as there will be more than one
397 // index value when supertraits are involved.
398instance.def = ty::InstanceKind::Virtual(method_id, 0);
399let abstract_trait_args =
400 tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
401 instance.args = instance.args.rebase_onto(tcx, ancestor, abstract_trait_args);
402 } else if tcx.is_closure_like(instance.def_id()) {
403// We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
404 // instantiate it, and take the type of its only method as our own.
405let closure_ty = instance.ty(tcx, ty::TypingEnv::fully_monomorphized());
406let (trait_id, inputs) = match closure_ty.kind() {
407 ty::Closure(..) => {
408let closure_args = instance.args.as_closure();
409let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
410let tuple_args =
411 tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
412 (trait_id, Some(tuple_args))
413 }
414 ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() {
415 hir::CoroutineKind::Coroutine(..) => (
416 tcx.require_lang_item(LangItem::Coroutine, DUMMY_SP),
417Some(instance.args.as_coroutine().resume_ty()),
418 ),
419 hir::CoroutineKind::Desugared(desugaring, _) => {
420let lang_item = match desugaring {
421 hir::CoroutineDesugaring::Async => LangItem::Future,
422 hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator,
423 hir::CoroutineDesugaring::Gen => LangItem::Iterator,
424 };
425 (tcx.require_lang_item(lang_item, DUMMY_SP), None)
426 }
427 },
428 ty::CoroutineClosure(..) => (
429 tcx.require_lang_item(LangItem::FnOnce, DUMMY_SP),
430Some(
431 tcx.instantiate_bound_regions_with_erased(
432 instance.args.as_coroutine_closure().coroutine_closure_sig(),
433 )
434 .tupled_inputs_ty,
435 ),
436 ),
437 x => bug!("Unexpected type kind for closure-like: {x:?}"),
438 };
439let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
440let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, concrete_args);
441let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
442let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
443// There should be exactly one method on this trait, and it should be the one we're
444 // defining.
445let call = tcx
446 .associated_items(trait_id)
447 .in_definition_order()
448 .find(|it| it.is_fn())
449 .expect("No call-family function on closure-like Fn trait?")
450 .def_id;
451452 instance.def = ty::InstanceKind::Virtual(call, 0);
453 instance.args = abstract_args;
454 }
455 }
456457 instance
458}
459460fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
461match instance.def {
462 ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
463tcx.opt_associated_item(def_id).map(|item| item.def_id)
464 }
465_ => None,
466 }
467}
468469/// Determines if an instance represents a trait method implementation and returns the necessary
470/// information for type erasure.
471///
472/// This function handles two main cases:
473///
474/// * **Implementation in an `impl` block**: When the instance represents a concrete implementation
475/// of a trait method in an `impl` block, it extracts the trait reference, method ID, and trait
476/// ID from the implementation. The method ID is obtained from the `trait_item_def_id` field of
477/// the associated item, which points to the original trait method definition.
478///
479/// * **Provided method in a `trait` block or synthetic `shim`**: When the instance represents a
480/// default implementation provided in the trait definition itself or a synthetic shim, it uses
481/// the instance's own `def_id` as the method ID and determines the trait ID from the associated
482/// item.
483///
484fn implemented_method<'tcx>(
485 tcx: TyCtxt<'tcx>,
486 instance: Instance<'tcx>,
487) -> Option<(ty::EarlyBinder<'tcx, TraitRef<'tcx>>, DefId, DefId)> {
488let trait_ref;
489let method_id;
490let trait_id;
491let trait_method;
492let assoc = tcx.opt_associated_item(instance.def_id())?;
493let ancestor = if let AssocContainer::TraitImpl(Ok(trait_method_id)) = assoc.container {
494let impl_id = tcx.parent(instance.def_id());
495trait_ref = tcx.impl_trait_ref(impl_id);
496method_id = trait_method_id;
497trait_method = tcx.associated_item(method_id);
498trait_id = trait_ref.skip_binder().def_id;
499impl_id500 } else if let AssocContainer::Trait = assoc.container
501 && let Some(trait_method_def_id) = default_or_shim(tcx, instance)
502 {
503// Provided method in a `trait` block or a synthetic `shim`
504trait_method = assoc;
505method_id = trait_method_def_id;
506trait_id = tcx.parent(method_id);
507trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
508trait_id509 } else {
510return None;
511 };
512let vtable_possible = traits::is_vtable_safe_method(tcx, trait_id, trait_method)
513 && tcx.is_dyn_compatible(trait_id);
514vtable_possible.then_some((trait_ref, method_id, ancestor))
515}