about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_codegen_cranelift/src/unsize.rs63
-rw-r--r--compiler/rustc_codegen_cranelift/src/vtable.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs107
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs27
-rw-r--r--compiler/rustc_middle/src/query/mod.rs5
-rw-r--r--compiler/rustc_mir/src/interpret/cast.rs32
-rw-r--r--compiler/rustc_mir/src/interpret/operand.rs20
-rw-r--r--compiler/rustc_mir/src/interpret/traits.rs22
-rw-r--r--compiler/rustc_query_impl/src/keys.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs16
10 files changed, 211 insertions, 93 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs
index b9d379c6117..d594731b4df 100644
--- a/compiler/rustc_codegen_cranelift/src/unsize.rs
+++ b/compiler/rustc_codegen_cranelift/src/unsize.rs
@@ -25,39 +25,73 @@ pub(crate) fn unsized_info<'tcx>(
             .bcx
             .ins()
             .iconst(fx.pointer_type, len.eval_usize(fx.tcx, ParamEnv::reveal_all()) as i64),
-        (&ty::Dynamic(..), &ty::Dynamic(..)) => {
-            // For now, upcasts are limited to changes in marker
-            // traits, and hence never actually require an actual
-            // change to the vtable.
-            old_info.expect("unsized_info: missing old info for trait upcast")
+        (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
+            let old_info =
+                old_info.expect("unsized_info: missing old info for trait upcasting coercion");
+            if data_a.principal_def_id() == data_b.principal_def_id() {
+                return old_info;
+            }
+            // trait upcasting coercion
+
+            // if both of the two `principal`s are `None`, this function would have returned early above.
+            // and if one of the two `principal`s is `None`, typechecking would have rejected this case.
+            let principal_a = data_a
+                .principal()
+                .expect("unsized_info: missing principal trait for trait upcasting coercion");
+            let principal_b = data_b
+                .principal()
+                .expect("unsized_info: missing principal trait for trait upcasting coercion");
+
+            let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
+                principal_a.with_self_ty(fx.tcx, source),
+                principal_b.with_self_ty(fx.tcx, source),
+            ));
+
+            if let Some(entry_idx) = vptr_entry_idx {
+                let entry_idx = u32::try_from(entry_idx).unwrap();
+                let entry_offset = entry_idx * fx.pointer_type.bytes();
+                let vptr_ptr = Pointer::new(old_info).offset_i64(fx, entry_offset.into()).load(
+                    fx,
+                    fx.pointer_type,
+                    crate::vtable::vtable_memflags(),
+                );
+                vptr_ptr
+            } else {
+                old_info
+            }
         }
         (_, &ty::Dynamic(ref data, ..)) => crate::vtable::get_vtable(fx, source, data.principal()),
         _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
     }
 }
 
-/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer.
-fn unsize_thin_ptr<'tcx>(
+/// Coerce `src` to `dst_ty`.
+fn unsize_ptr<'tcx>(
     fx: &mut FunctionCx<'_, '_, 'tcx>,
     src: Value,
     src_layout: TyAndLayout<'tcx>,
     dst_layout: TyAndLayout<'tcx>,
+    old_info: Option<Value>,
 ) -> (Value, Value) {
     match (&src_layout.ty.kind(), &dst_layout.ty.kind()) {
         (&ty::Ref(_, a, _), &ty::Ref(_, b, _))
         | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
         | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
             assert!(!fx.layout_of(a).is_unsized());
-            (src, unsized_info(fx, a, b, None))
+            (src, unsized_info(fx, a, b, old_info))
         }
         (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
             let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty());
             assert!(!fx.layout_of(a).is_unsized());
-            (src, unsized_info(fx, a, b, None))
+            (src, unsized_info(fx, a, b, old_info))
         }
         (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
             assert_eq!(def_a, def_b);
 
+            if src_layout == dst_layout {
+                return (src, old_info.unwrap());
+            }
+
             let mut result = None;
             for i in 0..src_layout.fields.count() {
                 let src_f = src_layout.field(fx, i);
@@ -71,11 +105,11 @@ fn unsize_thin_ptr<'tcx>(
                 let dst_f = dst_layout.field(fx, i);
                 assert_ne!(src_f.ty, dst_f.ty);
                 assert_eq!(result, None);
-                result = Some(unsize_thin_ptr(fx, src, src_f, dst_f));
+                result = Some(unsize_ptr(fx, src, src_f, dst_f, old_info));
             }
             result.unwrap()
         }
