basic dyn* support for Miri

This commit is contained in:
Ralf Jung 2023-02-06 16:00:54 +01:00
parent dc89a803d6
commit b2f58146b9
16 changed files with 325 additions and 84 deletions

View File

@ -312,6 +312,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
} }
/// `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( fn unsize_into_ptr(
&mut self, &mut self,
src: &OpTy<'tcx, M::Provenance>, src: &OpTy<'tcx, M::Provenance>,
@ -335,7 +337,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
); );
self.write_immediate(val, dest) 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)?; let val = self.read_immediate(src)?;
if data_a.principal() == data_b.principal() { if data_a.principal() == data_b.principal() {
// A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables. // A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables.
@ -359,7 +361,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
_ => { _ => {
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
)
} }
} }
} }

View File

@ -632,7 +632,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
Ok(Some((size, align))) Ok(Some((size, align)))
} }
ty::Dynamic(..) => { ty::Dynamic(_, _, ty::Dyn) => {
let vtable = metadata.unwrap_meta().to_pointer(self)?; let vtable = metadata.unwrap_meta().to_pointer(self)?;
// Read size and align from vtable (already checks size). // Read size and align from vtable (already checks size).
Ok(Some(self.get_vtable_size_and_align(vtable)?)) Ok(Some(self.get_vtable_size_and_align(vtable)?))

View File

@ -242,7 +242,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
let mplace = self.ecx.ref_to_mplace(&value)?; let mplace = self.ecx.ref_to_mplace(&value)?;
assert_eq!(mplace.layout.ty, referenced_ty); assert_eq!(mplace.layout.ty, referenced_ty);
// Handle trait object vtables. // 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() tcx.struct_tail_erasing_lifetimes(referenced_ty, self.ecx.param_env).kind()
{ {
let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?; let ptr = mplace.meta.unwrap_meta().to_pointer(&tcx)?;

View File

@ -255,7 +255,22 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
} }
} }
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, &self,
offset: Size, offset: Size,
meta: MemPlaceMeta<Prov>, meta: MemPlaceMeta<Prov>,
@ -276,6 +291,9 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
} }
} }
/// 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( pub fn offset(
&self, &self,
offset: Size, offset: Size,

View File

@ -26,6 +26,7 @@ pub enum MemPlaceMeta<Prov: Provenance = AllocId> {
} }
impl<Prov: Provenance> MemPlaceMeta<Prov> { 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> { pub fn unwrap_meta(self) -> Scalar<Prov> {
match self { match self {
Self::Meta(s) => s, Self::Meta(s) => s,
@ -147,12 +148,16 @@ impl<Prov: Provenance> MemPlace<Prov> {
} }
#[inline] #[inline]
pub fn offset_with_meta<'tcx>( pub(super) fn offset_with_meta<'tcx>(
self, self,
offset: Size, offset: Size,
meta: MemPlaceMeta<Prov>, meta: MemPlaceMeta<Prov>,
cx: &impl HasDataLayout, cx: &impl HasDataLayout,
) -> InterpResult<'tcx, Self> { ) -> 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 }) Ok(MemPlace { ptr: self.ptr.offset(offset, cx)?, meta })
} }
} }
@ -182,8 +187,11 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
MPlaceTy { mplace: MemPlace { ptr, meta: MemPlaceMeta::None }, layout, align } 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] #[inline]
pub fn offset_with_meta( pub(crate) fn offset_with_meta(
&self, &self,
offset: Size, offset: Size,
meta: MemPlaceMeta<Prov>, meta: MemPlaceMeta<Prov>,
@ -197,6 +205,9 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
}) })
} }
/// Offset the place in memory.
///
/// This can go wrong very easily if you give the wrong layout for the new place!
pub fn offset( pub fn offset(
&self, &self,
offset: Size, offset: Size,
@ -241,14 +252,6 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
} }
} }
} }
#[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. // These are defined here because they produce a place.
@ -266,7 +269,12 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
#[inline(always)] #[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { 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 @@ impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
#[inline(always)] #[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980) #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { 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 @@ where
} }
/// 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.
/// Aso returns the vtable.
pub(super) fn unpack_dyn_trait( pub(super) fn unpack_dyn_trait(
&self, &self,
mplace: &MPlaceTy<'tcx, M::Provenance>, mplace: &MPlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::Provenance>, Pointer<Option<M::Provenance>>)> {
let vtable = mplace.vtable().to_pointer(self)?; // also sanity checks the type 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 (ty, _) = self.get_ptr_vtable(vtable)?;
let layout = self.layout_of(ty)?; let layout = self.layout_of(ty)?;
@ -820,7 +838,26 @@ where
layout, layout,
align: layout.align.abi, 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))
} }
} }

View File

