mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
interpret: dyn trait metadata check: equate traits in a proper way
This commit is contained in:
parent
d041b7cf30
commit
3757136d8e
@ -884,9 +884,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
|
throw_ub!(InvalidVTablePointer(Pointer::new(alloc_id, offset)))
|
||||||
};
|
};
|
||||||
if let Some(expected_trait) = expected_trait {
|
if let Some(expected_trait) = expected_trait {
|
||||||
if vtable_trait != expected_trait.principal() {
|
self.check_vtable_for_type(vtable_trait, expected_trait)?;
|
||||||
throw_ub!(InvalidVTableTrait { expected_trait, vtable_trait });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
|
use rustc_infer::traits::ObligationCause;
|
||||||
use rustc_middle::mir::interpret::{InterpResult, Pointer};
|
use rustc_middle::mir::interpret::{InterpResult, Pointer};
|
||||||
use rustc_middle::ty::layout::LayoutOf;
|
use rustc_middle::ty::layout::LayoutOf;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
use rustc_target::abi::{Align, Size};
|
use rustc_target::abi::{Align, Size};
|
||||||
|
use rustc_trait_selection::traits::ObligationCtxt;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use super::util::ensure_monomorphic_enough;
|
use super::util::ensure_monomorphic_enough;
|
||||||
use super::{InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable};
|
use super::{throw_ub, InterpCx, MPlaceTy, Machine, MemPlaceMeta, OffsetMode, Projectable};
|
||||||
|
|
||||||
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
/// Creates a dynamic vtable for the given type and vtable origin. This is used only for
|
||||||
@ -44,6 +47,37 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||||||
Ok((layout.size, layout.align.abi))
|
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.
|
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
||||||
pub(super) fn unpack_dyn_trait(
|
pub(super) fn unpack_dyn_trait(
|
||||||
&self,
|
&self,
|
||||||
|
@ -0,0 +1,40 @@
|
|||||||
|
#![feature(ptr_metadata)]
|
||||||
|
// This test is the result of minimizing the `emplacable` crate to reproduce
|
||||||
|
// <https://github.com/rust-lang/miri/issues/3541>.
|
||||||
|
|
||||||
|
use std::{ops::FnMut, ptr::Pointee};
|
||||||
|
|
||||||
|
pub type EmplacerFn<'a, T> = dyn for<'b> FnMut(<T as Pointee>::Metadata) + 'a;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Emplacer<'a, T>(EmplacerFn<'a, T>)
|
||||||
|
where
|
||||||
|
T: ?Sized;
|
||||||
|
|
||||||
|
impl<'a, T> Emplacer<'a, T>
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
{
|
||||||
|
pub unsafe fn from_fn<'b>(emplacer_fn: &'b mut EmplacerFn<'a, T>) -> &'b mut Self {
|
||||||
|
// This used to trigger:
|
||||||
|
// constructing invalid value: wrong trait in wide pointer vtable: expected
|
||||||
|
// `std::ops::FnMut(<[std::boxed::Box<i32>] as std::ptr::Pointee>::Metadata)`, but encountered
|
||||||
|
// `std::ops::FnMut<(usize,)>`.
|
||||||
|
unsafe { &mut *((emplacer_fn as *mut EmplacerFn<'a, T>) as *mut Self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn box_new_with<T>()
|
||||||
|
where
|
||||||
|
T: ?Sized,
|
||||||
|
{
|
||||||
|
let emplacer_closure = &mut |_meta| {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { Emplacer::<T>::from_fn(emplacer_closure) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
box_new_with::<[Box<i32>]>();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user