-        _ => bug!("unsize_thin_ptr: called on bad types"),
+        _ => bug!("unsize_ptr: called on bad types"),
     }
 }
 
@@ -91,12 +125,11 @@ pub(crate) fn coerce_unsized_into<'tcx>(
     let mut coerce_ptr = || {
         let (base, info) =
             if fx.layout_of(src.layout().ty.builtin_deref(true).unwrap().ty).is_unsized() {
-                // fat-ptr to fat-ptr unsize preserves the vtable
-                // i.e., &'a fmt::Debug+Send => &'a fmt::Debug
-                src.load_scalar_pair(fx)
+                let (old_base, old_info) = src.load_scalar_pair(fx);
+                unsize_ptr(fx, old_base, src.layout(), dst.layout(), Some(old_info))
             } else {
                 let base = src.load_scalar(fx);
-                unsize_thin_ptr(fx, base, src.layout(), dst.layout())
+                unsize_ptr(fx, base, src.layout(), dst.layout(), None)
             };
         dst.write_cvalue(fx, CValue::by_val_pair(base, info, dst.layout()));
     };
diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs
index 4a5f9f133a2..1b315874308 100644
--- a/compiler/rustc_codegen_cranelift/src/vtable.rs
+++ b/compiler/rustc_codegen_cranelift/src/vtable.rs
@@ -5,7 +5,7 @@
 use crate::constant::data_id_for_alloc_id;
 use crate::prelude::*;
 
-fn vtable_memflags() -> MemFlags {
+pub(crate) fn vtable_memflags() -> MemFlags {
     let mut flags = MemFlags::trusted(); // A vtable access is always aligned and will never trap.
     flags.set_readonly(); // A vtable is always read-only.
     flags
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index be2bf8b1997..87b342e8443 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -23,7 +23,6 @@ use rustc_middle::middle::cstore::EncodedMetadata;
 use rustc_middle::middle::lang_items;
 use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem};
 use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout};
-use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_session::cgu_reuse_tracker::CguReuse;
@@ -32,6 +31,7 @@ use rustc_session::Session;
 use rustc_span::symbol::sym;
 use rustc_target::abi::{Align, LayoutOf, VariantIdx};
 
+use std::convert::TryFrom;
 use std::ops::{Deref, DerefMut};
 use std::time::{Duration, Instant};
 
@@ -128,55 +128,92 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
 ///
 /// The `old_info` argument is a bit odd. It is intended for use in an upcast,
 /// where the new vtable for an object will be derived from the old one.
-pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>(
-    cx: &Cx,
+pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+    bx: &mut Bx,
     source: Ty<'tcx>,
     target: Ty<'tcx>,
-    old_info: Option<Cx::Value>,
-) -> Cx::Value {
+    old_info: Option<Bx::Value>,
+) -> Bx::Value {
+    let cx = bx.cx();
     let (source, target) =
-        cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env());
+        cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env());
     match (source.kind(), target.kind()) {
         (&ty::Array(_, len), &ty::Slice(_)) => {
             cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
         }
-        (&ty::Dynamic(..), &ty::Dynamic(..)) => {
-            // For now, upcasts are limited to changes in marker
-            // traits, and hence never actually require an actual
-            // change to the vtable.
-            old_info.expect("unsized_info: missing old info for trait upcast")
+        (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
+            let old_info =
+                old_info.expect("unsized_info: missing old info for trait upcasting coercion");
+            if data_a.principal_def_id() == data_b.principal_def_id() {
+                return old_info;
+            }
+
+            // trait upcasting coercion
+
+            // if both of the two `principal`s are `None`, this function would have returned early above.
+            // and if one of the two `principal`s is `None`, typechecking would have rejected this case.
+            let principal_a = data_a
+                .principal()
+                .expect("unsized_info: missing principal trait for trait upcasting coercion");
+            let principal_b = data_b
+                .principal()
+                .expect("unsized_info: missing principal trait for trait upcasting coercion");
+
+            let vptr_entry_idx = cx.tcx().vtable_trait_upcasting_coercion_new_vptr_slot((
+                principal_a.with_self_ty(cx.tcx(), source),
+                principal_b.with_self_ty(cx.tcx(), source),
+            ));
+
+            if let Some(entry_idx) = vptr_entry_idx {
+                let ptr_ty = cx.type_i8p();
+                let ptr_align = cx.tcx().data_layout.pointer_align.abi;
+                let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
+                let gep =
+                    bx.inbounds_gep(llvtable, &[bx.const_usize(u64::try_from(entry_idx).unwrap())]);
+                let new_vptr = bx.load(ptr_ty, gep, ptr_align);
+                bx.nonnull_metadata(new_vptr);
+                // Vtable loads are invariant.
+                bx.set_invariant_load(new_vptr);
+                new_vptr
+            } else {
+                old_info
+            }
         }
         (_, &ty::Dynamic(ref data, ..)) => {
-            let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)).field(cx, FAT_PTR_EXTRA);
-            cx.const_ptrcast(
-                meth::get_vtable(cx, source, data.principal()),
-                cx.backend_type(vtable_ptr),
-            )
+            let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
+                cx.layout_of(cx.tcx().mk_mut_ptr(target)),
+                1,
+                true,
+            );
+            cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
         }
         _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
     }
 }
 
-/// Coerces `src` to `dst_ty`. `src_ty` must be a thin pointer.
-pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
+pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     bx: &mut Bx,
     src: Bx::Value,
     src_ty: Ty<'tcx>,
     dst_ty: Ty<'tcx>,
+    old_info: Option<Bx::Value>,
 ) -> (Bx::Value, Bx::Value) {
-    debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty);
+    debug!("unsize_ptr: {:?} => {:?}", src_ty, dst_ty);
     match (src_ty.kind(), dst_ty.kind()) {
         (&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
         | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
-            assert!(bx.cx().type_is_sized(a));
+            assert_eq!(bx.cx().type_is_sized(a), old_info.is_none());
             let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b)));
-            (bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None))
+            (bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, old_info))
         }
         (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
             assert_eq!(def_a, def_b);
-
             let src_layout = bx.cx().layout_of(src_ty);
             let dst_layout = bx.cx().layout_of(dst_ty);
+            if src_ty == dst_ty {
+                return (src, old_info.unwrap());
+            }
             let mut result = None;
             for i in 0..src_layout.fields.count() {
                 let src_f = src_layout.field(bx.cx(), i);
@@ -190,18 +227,15 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 let dst_f = dst_layout.field(bx.cx(), i);
                 assert_ne!(src_f.ty, dst_f.ty);
                 assert_eq!(result, None);
-                result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty));
+                result = Some(unsize_ptr(bx, src, src_f.ty, dst_f.ty, old_info));
             }
             let (lldata, llextra) = result.unwrap();
+            let lldata_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true);
+            let llextra_ty = bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true);
             // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types.
-            // FIXME(eddyb) move these out of this `match` arm, so they're always
-            // applied, uniformly, no matter the source/destination types.
-            (
-                bx.bitcast(lldata, bx.cx().scalar_pair_element_backend_type(dst_layout, 0, true)),
-                bx.bitcast(llextra, bx.cx().scalar_pair_element_backend_type(dst_layout, 1, true)),
-            )
+            (bx.bitcast(lldata, lldata_ty), bx.bitcast(llextra, llextra_ty))
         }
