Skip to main content

rustc_sanitizers/cfi/typeid/itanium_cxx_abi/
transform.rs

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.
6
7use std::iter;
8
9use rustc_hir::{self as hir, LangItem, find_attr};
10use rustc_middle::bug;
11use rustc_middle::ty::{
12    self, AssocContainer, ExistentialPredicateStableCmpExt as _, Instance, IntTy, List, TraitRef,
13    Ty, 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};
19
20use crate::cfi::typeid::TypeIdOptions;
21use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions;
22
23/// Options for transform_ty.
24pub(crate) type TransformTyOptions = TypeIdOptions;
25
26pub(crate) struct TransformTy<'tcx> {
27    tcx: TyCtxt<'tcx>,
28    options: TransformTyOptions,
29    parents: Vec<Ty<'tcx>>,
30}
31
32impl<'tcx> TransformTy<'tcx> {
33    pub(crate) fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
34        TransformTy { tcx, options, parents: Vec::new() }
35    }
36}
37
38/// 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.
50    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
51        match 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::Never
61            | ty::Pat(..)
62            | ty::Slice(..)
63            | ty::Str
64            | ty::Tuple(..)
65            | ty::UnsafeBinder(_) => t.super_fold_with(self),
66
67            // 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) => {
70                let inner = self.fold_ty(inner);
71                Ty::new_array_with_const_len(self.tcx, inner, len)
72            }
73
74            ty::Bool => {
75                if 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.
82                    self.tcx.types.u8
83                } else {
84                    t
85                }
86            }
87
88            ty::Char => {
89                if 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.
92                    self.tcx.types.u32
93                } else {
94                    t
95                }
96            }
97
98            ty::Int(..) | ty::Uint(..) => {
99                if 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.)
105                    match t.kind() {
106                        ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
107                            16 => self.tcx.types.i16,
108                            32 => self.tcx.types.i32,
109                            64 => self.tcx.types.i64,
110                            128 => 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 `{}`",
113                                self.tcx.sess.target.pointer_width
114                            ),
115                        },
116                        ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
117                            16 => self.tcx.types.u16,
118                            32 => self.tcx.types.u32,
119                            64 => self.tcx.types.u64,
120                            128 => 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 `{}`",
123                                self.tcx.sess.target.pointer_width
124                            ),
125                        },
126                        _ => t,
127                    }
128                } else {
129                    t
130                }
131            }
132
133            ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
134
135            ty::Adt(adt_def, args) => {
136                if 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.
140                    if {
        {
            '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 { .. }) {
141                        return t;
142                    }
143                    let variant = adt_def.non_enum_variant();
144                    let typing_env = ty::TypingEnv::post_analysis(self.tcx, variant.def_id);
145                    let field = variant.fields.iter().find(|field| {
146                        let ty = self.tcx.type_of(field.did).instantiate_identity();
147                        let is_zst = self
148                            .tcx
149                            .layout_of(typing_env.as_query_input(ty))
150                            .is_ok_and(|layout| layout.is_zst());
151                        !is_zst
152                    });
153                    if let Some(field) = field {
154                        let ty0 = self.tcx.normalize_erasing_regions(
155                            ty::TypingEnv::fully_monomorphized(),
156                            field.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.
161
162                        // 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.
164                        self.parents.push(t);
165                        let ty = if ty0.is_any_ptr() && ty0.contains(t) {
166                            let options = self.options;
167                            self.options |= TransformTyOptions::GENERALIZE_POINTERS;
168                            let ty = ty0.fold_with(self);
169                            self.options = options;
170                            ty
171                        } else {
172                            ty0.fold_with(self)
173                        };
174                        self.parents.pop();
175                        ty
176                    } else {
177                        // Transform repr(transparent) types without non-ZST field into ()
178                        self.tcx.types.unit
179                    }
180                } else {
181                    t.super_fold_with(self)
182                }
183            }
184
185            ty::Ref(..) => {
186                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
187                    if t.is_mutable_ptr() {
188                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
189                    } else {
190                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
191                    }
192                } else {
193                    t.super_fold_with(self)
194                }
195            }
196
197            ty::RawPtr(..) => {
198                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
199                    if t.is_mutable_ptr() {
200                        Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
201                    } else {
202                        Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
203                    }
204                } else {
205                    t.super_fold_with(self)
206                }
207            }
208
209            ty::FnPtr(..) => {
210                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
211                    Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
212                } else {
213                    t.super_fold_with(self)
214                }
215            }
216
217            ty::Alias(..) => self.fold_ty(
218                self.tcx.normalize_erasing_regions(ty::TypingEnv::fully_monomorphized(), t),
219            ),
220
221            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    }
226
227    fn cx(&self) -> TyCtxt<'tcx> {
228        self.tcx
229    }
230}
231
232x;#[instrument(skip(tcx), ret)]
233fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
234    assert!(!poly_trait_ref.has_non_region_param());
235    let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
236        ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
237    });
238    let 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| {
246                        let projection_term = ty::AliasTerm::new_from_args(
247                            tcx,
248                            assoc_item.def_id,
249                            super_trait_ref.args,
250                        );
251                        let term = tcx.normalize_erasing_regions(
252                            ty::TypingEnv::fully_monomorphized(),
253                            projection_term.to_term(tcx),
254                        );
255                        debug!("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()));
267    let 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}
272
273/// 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>,
305    mut instance: Instance<'tcx>,
306    options: TransformTyOptions,
307) -> Instance<'tcx> {
308    // FIXME: account for async-drop-glue
309    if (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        //
326        let def_id = tcx
327            .lang_items()
328            .drop_trait()
329            .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
330        let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(
331            tcx,
332            def_id,
333            ty::List::empty(),
334        ));
335        let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
336        let 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.
341        let upcast_ty = match tcx.trait_of_assoc(def_id) {
342            Some(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
347            None => instance.args.type_at(0),
348        };
349        let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else {
350            bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}");
351        };
352        let self_ty = if preds.principal().is_some() {
353            let 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.
362            tcx.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).
371        let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, instance.args);
372        let 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    }
375
376    if !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.
379        if 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
382            let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
383                instance.args,
384                ty::TypingEnv::fully_monomorphized(),
385                trait_ref,
386            );
387            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
388
389            // 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.
398            instance.def = ty::InstanceKind::Virtual(method_id, 0);
399            let 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.
405            let closure_ty = instance.ty(tcx, ty::TypingEnv::fully_monomorphized());
406            let (trait_id, inputs) = match closure_ty.kind() {
407                ty::Closure(..) => {
408                    let closure_args = instance.args.as_closure();
409                    let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
410                    let 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),
417                        Some(instance.args.as_coroutine().resume_ty()),
418                    ),
419                    hir::CoroutineKind::Desugared(desugaring, _) => {
420                        let 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),
430                    Some(
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            };
439            let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
440            let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, concrete_args);
441            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
442            let 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.
445            let 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;
451
452            instance.def = ty::InstanceKind::Virtual(call, 0);
453            instance.args = abstract_args;
454        }
455    }
456
457    instance
458}
459
460fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
461    match instance.def {
462        ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
463            tcx.opt_associated_item(def_id).map(|item| item.def_id)
464        }
465        _ => None,
466    }
467}
468
469/// 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)> {
488    let trait_ref;
489    let method_id;
490    let trait_id;
491    let trait_method;
492    let assoc = tcx.opt_associated_item(instance.def_id())?;
493    let ancestor = if let AssocContainer::TraitImpl(Ok(trait_method_id)) = assoc.container {
494        let impl_id = tcx.parent(instance.def_id());
495        trait_ref = tcx.impl_trait_ref(impl_id);
496        method_id = trait_method_id;
497        trait_method = tcx.associated_item(method_id);
498        trait_id = trait_ref.skip_binder().def_id;
499        impl_id
500    } 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`
504        trait_method = assoc;
505        method_id = trait_method_def_id;
506        trait_id = tcx.parent(method_id);
507        trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
508        trait_id
509    } else {
510        return None;
511    };
512    let vtable_possible = traits::is_vtable_safe_method(tcx, trait_id, trait_method)
513        && tcx.is_dyn_compatible(trait_id);
514    vtable_possible.then_some((trait_ref, method_id, ancestor))
515}