|
| 1 | +use rustc_infer::infer::TyCtxtInferExt; |
| 2 | +use rustc_infer::traits::ObligationCause; |
1 | 3 | use rustc_middle::mir::interpret::{InterpResult, Pointer};
|
2 | 4 | use rustc_middle::ty::layout::LayoutOf;
|
3 |
| -use rustc_middle::ty::{self, Ty, TyCtxt}; |
| 5 | +use rustc_middle::ty::{self, Ty}; |
4 | 6 | use rustc_target::abi::{Align, Size};
|
| 7 | +use rustc_trait_selection::traits::ObligationCtxt; |
5 | 8 | use tracing::trace;
|
6 | 9 |
|
7 | 10 | use super::util::ensure_monomorphic_enough;
|
8 |
| -use super::{InterpCx, Machine}; |
| 11 | +use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; |
9 | 12 |
|
10 | 13 | impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
11 | 14 | /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
@@ -33,28 +36,90 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
33 | 36 | Ok(vtable_ptr.into())
|
34 | 37 | }
|
35 | 38 |
|
36 |
| - /// Returns a high-level representation of the entries of the given vtable. |
37 |
| - pub fn get_vtable_entries( |
38 |
| - &self, |
39 |
| - vtable: Pointer<Option<M::Provenance>>, |
40 |
| - ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { |
41 |
| - let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; |
42 |
| - Ok(if let Some(poly_trait_ref) = poly_trait_ref { |
43 |
| - let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); |
44 |
| - let trait_ref = self.tcx.erase_regions(trait_ref); |
45 |
| - self.tcx.vtable_entries(trait_ref) |
46 |
| - } else { |
47 |
| - TyCtxt::COMMON_VTABLE_ENTRIES |
48 |
| - }) |
49 |
| - } |
50 |
| - |
51 | 39 | pub fn get_vtable_size_and_align(
|
52 | 40 | &self,
|
53 | 41 | vtable: Pointer<Option<M::Provenance>>,
|
| 42 | + expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>, |
54 | 43 | ) -> InterpResult<'tcx, (Size, Align)> {
|
55 |
| - let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; |
| 44 | + let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; |
56 | 45 | let layout = self.layout_of(ty)?;
|
57 | 46 | assert!(layout.is_sized(), "there are no vtables for unsized types");
|
58 | 47 | Ok((layout.size, layout.align.abi))
|
59 | 48 | }
|
| 49 | + |
| 50 | + /// Check that the given vtable trait is valid for a pointer/reference/place with the given |
| 51 | + /// expected trait type. |
| 52 | + pub(super) fn check_vtable_for_type( |
| 53 | + &self, |
| 54 | + vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>, |
| 55 | + expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
| 56 | + ) -> InterpResult<'tcx> { |
| 57 | + // Fast path: if they are equal, it's all fine. |
| 58 | + if expected_trait.principal() == vtable_trait { |
| 59 | + return Ok(()); |
| 60 | + } |
| 61 | + if let (Some(expected_trait), Some(vtable_trait)) = |
| 62 | + (expected_trait.principal(), vtable_trait) |
| 63 | + { |
| 64 | + // Slow path: spin up an inference context to check if these traits are sufficiently equal. |
| 65 | + let infcx = self.tcx.infer_ctxt().build(); |
| 66 | + let ocx = ObligationCtxt::new(&infcx); |
| 67 | + let cause = ObligationCause::dummy_with_span(self.cur_span()); |
| 68 | + // equate the two trait refs after normalization |
| 69 | + let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait); |
| 70 | + let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait); |
| 71 | + if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() { |
| 72 | + if ocx.select_all_or_error().is_empty() { |
| 73 | + // All good. |
| 74 | + return Ok(()); |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); |
| 79 | + } |
| 80 | + |
| 81 | + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. |
| 82 | + pub(super) fn unpack_dyn_trait( |
| 83 | + &self, |
| 84 | + mplace: &MPlaceTy<'tcx, M::Provenance>, |
| 85 | + expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
| 86 | + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { |
| 87 | + assert!( |
| 88 | + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), |
| 89 | + "`unpack_dyn_trait` only makes sense on `dyn*` types" |
| 90 | + ); |
| 91 | + let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; |
| 92 | + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; |
| 93 | + // This is a kind of transmute, from a place with unsized type and metadata to |
| 94 | + // a place with sized type and no metadata. |
| 95 | + let layout = self.layout_of(ty)?; |
| 96 | + let mplace = mplace.offset_with_meta( |
| 97 | + Size::ZERO, |
| 98 | + OffsetMode::Wrapping, |
| 99 | + MemPlaceMeta::None, |
| 100 | + layout, |
| 101 | + self, |
| 102 | + )?; |
| 103 | + Ok(mplace) |
| 104 | + } |
| 105 | + |
| 106 | + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. |
| 107 | + pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>( |
| 108 | + &self, |
| 109 | + val: &P, |
| 110 | + expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, |
| 111 | + ) -> InterpResult<'tcx, P> { |
| 112 | + assert!( |
| 113 | + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), |
| 114 | + "`unpack_dyn_star` only makes sense on `dyn*` types" |
| 115 | + ); |
| 116 | + let data = self.project_field(val, 0)?; |
| 117 | + let vtable = self.project_field(val, 1)?; |
| 118 | + let vtable = self.read_pointer(&vtable.to_op(self)?)?; |
| 119 | + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; |
| 120 | + // `data` is already the right thing but has the wrong type. So we transmute it. |
| 121 | + let layout = self.layout_of(ty)?; |
| 122 | + let data = data.transmute(layout, self)?; |
| 123 | + Ok(data) |
| 124 | + } |
60 | 125 | }
|
0 commit comments