-        _ => bug!("unsize_thin_ptr: called on bad types"),
+        _ => bug!("unsize_ptr: called on bad types"),
     }
 }
 
@@ -217,17 +251,8 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
     match (src_ty.kind(), dst_ty.kind()) {
         (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => {
             let (base, info) = match bx.load_operand(src).val {
-                OperandValue::Pair(base, info) => {
-                    // fat-ptr to fat-ptr unsize preserves the vtable
-                    // i.e., &'a fmt::Debug+Send => &'a fmt::Debug
-                    // So we need to pointercast the base to ensure
-                    // the types match up.
-                    // FIXME(eddyb) use `scalar_pair_element_backend_type` here,
-                    // like `unsize_thin_ptr` does.
-                    let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR);
-                    (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info)
-                }
-                OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src_ty, dst_ty),
+                OperandValue::Pair(base, info) => unsize_ptr(bx, base, src_ty, dst_ty, Some(info)),
+                OperandValue::Immediate(base) => unsize_ptr(bx, base, src_ty, dst_ty, None),
                 OperandValue::Ref(..) => bug!(),
             };
             OperandValue::Pair(base, info).store(bx, dst);
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 530de3de9e8..7e1dfeb2457 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -220,34 +220,23 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                     mir::CastKind::Pointer(PointerCast::Unsize) => {
                         assert!(bx.cx().is_backend_scalar_pair(cast));
-                        match operand.val {
+                        let (lldata, llextra) = match operand.val {
                             OperandValue::Pair(lldata, llextra) => {
                                 // unsize from a fat pointer -- this is a
-                                // "trait-object-to-supertrait" coercion, for
-                                // example, `&'a fmt::Debug + Send => &'a fmt::Debug`.
-
-                                // HACK(eddyb) have to bitcast pointers
-                                // until LLVM removes pointee types.
-                                let lldata = bx.pointercast(
-                                    lldata,
-                                    bx.cx().scalar_pair_element_backend_type(cast, 0, true),
-                                );
-                                OperandValue::Pair(lldata, llextra)
+                                // "trait-object-to-supertrait" coercion.
+                                (lldata, Some(llextra))
                             }
                             OperandValue::Immediate(lldata) => {
                                 // "standard" unsize
-                                let (lldata, llextra) = base::unsize_thin_ptr(
-                                    &mut bx,
-                                    lldata,
-                                    operand.layout.ty,
-                                    cast.ty,
-                                );
-                                OperandValue::Pair(lldata, llextra)
+                                (lldata, None)
                             }
                             OperandValue::Ref(..) => {
                                 bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand);
                             }
-                        }
+                        };
+                        let (lldata, llextra) =
+                            base::unsize_ptr(&mut bx, lldata, operand.layout.ty, cast.ty, llextra);
+                        OperandValue::Pair(lldata, llextra)
                     }
                     mir::CastKind::Pointer(PointerCast::MutToConstPointer)
                     | mir::CastKind::Misc
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 2de836c058c..3c16852df05 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -977,6 +977,11 @@ rustc_queries! {
         desc { |tcx| "finding all vtable entries for trait {}", tcx.def_path_str(key.def_id()) }
     }
 
+    query vtable_trait_upcasting_coercion_new_vptr_slot(key: (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>)) -> Option<usize> {
+        desc { |tcx| "finding the slot within vtable for trait {} vtable ptr during trait upcasting coercion from {} vtable",
+            tcx.def_path_str(key.1.def_id()), tcx.def_path_str(key.0.def_id()) }
+    }
+
     query codegen_fulfill_obligation(
         key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
     ) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
