diff options
| author | Guillaume Gomez <guillaume1.gomez@gmail.com> | 2024-06-12 15:44:59 +0200 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-06-12 15:44:59 +0200 | 
| commit | 51a58c59f3ff8ec39da77c1c3d0b780568b9c202 (patch) | |
| tree | cda215bf270a33269c8010ce2a735b1b818d9294 /compiler/rustc_const_eval/src/interpret/traits.rs | |
| parent | c21de3c91eeb9fbb70ef616bf30ed31962c11e90 (diff) | |
| parent | 3757136d8e0d8ddca294453e5a5ce70cfa3417e9 (diff) | |
| download | rust-51a58c59f3ff8ec39da77c1c3d0b780568b9c202.tar.gz rust-51a58c59f3ff8ec39da77c1c3d0b780568b9c202.zip | |
Rollup merge of #126232 - RalfJung:dyn-trait-equality, r=oli-obk
interpret: dyn trait metadata check: equate traits in a proper way Hopefully fixes https://github.com/rust-lang/miri/issues/3541... unfortunately we don't have a testcase. The first commit is just a refactor without functional change. r? `@oli-obk`
Diffstat (limited to 'compiler/rustc_const_eval/src/interpret/traits.rs')
| -rw-r--r-- | compiler/rustc_const_eval/src/interpret/traits.rs | 101 | 
1 files changed, 83 insertions, 18 deletions
| diff --git a/compiler/rustc_const_eval/src/interpret/traits.rs b/compiler/rustc_const_eval/src/interpret/traits.rs index 244a6ba48a4..bd2c6519421 100644 --- a/compiler/rustc_const_eval/src/interpret/traits.rs +++ b/compiler/rustc_const_eval/src/interpret/traits.rs @@ -1,11 +1,14 @@ +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ObligationCause; use rustc_middle::mir::interpret::{InterpResult, Pointer}; use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty}; use rustc_target::abi::{Align, Size}; +use rustc_trait_selection::traits::ObligationCtxt; use tracing::trace; use super::util::ensure_monomorphic_enough; -use super::{InterpCx, Machine}; +use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable}; impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// 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> { Ok(vtable_ptr.into()) } - /// Returns a high-level representation of the entries of the given vtable. - pub fn get_vtable_entries( - &self, - vtable: Pointer<Option<M::Provenance>>, - ) -> InterpResult<'tcx, &'tcx [ty::VtblEntry<'tcx>]> { - let (ty, poly_trait_ref) = self.get_ptr_vtable(vtable)?; - Ok(if let Some(poly_trait_ref) = poly_trait_ref { - let trait_ref = poly_trait_ref.with_self_ty(*self.tcx, ty); - let trait_ref = self.tcx.erase_regions(trait_ref); - self.tcx.vtable_entries(trait_ref) - } else { - TyCtxt::COMMON_VTABLE_ENTRIES - }) - } - pub fn get_vtable_size_and_align( &self, vtable: Pointer<Option<M::Provenance>>, + expected_trait: Option<&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>>, ) -> InterpResult<'tcx, (Size, Align)> { - let (ty, _trait_ref) = self.get_ptr_vtable(vtable)?; + let ty = self.get_ptr_vtable_ty(vtable, expected_trait)?; let layout = self.layout_of(ty)?; assert!(layout.is_sized(), "there are no vtables for unsized types"); Ok((layout.size, layout.align.abi)) } + + /// Check that the given vtable trait is valid for a pointer/reference/place with the given + /// expected trait type. + pub(super) fn check_vtable_for_type( + &self, + vtable_trait: Option<ty::PolyExistentialTraitRef<'tcx>>, + expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + ) -> InterpResult<'tcx> { + // Fast path: if they are equal, it's all fine. + if expected_trait.principal() == vtable_trait { + return Ok(()); + } + if let (Some(expected_trait), Some(vtable_trait)) = + (expected_trait.principal(), vtable_trait) + { + // Slow path: spin up an inference context to check if these traits are sufficiently equal. + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + let cause = ObligationCause::dummy_with_span(self.cur_span()); + // equate the two trait refs after normalization + let expected_trait = ocx.normalize(&cause, self.param_env, expected_trait); + let vtable_trait = ocx.normalize(&cause, self.param_env, vtable_trait); + if ocx.eq(&cause, self.param_env, expected_trait, vtable_trait).is_ok() { + if ocx.select_all_or_error().is_empty() { + // All good. + return Ok(()); + } + } + } + throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait }); + } + + /// Turn a place with a `dyn Trait` type into a place with the actual dynamic type. + pub(super) fn unpack_dyn_trait( + &self, + mplace: &MPlaceTy<'tcx, M::Provenance>, + expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { + assert!( + matches!(mplace.layout.ty.kind(), ty::Dynamic(_, _, ty::Dyn)), + "`unpack_dyn_trait` only makes sense on `dyn*` types" + ); + let vtable = mplace.meta().unwrap_meta().to_pointer(self)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // This is a kind of transmute, from a place with unsized type and metadata to + // a place with sized type and no metadata. + let layout = self.layout_of(ty)?; + let mplace = mplace.offset_with_meta( + Size::ZERO, + OffsetMode::Wrapping, + MemPlaceMeta::None, + layout, + self, + )?; + Ok(mplace) + } + + /// Turn a `dyn* Trait` type into an value with the actual dynamic type. + pub(super) fn unpack_dyn_star<P: Projectable<'tcx, M::Provenance>>( + &self, + val: &P, + expected_trait: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, + ) -> InterpResult<'tcx, P> { + assert!( + matches!(val.layout().ty.kind(), ty::Dynamic(_, _, ty::DynStar)), + "`unpack_dyn_star` only makes sense on `dyn*` types" + ); + let data = self.project_field(val, 0)?; + let vtable = self.project_field(val, 1)?; + let vtable = self.read_pointer(&vtable.to_op(self)?)?; + let ty = self.get_ptr_vtable_ty(vtable, Some(expected_trait))?; + // `data` is already the right thing but has the wrong type. So we transmute it. + let layout = self.layout_of(ty)?; + let data = data.transmute(layout, self)?; + Ok(data) + } } | 
