Auto merge of #55125 - RalfJung:stacked-borrows, r=oli-obk
miri engine: Hooks for basic stacked borrows r? @oli-obk
This commit is contained in:
commit
66910ba686
14 changed files with 313 additions and 149 deletions
|
|
@ -524,7 +524,7 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
|
||||
pub struct Allocation<Tag=()> {
|
||||
pub struct Allocation<Tag=(),Extra=()> {
|
||||
/// The actual bytes of the allocation.
|
||||
/// Note that the bytes of a pointer represent the offset of the pointer
|
||||
pub bytes: Vec<u8>,
|
||||
|
|
@ -541,9 +541,11 @@ pub struct Allocation<Tag=()> {
|
|||
/// Also used by codegen to determine if a static should be put into mutable memory,
|
||||
/// which happens for `static mut` and `static` with interior mutability.
|
||||
pub mutability: Mutability,
|
||||
/// Extra state for the machine.
|
||||
pub extra: Extra,
|
||||
}
|
||||
|
||||
impl<Tag> Allocation<Tag> {
|
||||
impl<Tag, Extra: Default> Allocation<Tag, Extra> {
|
||||
/// Creates a read-only allocation initialized by the given bytes
|
||||
pub fn from_bytes(slice: &[u8], align: Align) -> Self {
|
||||
let mut undef_mask = UndefMask::new(Size::ZERO);
|
||||
|
|
@ -554,6 +556,7 @@ impl<Tag> Allocation<Tag> {
|
|||
undef_mask,
|
||||
align,
|
||||
mutability: Mutability::Immutable,
|
||||
extra: Extra::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -569,6 +572,7 @@ impl<Tag> Allocation<Tag> {
|
|||
undef_mask: UndefMask::new(size),
|
||||
align,
|
||||
mutability: Mutability::Mutable,
|
||||
extra: Extra::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ use std::collections::hash_map::Entry;
|
|||
use rustc::hir::{self, def_id::DefId};
|
||||
use rustc::mir::interpret::ConstEvalErr;
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt};
|
||||
use rustc::ty::layout::{self, LayoutOf, TyLayout};
|
||||
use rustc::ty::{self, Ty, TyCtxt, Instance, query::TyCtxtAt};
|
||||
use rustc::ty::layout::{self, Size, LayoutOf, TyLayout};
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -28,13 +28,10 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use syntax::ast::Mutability;
|
||||
use syntax::source_map::{Span, DUMMY_SP};
|
||||
|
||||
use rustc::mir::interpret::{
|
||||
EvalResult, EvalError, EvalErrorKind, GlobalId,
|
||||
Scalar, Allocation, AllocId, ConstValue,
|
||||
};
|
||||
use interpret::{self,
|
||||
PlaceTy, MemPlace, OpTy, Operand, Value,
|
||||
EvalContext, StackPopCleanup, MemoryKind,
|
||||
PlaceTy, MemPlace, OpTy, Operand, Value, Pointer, Scalar, ConstValue,
|
||||
EvalResult, EvalError, EvalErrorKind, GlobalId, EvalContext, StackPopCleanup,
|
||||
Allocation, AllocId, MemoryKind,
|
||||
snapshot,
|
||||
};
|
||||
|
||||
|
|
@ -53,7 +50,7 @@ pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
|
|||
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'mir, 'tcx>> {
|
||||
debug!("mk_borrowck_eval_cx: {:?}", instance);
|
||||
let param_env = tcx.param_env(instance.def_id());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
|
||||
// insert a stack frame so any queries have the correct substs
|
||||
// cannot use `push_stack_frame`; if we do `const_prop` explodes
|
||||
ecx.stack.push(interpret::Frame {
|
||||
|
|
@ -76,7 +73,7 @@ pub fn mk_eval_cx<'a, 'tcx>(
|
|||
) -> EvalResult<'tcx, CompileTimeEvalContext<'a, 'tcx, 'tcx>> {
|
||||
debug!("mk_eval_cx: {:?}, {:?}", instance, param_env);
|
||||
let span = tcx.def_span(instance.def_id());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
|
||||
let mir = ecx.load_mir(instance.def)?;
|
||||
// insert a stack frame so any queries have the correct substs
|
||||
ecx.push_stack_frame(
|
||||
|
|
@ -155,7 +152,7 @@ fn eval_body_and_ecx<'a, 'mir, 'tcx>(
|
|||
// and try improving it down the road when more information is available
|
||||
let span = tcx.def_span(cid.instance.def_id());
|
||||
let span = mir.map(|mir| mir.span).unwrap_or(span);
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new(), ());
|
||||
let mut ecx = EvalContext::new(tcx.at(span), param_env, CompileTimeInterpreter::new());
|
||||
let r = eval_body_using_ecx(&mut ecx, cid, mir, param_env);
|
||||
(r, ecx)
|
||||
}
|
||||
|
|
@ -333,16 +330,25 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
|
|||
type CompileTimeEvalContext<'a, 'mir, 'tcx> =
|
||||
EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>;
|
||||
|
||||
impl interpret::MayLeak for ! {
|
||||
#[inline(always)]
|
||||
fn may_leak(self) -> bool {
|
||||
// `self` is uninhabited
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
|
||||
for CompileTimeInterpreter<'a, 'mir, 'tcx>
|
||||
{
|
||||
type MemoryData = ();
|
||||
type MemoryKinds = !;
|
||||
type AllocExtra = ();
|
||||
type PointerTag = ();
|
||||
|
||||
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
|
||||
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation)>;
|
||||
|
||||
const STATIC_KIND: Option<!> = None; // no copying of statics allowed
|
||||
const ENABLE_PTR_TRACKING_HOOKS: bool = false; // we don't have no provenance
|
||||
|
||||
#[inline(always)]
|
||||
fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
|
||||
|
|
@ -456,6 +462,26 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
|
|||
&ecx.stack[..],
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tag_reference(
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_ptr: Pointer<Self::PointerTag>,
|
||||
_pointee_ty: Ty<'tcx>,
|
||||
_pointee_size: Size,
|
||||
_borrow_kind: Option<mir::BorrowKind>,
|
||||
) -> EvalResult<'tcx, Self::PointerTag> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tag_dereference(
|
||||
_ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_ptr: Pointer<Self::PointerTag>,
|
||||
_ptr_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx, Self::PointerTag> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Project to a field of a (variant of a) const
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
kind: CastKind,
|
||||
dest: PlaceTy<'tcx, M::PointerTag>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let src_layout = src.layout;
|
||||
let dst_layout = dest.layout;
|
||||
use rustc::mir::CastKind::*;
|
||||
match kind {
|
||||
Unsize => {
|
||||
|
|
@ -46,15 +44,28 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
}
|
||||
|
||||
Misc => {
|
||||
let src_layout = src.layout;
|
||||
let src = self.read_value(src)?;
|
||||
|
||||
let src = if M::ENABLE_PTR_TRACKING_HOOKS && src_layout.ty.is_region_ptr() {
|
||||
// The only `Misc` casts on references are those creating raw pointers.
|
||||
assert!(dest.layout.ty.is_unsafe_ptr());
|
||||
// For the purpose of the "ptr tag hooks", treat this as creating
|
||||
// a new, raw reference.
|
||||
let place = self.ref_to_mplace(src)?;
|
||||
self.create_ref(place, None)?
|
||||
} else {
|
||||
*src
|
||||
};
|
||||
|
||||
if self.type_is_fat_ptr(src_layout.ty) {
|
||||
match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
|
||||
match (src, self.type_is_fat_ptr(dest.layout.ty)) {
|
||||
// pointers to extern types
|
||||
(Value::Scalar(_),_) |
|
||||
// slices and trait objects to other slices/trait objects
|
||||
(Value::ScalarPair(..), true) => {
|
||||
// No change to value
|
||||
self.write_value(*src, dest)?;
|
||||
self.write_value(src, dest)?;
|
||||
}
|
||||
// slices and trait objects to thin pointers (dropping the metadata)
|
||||
(Value::ScalarPair(data, _), false) => {
|
||||
|
|
@ -65,11 +76,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
match src_layout.variants {
|
||||
layout::Variants::Single { index } => {
|
||||
if let Some(def) = src_layout.ty.ty_adt_def() {
|
||||
// Cast from a univariant enum
|
||||
assert!(src_layout.is_zst());
|
||||
let discr_val = def
|
||||
.discriminant_for_variant(*self.tcx, index)
|
||||
.val;
|
||||
return self.write_scalar(
|
||||
Scalar::from_uint(discr_val, dst_layout.size),
|
||||
Scalar::from_uint(discr_val, dest.layout.size),
|
||||
dest);
|
||||
}
|
||||
}
|
||||
|
|
@ -85,7 +98,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
|
||||
ReifyFnPointer => {
|
||||
// The src operand does not matter, just its type
|
||||
match src_layout.ty.sty {
|
||||
match src.layout.ty.sty {
|
||||
ty::FnDef(def_id, substs) => {
|
||||
if self.tcx.has_attr(def_id, "rustc_args_required_const") {
|
||||
bug!("reifying a fn ptr that requires \
|
||||
|
|
@ -117,7 +130,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
|
||||
ClosureFnPointer => {
|
||||
// The src operand does not matter, just its type
|
||||
match src_layout.ty.sty {
|
||||
match src.layout.ty.sty {
|
||||
ty::Closure(def_id, substs) => {
|
||||
let substs = self.tcx.subst_and_normalize_erasing_regions(
|
||||
self.substs(),
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
use std::fmt::Write;
|
||||
use std::mem;
|
||||
|
||||
use syntax::source_map::{self, Span, DUMMY_SP};
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::map::definitions::DefPathData;
|
||||
|
|
@ -29,8 +30,6 @@ use rustc::mir::interpret::{
|
|||
};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
||||
use syntax::source_map::{self, Span};
|
||||
|
||||
use super::{
|
||||
Value, Operand, MemPlace, MPlaceTy, Place, PlaceTy, ScalarMaybeUndef,
|
||||
Memory, Machine
|
||||
|
|
@ -205,63 +204,59 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
machine: M,
|
||||
memory_data: M::MemoryData,
|
||||
) -> Self {
|
||||
EvalContext {
|
||||
machine,
|
||||
tcx,
|
||||
param_env,
|
||||
memory: Memory::new(tcx, memory_data),
|
||||
memory: Memory::new(tcx),
|
||||
stack: Vec::new(),
|
||||
vtables: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
|
||||
&self.memory
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> {
|
||||
&mut self.memory
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag>] {
|
||||
&self.stack
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn cur_frame(&self) -> usize {
|
||||
assert!(self.stack.len() > 0);
|
||||
self.stack.len() - 1
|
||||
}
|
||||
|
||||
/// Mark a storage as live, killing the previous content and returning it.
|
||||
/// Remember to deallocate that!
|
||||
pub fn storage_live(
|
||||
&mut self,
|
||||
local: mir::Local
|
||||
) -> EvalResult<'tcx, LocalValue<M::PointerTag>> {
|
||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
|
||||
trace!("{:?} is now live", local);
|
||||
|
||||
let layout = self.layout_of_local(self.cur_frame(), local)?;
|
||||
let init = LocalValue::Live(self.uninit_operand(layout)?);
|
||||
// StorageLive *always* kills the value that's currently stored
|
||||
Ok(mem::replace(&mut self.frame_mut().locals[local], init))
|
||||
#[inline(always)]
|
||||
pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag> {
|
||||
self.stack.last().expect("no call frames exist")
|
||||
}
|
||||
|
||||
/// Returns the old value of the local.
|
||||
/// Remember to deallocate that!
|
||||
pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue<M::PointerTag> {
|
||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
||||
trace!("{:?} is now dead", local);
|
||||
|
||||
mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
|
||||
#[inline(always)]
|
||||
pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag> {
|
||||
self.stack.last_mut().expect("no call frames exist")
|
||||
}
|
||||
|
||||
pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value<M::PointerTag>> {
|
||||
let ptr = self.memory.allocate_static_bytes(s.as_bytes());
|
||||
Ok(Value::new_slice(Scalar::Ptr(ptr), s.len() as u64, self.tcx.tcx))
|
||||
#[inline(always)]
|
||||
pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> {
|
||||
self.frame().mir
|
||||
}
|
||||
|
||||
pub fn substs(&self) -> &'tcx Substs<'tcx> {
|
||||
if let Some(frame) = self.stack.last() {
|
||||
frame.instance.substs
|
||||
} else {
|
||||
Substs::empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn resolve(
|
||||
|
|
@ -285,10 +280,14 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
).ok_or_else(|| EvalErrorKind::TooGeneric.into())
|
||||
}
|
||||
|
||||
pub(super) fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
|
||||
pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_sized(self.tcx, self.param_env)
|
||||
}
|
||||
|
||||
pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_freeze(*self.tcx, self.param_env, DUMMY_SP)
|
||||
}
|
||||
|
||||
pub fn load_mir(
|
||||
&self,
|
||||
instance: ty::InstanceDef<'tcx>,
|
||||
|
|
@ -336,6 +335,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
self.layout_of(local_ty)
|
||||
}
|
||||
|
||||
pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value<M::PointerTag>> {
|
||||
let ptr = self.memory.allocate_static_bytes(s.as_bytes());
|
||||
Ok(Value::new_slice(Scalar::Ptr(ptr), s.len() as u64, self.tcx.tcx))
|
||||
}
|
||||
|
||||
/// Return the actual dynamic size and alignment of the place at the given type.
|
||||
/// Only the "meta" (metadata) part of the place matters.
|
||||
/// This can fail to provide an answer for extern types.
|
||||
|
|
@ -354,11 +358,11 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
// and it also rounds up to alignment, which we want to avoid,
|
||||
// as the unsized field's alignment could be smaller.
|
||||
assert!(!layout.ty.is_simd());
|
||||
debug!("DST layout: {:?}", layout);
|
||||
trace!("DST layout: {:?}", layout);
|
||||
|
||||
let sized_size = layout.fields.offset(layout.fields.count() - 1);
|
||||
let sized_align = layout.align;
|
||||
debug!(
|
||||
trace!(
|
||||
"DST {} statically sized prefix size: {:?} align: {:?}",
|
||||
layout.ty,
|
||||
sized_size,
|
||||
|
|
@ -434,6 +438,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
return_place: Option<PlaceTy<'tcx, M::PointerTag>>,
|
||||
return_to_block: StackPopCleanup,
|
||||
) -> EvalResult<'tcx> {
|
||||
if self.stack.len() > 1 { // FIXME should be "> 0", printing topmost frame crashes rustc...
|
||||
debug!("PAUSING({}) {}", self.cur_frame(), self.frame().instance);
|
||||
}
|
||||
::log_settings::settings().indentation += 1;
|
||||
|
||||
// first push a stack frame so we have access to the local substs
|
||||
|
|
@ -498,6 +505,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
self.frame_mut().locals = locals;
|
||||
}
|
||||
|
||||
if self.stack.len() > 1 { // FIXME no check should be needed, but some instances ICE
|
||||
debug!("ENTERING({}) {}", self.cur_frame(), self.frame().instance);
|
||||
}
|
||||
|
||||
if self.stack.len() > self.tcx.sess.const_eval_stack_frame_limit {
|
||||
err!(StackFrameLimitReached)
|
||||
} else {
|
||||
|
|
@ -506,6 +517,9 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
}
|
||||
|
||||
pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> {
|
||||
if self.stack.len() > 1 { // FIXME no check should be needed, but some instances ICE
|
||||
debug!("LEAVING({}) {}", self.cur_frame(), self.frame().instance);
|
||||
}
|
||||
::log_settings::settings().indentation -= 1;
|
||||
let frame = self.stack.pop().expect(
|
||||
"tried to pop a stack frame, but there were none",
|
||||
|
|
@ -549,9 +563,37 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
return err!(Unreachable);
|
||||
}
|
||||
|
||||
if self.stack.len() > 1 { // FIXME should be "> 0", printing topmost frame crashes rustc...
|
||||
debug!("CONTINUING({}) {}", self.cur_frame(), self.frame().instance);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mark a storage as live, killing the previous content and returning it.
|
||||
/// Remember to deallocate that!
|
||||
pub fn storage_live(
|
||||
&mut self,
|
||||
local: mir::Local
|
||||
) -> EvalResult<'tcx, LocalValue<M::PointerTag>> {
|
||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place live");
|
||||
trace!("{:?} is now live", local);
|
||||
|
||||
let layout = self.layout_of_local(self.cur_frame(), local)?;
|
||||
let init = LocalValue::Live(self.uninit_operand(layout)?);
|
||||
// StorageLive *always* kills the value that's currently stored
|
||||
Ok(mem::replace(&mut self.frame_mut().locals[local], init))
|
||||
}
|
||||
|
||||
/// Returns the old value of the local.
|
||||
/// Remember to deallocate that!
|
||||
pub fn storage_dead(&mut self, local: mir::Local) -> LocalValue<M::PointerTag> {
|
||||
assert!(local != mir::RETURN_PLACE, "Cannot make return place dead");
|
||||
trace!("{:?} is now dead", local);
|
||||
|
||||
mem::replace(&mut self.frame_mut().locals[local], LocalValue::Dead)
|
||||
}
|
||||
|
||||
pub(super) fn deallocate_local(
|
||||
&mut self,
|
||||
local: LocalValue<M::PointerTag>,
|
||||
|
|
@ -576,28 +618,6 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tc
|
|||
.map_err(|err| EvalErrorKind::ReferencedConstant(err).into())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag> {
|
||||
self.stack.last().expect("no call frames exist")
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag> {
|
||||
self.stack.last_mut().expect("no call frames exist")
|
||||
}
|
||||
|
||||
pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> {
|
||||
self.frame().mir
|
||||
}
|
||||
|
||||
pub fn substs(&self) -> &'tcx Substs<'tcx> {
|
||||
if let Some(frame) = self.stack.last() {
|
||||
frame.instance.substs
|
||||
} else {
|
||||
Substs::empty()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump_place(&self, place: Place<M::PointerTag>) {
|
||||
// Debug output
|
||||
if !log_enabled!(::log::Level::Trace) {
|
||||
|
|
|
|||
|
|
@ -16,11 +16,25 @@ use std::borrow::{Borrow, Cow};
|
|||
use std::hash::Hash;
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::mir::interpret::{Allocation, AllocId, EvalResult, Scalar};
|
||||
use rustc::mir;
|
||||
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
|
||||
use rustc::ty::{self, Ty, layout::{Size, TyLayout}, query::TyCtxtAt};
|
||||
|
||||
use super::{EvalContext, PlaceTy, OpTy, MemoryKind};
|
||||
use super::{
|
||||
Allocation, AllocId, EvalResult, Scalar,
|
||||
EvalContext, PlaceTy, OpTy, Pointer, MemoryKind,
|
||||
};
|
||||
|
||||
/// Classifying memory accesses
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum MemoryAccess {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
/// Whether this kind of memory is allowed to leak
|
||||
pub trait MayLeak: Copy {
|
||||
fn may_leak(self) -> bool;
|
||||
}
|
||||
|
||||
/// The functionality needed by memory to manage its allocations
|
||||
pub trait AllocMap<K: Hash + Eq, V> {
|
||||
|
|
@ -62,29 +76,36 @@ pub trait AllocMap<K: Hash + Eq, V> {
|
|||
/// Methods of this trait signifies a point where CTFE evaluation would fail
|
||||
/// and some use case dependent behaviour can instead be applied.
|
||||
pub trait Machine<'a, 'mir, 'tcx>: Sized {
|
||||
/// Additional data that can be accessed via the Memory
|
||||
type MemoryData;
|
||||
|
||||
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
|
||||
type MemoryKinds: ::std::fmt::Debug + Copy + Eq;
|
||||
type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
|
||||
|
||||
/// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
|
||||
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
|
||||
type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static;
|
||||
|
||||
/// Extra data stored in every allocation.
|
||||
type AllocExtra: ::std::fmt::Debug + Default + Clone;
|
||||
|
||||
/// Memory's allocation map
|
||||
type MemoryMap:
|
||||
AllocMap<AllocId, (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag>)> +
|
||||
AllocMap<
|
||||
AllocId,
|
||||
(MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
|
||||
> +
|
||||
Default +
|
||||
Clone;
|
||||
|
||||
/// Tag tracked alongside every pointer. This is inert for now, in preparation for
|
||||
/// a future implementation of "Stacked Borrows"
|
||||
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
|
||||
type PointerTag: ::std::fmt::Debug + Default + Copy + Eq + Hash + 'static;
|
||||
|
||||
/// The memory kind to use for copied statics -- or None if those are not supported.
|
||||
/// Statics are copied under two circumstances: When they are mutated, and when
|
||||
/// `static_with_default_tag` or `find_foreign_static` (see below) returns an owned allocation
|
||||
/// that is added to the memory so that the work is not done twice.
|
||||
const STATIC_KIND: Option<Self::MemoryKinds>;
|
||||
|
||||
/// As an optimization, you can prevent the pointer tracking hooks from ever being
|
||||
/// called. You should only do this if you do not care about provenance tracking.
|
||||
/// This controls the `tag_reference` and `tag_dereference` hooks.
|
||||
const ENABLE_PTR_TRACKING_HOOKS: bool;
|
||||
|
||||
/// Whether to enforce the validity invariant
|
||||
fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
|
||||
|
||||
|
|
@ -127,7 +148,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
|
|||
fn find_foreign_static(
|
||||
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag>>>;
|
||||
) -> EvalResult<'tcx, Cow<'tcx, Allocation<Self::PointerTag, Self::AllocExtra>>>;
|
||||
|
||||
/// Called to turn an allocation obtained from the `tcx` into one that has
|
||||
/// the appropriate tags on each pointer.
|
||||
|
|
@ -138,7 +159,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
|
|||
/// owned allocation to the map even when the map is shared.)
|
||||
fn static_with_default_tag(
|
||||
alloc: &'_ Allocation
|
||||
) -> Cow<'_, Allocation<Self::PointerTag>>;
|
||||
) -> Cow<'_, Allocation<Self::PointerTag, Self::AllocExtra>>;
|
||||
|
||||
/// Called for all binary operations on integer(-like) types when one operand is a pointer
|
||||
/// value, and for the `Offset` operation that is inherently about pointers.
|
||||
|
|
@ -153,15 +174,57 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
|
|||
right_layout: TyLayout<'tcx>,
|
||||
) -> EvalResult<'tcx, (Scalar<Self::PointerTag>, bool)>;
|
||||
|
||||
/// Heap allocations via the `box` keyword
|
||||
///
|
||||
/// Returns a pointer to the allocated memory
|
||||
/// Heap allocations via the `box` keyword.
|
||||
fn box_alloc(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
dest: PlaceTy<'tcx, Self::PointerTag>,
|
||||
) -> EvalResult<'tcx>;
|
||||
|
||||
/// Hook for performing extra checks on a memory access.
|
||||
///
|
||||
/// Takes read-only access to the allocation so we can keep all the memory read
|
||||
/// operations take `&self`. Use a `RefCell` in `AllocExtra` if you
|
||||
/// need to mutate.
|
||||
#[inline]
|
||||
fn memory_accessed(
|
||||
_alloc: &Allocation<Self::PointerTag, Self::AllocExtra>,
|
||||
_ptr: Pointer<Self::PointerTag>,
|
||||
_size: Size,
|
||||
_access: MemoryAccess,
|
||||
) -> EvalResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hook for performing extra checks when memory gets deallocated.
|
||||
#[inline]
|
||||
fn memory_deallocated(
|
||||
_alloc: &mut Allocation<Self::PointerTag, Self::AllocExtra>,
|
||||
_ptr: Pointer<Self::PointerTag>,
|
||||
) -> EvalResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Executed when evaluating the `&` operator: Creating a new reference.
|
||||
/// This has the chance to adjust the tag.
|
||||
/// `borrow_kind` can be `None` in case a raw ptr is being created.
|
||||
fn tag_reference(
|
||||
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
ptr: Pointer<Self::PointerTag>,
|
||||
pointee_ty: Ty<'tcx>,
|
||||
pointee_size: Size,
|
||||
borrow_kind: Option<mir::BorrowKind>,
|
||||
) -> EvalResult<'tcx, Self::PointerTag>;
|
||||
|
||||
/// Executed when evaluating the `*` operator: Following a reference.
|
||||
/// This has the change to adjust the tag.
|
||||
fn tag_dereference(
|
||||
ecx: &EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
ptr: Pointer<Self::PointerTag>,
|
||||
ptr_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx, Self::PointerTag>;
|
||||
|
||||
/// Execute a validation operation
|
||||
#[inline]
|
||||
fn validation_op(
|
||||
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
|
||||
_op: ::rustc::mir::ValidationOp,
|
||||
|
|
|
|||
|
|
@ -22,17 +22,16 @@ use std::borrow::Cow;
|
|||
|
||||
use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt};
|
||||
use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout};
|
||||
use rustc::mir::interpret::{
|
||||
Pointer, AllocId, Allocation, ConstValue, GlobalId,
|
||||
EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
|
||||
truncate
|
||||
};
|
||||
pub use rustc::mir::interpret::{write_target_uint, read_target_uint};
|
||||
pub use rustc::mir::interpret::{truncate, write_target_uint, read_target_uint};
|
||||
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
|
||||
|
||||
use syntax::ast::Mutability;
|
||||
|
||||
use super::{Machine, AllocMap, ScalarMaybeUndef};
|
||||
use super::{
|
||||
Pointer, AllocId, Allocation, ConstValue, GlobalId,
|
||||
EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
|
||||
Machine, MemoryAccess, AllocMap, MayLeak, ScalarMaybeUndef,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||
pub enum MemoryKind<T> {
|
||||
|
|
@ -44,12 +43,20 @@ pub enum MemoryKind<T> {
|
|||
Machine(T),
|
||||
}
|
||||
|
||||
impl<T: MayLeak> MayLeak for MemoryKind<T> {
|
||||
#[inline]
|
||||
fn may_leak(self) -> bool {
|
||||
match self {
|
||||
MemoryKind::Stack => false,
|
||||
MemoryKind::Vtable => true,
|
||||
MemoryKind::Machine(k) => k.may_leak()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `Memory` has to depend on the `Machine` because some of its operations
|
||||
// (e.g. `get`) call a `Machine` hook.
|
||||
pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
|
||||
/// Additional data required by the Machine
|
||||
pub data: M::MemoryData,
|
||||
|
||||
/// Allocations local to this instance of the miri engine. The kind
|
||||
/// helps ensure that the same mechanism is used for allocation and
|
||||
/// deallocation. When an allocation is not found here, it is a
|
||||
|
|
@ -91,11 +98,9 @@ impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> HasDataLayout
|
|||
// carefully copy only the reachable parts.
|
||||
impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>>
|
||||
Clone for Memory<'a, 'mir, 'tcx, M>
|
||||
where M::MemoryData: Clone
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Memory {
|
||||
data: self.data.clone(),
|
||||
alloc_map: self.alloc_map.clone(),
|
||||
dead_alloc_map: self.dead_alloc_map.clone(),
|
||||
tcx: self.tcx,
|
||||
|
|
@ -104,9 +109,8 @@ impl<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>>
|
|||
}
|
||||
|
||||
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
||||
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
|
||||
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>) -> Self {
|
||||
Memory {
|
||||
data,
|
||||
alloc_map: Default::default(),
|
||||
dead_alloc_map: FxHashMap::default(),
|
||||
tcx,
|
||||
|
|
@ -123,7 +127,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
|
||||
pub fn allocate_with(
|
||||
&mut self,
|
||||
alloc: Allocation<M::PointerTag>,
|
||||
alloc: Allocation<M::PointerTag, M::AllocExtra>,
|
||||
kind: MemoryKind<M::MemoryKinds>,
|
||||
) -> EvalResult<'tcx, AllocId> {
|
||||
let id = self.tcx.alloc_map.lock().reserve();
|
||||
|
|
@ -186,13 +190,13 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
size_and_align: Option<(Size, Align)>,
|
||||
kind: MemoryKind<M::MemoryKinds>,
|
||||
) -> EvalResult<'tcx> {
|
||||
debug!("deallocating: {}", ptr.alloc_id);
|
||||
trace!("deallocating: {}", ptr.alloc_id);
|
||||
|
||||
if ptr.offset.bytes() != 0 {
|
||||
return err!(DeallocateNonBasePtr);
|
||||
}
|
||||
|
||||
let (alloc_kind, alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
|
||||
let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
|
||||
Some(alloc) => alloc,
|
||||
None => {
|
||||
// Deallocating static memory -- always an error
|
||||
|
|
@ -227,6 +231,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
}
|
||||
|
||||
// Let the machine take some extra action
|
||||
M::memory_deallocated(&mut alloc, ptr)?;
|
||||
|
||||
// Don't forget to remember size and align of this now-dead allocation
|
||||
let old = self.dead_alloc_map.insert(
|
||||
ptr.alloc_id,
|
||||
|
|
@ -334,7 +341,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
fn get_static_alloc(
|
||||
tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
|
||||
id: AllocId,
|
||||
) -> EvalResult<'tcx, Cow<'tcx, Allocation<M::PointerTag>>> {
|
||||
) -> EvalResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
|
||||
let alloc = tcx.alloc_map.lock().get(id);
|
||||
let def_id = match alloc {
|
||||
Some(AllocType::Memory(mem)) => {
|
||||
|
|
@ -376,7 +383,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::PointerTag>> {
|
||||
pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
|
||||
// The error type of the inner closure here is somewhat funny. We have two
|
||||
// ways of "erroring": An actual error, or because we got a reference from
|
||||
// `get_static_alloc` that we can actually use directly without inserting anything anywhere.
|
||||
|
|
@ -409,7 +416,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
pub fn get_mut(
|
||||
&mut self,
|
||||
id: AllocId,
|
||||
) -> EvalResult<'tcx, &mut Allocation<M::PointerTag>> {
|
||||
) -> EvalResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
|
||||
let tcx = self.tcx;
|
||||
let a = self.alloc_map.get_mut_or(id, || {
|
||||
// Need to make a copy, even if `get_static_alloc` is able
|
||||
|
|
@ -482,12 +489,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
self.dump_allocs(vec![id]);
|
||||
}
|
||||
|
||||
fn dump_alloc_helper<Tag>(
|
||||
fn dump_alloc_helper<Tag, Extra>(
|
||||
&self,
|
||||
allocs_seen: &mut FxHashSet<AllocId>,
|
||||
allocs_to_print: &mut VecDeque<AllocId>,
|
||||
mut msg: String,
|
||||
alloc: &Allocation<Tag>,
|
||||
alloc: &Allocation<Tag, Extra>,
|
||||
extra: String,
|
||||
) {
|
||||
use std::fmt::Write;
|
||||
|
|
@ -590,13 +597,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
pub fn leak_report(&self) -> usize {
|
||||
trace!("### LEAK REPORT ###");
|
||||
let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| {
|
||||
// exclude statics and vtables
|
||||
let exclude = match kind {
|
||||
MemoryKind::Stack => false,
|
||||
MemoryKind::Vtable => true,
|
||||
MemoryKind::Machine(k) => Some(k) == M::STATIC_KIND,
|
||||
};
|
||||
if exclude { None } else { Some(id) }
|
||||
if kind.may_leak() { None } else { Some(id) }
|
||||
});
|
||||
let n = leaks.len();
|
||||
self.dump_allocs(leaks);
|
||||
|
|
@ -633,6 +634,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
let alloc = self.get(ptr.alloc_id)?;
|
||||
M::memory_accessed(alloc, ptr, size, MemoryAccess::Read)?;
|
||||
|
||||
assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
|
||||
assert_eq!(size.bytes() as usize as u64, size.bytes());
|
||||
let offset = ptr.offset.bytes() as usize;
|
||||
|
|
@ -677,6 +680,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
self.clear_relocations(ptr, size)?;
|
||||
|
||||
let alloc = self.get_mut(ptr.alloc_id)?;
|
||||
M::memory_accessed(alloc, ptr, size, MemoryAccess::Write)?;
|
||||
|
||||
assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
|
||||
assert_eq!(size.bytes() as usize as u64, size.bytes());
|
||||
let offset = ptr.offset.bytes() as usize;
|
||||
|
|
@ -687,8 +692,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
|
|||
/// Interning (for CTFE)
|
||||
impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M>
|
||||
where
|
||||
M: Machine<'a, 'mir, 'tcx, PointerTag=()>,
|
||||
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<()>)>,
|
||||
M: Machine<'a, 'mir, 'tcx, PointerTag=(), AllocExtra=()>,
|
||||
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
|
||||
{
|
||||
/// mark an allocation as static and initialized, either mutable or not
|
||||
pub fn intern_static(
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ mod traits;
|
|||
mod validity;
|
||||
mod intrinsics;
|
||||
|
||||
pub use rustc::mir::interpret::*; // have all the `interpret` symbols in one place: here
|
||||
|
||||
pub use self::eval_context::{
|
||||
EvalContext, Frame, StackPopCleanup, LocalValue,
|
||||
};
|
||||
|
|
@ -32,7 +34,7 @@ pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
|
|||
|
||||
pub use self::memory::{Memory, MemoryKind};
|
||||
|
||||
pub use self::machine::{Machine, AllocMap};
|
||||
pub use self::machine::{Machine, AllocMap, MemoryAccess, MayLeak};
|
||||
|
||||
pub use self::operand::{ScalarMaybeUndef, Value, ValTy, Operand, OpTy};
|
||||
|
||||
|
|
|
|||
|
|
@ -144,17 +144,6 @@ impl<Tag> MemPlace<Tag> {
|
|||
// it now must be aligned.
|
||||
self.to_scalar_ptr_align().0.to_ptr()
|
||||
}
|
||||
|
||||
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
|
||||
/// This is the inverse of `ref_to_mplace`.
|
||||
pub fn to_ref(self) -> Value<Tag> {
|
||||
// We ignore the alignment of the place here -- special handling for packed structs ends
|
||||
// at the `&` operator.
|
||||
match self.meta {
|
||||
None => Value::Scalar(self.ptr.into()),
|
||||
Some(meta) => Value::ScalarPair(self.ptr.into(), meta.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
|
||||
|
|
@ -267,25 +256,59 @@ impl<'a, 'mir, 'tcx, Tag, M> EvalContext<'a, 'mir, 'tcx, M>
|
|||
where
|
||||
Tag: ::std::fmt::Debug+Default+Copy+Eq+Hash+'static,
|
||||
M: Machine<'a, 'mir, 'tcx, PointerTag=Tag>,
|
||||
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag>)>,
|
||||
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
|
||||
{
|
||||
/// Take a value, which represents a (thin or fat) reference, and make it a place.
|
||||
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref`.
|
||||
/// Alignment is just based on the type. This is the inverse of `create_ref`.
|
||||
pub fn ref_to_mplace(
|
||||
&self, val: ValTy<'tcx, M::PointerTag>
|
||||
&self,
|
||||
val: ValTy<'tcx, M::PointerTag>,
|
||||
) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
|
||||
let ptr = match val.to_scalar_ptr()? {
|
||||
Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
|
||||
// Machine might want to track the `*` operator
|
||||
let tag = M::tag_dereference(self, ptr, val.layout.ty)?;
|
||||
Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
|
||||
}
|
||||
other => other,
|
||||
};
|
||||
|
||||
let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
|
||||
let layout = self.layout_of(pointee_type)?;
|
||||
let align = layout.align;
|
||||
|
||||
let mplace = match *val {
|
||||
Value::Scalar(ptr) =>
|
||||
MemPlace { ptr: ptr.not_undef()?, align, meta: None },
|
||||
Value::ScalarPair(ptr, meta) =>
|
||||
MemPlace { ptr: ptr.not_undef()?, align, meta: Some(meta.not_undef()?) },
|
||||
Value::Scalar(_) =>
|
||||
MemPlace { ptr, align, meta: None },
|
||||
Value::ScalarPair(_, meta) =>
|
||||
MemPlace { ptr, align, meta: Some(meta.not_undef()?) },
|
||||
};
|
||||
Ok(MPlaceTy { mplace, layout })
|
||||
}
|
||||
|
||||
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
|
||||
/// This is the inverse of `ref_to_mplace`.
|
||||
pub fn create_ref(
|
||||
&mut self,
|
||||
place: MPlaceTy<'tcx, M::PointerTag>,
|
||||
borrow_kind: Option<mir::BorrowKind>,
|
||||
) -> EvalResult<'tcx, Value<M::PointerTag>> {
|
||||
let ptr = match place.ptr {
|
||||
Scalar::Ptr(ptr) if M::ENABLE_PTR_TRACKING_HOOKS => {
|
||||
// Machine might want to track the `&` operator
|
||||
let (size, _) = self.size_and_align_of_mplace(place)?
|
||||
.expect("create_ref cannot determine size");
|
||||
let tag = M::tag_reference(self, ptr, place.layout.ty, size, borrow_kind)?;
|
||||
Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag))
|
||||
},
|
||||
other => other,
|
||||
};
|
||||
Ok(match place.meta {
|
||||
None => Value::Scalar(ptr.into()),
|
||||
Some(meta) => Value::ScalarPair(ptr.into(), meta.into()),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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.
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
|
|||
type Item = AllocationSnapshot<'a>;
|
||||
|
||||
fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
|
||||
let Allocation { bytes, relocations, undef_mask, align, mutability } = self;
|
||||
let Allocation { bytes, relocations, undef_mask, align, mutability, extra: () } = self;
|
||||
|
||||
AllocationSnapshot {
|
||||
bytes,
|
||||
|
|
|
|||
|
|
@ -248,9 +248,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
)?;
|
||||
}
|
||||
|
||||
Ref(_, _, ref place) => {
|
||||
Ref(_, borrow_kind, ref place) => {
|
||||
let src = self.eval_place(place)?;
|
||||
let val = self.force_allocation(src)?.to_ref();
|
||||
let val = self.force_allocation(src)?;
|
||||
let val = self.create_ref(val, Some(borrow_kind))?;
|
||||
self.write_value(val, dest)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -446,7 +446,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
};
|
||||
|
||||
let arg = OpTy {
|
||||
op: Operand::Immediate(place.to_ref()),
|
||||
op: Operand::Immediate(self.create_ref(
|
||||
place,
|
||||
None // this is a "raw reference"
|
||||
)?),
|
||||
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
ty: Ty<'tcx>,
|
||||
poly_trait_ref: ty::PolyExistentialTraitRef<'tcx>,
|
||||
) -> EvalResult<'tcx, Pointer<M::PointerTag>> {
|
||||
debug!("get_vtable(trait_ref={:?})", poly_trait_ref);
|
||||
trace!("get_vtable(trait_ref={:?})", poly_trait_ref);
|
||||
|
||||
let (ty, poly_trait_ref) = self.tcx.erase_regions(&(ty, poly_trait_ref));
|
||||
|
||||
|
|
|
|||
|
|
@ -163,6 +163,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
scalar_format(value), path, "a valid unicode codepoint");
|
||||
},
|
||||
ty::Float(_) | ty::Int(_) | ty::Uint(_) => {
|
||||
// NOTE: Keep this in sync with the array optimization for int/float
|
||||
// types below!
|
||||
let size = value.layout.size;
|
||||
let value = value.to_scalar_or_undef();
|
||||
if const_mode {
|
||||
|
|
@ -511,6 +513,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
// This is the size in bytes of the whole array.
|
||||
let size = Size::from_bytes(ty_size * len);
|
||||
|
||||
// NOTE: Keep this in sync with the handling of integer and float
|
||||
// types above, in `validate_primitive_type`.
|
||||
// In run-time mode, we accept pointers in here. This is actually more
|
||||
// permissive than a per-element check would be, e.g. we accept
|
||||
// an &[u8] that contains a pointer even though bytewise checking would
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 8b14b03368429e6ee2a8ac0e0c876505606ab1f1
|
||||
Subproject commit bbb1d80703f272a5592ceeb3832a489776512251
|
||||
Loading…
Add table
Add a link
Reference in a new issue