interpret: make MemPlace, Place, Operand types private to the interpreter
This commit is contained in:
parent
a989e25f1b
commit
fa5f13775a
41 changed files with 417 additions and 338 deletions
|
@ -79,7 +79,7 @@ fn eval_body_using_ecx<'mir, 'tcx>(
|
|||
intern_const_alloc_recursive(ecx, intern_kind, &ret)?;
|
||||
// we leave alignment checks off, since this `ecx` will not be used for further evaluation anyway
|
||||
|
||||
debug!("eval_body_using_ecx done: {:?}", *ret);
|
||||
debug!("eval_body_using_ecx done: {:?}", ret);
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ pub(super) fn op_to_const<'tcx>(
|
|||
// We know `offset` is relative to the allocation, so we can use `into_parts`.
|
||||
let to_const_value = |mplace: &MPlaceTy<'_>| {
|
||||
debug!("to_const_value(mplace: {:?})", mplace);
|
||||
match mplace.ptr.into_parts() {
|
||||
match mplace.ptr().into_parts() {
|
||||
(Some(alloc_id), offset) => {
|
||||
let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
|
||||
ConstValue::ByRef { alloc, offset }
|
||||
|
@ -370,7 +370,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
|
|||
inner = true;
|
||||
}
|
||||
};
|
||||
let alloc_id = mplace.ptr.provenance.unwrap();
|
||||
let alloc_id = mplace.ptr().provenance.unwrap();
|
||||
|
||||
// Validation failed, report an error. This is always a hard error.
|
||||
if let Err(error) = validation {
|
||||
|
|
|
@ -30,7 +30,7 @@ pub(crate) fn const_caller_location(
|
|||
if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
|
||||
bug!("intern_const_alloc_recursive should not error in this case")
|
||||
}
|
||||
ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx))
|
||||
ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr(), &tcx))
|
||||
}
|
||||
|
||||
// We forbid type-level constants that contain more than `VALTREE_MAX_NODES` nodes.
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::const_eval::CanAccessStatics;
|
|||
use crate::interpret::MPlaceTy;
|
||||
use crate::interpret::{
|
||||
intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
|
||||
MemoryKind, Place, Projectable, Scalar,
|
||||
MemoryKind, PlaceTy, Projectable, Scalar,
|
||||
};
|
||||
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
|
||||
use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
|
||||
|
@ -318,7 +318,7 @@ fn valtree_into_mplace<'tcx>(
|
|||
let len_scalar = Scalar::from_target_usize(len as u64, &tcx);
|
||||
|
||||
Immediate::ScalarPair(
|
||||
Scalar::from_maybe_pointer((*pointee_place).ptr, &tcx),
|
||||
Scalar::from_maybe_pointer(pointee_place.ptr(), &tcx),
|
||||
len_scalar,
|
||||
)
|
||||
}
|
||||
|
@ -383,5 +383,5 @@ fn valtree_into_mplace<'tcx>(
|
|||
}
|
||||
|
||||
fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: &MPlaceTy<'tcx>) {
|
||||
trace!("{:?}", ecx.dump_place(Place::Ptr(**place)));
|
||||
trace!("{:?}", ecx.dump_place(&PlaceTy::from(place.clone())));
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou
|
|||
|
||||
use super::{
|
||||
AllocId, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace,
|
||||
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, PointerArithmetic, Provenance,
|
||||
Scalar, StackPopJump,
|
||||
MemPlaceMeta, Memory, MemoryKind, Operand, Place, PlaceTy, Pointer, PointerArithmetic,
|
||||
Projectable, Provenance, Scalar, StackPopJump,
|
||||
};
|
||||
use crate::errors::{self, ErroneousConstUsed};
|
||||
use crate::util;
|
||||
|
@ -155,17 +155,26 @@ pub enum StackPopCleanup {
|
|||
}
|
||||
|
||||
/// State of a local variable including a memoized layout
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct LocalState<'tcx, Prov: Provenance = AllocId> {
|
||||
pub value: LocalValue<Prov>,
|
||||
value: LocalValue<Prov>,
|
||||
/// Don't modify if `Some`, this is only used to prevent computing the layout twice.
|
||||
/// Avoids computing the layout of locals that are never actually initialized.
|
||||
pub layout: Cell<Option<TyAndLayout<'tcx>>>,
|
||||
layout: Cell<Option<TyAndLayout<'tcx>>>,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for LocalState<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("LocalState")
|
||||
.field("value", &self.value)
|
||||
.field("ty", &self.layout.get().map(|l| l.ty))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Current value of a local variable
|
||||
#[derive(Copy, Clone, Debug)] // Miri debug-prints these
|
||||
pub enum LocalValue<Prov: Provenance = AllocId> {
|
||||
pub(super) enum LocalValue<Prov: Provenance = AllocId> {
|
||||
/// This local is not currently alive, and cannot be used at all.
|
||||
Dead,
|
||||
/// A normal, live local.
|
||||
|
@ -176,10 +185,27 @@ pub enum LocalValue<Prov: Provenance = AllocId> {
|
|||
Live(Operand<Prov>),
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> LocalState<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> LocalState<'tcx, Prov> {
|
||||
pub fn make_live_uninit(&mut self) {
|
||||
self.value = LocalValue::Live(Operand::Immediate(Immediate::Uninit));
|
||||
}
|
||||
|
||||
/// This is a hack because Miri needs a way to visit all the provenance in a `LocalState`
|
||||
/// without having a layout or `TyCtxt` available, and we want to keep the `Operand` type
|
||||
/// private.
|
||||
pub fn as_mplace_or_imm(
|
||||
&self,
|
||||
) -> Option<Either<(Pointer<Option<Prov>>, MemPlaceMeta<Prov>), Immediate<Prov>>> {
|
||||
match self.value {
|
||||
LocalValue::Dead => None,
|
||||
LocalValue::Live(Operand::Indirect(mplace)) => Some(Left((mplace.ptr, mplace.meta))),
|
||||
LocalValue::Live(Operand::Immediate(imm)) => Some(Right(imm)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the local's value or error if the local is not yet live or not live anymore.
|
||||
#[inline(always)]
|
||||
pub fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
|
||||
pub(super) fn access(&self) -> InterpResult<'tcx, &Operand<Prov>> {
|
||||
match &self.value {
|
||||
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
|
||||
LocalValue::Live(val) => Ok(val),
|
||||
|
@ -189,10 +215,10 @@ impl<'tcx, Prov: Provenance + 'static> LocalState<'tcx, Prov> {
|
|||
/// Overwrite the local. If the local can be overwritten in place, return a reference
|
||||
/// to do so; otherwise return the `MemPlace` to consult instead.
|
||||
///
|
||||
/// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from
|
||||
/// anywhere else. You may be invalidating machine invariants if you do!
|
||||
/// Note: Before calling this, call the `before_access_local_mut` machine hook! You may be
|
||||
/// invalidating machine invariants otherwise!
|
||||
#[inline(always)]
|
||||
pub fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
|
||||
pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand<Prov>> {
|
||||
match &mut self.value {
|
||||
LocalValue::Dead => throw_ub!(DeadLocal), // could even be "invalid program"?
|
||||
LocalValue::Live(val) => Ok(val),
|
||||
|
@ -694,7 +720,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, Option<(Size, Align)>> {
|
||||
self.size_and_align_of(&mplace.meta, &mplace.layout)
|
||||
self.size_and_align_of(&mplace.meta(), &mplace.layout)
|
||||
}
|
||||
|
||||
#[instrument(skip(self, body, return_place, return_to_block), level = "debug")]
|
||||
|
@ -826,7 +852,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
.expect("return place should always be live");
|
||||
let dest = self.frame().return_place.clone();
|
||||
let err = self.copy_op(&op, &dest, /*allow_transmute*/ true);
|
||||
trace!("return value: {:?}", self.dump_place(*dest));
|
||||
trace!("return value: {:?}", self.dump_place(&dest));
|
||||
// We delay actually short-circuiting on this error until *after* the stack frame is
|
||||
// popped, since we want this error to be attributed to the caller, whose type defines
|
||||
// this transmute.
|
||||
|
@ -974,7 +1000,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
// Need to allocate some memory, since `Immediate::Uninit` cannot be unsized.
|
||||
let dest_place = self.allocate_dyn(layout, MemoryKind::Stack, meta)?;
|
||||
Operand::Indirect(*dest_place)
|
||||
Operand::Indirect(*dest_place.mplace())
|
||||
} else {
|
||||
assert!(!meta.has_meta()); // we're dropping the metadata
|
||||
// Just make this an efficient immediate.
|
||||
|
@ -1068,8 +1094,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn dump_place(&self, place: Place<M::Provenance>) -> PlacePrinter<'_, 'mir, 'tcx, M> {
|
||||
PlacePrinter { ecx: self, place }
|
||||
pub fn dump_place(
|
||||
&self,
|
||||
place: &PlaceTy<'tcx, M::Provenance>,
|
||||
) -> PlacePrinter<'_, 'mir, 'tcx, M> {
|
||||
PlacePrinter { ecx: self, place: *place.place() }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
|
|
@ -25,7 +25,7 @@ use rustc_ast::Mutability;
|
|||
|
||||
use super::{
|
||||
AllocId, Allocation, ConstAllocation, InterpCx, MPlaceTy, Machine, MemoryKind, PlaceTy,
|
||||
ValueVisitor,
|
||||
Projectable, ValueVisitor,
|
||||
};
|
||||
use crate::const_eval;
|
||||
use crate::errors::{DanglingPtrInFinal, UnsupportedUntypedPointer};
|
||||
|
@ -177,7 +177,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
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)?;
|
||||
let ptr = mplace.meta().unwrap_meta().to_pointer(&tcx)?;
|
||||
if let Some(alloc_id) = ptr.provenance {
|
||||
// Explicitly choose const mode here, since vtables are immutable, even
|
||||
// if the reference of the fat pointer is mutable.
|
||||
|
@ -191,7 +191,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
}
|
||||
// Check if we have encountered this pointer+layout combination before.
|
||||
// Only recurse for allocation-backed pointers.
|
||||
if let Some(alloc_id) = mplace.ptr.provenance {
|
||||
if let Some(alloc_id) = mplace.ptr().provenance {
|
||||
// Compute the mode with which we intern this. Our goal here is to make as many
|
||||
// statics as we can immutable so they can be placed in read-only memory by LLVM.
|
||||
let ref_mode = match self.mode {
|
||||
|
@ -267,7 +267,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx, const_eval::Memory
|
|||
|
||||
// If there is no provenance in this allocation, it does not contain references
|
||||
// that point to another allocation, and we can avoid the interning walk.
|
||||
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr, size, align)? {
|
||||
if let Some(alloc) = self.ecx.get_ptr_alloc(mplace.ptr(), size, align)? {
|
||||
if !alloc.has_provenance() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ pub fn intern_const_alloc_recursive<
|
|||
leftover_allocations,
|
||||
// The outermost allocation must exist, because we allocated it with
|
||||
// `Memory::allocate`.
|
||||
ret.ptr.provenance.unwrap(),
|
||||
ret.ptr().provenance.unwrap(),
|
||||
base_intern_mode,
|
||||
Some(ret.layout.ty),
|
||||
);
|
||||
|
@ -466,7 +466,7 @@ impl<'mir, 'tcx: 'mir, M: super::intern::CompileTimeMachine<'mir, 'tcx, !>>
|
|||
) -> InterpResult<'tcx, ConstAllocation<'tcx>> {
|
||||
let dest = self.allocate(layout, MemoryKind::Stack)?;
|
||||
f(self, &dest.clone().into())?;
|
||||
let mut alloc = self.memory.alloc_map.remove(&dest.ptr.provenance.unwrap()).unwrap().1;
|
||||
let mut alloc = self.memory.alloc_map.remove(&dest.ptr().provenance.unwrap()).unwrap().1;
|
||||
alloc.mutability = Mutability::Not;
|
||||
Ok(self.tcx.mk_const_alloc(alloc))
|
||||
}
|
||||
|
|
|
@ -466,7 +466,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
_ => return Ok(false),
|
||||
}
|
||||
|
||||
trace!("{:?}", self.dump_place(**dest));
|
||||
trace!("{:?}", self.dump_place(dest));
|
||||
self.go_to_block(ret);
|
||||
Ok(true)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::const_eval::CheckAlignment;
|
|||
|
||||
use super::{
|
||||
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
|
||||
InterpResult, MPlaceTy, MemoryKind, OpTy, PlaceTy, Pointer, Provenance, Scalar,
|
||||
};
|
||||
|
||||
/// Data returned by Machine::stack_pop,
|
||||
|
@ -237,22 +237,22 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
|
|||
right: &ImmTy<'tcx, Self::Provenance>,
|
||||
) -> InterpResult<'tcx, (Scalar<Self::Provenance>, bool, Ty<'tcx>)>;
|
||||
|
||||
/// Called to write the specified `local` from the `frame`.
|
||||
/// Called before writing the specified `local` of the `frame`.
|
||||
/// Since writing a ZST is not actually accessing memory or locals, this is never invoked
|
||||
/// for ZST reads.
|
||||
///
|
||||
/// Due to borrow checker trouble, we indicate the `frame` as an index rather than an `&mut
|
||||
/// Frame`.
|
||||
#[inline]
|
||||
fn access_local_mut<'a>(
|
||||
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
|
||||
frame: usize,
|
||||
local: mir::Local,
|
||||
) -> InterpResult<'tcx, &'a mut Operand<Self::Provenance>>
|
||||
#[inline(always)]
|
||||
fn before_access_local_mut<'a>(
|
||||
_ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
|
||||
_frame: usize,
|
||||
_local: mir::Local,
|
||||
) -> InterpResult<'tcx>
|
||||
where
|
||||
'tcx: 'mir,
|
||||
{
|
||||
ecx.stack_mut()[frame].locals[local].access_mut()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Called before a basic block terminator is executed.
|
||||
|
|
|
@ -20,16 +20,21 @@ mod visitor;
|
|||
|
||||
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
|
||||
|
||||
pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup};
|
||||
pub use self::eval_context::{Frame, FrameInfo, InterpCx, StackPopCleanup};
|
||||
pub use self::intern::{intern_const_alloc_recursive, InternKind};
|
||||
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
|
||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Operand, Readable};
|
||||
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy, Writeable};
|
||||
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
||||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||
pub use self::projection::Projectable;
|
||||
pub use self::terminator::FnArg;
|
||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||
pub use self::visitor::ValueVisitor;
|
||||
|
||||
use self::{
|
||||
operand::Operand,
|
||||
place::{MemPlace, Place},
|
||||
};
|
||||
|
||||
pub(crate) use self::intrinsics::eval_nullary_intrinsic;
|
||||
use eval_context::{from_known_layout, mir_assign_valid_types};
|
||||
|
|
|
@ -88,7 +88,7 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||
|
||||
// ScalarPair needs a type to interpret, so we often have an immediate and a type together
|
||||
// as input for binary and cast operations.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct ImmTy<'tcx, Prov: Provenance = AllocId> {
|
||||
imm: Immediate<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
|
@ -134,6 +134,12 @@ impl<Prov: Provenance> std::fmt::Display for ImmTy<'_, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for ImmTy<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ImmTy").field("imm", &self.imm).field("ty", &self.layout.ty).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> {
|
||||
type Target = Immediate<Prov>;
|
||||
#[inline(always)]
|
||||
|
@ -142,51 +148,6 @@ impl<'tcx, Prov: Provenance> std::ops::Deref for ImmTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
/// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
|
||||
/// or still in memory. The latter is an optimization, to delay reading that chunk of
|
||||
/// memory and to avoid having to store arbitrary-sized data here.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Operand<Prov: Provenance = AllocId> {
|
||||
Immediate(Immediate<Prov>),
|
||||
Indirect(MemPlace<Prov>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
||||
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for an `OpTy`!
|
||||
/// `None` means "alignment does not matter since this is a by-value operand"
|
||||
/// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`.
|
||||
/// Also CTFE ignores alignment anyway, so this is for Miri only.
|
||||
pub align: Option<Align>,
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> std::ops::Deref for OpTy<'tcx, Prov> {
|
||||
type Target = Operand<Prov>;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Operand<Prov> {
|
||||
&self.op
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Indirect(*mplace), layout: mplace.layout, align: Some(mplace.align) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: ImmTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
||||
#[inline]
|
||||
pub fn from_scalar(val: Scalar<Prov>, layout: TyAndLayout<'tcx>) -> Self {
|
||||
|
@ -319,7 +280,61 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
/// An `Operand` is the result of computing a `mir::Operand`. It can be immediate,
|
||||
/// or still in memory. The latter is an optimization, to delay reading that chunk of
|
||||
/// memory and to avoid having to store arbitrary-sized data here.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) enum Operand<Prov: Provenance = AllocId> {
|
||||
Immediate(Immediate<Prov>),
|
||||
Indirect(MemPlace<Prov>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OpTy<'tcx, Prov: Provenance = AllocId> {
|
||||
op: Operand<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for an `OpTy`!
|
||||
/// `None` means "alignment does not matter since this is a by-value operand"
|
||||
/// (`Operand::Immediate`); this field is only relevant for `Operand::Indirect`.
|
||||
/// Also CTFE ignores alignment anyway, so this is for Miri only.
|
||||
pub align: Option<Align>,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for OpTy<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("OpTy").field("op", &self.op).field("ty", &self.layout.ty).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<ImmTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(val: ImmTy<'tcx, Prov>) -> Self {
|
||||
OpTy { op: Operand::Immediate(val.imm), layout: val.layout, align: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
OpTy {
|
||||
op: Operand::Indirect(*mplace.mplace()),
|
||||
layout: mplace.layout,
|
||||
align: Some(mplace.align),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
pub(super) fn op(&self) -> &Operand<Prov> {
|
||||
&self.op
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
|
@ -328,7 +343,7 @@ impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Pr
|
|||
#[inline]
|
||||
fn meta(&self) -> MemPlaceMeta<Prov> {
|
||||
match self.as_mplace_or_imm() {
|
||||
Left(mplace) => mplace.meta,
|
||||
Left(mplace) => mplace.meta(),
|
||||
Right(_) => {
|
||||
debug_assert!(self.layout.is_sized(), "unsized immediates are not a thing");
|
||||
MemPlaceMeta::None
|
||||
|
@ -362,18 +377,19 @@ impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for OpTy<'tcx, Pr
|
|||
}
|
||||
}
|
||||
|
||||
/// The `Readable` trait describes interpreter values that one can read from.
|
||||
pub trait Readable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>>;
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
self.as_mplace_or_imm()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> Readable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
Left(self.clone())
|
||||
|
@ -535,7 +551,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
/// Turn the wide MPlace into a string (must already be dereferenced!)
|
||||
pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> {
|
||||
let len = mplace.len(self)?;
|
||||
let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len))?;
|
||||
let bytes = self.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len))?;
|
||||
let str = std::str::from_utf8(bytes).map_err(|err| err_ub!(InvalidStr(err)))?;
|
||||
Ok(str)
|
||||
}
|
||||
|
@ -630,7 +646,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
op = self.project(&op, elem)?
|
||||
}
|
||||
|
||||
trace!("eval_place_to_op: got {:?}", *op);
|
||||
trace!("eval_place_to_op: got {:?}", op);
|
||||
// Sanity-check the type we ended up with.
|
||||
debug_assert!(
|
||||
mir_assign_valid_types(
|
||||
|
@ -673,7 +689,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
self.eval_mir_constant(&c, Some(constant.span), layout)?
|
||||
}
|
||||
};
|
||||
trace!("{:?}: {:?}", mir_op, *op);
|
||||
trace!("{:?}: {:?}", mir_op, op);
|
||||
Ok(op)
|
||||
}
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ impl<Prov: Provenance> MemPlaceMeta<Prov> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
pub struct MemPlace<Prov: Provenance = AllocId> {
|
||||
pub(super) struct MemPlace<Prov: Provenance = AllocId> {
|
||||
/// The pointer can be a pure integer, with the `None` provenance.
|
||||
pub ptr: Pointer<Option<Prov>>,
|
||||
/// Metadata for unsized places. Interpretation is up to the type.
|
||||
|
@ -60,68 +60,6 @@ pub struct MemPlace<Prov: Provenance = AllocId> {
|
|||
pub meta: MemPlaceMeta<Prov>,
|
||||
}
|
||||
|
||||
/// A MemPlace with its layout. Constructing it is only possible in this module.
|
||||
#[derive(Clone, Hash, Eq, PartialEq, Debug)]
|
||||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
mplace: MemPlace<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for a `MPlaceTy`!
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> std::ops::Deref for MPlaceTy<'tcx, Prov> {
|
||||
type Target = MemPlace<Prov>;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &MemPlace<Prov> {
|
||||
&self.mplace
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Place<Prov: Provenance = AllocId> {
|
||||
/// A place referring to a value allocated in the `Memory` system.
|
||||
Ptr(MemPlace<Prov>),
|
||||
|
||||
/// To support alloc-free locals, we are able to write directly to a local. The offset indicates
|
||||
/// where in the local this place is located; if it is `None`, no projection has been applied.
|
||||
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
|
||||
/// (Without that optimization, we'd just always be a `MemPlace`.)
|
||||
/// Note that this only stores the frame index, not the thread this frame belongs to -- that is
|
||||
/// implicit. This means a `Place` must never be moved across interpreter thread boundaries!
|
||||
///
|
||||
/// This variant shall not be used for unsized types -- those must always live in memory.
|
||||
Local { frame: usize, local: mir::Local, offset: Option<Size> },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for a `PlaceTy`!
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> std::ops::Deref for PlaceTy<'tcx, Prov> {
|
||||
type Target = Place<Prov>;
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Place<Prov> {
|
||||
&self.place
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
PlaceTy { place: Place::Ptr(*mplace), layout: mplace.layout, align: mplace.align }
|
||||
}
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> MemPlace<Prov> {
|
||||
#[inline(always)]
|
||||
pub fn from_ptr(ptr: Pointer<Option<Prov>>) -> Self {
|
||||
|
@ -165,6 +103,27 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A MemPlace with its layout. Constructing it is only possible in this module.
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub struct MPlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
mplace: MemPlace<Prov>,
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for a `MPlaceTy`!
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for MPlaceTy<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MPlaceTy")
|
||||
.field("mplace", &self.mplace)
|
||||
.field("ty", &self.layout.ty)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
||||
/// Produces a MemPlace that works for ZST but nothing else.
|
||||
/// Conceptually this is a new allocation, but it doesn't actually create an allocation so you
|
||||
|
@ -194,9 +153,29 @@ impl<'tcx, Prov: Provenance> MPlaceTy<'tcx, Prov> {
|
|||
align: layout.align.abi,
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust the provenance of the main pointer (metadata is unaffected).
|
||||
pub fn map_provenance(self, f: impl FnOnce(Option<Prov>) -> Option<Prov>) -> Self {
|
||||
MPlaceTy { mplace: self.mplace.map_provenance(f), ..self }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(super) fn mplace(&self) -> &MemPlace<Prov> {
|
||||
&self.mplace
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn ptr(&self) -> Pointer<Option<Prov>> {
|
||||
self.mplace.ptr
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_ref(&self, cx: &impl HasDataLayout) -> Immediate<Prov> {
|
||||
self.mplace.to_ref(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
|
@ -204,7 +183,7 @@ impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'tcx
|
|||
|
||||
#[inline(always)]
|
||||
fn meta(&self) -> MemPlaceMeta<Prov> {
|
||||
self.meta
|
||||
self.mplace.meta
|
||||
}
|
||||
|
||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
|
@ -229,7 +208,76 @@ impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for MPlaceTy<'tcx
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) enum Place<Prov: Provenance = AllocId> {
|
||||
/// A place referring to a value allocated in the `Memory` system.
|
||||
Ptr(MemPlace<Prov>),
|
||||
|
||||
/// To support alloc-free locals, we are able to write directly to a local. The offset indicates
|
||||
/// where in the local this place is located; if it is `None`, no projection has been applied.
|
||||
/// Such projections are meaningful even if the offset is 0, since they can change layouts.
|
||||
/// (Without that optimization, we'd just always be a `MemPlace`.)
|
||||
/// Note that this only stores the frame index, not the thread this frame belongs to -- that is
|
||||
/// implicit. This means a `Place` must never be moved across interpreter thread boundaries!
|
||||
///
|
||||
/// This variant shall not be used for unsized types -- those must always live in memory.
|
||||
Local { frame: usize, local: mir::Local, offset: Option<Size> },
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlaceTy<'tcx, Prov: Provenance = AllocId> {
|
||||
place: Place<Prov>, // Keep this private; it helps enforce invariants.
|
||||
pub layout: TyAndLayout<'tcx>,
|
||||
/// rustc does not have a proper way to represent the type of a field of a `repr(packed)` struct:
|
||||
/// it needs to have a different alignment than the field type would usually have.
|
||||
/// So we represent this here with a separate field that "overwrites" `layout.align`.
|
||||
/// This means `layout.align` should never be used for a `PlaceTy`!
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl<Prov: Provenance> std::fmt::Debug for PlaceTy<'_, Prov> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PlaceTy").field("place", &self.place).field("ty", &self.layout.ty).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> From<MPlaceTy<'tcx, Prov>> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn from(mplace: MPlaceTy<'tcx, Prov>) -> Self {
|
||||
PlaceTy { place: Place::Ptr(mplace.mplace), layout: mplace.layout, align: mplace.align }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
pub(super) fn place(&self) -> &Place<Prov> {
|
||||
&self.place
|
||||
}
|
||||
|
||||
/// A place is either an mplace or some local.
|
||||
#[inline(always)]
|
||||
pub fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
|
||||
match self.place {
|
||||
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
|
||||
Place::Local { frame, local, offset } => Right((frame, local, offset)),
|
||||
}
|
||||
}
|
||||
|
||||
#[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_or_else(|| {
|
||||
bug!(
|
||||
"PlaceTy of type {} was a local when it was expected to be an MPlace",
|
||||
self.layout.ty
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn layout(&self) -> TyAndLayout<'tcx> {
|
||||
self.layout
|
||||
|
@ -238,7 +286,7 @@ impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx,
|
|||
#[inline]
|
||||
fn meta(&self) -> MemPlaceMeta<Prov> {
|
||||
match self.as_mplace_or_local() {
|
||||
Left(mplace) => mplace.meta,
|
||||
Left(mplace) => mplace.meta(),
|
||||
Right(_) => {
|
||||
debug_assert!(self.layout.is_sized(), "unsized locals should live in memory");
|
||||
MemPlaceMeta::None
|
||||
|
@ -286,11 +334,11 @@ impl<'tcx, Prov: Provenance + 'static> Projectable<'tcx, Prov> for PlaceTy<'tcx,
|
|||
impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
pub fn as_mplace_or_imm(&self) -> Either<MPlaceTy<'tcx, Prov>, ImmTy<'tcx, Prov>> {
|
||||
match **self {
|
||||
match self.op() {
|
||||
Operand::Indirect(mplace) => {
|
||||
Left(MPlaceTy { mplace, layout: self.layout, align: self.align.unwrap() })
|
||||
Left(MPlaceTy { mplace: *mplace, layout: self.layout, align: self.align.unwrap() })
|
||||
}
|
||||
Operand::Immediate(imm) => Right(ImmTy::from_immediate(imm, self.layout)),
|
||||
Operand::Immediate(imm) => Right(ImmTy::from_immediate(*imm, self.layout)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,30 +354,7 @@ impl<'tcx, Prov: Provenance> OpTy<'tcx, Prov> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> PlaceTy<'tcx, Prov> {
|
||||
/// A place is either an mplace or some local.
|
||||
#[inline(always)]
|
||||
pub fn as_mplace_or_local(
|
||||
&self,
|
||||
) -> Either<MPlaceTy<'tcx, Prov>, (usize, mir::Local, Option<Size>)> {
|
||||
match **self {
|
||||
Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout, align: self.align }),
|
||||
Place::Local { frame, local, offset } => Right((frame, local, offset)),
|
||||
}
|
||||
}
|
||||
|
||||
#[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_or_else(|| {
|
||||
bug!(
|
||||
"PlaceTy of type {} was a local when it was expected to be an MPlace",
|
||||
self.layout.ty
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The `Weiteable` trait describes interpreter values that can be written to.
|
||||
pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
|
@ -341,7 +366,7 @@ pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> {
|
|||
) -> InterpResult<'tcx, MPlaceTy<'tcx, Prov>>;
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
|
@ -360,7 +385,7 @@ impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for PlaceTy<'tcx, P
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
||||
#[inline(always)]
|
||||
fn as_mplace_or_local(
|
||||
&self,
|
||||
|
@ -381,7 +406,7 @@ impl<'tcx, Prov: Provenance + 'static> Writeable<'tcx, Prov> for MPlaceTy<'tcx,
|
|||
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
|
||||
impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
|
||||
where
|
||||
Prov: Provenance + 'static,
|
||||
Prov: Provenance,
|
||||
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
||||
{
|
||||
/// Take a value, which represents a (thin or wide) reference, and make it a place.
|
||||
|
@ -415,7 +440,7 @@ where
|
|||
&self,
|
||||
mplace: &MPlaceTy<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> {
|
||||
let imm = mplace.to_ref(self);
|
||||
let imm = mplace.mplace.to_ref(self);
|
||||
let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?;
|
||||
Ok(ImmTy::from_immediate(imm, layout))
|
||||
}
|
||||
|
@ -449,7 +474,7 @@ where
|
|||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
self.get_ptr_alloc(mplace.ptr, size, mplace.align)
|
||||
self.get_ptr_alloc(mplace.ptr(), size, mplace.align)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -462,7 +487,7 @@ where
|
|||
.size_and_align_of_mplace(&mplace)?
|
||||
.unwrap_or((mplace.layout.size, mplace.layout.align.abi));
|
||||
// Due to packed places, only `mplace.align` matters.
|
||||
self.get_ptr_alloc_mut(mplace.ptr, size, mplace.align)
|
||||
self.get_ptr_alloc_mut(mplace.ptr(), size, mplace.align)
|
||||
}
|
||||
|
||||
/// Check if this mplace is dereferenceable and sufficiently aligned.
|
||||
|
@ -473,7 +498,7 @@ where
|
|||
// Due to packed places, only `mplace.align` matters.
|
||||
let align =
|
||||
if M::enforce_alignment(self).should_check() { mplace.align } else { Align::ONE };
|
||||
self.check_ptr_access_align(mplace.ptr, size, align, CheckInAllocMsg::DerefTest)?;
|
||||
self.check_ptr_access_align(mplace.ptr(), size, align, CheckInAllocMsg::DerefTest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -542,7 +567,7 @@ where
|
|||
place = self.project(&place, elem)?
|
||||
}
|
||||
|
||||
trace!("{:?}", self.dump_place(place.place));
|
||||
trace!("{:?}", self.dump_place(&place));
|
||||
// Sanity-check the type we ended up with.
|
||||
debug_assert!(
|
||||
mir_assign_valid_types(
|
||||
|
@ -618,7 +643,8 @@ where
|
|||
// just fall back to the indirect path.
|
||||
dest.force_mplace(self)?
|
||||
} else {
|
||||
match M::access_local_mut(self, frame, local)? {
|
||||
M::before_access_local_mut(self, frame, local)?;
|
||||
match self.stack_mut()[frame].locals[local].access_mut()? {
|
||||
Operand::Immediate(local_val) => {
|
||||
// Local can be updated in-place.
|
||||
*local_val = src;
|
||||
|
@ -738,7 +764,8 @@ where
|
|||
// FIXME: share the logic with `write_immediate_no_validate`.
|
||||
dest.force_mplace(self)?
|
||||
} else {
|
||||
match M::access_local_mut(self, frame, local)? {
|
||||
M::before_access_local_mut(self, frame, local)?;
|
||||
match self.stack_mut()[frame].locals[local].access_mut()? {
|
||||
Operand::Immediate(local) => {
|
||||
*local = Immediate::Uninit;
|
||||
return Ok(());
|
||||
|
@ -832,7 +859,7 @@ where
|
|||
*src_val,
|
||||
src.layout(),
|
||||
dest_mem.align,
|
||||
*dest_mem,
|
||||
dest_mem.mplace,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
@ -859,7 +886,12 @@ where
|
|||
// (Or as the `Assign` docs put it, assignments "not producing primitives" must be
|
||||
// non-overlapping.)
|
||||
self.mem_copy(
|
||||
src.ptr, src.align, dest.ptr, dest.align, dest_size, /*nonoverlapping*/ true,
|
||||
src.ptr(),
|
||||
src.align,
|
||||
dest.ptr(),
|
||||
dest.align,
|
||||
dest_size,
|
||||
/*nonoverlapping*/ true,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -874,7 +906,8 @@ where
|
|||
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
|
||||
let mplace = match place.place {
|
||||
Place::Local { frame, local, offset } => {
|
||||
let whole_local = match M::access_local_mut(self, frame, local)? {
|
||||
M::before_access_local_mut(self, frame, local)?;
|
||||
let whole_local = match self.stack_mut()[frame].locals[local].access_mut()? {
|
||||
&mut Operand::Immediate(local_val) => {
|
||||
// We need to make an allocation.
|
||||
|
||||
|
@ -894,16 +927,16 @@ where
|
|||
local_val,
|
||||
local_layout,
|
||||
local_layout.align.abi,
|
||||
*mplace,
|
||||
mplace.mplace,
|
||||
)?;
|
||||
}
|
||||
M::after_local_allocated(self, frame, local, &mplace)?;
|
||||
// Now we can call `access_mut` again, asserting it goes well, and actually
|
||||
// overwrite things. This points to the entire allocation, not just the part
|
||||
// the place refers to, i.e. we do this before we apply `offset`.
|
||||
*M::access_local_mut(self, frame, local).unwrap() =
|
||||
Operand::Indirect(*mplace);
|
||||
*mplace
|
||||
*self.stack_mut()[frame].locals[local].access_mut().unwrap() =
|
||||
Operand::Indirect(mplace.mplace);
|
||||
mplace.mplace
|
||||
}
|
||||
&mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
|
||||
};
|
||||
|
@ -1011,12 +1044,12 @@ where
|
|||
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 vtable = mplace.meta().unwrap_meta().to_pointer(self)?;
|
||||
let (ty, _) = self.get_ptr_vtable(vtable)?;
|
||||
let layout = self.layout_of(ty)?;
|
||||
|
||||
let mplace = MPlaceTy {
|
||||
mplace: MemPlace { meta: MemPlaceMeta::None, ..**mplace },
|
||||
mplace: MemPlace { meta: MemPlaceMeta::None, ..mplace.mplace },
|
||||
layout,
|
||||
align: layout.align.abi,
|
||||
};
|
||||
|
|
|
@ -89,7 +89,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||
}
|
||||
|
||||
/// A type representing iteration over the elements of an array.
|
||||
pub struct ArrayIterator<'tcx, 'a, Prov: Provenance + 'static, P: Projectable<'tcx, Prov>> {
|
||||
pub struct ArrayIterator<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> {
|
||||
base: &'a P,
|
||||
range: Range<u64>,
|
||||
stride: Size,
|
||||
|
@ -97,9 +97,7 @@ pub struct ArrayIterator<'tcx, 'a, Prov: Provenance + 'static, P: Projectable<'t
|
|||
_phantom: PhantomData<Prov>, // otherwise it says `Prov` is never used...
|
||||
}
|
||||
|
||||
impl<'tcx, 'a, Prov: Provenance + 'static, P: Projectable<'tcx, Prov>>
|
||||
ArrayIterator<'tcx, 'a, Prov, P>
|
||||
{
|
||||
impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx, 'a, Prov, P> {
|
||||
/// Should be the same `ecx` on each call, and match the one used to create the iterator.
|
||||
pub fn next<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||
&mut self,
|
||||
|
@ -113,7 +111,7 @@ impl<'tcx, 'a, Prov: Provenance + 'static, P: Projectable<'tcx, Prov>>
|
|||
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
|
||||
impl<'mir, 'tcx: 'mir, Prov, M> InterpCx<'mir, 'tcx, M>
|
||||
where
|
||||
Prov: Provenance + 'static,
|
||||
Prov: Provenance,
|
||||
M: Machine<'mir, 'tcx, Provenance = Prov>,
|
||||
{
|
||||
/// Offset a pointer to project to a field of a struct/union. Unlike `place_field`, this is
|
||||
|
|
|
@ -204,7 +204,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// avoid writing each operand individually and instead just make many copies
|
||||
// of the first element.
|
||||
let elem_size = first.layout.size;
|
||||
let first_ptr = first.ptr;
|
||||
let first_ptr = first.ptr();
|
||||
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
||||
// For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as
|
||||
// that place might be more aligned than its type mandates (a `u8` array could
|
||||
|
@ -305,7 +305,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
trace!("{:?}", self.dump_place(*dest));
|
||||
trace!("{:?}", self.dump_place(&dest));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -793,7 +793,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
throw_ub_custom!(fluent::const_eval_dyn_star_call_vtable_mismatch);
|
||||
}
|
||||
|
||||
(vptr, dyn_ty, recv.ptr)
|
||||
(vptr, dyn_ty, recv.ptr())
|
||||
} else {
|
||||
// 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`.)
|
||||
|
@ -810,7 +810,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
assert!(receiver_place.layout.is_unsized());
|
||||
|
||||
// Get the required information from the vtable.
|
||||
let vptr = receiver_place.meta.unwrap_meta().to_pointer(self)?;
|
||||
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_custom!(fluent::const_eval_dyn_call_vtable_mismatch);
|
||||
|
@ -819,7 +819,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
// 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)
|
||||
(vptr, dyn_ty, receiver_place.ptr())
|
||||
};
|
||||
|
||||
// Now determine the actual method to call. We can do that in two different ways and
|
||||
|
|
|
@ -360,7 +360,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
// Handle wide pointers.
|
||||
// Check metadata early, for better diagnostics
|
||||
if place.layout.is_unsized() {
|
||||
self.check_wide_ptr_meta(place.meta, place.layout)?;
|
||||
self.check_wide_ptr_meta(place.meta(), place.layout)?;
|
||||
}
|
||||
// Make sure this is dereferenceable and all.
|
||||
let size_and_align = try_validation!(
|
||||
|
@ -379,7 +379,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
// Direct call to `check_ptr_access_align` checks alignment even on CTFE machines.
|
||||
try_validation!(
|
||||
self.ecx.check_ptr_access_align(
|
||||
place.ptr,
|
||||
place.ptr(),
|
||||
size,
|
||||
align,
|
||||
CheckInAllocMsg::InboundsTest, // will anyway be replaced by validity message
|
||||
|
@ -414,7 +414,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
|
||||
// Proceed recursively even for ZST, no reason to skip them!
|
||||
// `!` is a ZST and we want to validate it.
|
||||
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
|
||||
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr()) {
|
||||
// Let's see what kind of memory this points to.
|
||||
let alloc_kind = self.ecx.tcx.try_get_global_alloc(alloc_id);
|
||||
match alloc_kind {
|
||||
|
@ -521,7 +521,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
|||
let place =
|
||||
self.ecx.ref_to_mplace(&self.read_immediate(value, ExpectedKind::RawPtr)?)?;
|
||||
if place.layout.is_unsized() {
|
||||
self.check_wide_ptr_meta(place.meta, place.layout)?;
|
||||
self.check_wide_ptr_meta(place.meta(), place.layout)?;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
@ -739,7 +739,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
let mplace = op.assert_mem_place(); // strings are unsized and hence never immediate
|
||||
let len = mplace.len(self.ecx)?;
|
||||
try_validation!(
|
||||
self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr, Size::from_bytes(len)),
|
||||
self.ecx.read_bytes_ptr_strip_provenance(mplace.ptr(), Size::from_bytes(len)),
|
||||
self.path,
|
||||
Ub(InvalidUninitBytes(..)) => Uninit { expected: ExpectedKind::Str },
|
||||
Unsup(ReadPointerAsInt(_)) => PointerAsInt { expected: ExpectedKind::Str }
|
||||
|
@ -789,7 +789,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
|
|||
// to reject those pointers, we just do not have the machinery to
|
||||
// talk about parts of a pointer.
|
||||
// We also accept uninit, for consistency with the slow path.
|
||||
let alloc = self.ecx.get_ptr_alloc(mplace.ptr, size, mplace.align)?.expect("we already excluded size 0");
|
||||
let alloc = self.ecx.get_ptr_alloc(mplace.ptr(), size, mplace.align)?.expect("we already excluded size 0");
|
||||
|
||||
match alloc.get_bytes_strip_provenance() {
|
||||
// In the happy case, we needn't check anything else.
|
||||
|
|
|
@ -54,7 +54,7 @@ fn might_permit_raw_init_strict<'tcx>(
|
|||
|
||||
if kind == ValidityRequirement::Zero {
|
||||
cx.write_bytes_ptr(
|
||||
allocated.ptr,
|
||||
allocated.ptr(),
|
||||
std::iter::repeat(0_u8).take(ty.layout.size().bytes_usize()),
|
||||
)
|
||||
.expect("failed to write bytes for zero valid check");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue