rustc_const_eval/interpret/
traits.rs

1use 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    /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
13    /// objects.
14    ///
15    /// The `dyn_ty` encodes the erased self type. Hence, if we are making an object
16    /// `Foo<dyn Trait<Assoc = A> + Send>` from a value of type `Foo<T>`, then `dyn_ty`
17    /// would be `Trait<Assoc = A> + Send`. If this list doesn't have a principal trait ref,
18    /// we only need the basic vtable prefix (drop, size, align).
19    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        // All vtables must be monomorphic, bail out otherwise.
29        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    /// Check that the given vtable trait is valid for a pointer/reference/place with the given
65    /// expected trait type.
66    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        // We check validity by comparing the lists of predicates for equality. We *could* instead
72        // check that the dynamic type to which the vtable belongs satisfies all the expected
73        // predicates, but that would likely be a lot slower and seems unnecessarily permissive.
74
75        // FIXME: we are skipping auto traits for now, but might revisit this in the future.
76        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        // `skip_binder` here is okay because `stable_cmp` doesn't look at binders
79        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        // This checks whether there is a subtyping relation between the predicates in either direction.
89        // For example:
90        // - casting between `dyn for<'a> Trait<fn(&'a u8)>` and `dyn Trait<fn(&'static u8)>` is OK
91        // - casting between `dyn Trait<for<'a> fn(&'a u8)>` and either of the above is UB
92        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    /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
105    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        // This is a kind of transmute, from a place with unsized type and metadata to
117        // a place with sized type and no metadata.
118        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    /// Turn a `dyn* Trait` type into an value with the actual dynamic type.
130    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        // `data` is already the right thing but has the wrong type. So we transmute it.
144        let layout = self.layout_of(ty)?;
145        let data = data.transmute(layout, self)?;
146        interp_ok(data)
147    }
148}