diff --git a/compiler/rustc_mir/src/interpret/cast.rs b/compiler/rustc_mir/src/interpret/cast.rs
index 514c1aa9646..cee4c2e30aa 100644
--- a/compiler/rustc_mir/src/interpret/cast.rs
+++ b/compiler/rustc_mir/src/interpret/cast.rs
@@ -269,12 +269,34 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
                 self.write_immediate(val, dest)
             }
-            (&ty::Dynamic(..), &ty::Dynamic(..)) => {
-                // For now, upcasts are limited to changes in marker
-                // traits, and hence never actually require an actual
-                // change to the vtable.
+            (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
                 let val = self.read_immediate(src)?;
-                self.write_immediate(*val, dest)
+                if data_a.principal_def_id() == data_b.principal_def_id() {
+                    return self.write_immediate(*val, dest);
+                }
+                // trait upcasting coercion
+                let principal_a = data_a.principal().expect(
+                    "unsize_into_ptr: missing principal trait for trait upcasting coercion",
+                );
+                let principal_b = data_b.principal().expect(
+                    "unsize_into_ptr: missing principal trait for trait upcasting coercion",
+                );
+
+                let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
+                    principal_a.with_self_ty(*self.tcx, src_pointee_ty),
+                    principal_b.with_self_ty(*self.tcx, src_pointee_ty),
+                ));
+
+                if let Some(entry_idx) = vptr_entry_idx {
+                    let entry_idx = u64::try_from(entry_idx).unwrap();
+                    let (old_data, old_vptr) = val.to_scalar_pair()?;
+                    let old_vptr = self.scalar_to_ptr(old_vptr);
+                    let new_vptr = self
+                        .read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
+                    self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
+                } else {
+                    self.write_immediate(*val, dest)
+                }
             }
             (_, &ty::Dynamic(ref data, _)) => {
                 // Initial cast from sized to dyn trait
diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs
index aba7db78168..d5bc2b1e2ea 100644
--- a/compiler/rustc_mir/src/interpret/operand.rs
+++ b/compiler/rustc_mir/src/interpret/operand.rs
@@ -63,15 +63,19 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
         Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into())
     }
 
-    pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>, cx: &impl HasDataLayout) -> Self {
-        Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_pointer(vtable, cx))
+    pub fn new_dyn_trait(
+        val: Scalar<Tag>,
+        vtable: Pointer<Option<Tag>>,
+        cx: &impl HasDataLayout,
+    ) -> Self {
+        Immediate::ScalarPair(val.into(), ScalarMaybeUninit::from_maybe_pointer(vtable, cx))
     }
 
     #[inline]
     pub fn to_scalar_or_uninit(self) -> ScalarMaybeUninit<Tag> {
         match self {
             Immediate::Scalar(val) => val,
-            Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
+            Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"),
         }
     }
 
@@ -79,6 +83,16 @@ impl<'tcx, Tag: Provenance> Immediate<Tag> {
     pub fn to_scalar(self) -> InterpResult<'tcx, Scalar<Tag>> {
         self.to_scalar_or_uninit().check_init()
     }
+
+    #[inline]
+    pub fn to_scalar_pair(self) -> InterpResult<'tcx, (Scalar<Tag>, Scalar<Tag>)> {
+        match self {
+            Immediate::ScalarPair(val1, val2) => Ok((val1.check_init()?, val2.check_init()?)),
+            Immediate::Scalar(..) => {
+                bug!("Got a scalar where a scalar pair was expected")
+            }
+        }
+    }
 }
 
 // ScalarPair needs a type to interpret, so we often have an immediate and a type together
diff --git a/compiler/rustc_mir/src/interpret/traits.rs b/compiler/rustc_mir/src/interpret/traits.rs
index 7a93fcee78e..a6ba00ec695 100644
--- a/compiler/rustc_mir/src/interpret/traits.rs
+++ b/compiler/rustc_mir/src/interpret/traits.rs
@@ -21,7 +21,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         &mut self,
         ty: Ty<'tcx>,
         poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