@ -538,10 +538,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively // pointer or `dyn Trait` type, but it could be wrapped in newtypes. So recursively
// unwrap those newtypes until we are there. // unwrap those newtypes until we are there.
let mut receiver = args[0].clone(); let mut receiver = args[0].clone();
let receiver_place = loop { let receiver = loop {
match receiver.layout.ty.kind() { match receiver.layout.ty.kind() {
ty::Ref(..) | ty::RawPtr(..) => break self.deref_operand(&receiver)?, ty::Dynamic(..) | ty::Ref(..) | ty::RawPtr(..) => break receiver,
ty::Dynamic(..) => break receiver.assert_mem_place(), // no immediate unsized values
_ => { _ => {
// Not there yet, search for the only non-ZST field. // Not there yet, search for the only non-ZST field.
let mut non_zst_field = None; let mut non_zst_field = None;
@ -567,39 +566,83 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
} }
}; };
// 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. // break self.deref_operand(&receiver)?.into();
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
let (dyn_ty, dyn_trait) = self.get_ptr_vtable(vptr)?; // Obtain the underlying trait we are working on, and the adjusted receiver argument.
if dyn_trait != data.principal() { let recv_ty = receiver.layout.ty;
throw_ub_format!( let (vptr, dyn_ty, adjusted_receiver) = match recv_ty.kind() {
"`dyn` call on a pointer whose vtable does not match its type" 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 // Now determine the actual method to call. We can do that in two different ways and
// compare them to ensure everything fits. // compare them to ensure everything fits.
let Some(ty::VtblEntry::Method(fn_inst)) = self.get_vtable_entries(vptr)?.get(idx).copied() else { 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") 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) { if cfg!(debug_assertions) {
let tcx = *self.tcx; let tcx = *self.tcx;
let trait_def_id = tcx.trait_of_item(def_id).unwrap(); let trait_def_id = tcx.trait_of_item(def_id).unwrap();
let virtual_trait_ref = let virtual_trait_ref =
ty::TraitRef::from_method(tcx, trait_def_id, instance.substs); 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 = let existential_trait_ref =
ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
@ -614,17 +657,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
assert_eq!(fn_inst, concrete_method); assert_eq!(fn_inst, concrete_method);
} }
// `*mut receiver_place.layout.ty` is almost the layout that we // Adjust receiver argument. Layout can be any (thin) ptr.
// want for args[0]: We have to project to field 0 because we want args[0] = ImmTy::from_immediate(
// a thin pointer. Scalar::from_maybe_pointer(adjusted_receiver, self).into(),
assert!(receiver_place.layout.is_unsized()); self.layout_of(self.tcx.mk_mut_ptr(dyn_ty))?,
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); .into();
// Adjust receiver argument.
args[0] = OpTy::from(ImmTy::from_immediate(
Scalar::from_maybe_pointer(receiver_place.ptr, self).into(),
this_receiver_ptr,
));
trace!("Patched receiver operand to {:#?}", args[0]); trace!("Patched receiver operand to {:#?}", args[0]);
// recurse with concrete function // recurse with concrete function
self.eval_fn_call( self.eval_fn_call(
@ -653,15 +691,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// implementation fail -- a problem shared by rustc. // implementation fail -- a problem shared by rustc.
let place = self.force_allocation(place)?; let place = self.force_allocation(place)?;
let (instance, place) = match place.layout.ty.kind() { let place = match place.layout.ty.kind() {
ty::Dynamic(..) => { ty::Dynamic(_, _, ty::Dyn) => {
// Dropping a trait object. Need to find actual drop fn. // Dropping a trait object. Need to find actual drop fn.
let place = self.unpack_dyn_trait(&place)?; self.unpack_dyn_trait(&place)?.0
let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); }
(instance, place) 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 fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?;
let arg = ImmTy::from_immediate( let arg = ImmTy::from_immediate(

View File

@ -23,18 +23,18 @@ use std::hash::Hash;
// for the validation errors // for the validation errors
use super::UndefinedBehaviorInfo::*; use super::UndefinedBehaviorInfo::*;
use super::{ use super::{
CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, AllocId, CheckInAllocMsg, GlobalAlloc, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy,
MemPlaceMeta, OpTy, Scalar, ValueVisitor, Machine, MemPlaceMeta, OpTy, Pointer, Scalar, ValueVisitor,
}; };
macro_rules! throw_validation_failure { 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(); let mut msg = String::new();
msg.push_str("encountered "); msg.push_str("encountered ");
write!(&mut msg, $($what_fmt),+).unwrap(); write!(&mut msg, $($what_fmt)*).unwrap();
$( $(
msg.push_str(", but expected "); 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 path = rustc_middle::ty::print::with_no_trimmed_paths!({
let where_ = &$where; let where_ = &$where;
@ -82,7 +82,7 @@ macro_rules! throw_validation_failure {
/// ///
macro_rules! try_validation { macro_rules! try_validation {
($e:expr, $where:expr, ($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 { match $e {
Ok(x) => x, Ok(x) => x,
@ -93,7 +93,7 @@ macro_rules! try_validation {
InterpError::UndefinedBehavior($($p)|+) => InterpError::UndefinedBehavior($($p)|+) =>
throw_validation_failure!( throw_validation_failure!(
$where, $where,
{ $( $what_fmt ),+ } $( expected { $( $expected_fmt ),+ } )? { $( $what_fmt )* } $( expected { $( $expected_fmt )* } )?
) )
),+, ),+,
#[allow(unreachable_patterns)] #[allow(unreachable_patterns)]
@ -335,7 +335,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
) -> InterpResult<'tcx> { ) -> InterpResult<'tcx> {
let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env); let tail = self.ecx.tcx.struct_tail_erasing_lifetimes(pointee.ty, self.ecx.param_env);
match tail.kind() { match tail.kind() {
ty::Dynamic(..) => { ty::Dynamic(_, _, ty::Dyn) => {
let vtable = meta.unwrap_meta().to_pointer(self.ecx)?; let vtable = meta.unwrap_meta().to_pointer(self.ecx)?;
// Make sure it is a genuine vtable pointer. // Make sure it is a genuine vtable pointer.
let (_ty, _trait) = try_validation!( let (_ty, _trait) = try_validation!(
@ -399,12 +399,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
{ {
"an unaligned {kind} (required {} byte alignment but found {})", "an unaligned {kind} (required {} byte alignment but found {})",
required.bytes(), required.bytes(),
has.bytes() has.bytes(),
}, },
DanglingIntPointer(0, _) => DanglingIntPointer(0, _) =>
{ "a null {kind}" }, { "a null {kind}" },
DanglingIntPointer(i, _) => 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 { .. } => PointerOutOfBounds { .. } =>
{ "a dangling {kind} (going beyond the bounds of its allocation)" }, { "a dangling {kind} (going beyond the bounds of its allocation)" },
// This cannot happen during const-eval (because interning already detects // This cannot happen during const-eval (because interning already detects

View File

@ -284,7 +284,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueMut<'mir, 'tcx, M>
&self, &self,
ecx: &InterpCx<'mir, 'tcx, M>, ecx: &InterpCx<'mir, 'tcx, M>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { ) -> 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) ecx.place_to_op(self)
} }
@ -421,15 +421,25 @@ macro_rules! make_value_visitor {
// Special treatment for special types, where the (static) layout is not sufficient. // Special treatment for special types, where the (static) layout is not sufficient.
match *ty.kind() { match *ty.kind() {
// If it is a trait object, switch to the real type that was used to create it. // 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 // unsized values are never immediate, so we can assert_mem_place
let op = v.to_op_for_read(self.ecx())?; let op = v.to_op_for_read(self.ecx())?;
let dest = op.assert_mem_place(); 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); trace!("walk_value: dyn object layout: {:#?}", inner_mplace.layout);
// recurse with the inner type // recurse with the inner type
return self.visit_field(&v, 0, &$value_trait::from_op(&inner_mplace.into())); 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 // Slices do not need special handling here: they have `Array` field
// placement with length 0, so we enter the `Array` case below which // placement with length 0, so we enter the `Array` case below which
// indirectly uses the metadata to determine the actual length. // indirectly uses the metadata to determine the actual length.

View File

@ -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 --> $DIR/branchless-select-i128-pointer.rs:LL:CC
| |
LL | / transmute::<_, &str>( LL | / transmute::<_, &str>(
@ -6,7 +6,7 @@ LL | |
LL | | !mask & transmute::<_, TwoPtrs>("false !") LL | | !mask & transmute::<_, TwoPtrs>("false !")
LL | | | mask & transmute::<_, TwoPtrs>("true !"), LL | | | mask & transmute::<_, TwoPtrs>("true !"),
LL | | ) 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: 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 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View File

@ -3,5 +3,5 @@
use std::mem; use std::mem;
fn main() { 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
} }

View File

@ -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 --> $DIR/dangling_ref1.rs:LL:CC
| |
LL | let _x: &i32 = unsafe { mem::transmute(16usize) }; 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: 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 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View 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,
}
}
}

View File

@ -0,0 +1 @@
dispatch_on_pin_mut: value: 1

View File

@ -167,7 +167,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:96:1 --> $DIR/raw-bytes.rs:96:1
| |
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; 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 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) { = 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 --> $DIR/raw-bytes.rs:99:1
| |
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; 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 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) { = note: the raw bytes of the constant (size: 4, align: 4) {

View File

@ -167,7 +167,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/raw-bytes.rs:96:1 --> $DIR/raw-bytes.rs:96:1
| |
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; 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 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) { = 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 --> $DIR/raw-bytes.rs:99:1
| |
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; 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 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) { = note: the raw bytes of the constant (size: 8, align: 8) {

View File

@ -85,7 +85,7 @@ error[E0080]: it is undefined behavior to use this value
--> $DIR/ub-ref-ptr.rs:43:1 --> $DIR/ub-ref-ptr.rs:43:1
| |
LL | const USIZE_AS_REF: &'static u8 = unsafe { mem::transmute(1337usize) }; 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 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) { = 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 --> $DIR/ub-ref-ptr.rs:46:1
| |
LL | const USIZE_AS_BOX: Box<u8> = unsafe { mem::transmute(1337usize) }; 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 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) { = note: the raw bytes of the constant (size: $SIZE, align: $ALIGN) {