basic dyn* support for Miri
This commit is contained in:
parent
dc89a803d6
commit
b2f58146b9
@ -312,6 +312,8 @@ fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance>
|
||||
}
|
||||
}
|
||||
|
||||
/// `src` is a *pointer to* a `source_ty`, and in `dest` we should store a pointer to th same
|
||||
/// data at type `cast_ty`.
|
||||
fn unsize_into_ptr(
|
||||
&mut self,
|
||||
src: &OpTy<'tcx, M::Provenance>,
|
||||
@ -335,7 +337,7 @@ fn unsize_into_ptr(
|
||||
);
|
||||
self.write_immediate(val, dest)
|
||||
}
|
||||
(ty::Dynamic(data_a, ..), ty::Dynamic(data_b, ..)) => {
|
||||
(ty::Dynamic(data_a, _, ty::Dyn), ty::Dynamic(data_b, _, ty::Dyn)) => {
|
||||
let val = self.read_immediate(src)?;
|
||||
if data_a.principal() == data_b.principal() {
|
||||
// A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables.
|
||||
@ -359,7 +361,12 @@ fn unsize_into_ptr(
|
||||
}
|
||||
|
||||
_ => {
|
||||
span_bug!(self.cur_span(), "invalid unsizing {:?} -> {:?}", src.layout.ty, cast_ty)
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"invalid pointer unsizing {:?} -> {:?}",
|
||||
src.layout.ty,
|
||||
cast_ty
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,7 +632,7 @@ pub(super) fn size_and_align_of(
|
||||
}
|
||||
Ok(Some((size, align)))
|
||||
}
|
||||
ty::Dynamic(..) => {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
let vtable = metadata.unwrap_meta().to_pointer(self)?;
|
||||
// Read size and align from vtable (already checks size).
|
||||
Ok(Some(self.get_vtable_size_and_align(vtable)?))
|
||||
|
@ -242,7 +242,7 @@ fn visit_value(&mut self, mplace: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
|
||||
let mplace = self.ecx.ref_to_mplace(&value)?;
|
||||
assert_eq!(mplace.layout.ty, referenced_ty);
|
||||
// Handle trait object vtables.
|
||||
if let ty::Dynamic(..) =
|
||||
if let ty::Dynamic(_, _, ty::Dyn) =
|
||||
tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind()
|
||||
{
|
||||
let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?;
|
||||
|
@ -255,7 +255,22 @@ pub fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn offset_with_meta(
|
||||
/// Replace the layout of this operand. There's basically no sanity check that this makes sense,
|
||||
/// you better know what you are doing! If this is an immediate, applying the wrong layout can
|
||||
/// not just lead to invalid data, it can actually *shift the data around* since the offsets of
|
||||
/// a ScalarPair are entirely determined by the layout, not the data.
|
||||
pub fn transmute(&self, layout: TyAndLayout<'tcx>) -> Self {
|
||||
assert_eq!(
|
||||
self.layout.size, layout.size,
|
||||
"transmuting with a size change, that doesn't seem right"
|
||||
);
|
||||
OpTy { layout, ..*self }
|
||||
}
|
||||
|
||||
/// Offset the operand in memory (if possible) and change its metadata.
|
||||
///
|
||||
/// This can go wrong very easily if you give the wrong layout for the new place!
|
||||
pub(super) fn offset_with_meta(
|
||||
&self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
@ -276,6 +291,9 @@ pub fn offset_with_meta(
|
||||
}
|
||||
}
|
||||
|
||||
/// Offset the operand in memory (if possible).
|
||||
///
|
||||
/// This can go wrong very easily if you give the wrong layout for the new place!
|
||||
pub fn offset(
|
||||
&self,
|
||||
offset: Size,
|
||||
|
@ -26,6 +26,7 @@ pub enum MemPlaceMeta<Prov: Provenance = AllocId> {
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> MemPlaceMeta<Prov> {
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn unwrap_meta(self) -> Scalar<Prov> {
|
||||
match self {
|
||||
Self::Meta(s) => s,
|
||||
@ -147,12 +148,16 @@ pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn offset_with_meta<'tcx>(
|
||||
pub(super) fn offset_with_meta<'tcx>(
|
||||
self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
cx: &impl HasDataLayout,
|
||||
) -> InterpResult<'tcx, Self> {
|
||||
debug_assert!(
|
||||
!meta.has_meta() || self.meta.has_meta(),
|
||||
"cannot use `offset_with_meta` to add metadata to a place"
|
||||
);
|
||||
Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta })
|
||||
}
|
||||
}
|
||||
@ -182,8 +187,11 @@ pub fn fake_alloc_zst(layout: TyAndLayout<'tcx>) -> Self {
|
||||
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align }
|
||||
}
|
||||
|
||||
/// Offset the place in memory and change its metadata.
|
||||
///
|
||||
/// This can go wrong very easily if you give the wrong layout for the new place!
|
||||
#[inline]
|
||||
pub fn offset_with_meta(
|
||||
pub(crate) fn offset_with_meta(
|
||||
&self,
|
||||
offset: Size,
|
||||
meta: MemPlaceMeta<Prov>,
|
||||
@ -197,6 +205,9 @@ pub fn offset_with_meta(
|
||||
})
|
||||
}
|
||||
|
||||
/// Offset the place in memory.
|
||||
///
|
||||
/// This can go wrong very easily if you give the wrong layout for the new place!
|
||||
pub fn offset(
|
||||
&self,
|
||||
offset: Size,
|
||||
@ -241,14 +252,6 @@ pub(crate) fn len(&self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn vtable(&self) -> Scalar<Prov> {
|
||||
match self.layout.ty.kind() {
|
||||
ty::Dynamic(..) => self.mplace.meta.unwrap_meta(),
|
||||
_ => bug!("vtable not supported on type {:?}", self.layout.ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These are defined here because they produce a place.
|
||||
@ -266,7 +269,12 @@ pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>
|
||||
#[inline(always)]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
|
||||
self.as_mplace_or_imm().left().unwrap()
|
||||
self.as_mplace_or_imm().left().unwrap_or_else(|| {
|
||||
bug!(
|
||||
"OpTy of type {} was immediate when it was expected to be an MPlace",
|
||||
self.layout.ty
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,7 +291,12 @@ pub fn as_mplace_or_local(&self) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Lo
|
||||
#[inline(always)]
|
||||
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
|
||||
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> {
|
||||
self.as_mplace_or_local().left().unwrap()
|
||||
self.as_mplace_or_local().left().unwrap_or_else(|| {
|
||||
bug!(
|
||||
"PlaceTy of type {} was a local when it was expected to be an MPlace",
|
||||
self.layout.ty
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -807,11 +820,16 @@ pub fn raw_const_to_mplace(
|
||||
}
|
||||
|
||||
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
|
||||
/// Aso returns the vtable.
|
||||
pub(super) fn unpack_dyn_trait(
|
||||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type
|
||||
) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer<Option<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(vtable)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
|
||||
@ -820,7 +838,26 @@ pub(super) fn unpack_dyn_trait(
|
||||
layout,
|
||||
align: layout.align.abi,
|
||||
};
|
||||
Ok(mplace)
|
||||
Ok((mplace, vtable))
|
||||
}
|
||||
|
||||
/// Turn an operand with a `dyn* Trait` type into an operand with the actual dynamic type.
|
||||
/// Aso returns the vtable.
|
||||
pub(super) fn unpack_dyn_star(
|
||||
&self,
|
||||
op: &OpTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, (OpTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> {
|
||||
assert!(
|
||||
matches!(op.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
|
||||
"`unpack_dyn_star` only makes sense on `dyn*` types"
|
||||
);
|
||||
let data = self.operand_field(&op, 0)?;
|
||||
let vtable = self.operand_field(&op, 1)?;
|
||||
let vtable = self.read_pointer(&vtable)?;
|
||||
let (ty, _) = self.get_ptr_vtable(vtable)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
let data = data.transmute(layout);
|
||||
Ok((data, vtable))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,10 +538,9 @@ pub(crate) fn eval_fn_call(
|
||||
// pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively
|
||||
// unwrap those newtypes until we are there.
|
||||
let mut receiver = args[0].clone();
|
||||
let receiver_place = loop {
|
||||
let receiver = loop {
|
||||
match receiver.layout.ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?,
|
||||
ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values
|
||||
ty::Dynamic(..) | ty::Ref(..) | ty::RawPtr(..) => break receiver,
|
||||
_ => {
|
||||
// Not there yet, search for the only non-ZST field.
|
||||
let mut non_zst_field = None;
|
||||
@ -567,39 +566,83 @@ pub(crate) fn eval_fn_call(
|
||||
}
|
||||
}
|
||||
};
|
||||
// Obtain the underlying trait we are working on.
|
||||
let receiver_tail = self
|
||||
.tcx
|
||||
.struct_tail_erasing_lifetimes(receiver_place.layout.ty, self.param_env);
|
||||
let ty::Dynamic(data, ..) = receiver_tail.kind() else {
|
||||
span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail)
|
||||
};
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub_format!(
|
||||
"`dyn` call on a pointer whose vtable does not match its type"
|
||||
);
|
||||
}
|
||||
// break self.deref_operand(&receiver)?.into();
|
||||
|
||||
// Obtain the underlying trait we are working on, and the adjusted receiver argument.
|
||||
let recv_ty = receiver.layout.ty;
|
||||
let (vptr, dyn_ty, adjusted_receiver) = match recv_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..)
|
||||
if matches!(
|
||||
recv_ty.builtin_deref(true).unwrap().ty.kind(),
|
||||
ty::Dynamic(_, _, ty::DynStar)
|
||||
) =>
|
||||
{
|
||||
let receiver = self.deref_operand(&receiver)?;
|
||||
let ty::Dynamic(data, ..) = receiver.layout.ty.kind() else { bug!() };
|
||||
let (recv, vptr) = self.unpack_dyn_star(&receiver.into())?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub_format!(
|
||||
"`dyn*` call on a pointer whose vtable does not match its type"
|
||||
);
|
||||
}
|
||||
let recv = recv.assert_mem_place(); // we passed an MPlaceTy to `unpack_dyn_star` so we definitely still have one
|
||||
|
||||
(vptr, dyn_ty, recv.ptr)
|
||||
}
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// Not clear how to handle this, so far we assume the receiver is always a pointer.
|
||||
span_bug!(
|
||||
self.cur_span(),
|
||||
"by-value calls on a `dyn*`... are those a thing?"
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
let receiver_place = match recv_ty.kind() {
|
||||
ty::Ref(..) | ty::RawPtr(..) => self.deref_operand(&receiver)?,
|
||||
ty::Dynamic(_, _, ty::Dyn) => receiver.assert_mem_place(), // unsized (`dyn`) cannot be immediate
|
||||
_ => bug!(),
|
||||
};
|
||||
// Doesn't have to be a `dyn Trait`, but the unsized tail must be `dyn Trait`.
|
||||
// (For that reason we also cannot use `unpack_dyn_trait`.)
|
||||
let receiver_tail = self.tcx.struct_tail_erasing_lifetimes(
|
||||
receiver_place.layout.ty,
|
||||
self.param_env,
|
||||
);
|
||||
let ty::Dynamic(data, _, ty::Dyn) = receiver_tail.kind() else {
|
||||
span_bug!(self.cur_span(), "dynamic call on non-`dyn` type {}", receiver_tail)
|
||||
};
|
||||
assert!(receiver_place.layout.is_unsized());
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
|
||||
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?;
|
||||
if dyn_trait != data.principal() {
|
||||
throw_ub_format!(
|
||||
"`dyn` call on a pointer whose vtable does not match its type"
|
||||
);
|
||||
}
|
||||
|
||||
// It might be surprising that we use a pointer as the receiver even if this
|
||||
// is a by-val case; this works because by-val passing of an unsized `dyn
|
||||
// Trait` to a function is actually desugared to a pointer.
|
||||
(vptr, dyn_ty, receiver_place.ptr)
|
||||
}
|
||||
};
|
||||
|
||||
// Now determine the actual method to call. We can do that in two different ways and
|
||||
// compare them to ensure everything fits.
|
||||
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else {
|
||||
throw_ub_format!("`dyn` call trying to call something that is not a method")
|
||||
};
|
||||
trace!("Virtual call dispatches to {fn_inst:#?}");
|
||||
if cfg!(debug_assertions) {
|
||||
let tcx = *self.tcx;
|
||||
|
||||
let trait_def_id = tcx.trait_of_item(def_id).unwrap();
|
||||
let virtual_trait_ref =
|
||||
ty::TraitRef::from_method(tcx, trait_def_id, instance.substs);
|
||||
assert_eq!(
|
||||
receiver_tail,
|
||||
virtual_trait_ref.self_ty(),
|
||||
"mismatch in underlying dyn trait computation within Miri and MIR building",
|
||||
);
|
||||
let existential_trait_ref =
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
|
||||
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
|
||||
@ -614,17 +657,12 @@ pub(crate) fn eval_fn_call(
|
||||
assert_eq!(fn_inst, concrete_method);
|
||||
}
|
||||
|
||||
// `*mut receiver_place.layout.ty` is almost the layout that we
|
||||
// want for args[0]: We have to project to field 0 because we want
|
||||
// a thin pointer.
|
||||
assert!(receiver_place.layout.is_unsized());
|
||||
let receiver_ptr_ty = self.tcx.mk_mut_ptr(receiver_place.layout.ty);
|
||||
let this_receiver_ptr = self.layout_of(receiver_ptr_ty)?.field(self, 0);
|
||||
// Adjust receiver argument.
|
||||
args[0] = OpTy::from(ImmTy::from_immediate(
|
||||
Scalar::from_maybe_pointer(receiver_place.ptr, self).into(),
|
||||
this_receiver_ptr,
|
||||
));
|
||||
// Adjust receiver argument. Layout can be any (thin) ptr.
|
||||
args[0] = ImmTy::from_immediate(
|
||||
Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
|
||||
self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?,
|
||||
)
|
||||
.into();
|
||||
trace!("Patched receiver operand to {:#?}", args[0]);
|
||||
// recurse with concrete function
|
||||
self.eval_fn_call(
|
||||
@ -653,15 +691,24 @@ fn drop_in_place(
|
||||
// implementation fail -- a problem shared by rustc.
|
||||
let place = self.force_allocation(place)?;
|
||||
|
||||
let (instance, place) = match place.layout.ty.kind() {
|
||||
ty::Dynamic(..) => {
|
||||
let place = match place.layout.ty.kind() {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
// Dropping a trait object. Need to find actual drop fn.
|
||||
let place = self.unpack_dyn_trait(&place)?;
|
||||
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
(instance, place)
|
||||
self.unpack_dyn_trait(&place)?.0
|
||||
}
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// Dropping a `dyn*`. Need to find actual drop fn.
|
||||
self.unpack_dyn_star(&place.into())?.0.assert_mem_place()
|
||||
}
|
||||
_ => {
|
||||
debug_assert_eq!(
|
||||
instance,
|
||||
ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty)
|
||||
);
|
||||
place
|
||||
}
|
||||
_ => (instance, place),
|
||||
};
|
||||
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
|
||||
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
|
||||
|
||||
let arg = ImmTy::from_immediate(
|
||||
|
@ -23,18 +23,18 @@
|
||||
// for the validation errors
|
||||
use super::UndefinedBehaviorInfo::*;
|
||||
use super::{
|
||||
CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine,
|
||||
MemPlaceMeta, OpTy, Scalar, ValueVisitor,
|
||||
AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
|
||||
Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor,
|
||||
};
|
||||
|
||||
macro_rules! throw_validation_failure {
|
||||
($where:expr, { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )?) => {{
|
||||
($where:expr, { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )?) => {{
|
||||
let mut msg = String::new();
|
||||
msg.push_str("encountered ");
|
||||
write!(&mut msg, $($what_fmt),+).unwrap();
|
||||
write!(&mut msg, $($what_fmt)*).unwrap();
|
||||
$(
|
||||
msg.push_str(", but expected ");
|
||||
write!(&mut msg, $($expected_fmt),+).unwrap();
|
||||
write!(&mut msg, $($expected_fmt)*).unwrap();
|
||||
)?
|
||||
let path = rustc_middle::ty::print::with_no_trimmed_paths!({
|
||||
let where_ = &$where;
|
||||
@ -82,7 +82,7 @@ macro_rules! throw_validation_failure {
|
||||
///
|
||||
macro_rules! try_validation {
|
||||
($e:expr, $where:expr,
|
||||
$( $( $p:pat_param )|+ => { $( $what_fmt:expr ),+ } $( expected { $( $expected_fmt:expr ),+ } )? ),+ $(,)?
|
||||
$( $( $p:pat_param )|+ => { $( $what_fmt:tt )* } $( expected { $( $expected_fmt:tt )* } )? ),+ $(,)?
|
||||
) => {{
|
||||
match $e {
|
||||
Ok(x) => x,
|
||||
@ -93,7 +93,7 @@ macro_rules! try_validation {
|
||||
InterpError::UndefinedBehavior($($p)|+) =>
|
||||
throw_validation_failure!(
|
||||
$where,
|
||||
{ $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )?
|
||||
{ $( $what_fmt )* } $( expected { $( $expected_fmt )* } )?
|
||||
)
|
||||
),+,
|
||||
#[allow(unreachable_patterns)]
|
||||
@ -335,7 +335,7 @@ fn check_wide_ptr_meta(
|
||||
) -> InterpResult<'tcx> {
|
||||
let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
|
||||
match tail.kind() {
|
||||
ty::Dynamic(..) => {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
|
||||
// Make sure it is a genuine vtable pointer.
|
||||
let (_ty, _trait) = try_validation!(
|
||||
@ -399,12 +399,15 @@ fn check_safe_pointer(
|
||||
{
|
||||
"an unaligned {kind} (required {} byte alignment but found {})",
|
||||
required.bytes(),
|
||||
has.bytes()
|
||||
has.bytes(),
|
||||
},
|
||||
DanglingIntPointer(0, _) =>
|
||||
{ "a null {kind}" },
|
||||
DanglingIntPointer(i, _) =>
|
||||
{ "a dangling {kind} (address {i:#x} is unallocated)" },
|
||||
{
|
||||
"a dangling {kind} ({pointer} has no provenance)",
|
||||
pointer = Pointer::<Option<AllocId>>::from_addr_invalid(*i),
|
||||
},
|
||||
PointerOutOfBounds { .. } =>
|
||||
{ "a dangling {kind} (going beyond the bounds of its allocation)" },
|
||||
// This cannot happen during const-eval (because interning already detects
|
||||
|
@ -284,7 +284,7 @@ fn to_op_for_read(
|
||||
&self,
|
||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
|
||||
// We `force_allocation` here so that `from_op` below can work.
|
||||
// No need for `force_allocation` since we are just going to read from this.
|
||||
ecx.place_to_op(self)
|
||||
}
|
||||
|
||||
@ -421,15 +421,25 @@ fn walk_value(&mut self, v: &Self::V) -> InterpResult<'tcx>
|
||||
// Special treatment for special types, where the (static) layout is not sufficient.
|
||||
match *ty.kind() {
|
||||
// If it is a trait object, switch to the real type that was used to create it.
|
||||
ty::Dynamic(..) => {
|
||||
ty::Dynamic(_, _, ty::Dyn) => {
|
||||
// Dyn types. This is unsized, and the actual dynamic type of the data is given by the
|
||||
// vtable stored in the place metadata.
|
||||
// unsized values are never immediate, so we can assert_mem_place
|
||||
let op = v.to_op_for_read(self.ecx())?;
|
||||
let dest = op.assert_mem_place();
|
||||
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?;
|
||||
let inner_mplace = self.ecx().unpack_dyn_trait(&dest)?.0;
|
||||
trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
|
||||
// recurse with the inner type
|
||||
return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into()));
|
||||
},
|
||||
ty::Dynamic(_, _, ty::DynStar) => {
|
||||
// DynStar types. Very different from a dyn type (but strangely part of the
|
||||
// same variant in `TyKind`): These are pairs where the 2nd component is the
|
||||
// vtable, and the first component is the data (which must be ptr-sized).
|
||||
let op = v.to_op_for_proj(self.ecx())?;
|
||||
let data = self.ecx().unpack_dyn_star(&op)?.0;
|
||||
return self.visit_field(&v, 0, &$value_trait::from_op(&data));
|
||||
}
|
||||
// Slices do not need special handling here: they have `Array` field
|
||||
// placement with length 0, so we enter the `Array` case below which
|
||||
// indirectly uses the metadata to determine the actual length.
|
||||
|
@ -1,4 +1,4 @@
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address $HEX is unallocated)
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling reference ($HEX[noalloc] has no provenance)
|
||||
--> $DIR/branchless-select-i128-pointer.rs:LL:CC
|
||||
|
|
||||
LL | / transmute::<_, &str>(
|
||||
@ -6,7 +6,7 @@ LL | |
|
||||
LL | | !mask & transmute::<_, TwoPtrs>("false !")
|
||||
LL | | | mask & transmute::<_, TwoPtrs>("true !"),
|
||||
LL | | )
|
||||
| |_____________^ constructing invalid value: encountered a dangling reference (address $HEX is unallocated)
|
||||
| |_____________^ constructing invalid value: encountered a dangling reference ($HEX[noalloc] has no provenance)
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
@ -3,5 +3,5 @@
|
||||
use std::mem;
|
||||
|
||||
fn main() {
|
||||
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated)
|
||||
let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (address 0x10 is unallocated)
|
||||
error: Undefined Behavior: constructing invalid value: encountered a dangling reference (0x10[noalloc] has no provenance)
|
||||
--> $DIR/dangling_ref1.rs:LL:CC
|
||||
|
|
||||
LL | let _x: &i32 = unsafe { mem::transmute(16usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x10 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x10[noalloc] has no provenance)
|
||||
|
|
||||
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||
|
118
src/tools/miri/tests/pass/dyn-star.rs
Normal file
118
src/tools/miri/tests/pass/dyn-star.rs
Normal file
@ -0,0 +1,118 @@
|
||||
// Dyn* handling leads to some funky reentrancy in Stacked Borrows, for some reason
|
||||
//@compile-flags: -Zmiri-disable-stacked-borrows
|
||||
#![feature(dyn_star)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
fn main() {
|
||||
make_dyn_star();
|
||||
method();
|
||||
box_();
|
||||
dispatch_on_pin_mut();
|
||||
dyn_star_to_dyn();
|
||||
dyn_to_dyn_star();
|
||||
}
|
||||
|
||||
fn dyn_star_to_dyn() {
|
||||
let x: dyn* Debug = &42;
|
||||
let x = Box::new(x) as Box<dyn Debug>;
|
||||
assert_eq!("42", format!("{x:?}"));
|
||||
}
|
||||
|
||||
fn dyn_to_dyn_star() {
|
||||
let x: Box<dyn Debug> = Box::new(42);
|
||||
let x = &x as dyn* Debug;
|
||||
assert_eq!("42", format!("{x:?}"));
|
||||
}
|
||||
|
||||
fn make_dyn_star() {
|
||||
fn make_dyn_star_coercion(i: usize) {
|
||||
let _dyn_i: dyn* Debug = i;
|
||||
}
|
||||
|
||||
fn make_dyn_star_explicit(i: usize) {
|
||||
let _dyn_i: dyn* Debug = i as dyn* Debug;
|
||||
}
|
||||
|
||||
make_dyn_star_coercion(42);
|
||||
make_dyn_star_explicit(42);
|
||||
}
|
||||
|
||||
fn method() {
|
||||
trait Foo {
|
||||
fn get(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Foo for usize {
|
||||
fn get(&self) -> usize {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke_dyn_star(i: dyn* Foo) -> usize {
|
||||
i.get()
|
||||
}
|
||||
|
||||
fn make_and_invoke_dyn_star(i: usize) -> usize {
|
||||
let dyn_i: dyn* Foo = i;
|
||||
invoke_dyn_star(dyn_i)
|
||||
}
|
||||
|
||||
assert_eq!(make_and_invoke_dyn_star(42), 42);
|
||||
}
|
||||
|
||||
fn box_() {
|
||||
fn make_dyn_star() -> dyn* Display {
|
||||
Box::new(42) as dyn* Display
|
||||
}
|
||||
|
||||
let x = make_dyn_star();
|
||||
assert_eq!(format!("{x}"), "42");
|
||||
}
|
||||
|
||||
fn dispatch_on_pin_mut() {
|
||||
use std::future::Future;
|
||||
|
||||
async fn foo(f: dyn* Future<Output = i32>) {
|
||||
println!("dispatch_on_pin_mut: value: {}", f.await);
|
||||
}
|
||||
|
||||
async fn async_main() {
|
||||
foo(Box::pin(async { 1 })).await
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------- //
|
||||
// Implementation Details Below...
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::task::*;
|
||||
|
||||
pub fn noop_waker() -> Waker {
|
||||
let raw = RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE);
|
||||
|
||||
// SAFETY: the contracts for RawWaker and RawWakerVTable are upheld
|
||||
unsafe { Waker::from_raw(raw) }
|
||||
}
|
||||
|
||||
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
|
||||
|
||||
unsafe fn noop_clone(_p: *const ()) -> RawWaker {
|
||||
RawWaker::new(std::ptr::null(), &NOOP_WAKER_VTABLE)
|
||||
}
|
||||
|
||||
unsafe fn noop(_p: *const ()) {}
|
||||
|
||||
let mut fut = async_main();
|
||||
|
||||
// Poll loop, just to test the future...
|
||||
let waker = noop_waker();
|
||||
let ctx = &mut Context::from_waker(&waker);
|
||||
|
||||
loop {
|
||||
match unsafe { Pin::new_unchecked(&mut fut).poll(ctx) } {
|
||||
Poll::Pending => {}
|
||||
Poll::Ready(()) => break,
|
||||
}
|
||||
}
|
||||
}
|
1
src/tools/miri/tests/pass/dyn-star.stdout
Normal file
1
src/tools/miri/tests/pass/dyn-star.stdout
Normal file
@ -0,0 +1 @@
|
||||
dispatch_on_pin_mut: value: 1
|
@ -167,7 +167,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:96:1
|
||||
|
|
||||
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance)
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 4, align: 4) {
|
||||
@ -178,7 +178,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:99:1
|
||||
|
|
||||
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance)
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 4, align: 4) {
|
||||
|
@ -167,7 +167,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:96:1
|
||||
|
|
||||
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance)
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 8) {
|
||||
@ -178,7 +178,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/raw-bytes.rs:99:1
|
||||
|
|
||||
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance)
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: 8, align: 8) {
|
||||
|
@ -85,7 +85,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-ref-ptr.rs:43:1
|
||||
|
|
||||
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (address 0x539 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling reference (0x539[noalloc] has no provenance)
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
@ -96,7 +96,7 @@ error[E0080]: it is undefined behavior to use this value
|
||||
--> $DIR/ub-ref-ptr.rs:46:1
|
||||
|
|
||||
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (address 0x539 is unallocated)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered a dangling box (0x539[noalloc] has no provenance)
|
||||
|
|
||||
= note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
|
||||
= note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {
|
||||
|
Loading…
Reference in New Issue
Block a user