-    ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
+    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
         trace!("get_vtable(trait_ref={:?})", poly_trait_ref);
 
         let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref));
@@ -34,7 +34,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;
 
-        Ok(vtable_ptr)
+        Ok(vtable_ptr.into())
     }
 
     /// Resolves the function at the specified slot in the provided
@@ -121,4 +121,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         }
         Ok((Size::from_bytes(size), align))
     }
+
+    pub fn read_new_vtable_after_trait_upcasting_from_vtable(
+        &self,
+        vtable: Pointer<Option<M::PointerTag>>,
+        idx: u64,
+    ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
+        let pointer_size = self.pointer_size();
+
+        let vtable_slot = vtable.offset(pointer_size * idx, self)?;
+        let new_vtable = self
+            .memory
+            .get(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
+            .expect("cannot be a ZST");
+
+        let new_vtable = self.scalar_to_ptr(new_vtable.read_ptr_sized(Size::ZERO)?.check_init()?);
+
+        Ok(new_vtable)
+    }
 }
diff --git a/compiler/rustc_query_impl/src/keys.rs b/compiler/rustc_query_impl/src/keys.rs
index 0ad360c7d89..ace7cffd16d 100644
--- a/compiler/rustc_query_impl/src/keys.rs
+++ b/compiler/rustc_query_impl/src/keys.rs
@@ -282,6 +282,16 @@ impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
     }
 }
 
+impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) {
+    #[inline(always)]
+    fn query_crate_is_local(&self) -> bool {
+        self.0.def_id().krate == LOCAL_CRATE
+    }
+    fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
+        tcx.def_span(self.0.def_id())
+    }
+}
+
 impl<'tcx> Key for GenericArg<'tcx> {
     #[inline(always)]
     fn query_crate_is_local(&self) -> bool {
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 693384602a7..585fcf795b7 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -756,16 +756,17 @@ fn vtable_trait_first_method_offset<'tcx>(
 }
 
 /// Find slot offset for trait vptr within vtable entries of another trait
-/// FIXME: This function is not yet used. Remove `#[allow(dead_code)]` when it's used in upcoming pr.
-#[allow(dead_code)]
-fn vtable_trait_vptr_slot_offset<'tcx>(
+pub fn vtable_trait_upcasting_coercion_new_vptr_slot(
     tcx: TyCtxt<'tcx>,
     key: (
-        ty::PolyTraitRef<'tcx>, // trait_to_be_found
-        ty::PolyTraitRef<'tcx>, // trait_owning_vtable
+        ty::PolyTraitRef<'tcx>, // trait owning vtable
+        ty::PolyTraitRef<'tcx>, // super trait ref
     ),
 ) -> Option<usize> {
-    let (trait_to_be_found, trait_owning_vtable) = key;
+    let (trait_owning_vtable, super_trait_ref) = key;
+    let super_trait_did = super_trait_ref.def_id();
+    // FIXME: take substsref part into account here after upcasting coercion allows the same def_id occur
+    // multiple times.
 
     let vtable_segment_callback = {
         let mut vptr_offset = 0;
@@ -776,7 +777,7 @@ fn vtable_trait_vptr_slot_offset<'tcx>(
                 }
                 VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => {
                     vptr_offset += util::count_own_vtable_entries(tcx, trait_ref);
-                    if trait_ref == trait_to_be_found {
+                    if trait_ref.def_id() == super_trait_did {
                         if emit_vptr {
                             return ControlFlow::Break(Some(vptr_offset));
                         } else {
@@ -810,6 +811,7 @@ pub fn provide(providers: &mut ty::query::Providers) {
         specializes: specialize::specializes,
         codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
         vtable_entries,
+        vtable_trait_upcasting_coercion_new_vptr_slot,
         subst_and_check_impossible_predicates,
         mir_abstract_const: |tcx, def_id| {
             let def_id = def_id.expect_local();