rustc_const_eval/interpret/
traits.rs1use rustc_abi::{Align, FieldIdx, Size};
2use rustc_middle::mir::interpret::{InterpResult, Pointer};
3use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt, Ty, TyCtxt, VtblEntry};
4use tracing::trace;
5
6use super::util::ensure_monomorphic_enough;
7use super::{
8 InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable, interp_ok, throw_ub,
9};
10
11impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
12 pub fn get_vtable_ptr(
20 &self,
21 ty: Ty<'tcx>,
22 dyn_ty: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
23 ) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
24 trace!("get_vtable(ty={ty:?}, dyn_ty={dyn_ty:?})");
25
26 let (ty, dyn_ty) = self.tcx.erase_regions((ty, dyn_ty));
27
28 ensure_monomorphic_enough(*self.tcx, ty)?;
30 ensure_monomorphic_enough(*self.tcx, dyn_ty)?;
31
32 let salt = M::get_global_alloc_salt(self, None);
33 let vtable_symbolic_allocation = self.tcx.reserve_and_set_vtable_alloc(ty, dyn_ty, salt);
34 let vtable_ptr = self.global_root_pointer(Pointer::from(vtable_symbolic_allocation))?;
35 interp_ok(vtable_ptr.into())
36 }
37
38 pub fn get_vtable_size_and_align(
39 &self,
40 vtable: Pointer<Option<M::Provenance>>,
41 expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>,
42 ) -> InterpResult<'tcx, (Size, Align)> {
43 let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?;
44 let layout = self.layout_of(ty)?;
45 assert!(layout.is_sized(), "there are no vtables for unsized types");
46 interp_ok((layout.size, layout.align.abi))
47 }
48
49 pub(super) fn vtable_entries(
50 &self,
51 trait_: Option<ty::PolyExistentialTraitRef<'tcx>>,
52 dyn_ty: Ty<'tcx>,
53 ) -> &'tcx [VtblEntry<'tcx>] {
54 if let Some(trait_) = trait_ {
55 let trait_ref = trait_.with_self_ty(*self.tcx, dyn_ty);
56 let trait_ref =
57 self.tcx.erase_regions(self.tcx.instantiate_bound_regions_with_erased(trait_ref));
58 self.tcx.vtable_entries(trait_ref)
59 } else {
60 TyCtxt::COMMON_VTABLE_ENTRIES
61 }
62 }
63
64 pub(super) fn check_vtable_for_type(
67 &self,
68 vtable_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
69 expected_dyn_type: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
70 ) -> InterpResult<'tcx> {
71 let mut sorted_vtable: Vec<_> = vtable_dyn_type.without_auto_traits().collect();
77 let mut sorted_expected: Vec<_> = expected_dyn_type.without_auto_traits().collect();
78 sorted_vtable.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder()));
80 sorted_vtable.dedup();
81 sorted_expected.sort_by(|a, b| a.skip_binder().stable_cmp(*self.tcx, &b.skip_binder()));
82 sorted_expected.dedup();
83
84 if sorted_vtable.len() != sorted_expected.len() {
85 throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type });
86 }
87
88 for (a_pred, b_pred) in std::iter::zip(sorted_vtable, sorted_expected) {
93 let a_pred = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, a_pred);
94 let b_pred = self.tcx.normalize_erasing_late_bound_regions(self.typing_env, b_pred);
95
96 if a_pred != b_pred {
97 throw_ub!(InvalidVTableTrait { vtable_dyn_type, expected_dyn_type });
98 }
99 }
100
101 interp_ok(())
102 }
103
104 pub(super) fn unpack_dyn_trait(
106 &self,
107 mplace: &MPlaceTy<'tcx, M::Provenance>,
108 expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
109 ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
110 assert!(
111 matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)),
112 "`unpack_dyn_trait` only makes sense on `dyn*` types"
113 );
114 let vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
115 let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?;
116 let layout = self.layout_of(ty)?;
119 let mplace = mplace.offset_with_meta(
120 Size::ZERO,
121 OffsetMode::Wrapping,
122 MemPlaceMeta::None,
123 layout,
124 self,
125 )?;
126 interp_ok(mplace)
127 }
128
129 pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>(
131 &self,
132 val: &P,
133 expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
134 ) -> InterpResult<'tcx, P> {
135 assert!(
136 matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
137 "`unpack_dyn_star` only makes sense on `dyn*` types"
138 );
139 let data = self.project_field(val, FieldIdx::ZERO)?;
140 let vtable = self.project_field(val, FieldIdx::ONE)?;
141 let vtable = self.read_pointer(&vtable.to_op(self)?)?;
142 let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?;
143 let layout = self.layout_of(ty)?;
145 let data = data.transmute(layout, self)?;
146 interp_ok(data)
147 }
148}