use std::convert::TryFrom;
use std::hash::Hash;
-use rustc::hir;
use rustc::mir;
use rustc::mir::interpret::truncate;
use rustc::ty::{self, Ty};
use rustc::ty::TypeFoldable;
use super::{
- GlobalId, AllocId, Allocation, Scalar, EvalResult, Pointer, PointerArithmetic,
+ GlobalId, AllocId, Allocation, Scalar, InterpResult, Pointer, PointerArithmetic,
InterpretCx, Machine, AllocMap, AllocationExtra,
RawConst, Immediate, ImmTy, ScalarMaybeUndef, Operand, OpTy, MemoryKind, LocalValue
};
/// metact the ptr part of the mplace
#[inline(always)]
- pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
+ pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
// At this point, we forget about the alignment information --
// the place has been turned into a reference, and no matter where it came from,
// it now must be aligned.
offset: Size,
meta: Option<Scalar<Tag>>,
cx: &impl HasDataLayout,
- ) -> EvalResult<'tcx, Self> {
+ ) -> InterpResult<'tcx, Self> {
Ok(MemPlace {
ptr: self.ptr.ptr_offset(offset, cx)?,
align: self.align.restrict_for_offset(offset),
meta: Option<Scalar<Tag>>,
layout: TyLayout<'tcx>,
cx: &impl HasDataLayout,
- ) -> EvalResult<'tcx, Self> {
+ ) -> InterpResult<'tcx, Self> {
Ok(MPlaceTy {
mplace: self.mplace.offset(offset, meta, cx)?,
layout,
}
#[inline]
- pub(super) fn len(self, cx: &impl HasDataLayout) -> EvalResult<'tcx, u64> {
+ pub(super) fn len(self, cx: &impl HasDataLayout) -> InterpResult<'tcx, u64> {
if self.layout.is_unsized() {
// We need to consult `meta` metadata
match self.layout.ty.sty {
}
#[inline]
- pub(super) fn vtable(self) -> EvalResult<'tcx, Pointer<Tag>> {
+ pub(super) fn vtable(self) -> InterpResult<'tcx, Pointer<Tag>> {
match self.layout.ty.sty {
ty::Dynamic(..) => self.mplace.meta.unwrap().to_ptr(),
_ => bug!("vtable not supported on type {:?}", self.layout.ty),
}
#[inline]
- pub fn to_ptr(self) -> EvalResult<'tcx, Pointer<Tag>> {
+ pub fn to_ptr(self) -> InterpResult<'tcx, Pointer<Tag>> {
self.to_mem_place().to_ptr()
}
}
impl<'a, 'mir, 'tcx, Tag, M> InterpretCx<'a, 'mir, 'tcx, M>
where
// FIXME: Working around https://github.com/rust-lang/rust/issues/54385
- Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static,
+ Tag: ::std::fmt::Debug + Copy + Eq + Hash + 'static,
M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>,
// FIXME: Working around https://github.com/rust-lang/rust/issues/24159
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
pub fn ref_to_mplace(
&self,
val: ImmTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
let layout = self.layout_of(pointee_type)?;
// Take an operand, representing a pointer, and dereference it to a place -- that
// will always be a MemPlace. Lives in `place.rs` because it creates a place.
- // This calls the "deref" machine hook, and counts as a deref as far as
- // Stacked Borrows is concerned.
pub fn deref_operand(
&self,
src: OpTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
let val = self.read_immediate(src)?;
trace!("deref to {} on {:?}", val.layout.ty, *val);
- let mut place = self.ref_to_mplace(val)?;
- // Pointer tag tracking might want to adjust the tag.
- let mutbl = match val.layout.ty.sty {
- // `builtin_deref` considers boxes immutable, that's useless for our purposes
- ty::Ref(_, _, mutbl) => Some(mutbl),
- ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
- ty::RawPtr(_) => None,
- _ => bug!("Unexpected pointer type {}", val.layout.ty),
- };
- place.mplace.ptr = M::tag_dereference(self, place, mutbl)?;
- Ok(place)
+ self.ref_to_mplace(val)
}
- /// Offset a pointer to project to a field. Unlike place_field, this is always
- /// possible without allocating, so it can take &self. Also return the field's layout.
+ /// Offset a pointer to project to a field. Unlike `place_field`, this is always
+ /// possible without allocating, so it can take `&self`. Also return the field's layout.
/// This supports both struct and array fields.
#[inline(always)]
pub fn mplace_field(
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
field: u64,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
// Not using the layout method because we want to compute on u64
let offset = match base.layout.fields {
layout::FieldPlacement::Arbitrary { ref offsets, .. } =>
&self,
base: MPlaceTy<'tcx, Tag>,
) ->
- EvalResult<'tcx, impl Iterator<Item=EvalResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a>
+ InterpResult<'tcx, impl Iterator<Item=InterpResult<'tcx, MPlaceTy<'tcx, Tag>>> + 'a>
{
let len = base.len(self)?; // also asserts that we have a type where this makes sense
let stride = match base.layout.fields {
base: MPlaceTy<'tcx, M::PointerTag>,
from: u64,
to: u64,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
let len = base.len(self)?; // also asserts that we have a type where this makes sense
assert!(from <= len - to);
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
variant: VariantIdx,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
// Downcasts only change the layout
assert!(base.meta.is_none());
Ok(MPlaceTy { layout: base.layout.for_variant(self, variant), ..base })
&self,
base: MPlaceTy<'tcx, M::PointerTag>,
proj_elem: &mir::PlaceElem<'tcx>,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::ProjectionElem::*;
Ok(match *proj_elem {
Field(field, _) => self.mplace_field(base, field.index() as u64)?,
&mut self,
base: PlaceTy<'tcx, M::PointerTag>,
field: u64,
- ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
// FIXME: We could try to be smarter and avoid allocation for fields that span the
// entire place.
let mplace = self.force_allocation(base)?;
&self,
base: PlaceTy<'tcx, M::PointerTag>,
variant: VariantIdx,
- ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
// Downcast just changes the layout
Ok(match base.place {
Place::Ptr(mplace) =>
&mut self,
base: PlaceTy<'tcx, M::PointerTag>,
proj_elem: &mir::ProjectionElem<mir::Local, Ty<'tcx>>,
- ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::ProjectionElem::*;
Ok(match *proj_elem {
Field(field, _) => self.place_field(base, field.index() as u64)?,
pub(super) fn eval_static_to_mplace(
&self,
place_static: &mir::Static<'tcx>
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::StaticKind;
Ok(match place_static.kind {
promoted: None
};
// Just create a lazy reference, so we can support recursive statics.
- // tcx takes are of assigning every static one and only one unique AllocId.
+ // tcx takes care of assigning every static one and only one unique AllocId.
// When the data here is ever actually used, memory will notice,
// and it knows how to deal with alloc_id that are present in the
// global table but not in its local memory: It calls back into tcx through
// a query, triggering the CTFE machinery to actually turn this lazy reference
// into a bunch of bytes. IOW, statics are evaluated with CTFE even when
// this InterpretCx uses another Machine (e.g., in miri). This is what we
- // want! This way, computing statics works concistently between codegen
+ // want! This way, computing statics works consistently between codegen
// and miri: They use the same query to eventually obtain a `ty::Const`
// and use that for further computation.
- let alloc = self.tcx.alloc_map.lock().intern_static(cid.instance.def_id());
- MPlaceTy::from_aligned_ptr(Pointer::from(alloc).with_default_tag(), layout)
+ //
+ // Notice that statics have *two* AllocIds: the lazy one, and the resolved
+ // one. Here we make sure that the interpreted program never sees the
+ // resolved ID. Also see the doc comment of `Memory::get_static_alloc`.
+ let alloc_id = self.tcx.alloc_map.lock().create_static_alloc(cid.instance.def_id());
+ let ptr = self.tag_static_base_pointer(Pointer::from(alloc_id));
+ MPlaceTy::from_aligned_ptr(ptr, layout)
}
})
}
pub fn eval_place(
&mut self,
mir_place: &mir::Place<'tcx>,
- ) -> EvalResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, PlaceTy<'tcx, M::PointerTag>> {
use rustc::mir::PlaceBase;
mir_place.iterate(|place_base, place_projection| {
&mut self,
val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
self.write_immediate(Immediate::Scalar(val.into()), dest)
}
&mut self,
src: Immediate<M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
self.write_immediate_no_validate(src, dest)?;
if M::enforce_validity(self) {
Ok(())
}
+ /// Write an `Immediate` to memory.
+ #[inline(always)]
+ pub fn write_immediate_to_mplace(
+ &mut self,
+ src: Immediate<M::PointerTag>,
+ dest: MPlaceTy<'tcx, M::PointerTag>,
+ ) -> EvalResult<'tcx> {
+ self.write_immediate_to_mplace_no_validate(src, dest)?;
+
+ if M::enforce_validity(self) {
+ // Data got changed, better make sure it matches the type!
+ self.validate_operand(dest.into(), vec![], None, /*const_mode*/ false)?;
+ }
+
+ Ok(())
+ }
+
/// Write an immediate to a place.
/// If you use this you are responsible for validating that things got copied at the
/// right type.
&mut self,
src: Immediate<M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
if cfg!(debug_assertions) {
// This is a very common path, avoid some checks in release mode
assert!(!dest.layout.is_unsized(), "Cannot write unsized data");
}
/// Write an immediate to memory.
- /// If you use this you are responsible for validating that things git copied at the
+ /// If you use this you are responsible for validating that things got copied at the
/// right type.
fn write_immediate_to_mplace_no_validate(
&mut self,
value: Immediate<M::PointerTag>,
dest: MPlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
let (ptr, ptr_align) = dest.to_scalar_ptr_align();
// Note that it is really important that the type here is the right one, and matches the
// type things are read at. In case `src_val` is a `ScalarPair`, we don't do any magic here
&mut self,
src: OpTy<'tcx, M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
self.copy_op_no_validate(src, dest)?;
if M::enforce_validity(self) {
/// Copies the data from an operand to a place. This does not support transmuting!
/// Use `copy_op_transmute` if the layouts could disagree.
- /// Also, if you use this you are responsible for validating that things git copied at the
+ /// Also, if you use this you are responsible for validating that things get copied at the
/// right type.
fn copy_op_no_validate(
&mut self,
src: OpTy<'tcx, M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
// We do NOT compare the types for equality, because well-typed code can
// actually "transmute" `&mut T` to `&T` in an assignment without a cast.
assert!(src.layout.details == dest.layout.details,
&mut self,
src: OpTy<'tcx, M::PointerTag>,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
if src.layout.details == dest.layout.details {
// Fast path: Just use normal `copy_op`
return self.copy_op(src, dest);
&mut self,
place: PlaceTy<'tcx, M::PointerTag>,
meta: Option<Scalar<M::PointerTag>>,
- ) -> EvalResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option<Size>)> {
+ ) -> InterpResult<'tcx, (MPlaceTy<'tcx, M::PointerTag>, Option<Size>)> {
let (mplace, size) = match place.place {
Place::Local { frame, local } => {
match self.stack[frame].locals[local].access_mut()? {
pub fn force_allocation(
&mut self,
place: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
Ok(self.force_allocation_maybe_sized(place, None)?.0)
}
&mut self,
variant_index: VariantIdx,
dest: PlaceTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx> {
+ ) -> InterpResult<'tcx> {
match dest.layout.variants {
layout::Variants::Single { index } => {
assert_eq!(index, variant_index);
pub fn raw_const_to_mplace(
&self,
raw: RawConst<'tcx>,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
// This must be an allocation in `tcx`
assert!(self.tcx.alloc_map.lock().get(raw.alloc_id).is_some());
+ let ptr = self.tag_static_base_pointer(Pointer::from(raw.alloc_id));
let layout = self.layout_of(raw.ty)?;
- Ok(MPlaceTy::from_aligned_ptr(
- Pointer::new(raw.alloc_id, Size::ZERO).with_default_tag(),
- layout,
- ))
+ Ok(MPlaceTy::from_aligned_ptr(ptr, layout))
}
/// Turn a place with a `dyn Trait` type into a place with the actual dynamic type.
/// Also return some more information so drop doesn't have to run the same code twice.
pub(super) fn unpack_dyn_trait(&self, mplace: MPlaceTy<'tcx, M::PointerTag>)
- -> EvalResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
+ -> InterpResult<'tcx, (ty::Instance<'tcx>, MPlaceTy<'tcx, M::PointerTag>)> {
let vtable = mplace.vtable()?; // also sanity checks the type
let (instance, ty) = self.read_drop_type_from_vtable(vtable)?;
let layout = self.layout_of(ty)?;