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    Unnormalized,
15};
16use rustc_span::DUMMY_SP;
17use rustc_span::def_id::DefId;
18use rustc_trait_selection::traits;
19use tracing::{debug, instrument};
20
21use crate::cfi::typeid::TypeIdOptions;
22use crate::cfi::typeid::itanium_cxx_abi::encode::EncodeTyOptions;
23
24/// Options for transform_ty.
25pub(crate) type TransformTyOptions = TypeIdOptions;
26
27pub(crate) struct TransformTy<'tcx> {
28    tcx: TyCtxt<'tcx>,
29    options: TransformTyOptions,
30    parents: Vec<Ty<'tcx>>,
31}
32
33impl<'tcx> TransformTy<'tcx> {
34    pub(crate) fn new(tcx: TyCtxt<'tcx>, options: TransformTyOptions) -> Self {
35        TransformTy { tcx, options, parents: Vec::new() }
36    }
37}
38
39/// 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.
51    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
52        match 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::Never
62            | ty::Pat(..)
63            | ty::Slice(..)
64            | ty::Str
65            | ty::Tuple(..)
66            | ty::UnsafeBinder(_) => t.super_fold_with(self),
67
68            // 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) => {
71                let inner = self.fold_ty(inner);
72                Ty::new_array_with_const_len(self.tcx, inner, len)
73            }
74
75            ty::Bool => {
76                if 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.
83                    self.tcx.types.u8
84                } else {
85                    t
86                }
87            }
88
89            ty::Char => {
90                if 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.
93                    self.tcx.types.u32
94                } else {
95                    t
96                }
97            }
98
99            ty::Int(..) | ty::Uint(..) => {
100                if 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.)
106                    match t.kind() {
107                        ty::Int(IntTy::Isize) => match self.tcx.sess.target.pointer_width {
108                            16 => self.tcx.types.i16,
109                            32 => self.tcx.types.i32,
110                            64 => self.tcx.types.i64,
111                            128 => 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 `{}`",
114                                self.tcx.sess.target.pointer_width
115                            ),
116                        },
117                        ty::Uint(UintTy::Usize) => match self.tcx.sess.target.pointer_width {
118                            16 => self.tcx.types.u16,
119                            32 => self.tcx.types.u32,
120                            64 => self.tcx.types.u64,
121                            128 => 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 `{}`",
124                                self.tcx.sess.target.pointer_width
125                            ),
126                        },
127                        _ => t,
128                    }
129                } else {
130                    t
131                }
132            }
133
134            ty::Adt(..) if t.is_c_void(self.tcx) => self.tcx.types.unit,
135
136            ty::Adt(adt_def, args) => {
137                if 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.
141                    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 { .. }) {
142                        return t;
143                    }
144                    let variant = adt_def.non_enum_variant();
145                    let typing_env = ty::TypingEnv::post_analysis(self.tcx, variant.def_id);
146                    let field = variant.fields.iter().find(|field| {
147                        let ty = self.tcx.type_of(field.did).instantiate_identity().skip_norm_wip();
148                        let is_zst = self
149                            .tcx
150                            .layout_of(typing_env.as_query_input(ty))
151                            .is_ok_and(|layout| layout.is_zst());
152                        !is_zst
153                    });
154                    if let Some(field) = field {
155                        let ty0 = self.tcx.normalize_erasing_regions(
156                            ty::TypingEnv::fully_monomorphized(),
157                            Unnormalized::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.
162
163                        // 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.
165                        self.parents.push(t);
166                        let ty = if ty0.is_any_ptr() && ty0.contains(t) {
167                            let options = self.options;
168                            self.options |= TransformTyOptions::GENERALIZE_POINTERS;
169                            let ty = ty0.fold_with(self);
170                            self.options = options;
171                            ty
172                        } else {
173                            ty0.fold_with(self)
174                        };
175                        self.parents.pop();
176                        ty
177                    } else {
178                        // Transform repr(transparent) types without non-ZST field into ()
179                        self.tcx.types.unit
180                    }
181                } else {
182                    t.super_fold_with(self)
183                }
184            }
185
186            ty::Ref(..) => {
187                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
188                    if t.is_mutable_ptr() {
189                        Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
190                    } else {
191                        Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, self.tcx.types.unit)
192                    }
193                } else {
194                    t.super_fold_with(self)
195                }
196            }
197
198            ty::RawPtr(..) => {
199                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
200                    if t.is_mutable_ptr() {
201                        Ty::new_mut_ptr(self.tcx, self.tcx.types.unit)
202                    } else {
203                        Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
204                    }
205                } else {
206                    t.super_fold_with(self)
207                }
208            }
209
210            ty::FnPtr(..) => {
211                if self.options.contains(TransformTyOptions::GENERALIZE_POINTERS) {
212                    Ty::new_imm_ptr(self.tcx, self.tcx.types.unit)
213                } else {
214                    t.super_fold_with(self)
215                }
216            }
217
218            ty::Alias(..) => self.fold_ty(self.tcx.normalize_erasing_regions(
219                ty::TypingEnv::fully_monomorphized(),
220                Unnormalized::new_wip(t),
221            )),
222
223            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    }
228
229    fn cx(&self) -> TyCtxt<'tcx> {
230        self.tcx
231    }
232}
233
234x;#[instrument(skip(tcx), ret)]
235fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>) -> Ty<'tcx> {
236    assert!(!poly_trait_ref.has_non_region_param());
237    let principal_pred = poly_trait_ref.map_bound(|trait_ref| {
238        ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref))
239    });
240    let 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| {
248                        let projection_term = ty::AliasTerm::new_from_def_id(
249                            tcx,
250                            assoc_item.def_id,
251                            super_trait_ref.args,
252                        );
253                        let term = tcx.normalize_erasing_regions(
254                            ty::TypingEnv::fully_monomorphized(),
255                            Unnormalized::new_wip(projection_term.to_term(tcx)),
256                        );
257                        debug!("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()));
269    let 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}
274
275/// 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>,
307    mut instance: Instance<'tcx>,
308    options: TransformTyOptions,
309) -> Instance<'tcx> {
310    // FIXME: account for async-drop-glue
311    if (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        //
328        let def_id = tcx
329            .lang_items()
330            .drop_trait()
331            .unwrap_or_else(|| bug!("typeid_for_instance: couldn't get drop_trait lang item"));
332        let predicate = ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::new_from_args(
333            tcx,
334            def_id,
335            ty::List::empty(),
336        ));
337        let predicates = tcx.mk_poly_existential_predicates(&[ty::Binder::dummy(predicate)]);
338        let 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.
343        let upcast_ty = match tcx.trait_of_assoc(def_id) {
344            Some(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
349            None => instance.args.type_at(0),
350        };
351        let ty::Dynamic(preds, lifetime) = upcast_ty.kind() else {
352            bug!("Tried to remove autotraits from non-dynamic type {upcast_ty}");
353        };
354        let self_ty = if preds.principal().is_some() {
355            let 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.
364            tcx.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).
373        let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, instance.args);
374        let 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    }
377
378    if !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.
381        if 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
384            let trait_ref = tcx.instantiate_and_normalize_erasing_regions(
385                instance.args,
386                ty::TypingEnv::fully_monomorphized(),
387                trait_ref,
388            );
389            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
390
391            // 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.
400            instance.def = ty::InstanceKind::Virtual(method_id, 0);
401            let 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.
407            let closure_ty = instance.ty(tcx, ty::TypingEnv::fully_monomorphized());
408            let (trait_id, inputs) = match closure_ty.kind() {
409                ty::Closure(..) => {
410                    let closure_args = instance.args.as_closure();
411                    let trait_id = tcx.fn_trait_kind_to_def_id(closure_args.kind()).unwrap();
412                    let 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),
419                        Some(instance.args.as_coroutine().resume_ty()),
420                    ),
421                    hir::CoroutineKind::Desugared(desugaring, _) => {
422                        let 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),
432                    Some(
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            };
441            let concrete_args = tcx.mk_args_trait(closure_ty, inputs.map(Into::into));
442            let trait_ref = ty::TraitRef::new_from_args(tcx, trait_id, concrete_args);
443            let invoke_ty = trait_object_ty(tcx, ty::Binder::dummy(trait_ref));
444            let 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.
447            let 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;
453
454            instance.def = ty::InstanceKind::Virtual(call, 0);
455            instance.args = abstract_args;
456        }
457    }
458
459    instance
460}
461
462fn default_or_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Option<DefId> {
463    match instance.def {
464        ty::InstanceKind::Item(def_id) | ty::InstanceKind::FnPtrShim(def_id, _) => {
465            tcx.opt_associated_item(def_id).map(|item| item.def_id)
466        }
467        _ => None,
468    }
469}
470
471/// 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)> {
490    let trait_ref;
491    let method_id;
492    let trait_id;
493    let trait_method;
494    let assoc = tcx.opt_associated_item(instance.def_id())?;
495    let ancestor = if let AssocContainer::TraitImpl(Ok(trait_method_id)) = assoc.container {
496        let impl_id = tcx.parent(instance.def_id());
497        trait_ref = tcx.impl_trait_ref(impl_id);
498        method_id = trait_method_id;
499        trait_method = tcx.associated_item(method_id);
500        trait_id = trait_ref.skip_binder().def_id;
501        impl_id
502    } 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`
506        trait_method = assoc;
507        method_id = trait_method_def_id;
508        trait_id = tcx.parent(method_id);
509        trait_ref = ty::EarlyBinder::bind(TraitRef::from_assoc(tcx, trait_id, instance.args));
510        trait_id
511    } else {
512        return None;
513    };
514    let vtable_possible = traits::is_vtable_safe_method(tcx, trait_id, trait_method)
515        && tcx.is_dyn_compatible(trait_id);
516    vtable_possible.then_some((trait_ref, method_id, ancestor))
517}