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,
14Unnormalized,
15};
16use rustc_span::DUMMY_SP;
17use rustc_span::def_id::DefId;
18use rustc_trait_selection::traits;
19use tracing::{debug, instrument};
2021use crate::cfi::typeid::TypeIdOptions;
22use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions;
2324/// Options for transform_ty.
25pub(crate) type TransformTyOptions = TypeIdOptions;
2627pub(crate) struct TransformTy<'tcx> {
28 tcx: TyCtxt<'tcx>,
29 options: TransformTyOptions,
30 parents: Vec<Ty<'tcx>>,
31}
3233impl<'tcx> TransformTy<'tcx> {
34pub(crate) fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
35TransformTy { tcx, options, parents: Vec::new() }
36 }
37}
3839/// Transforms a ty:Ty for being encoded and used in the substitution dictionary.
40///
41/// * Transforms all c_void types into unit types.
42/// * Generalizes pointers if TransformTyOptions::GENERALIZE_POINTERS option is set.
43/// * Normalizes integers if TransformTyOptions::NORMALIZE_INTEGERS option is set.
44/// * Generalizes any repr(transparent) user-defined type that is either a pointer or reference, and
45/// either references itself or any other type that contains or references itself, to avoid a
46/// reference cycle.
47/// * Transforms repr(transparent) types without non-ZST field into ().
48///
49impl<'tcx> TypeFolder<TyCtxt<'tcx>> for TransformTy<'tcx> {
50// Transforms a ty:Ty for being encoded and used in the substitution dictionary.
51fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
52match t.kind() {
53 ty::Closure(..)
54 | ty::Coroutine(..)
55 | ty::CoroutineClosure(..)
56 | ty::CoroutineWitness(..)
57 | ty::Dynamic(..)
58 | ty::Float(..)
59 | ty::FnDef(..)
60 | ty::Foreign(..)
61 | ty::Never62 | ty::Pat(..)
63 | ty::Slice(..)
64 | ty::Str65 | ty::Tuple(..)
66 | ty::UnsafeBinder(_) => t.super_fold_with(self),
6768// Don't transform the type of the array length and keep it as `usize`.
69 // This is required for `try_to_target_usize` to work correctly.
70&ty::Array(inner, len) => {
71let inner = self.fold_ty(inner);
72Ty::new_array_with_const_len(self.tcx, inner, len)
73 }
7475 ty::Bool => {
76if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
77// Note: on all platforms that Rust's currently supports, its size and alignment
78 // are 1, and its ABI class is INTEGER - see Rust Layout and ABIs.
79 //
80 // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool.)
81 //
82 // Clang represents bool as an 8-bit unsigned integer.
83self.tcx.types.u8
84 } else {
85t86 }
87 }
8889 ty::Char => {
90if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
91// Since #118032, char is guaranteed to have the same size, alignment, and
92 // function call ABI as u32 on all platforms.
93self.tcx.types.u32
94 } else {
95t96 }
97 }
9899 ty::Int(..) | ty::Uint(..) => {
100if self.options.contains(EncodeTyOptions::NORMALIZE_INTEGERS) {
101// Note: C99 7.18.2.4 requires uintptr_t and intptr_t to be at least 16-bit
102 // wide. All platforms we currently support have a C platform, and as a
103 // consequence, isize/usize are at least 16-bit wide for all of them.
104 //
105 // (See https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize.)
106match t.kind() {
107 ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
10816 => self.tcx.types.i16,
10932 => self.tcx.types.i32,
11064 => self.tcx.types.i64,
111128 => self.tcx.types.i128,
112_ => ::rustc_middle::util::bug::bug_fmt(format_args!("fold_ty: unexpected pointer width `{0}`",
self.tcx.sess.target.pointer_width))bug!(
113"fold_ty: unexpected pointer width `{}`",
114self.tcx.sess.target.pointer_width
115 ),
116 },
117 ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
11816 => self.tcx.types.u16,
11932 => self.tcx.types.u32,
12064 => self.tcx.types.u64,
121128 => self.tcx.types.u128,
122_ => ::rustc_middle::util::bug::bug_fmt(format_args!("fold_ty: unexpected pointer width `{0}`",
self.tcx.sess.target.pointer_width))bug!(
123"fold_ty: unexpected pointer width `{}`",
124self.tcx.sess.target.pointer_width
125 ),
126 },
127_ => t,
128 }
129 } else {
130t131 }
132 }
133134 ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
135136 ty::Adt(adt_def, args) => {
137if adt_def.repr().transparent() && adt_def.is_struct() && !self.parents.contains(&t)
138 {
139// Don't transform repr(transparent) types with an user-defined CFI encoding to
140 // preserve the user-defined CFI encoding.
141if {
{
'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 { .. }) {
142return t;
143 }
144let variant = adt_def.non_enum_variant();
145let typing_env = ty::TypingEnv::post_analysis(self.tcx, variant.def_id);
146let field = variant.fields.iter().find(|field| {
147let ty = self.tcx.type_of(field.did).instantiate_identity().skip_norm_wip();
148let is_zst = self149 .tcx
150 .layout_of(typing_env.as_query_input(ty))
151 .is_ok_and(|layout| layout.is_zst());
152 !is_zst153 });
154if let Some(field) = field {
155let ty0 = self.tcx.normalize_erasing_regions(
156 ty::TypingEnv::fully_monomorphized(),
157Unnormalized::new_wip(field.ty(self.tcx, args)),
158 );
159// Generalize any repr(transparent) user-defined type that is either a
160 // pointer or reference, and either references itself or any other type that
161 // contains or references itself, to avoid a reference cycle.
162163 // If the self reference is not through a pointer, for example, due
164 // to using `PhantomData`, need to skip normalizing it if we hit it again.
165self.parents.push(t);
166let ty = if ty0.is_any_ptr() && ty0.contains(t) {
167let options = self.options;
168self.options |= TransformTyOptions::GENERALIZE_POINTERS;
169let ty = ty0.fold_with(self);
170self.options = options;
171ty172 } else {
173ty0.fold_with(self)
174 };
175self.parents.pop();
176ty177 } else {
178// Transform repr(transparent) types without non-ZST field into ()
179self.tcx.types.unit
180 }
181 } else {
182t.super_fold_with(self)
183 }
184 }
185186 ty::Ref(..) => {
187if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
188if t.is_mutable_ptr() {
189Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
190 } else {
191Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
192 }
193 } else {
194t.super_fold_with(self)
195 }
196 }
197198 ty::RawPtr(..) => {
199if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
200if t.is_mutable_ptr() {
201Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
202 } else {
203Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
204 }
205 } else {
206t.super_fold_with(self)
207 }
208 }
209210 ty::FnPtr(..) => {
211if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
212Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
213 } else {
214t.super_fold_with(self)
215 }
216 }
217218 ty::Alias(..) => self.fold_ty(self.tcx.normalize_erasing_regions(
219 ty::TypingEnv::fully_monomorphized(),
220Unnormalized::new_wip(t),
221 )),
222223 ty::Bound(..) | ty::Error(..) | ty::Infer(..) | ty::Param(..) | ty::Placeholder(..) => {
224::rustc_middle::util::bug::bug_fmt(format_args!("fold_ty: unexpected `{0:?}`",
t.kind()));bug!("fold_ty: unexpected `{:?}`", t.kind());
225 }
226 }
227 }
228229fn cx(&self) -> TyCtxt<'tcx> {
230self.tcx
231 }
232}
233234x;#[instrument(skip(tcx), ret)]235fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
236assert!(!poly_trait_ref.has_non_region_param());
237let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
238 ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
239 });
240let mut assoc_preds: Vec<_> = traits::supertraits(tcx, poly_trait_ref)
241 .flat_map(|super_poly_trait_ref| {
242 tcx.associated_items(super_poly_trait_ref.def_id())
243 .in_definition_order()
244 .filter(|item| item.is_type() || item.is_type_const())
245 .filter(|item| !tcx.generics_require_sized_self(item.def_id))
246 .map(move |assoc_item| {
247 super_poly_trait_ref.map_bound(|super_trait_ref| {
248let projection_term = ty::AliasTerm::new_from_def_id(
249 tcx,
250 assoc_item.def_id,
251 super_trait_ref.args,
252 );
253let term = tcx.normalize_erasing_regions(
254 ty::TypingEnv::fully_monomorphized(),
255 Unnormalized::new_wip(projection_term.to_term(tcx)),
256 );
257debug!("Projection {:?} -> {term}", projection_term.to_term(tcx),);
258 ty::ExistentialPredicate::Projection(
259 ty::ExistentialProjection::erase_self_ty(
260 tcx,
261 ty::ProjectionPredicate { projection_term, term },
262 ),
263 )
264 })
265 })
266 })
267 .collect();
268 assoc_preds.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
269let preds = tcx.mk_poly_existential_predicates_from_iter(
270 iter::once(principal_pred).chain(assoc_preds.into_iter()),
271 );
272 Ty::new_dynamic(tcx, preds, tcx.lifetimes.re_erased)
273}
274275/// Transforms an instance for LLVM CFI and cross-language LLVM CFI support using Itanium C++ ABI
276/// mangling.
277///
278/// typeid_for_instance is called at two locations, initially when declaring/defining functions and
279/// methods, and later during code generation at call sites, after type erasure might have occurred.
280///
281/// In the first call (i.e., when declaring/defining functions and methods), it encodes type ids for
282/// an FnAbi or Instance, and these type ids are attached to functions and methods. (These type ids
283/// are used later by the LowerTypeTests LLVM pass to aggregate functions in groups derived from
284/// these type ids.)
285///
286/// In the second call (i.e., during code generation at call sites), it encodes a type id for an
287/// FnAbi or Instance, after type erasure might have occurred, and this type id is used for testing
288/// if a function is member of the group derived from this type id. Therefore, in the first call to
289/// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at
290/// most as much information that would be available in the second call (i.e., during code
291/// generation at call sites); otherwise, the type ids would not match.
292///
293/// For this, it:
294///
295/// * Adjust the type ids of DropGlues (see below).
296/// * Adjusts the type ids of VTableShims to the type id expected in the call sites for the
297/// entry in the vtable (i.e., by using the signature of the closure passed as an argument to the
298/// shim, or by just removing self).
299/// * Performs type erasure for calls on trait objects by transforming self into a trait object of
300/// the trait that defines the method.
301/// * Performs type erasure for closures call methods by transforming self into a trait object of
302/// the Fn trait that defines the method (for being attached as a secondary type id).
303///
304#[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(304u32),
::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))]305pub(crate) fn transform_instance<'tcx>(
306 tcx: TyCtxt<'tcx>,
307mut instance: Instance<'tcx>,
308 options: TransformTyOptions,
309) -> Instance<'tcx> {
310// FIXME: account for async-drop-glue
311if (matches!(instance.def, ty::InstanceKind::Virtual(..))
312 && tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace))
313 || matches!(instance.def, ty::InstanceKind::DropGlue(..))
314 {
315// Adjust the type ids of DropGlues
316 //
317 // DropGlues may have indirect calls to one or more given types drop function. Rust allows
318 // for types to be erased to any trait object and retains the drop function for the original
319 // type, which means at the indirect call sites in DropGlues, when typeid_for_fnabi is
320 // called a second time, it only has information after type erasure and it could be a call
321 // on any arbitrary trait object. Normalize them to a synthesized Drop trait object, both on
322 // declaration/definition, and during code generation at call sites so they have the same
323 // type id and match.
324 //
325 // FIXME(rcvalle): This allows a drop call on any trait object to call the drop function of
326 // any other type.
327 //
328let def_id = tcx
329 .lang_items()
330 .drop_trait()
331 .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
332let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(
333 tcx,
334 def_id,
335 ty::List::empty(),
336 ));
337let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
338let self_ty = Ty::new_dynamic(tcx, predicates, tcx.lifetimes.re_erased);
339 instance.args = tcx.mk_args_trait(self_ty, List::empty());
340 } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def {
341// Transform self into a trait object of the trait that defines the method for virtual
342 // functions to match the type erasure done below.
343let upcast_ty = match tcx.trait_of_assoc(def_id) {
344Some(trait_id) => trait_object_ty(
345 tcx,
346 ty::Binder::dummy(ty::TraitRef::from_assoc(tcx, trait_id, instance.args)),
347 ),
348// drop_in_place won't have a defining trait, skip the upcast
349None => instance.args.type_at(0),
350 };
351let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else {
352bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}");
353 };
354let self_ty = if preds.principal().is_some() {
355let filtered_preds =
356 tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| {
357 !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..))
358 }));
359 Ty::new_dynamic(tcx, filtered_preds, *lifetime)
360 } else {
361// If there's no principal type, re-encode it as a unit, since we don't know anything
362 // about it. This technically discards the knowledge that it was a type that was made
363 // into a trait object at some point, but that's not a lot.
364tcx.types.unit
365 };
366 instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1));
367 } else if let ty::InstanceKind::VTableShim(def_id) = instance.def
368 && let Some(trait_id) = tcx.trait_of_assoc(def_id)
369 {
370// Adjust the type ids of VTableShims to the type id expected in the call sites for the
371 // entry in the vtable (i.e., by using the signature of the closure passed as an argument
372 // to the shim, or by just removing self).
373let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, instance.args);
374let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
375 instance.args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
376 }
377378if !options.contains(TransformTyOptions::USE_CONCRETE_SELF) {
379// Perform type erasure for calls on trait objects by transforming self into a trait object
380 // of the trait that defines the method.
381if let Some((trait_ref, method_id, ancestor)) = implemented_method(tcx, instance) {
382// Trait methods will have a Self polymorphic parameter, where the concreteized
383 // implementation will not. We need to walk back to the more general trait method
384let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
385 instance.args,
386 ty::TypingEnv::fully_monomorphized(),
387 trait_ref,
388 );
389let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
390391// At the call site, any call to this concrete function through a vtable will be
392 // `Virtual(method_id, idx)` with appropriate arguments for the method. Since we have the
393 // original method id, and we've recovered the trait arguments, we can make the callee
394 // instance we're computing the alias set for match the caller instance.
395 //
396 // Right now, our code ignores the vtable index everywhere, so we use 0 as a placeholder.
397 // If we ever *do* start encoding the vtable index, we will need to generate an alias set
398 // based on which vtables we are putting this method into, as there will be more than one
399 // index value when supertraits are involved.
400instance.def = ty::InstanceKind::Virtual(method_id, 0);
401let abstract_trait_args =
402 tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
403 instance.args = instance.args.rebase_onto(tcx, ancestor, abstract_trait_args);
404 } else if tcx.is_closure_like(instance.def_id()) {
405// We're either a closure or a coroutine. Our goal is to find the trait we're defined on,
406 // instantiate it, and take the type of its only method as our own.
407let closure_ty = instance.ty(tcx, ty::TypingEnv::fully_monomorphized());
408let (trait_id, inputs) = match closure_ty.kind() {
409 ty::Closure(..) => {
410let closure_args = instance.args.as_closure();
411let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
412let tuple_args =
413 tcx.instantiate_bound_regions_with_erased(closure_args.sig()).inputs()[0];
414 (trait_id, Some(tuple_args))
415 }
416 ty::Coroutine(..) => match tcx.coroutine_kind(instance.def_id()).unwrap() {
417 hir::CoroutineKind::Coroutine(..) => (
418 tcx.require_lang_item(LangItem::Coroutine, DUMMY_SP),
419Some(instance.args.as_coroutine().resume_ty()),
420 ),
421 hir::CoroutineKind::Desugared(desugaring, _) => {
422let lang_item = match desugaring {
423 hir::CoroutineDesugaring::Async => LangItem::Future,
424 hir::CoroutineDesugaring::AsyncGen => LangItem::AsyncIterator,
425 hir::CoroutineDesugaring::Gen => LangItem::Iterator,
426 };
427 (tcx.require_lang_item(lang_item, DUMMY_SP), None)
428 }
429 },
430 ty::CoroutineClosure(..) => (
431 tcx.require_lang_item(LangItem::FnOnce, DUMMY_SP),
432Some(
433 tcx.instantiate_bound_regions_with_erased(
434 instance.args.as_coroutine_closure().coroutine_closure_sig(),
435 )
436 .tupled_inputs_ty,
437 ),
438 ),
439 x => bug!("Unexpected type kind for closure-like: {x:?}"),
440 };
441let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
442let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, concrete_args);
443let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
444let abstract_args = tcx.mk_args_trait(invoke_ty, trait_ref.args.into_iter().skip(1));
445// There should be exactly one method on this trait, and it should be the one we're
446 // defining.
447let call = tcx
448 .associated_items(trait_id)
449 .in_definition_order()
450 .find(|it| it.is_fn())
451 .expect("No call-family function on closure-like Fn trait?")
452 .def_id;
453454 instance.def = ty::InstanceKind::Virtual(call, 0);
455 instance.args = abstract_args;
456 }
457 }
458459 instance
460}
461462fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
463match instance.def {
464 ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
465tcx.opt_associated_item(def_id).map(|item| item.def_id)
466 }
467_ => None,
468 }
469}
470471/// Determines if an instance represents a trait method implementation and returns the necessary
472/// information for type erasure.
473///
474/// This function handles two main cases:
475///
476/// * **Implementation in an `impl` block**: When the instance represents a concrete implementation
477/// of a trait method in an `impl` block, it extracts the trait reference, method ID, and trait
478/// ID from the implementation. The method ID is obtained from the `trait_item_def_id` field of
479/// the associated item, which points to the original trait method definition.
480///
481/// * **Provided method in a `trait` block or synthetic `shim`**: When the instance represents a
482/// default implementation provided in the trait definition itself or a synthetic shim, it uses
483/// the instance's own `def_id` as the method ID and determines the trait ID from the associated
484/// item.
485///
486fn implemented_method<'tcx>(
487 tcx: TyCtxt<'tcx>,
488 instance: Instance<'tcx>,
489) -> Option<(ty::EarlyBinder<'tcx, TraitRef<'tcx>>, DefId, DefId)> {
490let trait_ref;
491let method_id;
492let trait_id;
493let trait_method;
494let assoc = tcx.opt_associated_item(instance.def_id())?;
495let ancestor = if let AssocContainer::TraitImpl(Ok(trait_method_id)) = assoc.container {
496let impl_id = tcx.parent(instance.def_id());
497trait_ref = tcx.impl_trait_ref(impl_id);
498method_id = trait_method_id;
499trait_method = tcx.associated_item(method_id);
500trait_id = trait_ref.skip_binder().def_id;
501impl_id502 } else if let AssocContainer::Trait = assoc.container
503 && let Some(trait_method_def_id) = default_or_shim(tcx, instance)
504 {
505// Provided method in a `trait` block or a synthetic `shim`
506trait_method = assoc;
507method_id = trait_method_def_id;
508trait_id = tcx.parent(method_id);
509trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
510trait_id511 } else {
512return None;
513 };
514let vtable_possible = traits::is_vtable_safe_method(tcx, trait_id, trait_method)
515 && tcx.is_dyn_compatible(trait_id);
516vtable_possible.then_some((trait_ref, method_id, ancestor))
517}