/// Bounds in scope for polymorphic evaluations.
pub param_env: ty::ParamEnv<'tcx>,
- /// Virtual memory and call stack.
- pub(crate) state: EvalState<'a, 'mir, 'tcx, M>,
+ /// The virtual memory system.
+ pub memory: Memory<'a, 'mir, 'tcx, M>,
+
+ /// The virtual call stack.
+ pub(crate) stack: Vec<Frame<'mir, 'tcx>>,
/// The maximum number of stack frames allowed
pub(crate) stack_limit: usize,
pub(crate) loop_detector: InfiniteLoopDetector<'a, 'mir, 'tcx, M>,
}
-pub(crate) struct EvalState<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
- /// The virtual memory system.
- memory: Memory<'a, 'mir, 'tcx, M>,
-
- /// The virtual call stack.
- stack: Vec<Frame<'mir, 'tcx>>,
-}
-
-impl<'a, 'mir, 'tcx, M> Clone for EvalState<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- fn clone(&self) -> Self {
- EvalState {
- memory: self.memory.clone(),
- stack: self.stack.clone(),
- }
- }
-}
-
-impl<'a, 'mir, 'tcx, M> Eq for EvalState<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{}
-
-impl<'a, 'mir, 'tcx, M> PartialEq for EvalState<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- fn eq(&self, other: &Self) -> bool {
- self.memory == other.memory
- && self.stack == other.stack
- }
-}
-
-impl<'a, 'mir, 'tcx, M> Hash for EvalState<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
- 'tcx: 'a + 'mir,
-{
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.memory.hash(state);
- self.stack.hash(state);
- }
-}
/// A stack frame.
#[derive(Clone)]
pub struct Frame<'mir, 'tcx: 'mir> {
}
}
+/// The virtual machine state during const-evaluation at a given point in time.
+type EvalSnapshot<'a, 'mir, 'tcx, M>
+ = (M, Vec<Frame<'mir, 'tcx>>, Memory<'a, 'mir, 'tcx, M>);
+
pub(crate) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
- /// The set of all `EvalState` *hashes* observed by this detector.
+ /// The set of all `EvalSnapshot` *hashes* observed by this detector.
///
/// Not a proper bloom filter.
bloom: FxHashSet<u64>,
- /// The set of all `EvalState`s observed by this detector.
+ /// The set of all `EvalSnapshot`s observed by this detector.
///
- /// An `EvalState` will only be fully cloned once it has caused a collision
+ /// An `EvalSnapshot` will only be fully cloned once it has caused a collision
/// in `bloom`. As a result, the detector must observe *two* full cycles of
/// an infinite loop before it triggers.
- snapshots: FxHashSet<EvalState<'a, 'mir, 'tcx, M>>,
+ snapshots: FxHashSet<EvalSnapshot<'a, 'mir, 'tcx, M>>,
}
impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
+ where M: Eq + Hash + Machine<'mir, 'tcx>,
'tcx: 'a + 'mir,
{
fn default() -> Self {
}
impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M>
- where M: Machine<'mir, 'tcx>,
+ where M: Clone + Eq + Hash + Machine<'mir, 'tcx>,
'tcx: 'a + 'mir,
{
- pub fn observe(&mut self, snapshot: &EvalState<'a, 'mir, 'tcx, M>) -> Result<(), (/*TODO*/)> {
+ pub fn observe(
+ &mut self,
+ machine: &M,
+ stack: &Vec<Frame<'mir, 'tcx>>,
+ memory: &Memory<'a, 'mir, 'tcx, M>,
+ ) -> Result<(), (/*TODO*/)> {
+ let snapshot = (machine, stack, memory);
+
let mut fx = FxHasher::default();
snapshot.hash(&mut fx);
let hash = fx.finish();
return Ok(())
}
- if self.snapshots.insert(snapshot.clone()) {
+ if self.snapshots.insert((machine.clone(), stack.clone(), memory.clone())) {
// Spurious collision or first cycle
return Ok(())
}
param_env: ty::ParamEnv<'tcx>,
machine: M,
memory_data: M::MemoryData,
- ) -> Self {
+ ) -> Self
+ where M: Eq + Hash
+ {
EvalContext {
machine,
tcx,
param_env,
- state: EvalState {
- memory: Memory::new(tcx, memory_data),
- stack: Vec::new(),
- },
+ memory: Memory::new(tcx, memory_data),
+ stack: Vec::new(),
stack_limit: tcx.sess.const_eval_stack_frame_limit,
loop_detector: Default::default(),
steps_until_detector_enabled: MAX_TERMINATORS,
}
pub(crate) fn with_fresh_body<F: FnOnce(&mut Self) -> R, R>(&mut self, f: F) -> R {
- let stack = mem::replace(self.stack_mut(), Vec::new());
+ let stack = mem::replace(&mut self.stack, Vec::new());
let steps = mem::replace(&mut self.steps_until_detector_enabled, MAX_TERMINATORS);
let r = f(self);
- *self.stack_mut() = stack;
+ self.stack = stack;
self.steps_until_detector_enabled = steps;
r
}
let layout = self.layout_of(ty)?;
assert!(!layout.is_unsized(), "cannot alloc memory for unsized type");
- self.memory_mut().allocate(layout.size, layout.align, MemoryKind::Stack)
+ self.memory.allocate(layout.size, layout.align, MemoryKind::Stack)
}
pub fn memory(&self) -> &Memory<'a, 'mir, 'tcx, M> {
- &self.state.memory
+ &self.memory
}
pub fn memory_mut(&mut self) -> &mut Memory<'a, 'mir, 'tcx, M> {
- &mut self.state.memory
+ &mut self.memory
}
- #[inline]
pub fn stack(&self) -> &[Frame<'mir, 'tcx>] {
- &self.state.stack
- }
-
- pub fn stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx>> {
- &mut self.state.stack
+ &self.stack
}
#[inline]
pub fn cur_frame(&self) -> usize {
- assert!(self.stack().len() > 0);
- self.stack().len() - 1
+ assert!(self.stack.len() > 0);
+ self.stack.len() - 1
}
pub fn str_to_value(&mut self, s: &str) -> EvalResult<'tcx, Value> {
- let ptr = self.memory_mut().allocate_bytes(s.as_bytes());
+ let ptr = self.memory.allocate_bytes(s.as_bytes());
Ok(Scalar::Ptr(ptr).to_value_with_len(s.len() as u64, self.tcx.tcx))
}
}
ConstValue::ByRef(alloc, offset) => {
// FIXME: Allocate new AllocId for all constants inside
- let id = self.memory_mut().allocate_value(alloc.clone(), MemoryKind::Stack)?;
+ let id = self.memory.allocate_value(alloc.clone(), MemoryKind::Stack)?;
Ok(Value::ByRef(Pointer::new(id, offset).into(), alloc.align))
},
ConstValue::ScalarPair(a, b) => Ok(Value::ScalarPair(a, b)),
IndexVec::new()
};
- self.stack_mut().push(Frame {
+ self.stack.push(Frame {
mir,
block: mir::START_BLOCK,
return_to_block,
stmt: 0,
});
- self.memory_mut().cur_frame = self.cur_frame();
+ self.memory.cur_frame = self.cur_frame();
- if self.stack().len() > self.stack_limit {
+ if self.stack.len() > self.stack_limit {
err!(StackFrameLimitReached)
} else {
Ok(())
pub(super) fn pop_stack_frame(&mut self) -> EvalResult<'tcx> {
::log_settings::settings().indentation -= 1;
M::end_region(self, None)?;
- let frame = self.stack_mut().pop().expect(
+ let frame = self.stack.pop().expect(
"tried to pop a stack frame, but there were none",
);
- if !self.stack().is_empty() {
+ if !self.stack.is_empty() {
// TODO: Is this the correct time to start considering these accesses as originating from the returned-to stack frame?
- self.memory_mut().cur_frame = self.cur_frame();
+ self.memory.cur_frame = self.cur_frame();
}
match frame.return_to_block {
StackPopCleanup::MarkStatic(mutable) => {
if let Place::Ptr { ptr, .. } = frame.return_place {
// FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions
- self.memory_mut().mark_static_initialized(
+ self.memory.mark_static_initialized(
ptr.to_ptr()?.alloc_id,
mutable,
)?
if let Some(Value::ByRef(ptr, _align)) = local {
trace!("deallocating local");
let ptr = ptr.to_ptr()?;
- self.memory().dump_alloc(ptr.alloc_id);
- self.memory_mut().deallocate_local(ptr)?;
+ self.memory.dump_alloc(ptr.alloc_id);
+ self.memory.deallocate_local(ptr)?;
};
Ok(())
}
&mut self,
rvalue: &mir::Rvalue<'tcx>,
place: &mir::Place<'tcx>,
- ) -> EvalResult<'tcx> {
+ ) -> EvalResult<'tcx>
+ where M: Clone + Eq + Hash,
+ {
let dest = self.eval_place(place)?;
let dest_ty = self.place_ty(place);
let src = self.eval_place(place)?;
let ty = self.place_ty(place);
let (_, len) = src.elem_ty_and_len(ty, self.tcx.tcx);
- let defined = self.memory().pointer_size().bits() as u8;
+ let defined = self.memory.pointer_size().bits() as u8;
self.write_scalar(
dest,
Scalar::Bits {
let layout = self.layout_of(ty)?;
assert!(!layout.is_unsized(),
"SizeOf nullary MIR operator called for unsized type");
- let defined = self.memory().pointer_size().bits() as u8;
+ let defined = self.memory.pointer_size().bits() as u8;
self.write_scalar(
dest,
Scalar::Bits {
def_id,
substs,
).ok_or_else(|| EvalErrorKind::TooGeneric.into());
- let fn_ptr = self.memory_mut().create_fn_alloc(instance?);
+ let fn_ptr = self.memory.create_fn_alloc(instance?);
let valty = ValTy {
value: Value::Scalar(fn_ptr.into()),
ty: dest_ty,
substs,
ty::ClosureKind::FnOnce,
);
- let fn_ptr = self.memory_mut().create_fn_alloc(instance);
+ let fn_ptr = self.memory.create_fn_alloc(instance);
let valty = ValTy {
value: Value::Scalar(fn_ptr.into()),
ty: dest_ty,
pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> {
let new_place = match place {
Place::Local { frame, local } => {
- match self.stack()[frame].locals[local] {
+ match self.stack[frame].locals[local] {
None => return err!(DeadLocal),
Some(Value::ByRef(ptr, align)) => {
Place::Ptr {
}
}
Some(val) => {
- let ty = self.stack()[frame].mir.local_decls[local].ty;
- let ty = self.monomorphize(ty, self.stack()[frame].instance.substs);
+ let ty = self.stack[frame].mir.local_decls[local].ty;
+ let ty = self.monomorphize(ty, self.stack[frame].instance.substs);
let layout = self.layout_of(ty)?;
let ptr = self.alloc_ptr(ty)?;
- self.stack_mut()[frame].locals[local] =
+ self.stack[frame].locals[local] =
Some(Value::ByRef(ptr.into(), layout.align)); // it stays live
let place = Place::from_ptr(ptr, layout.align);
self.write_value(ValTy { value: val, ty }, place)?;
}
Place::Local { frame, local } => {
- let dest = self.stack()[frame].get_local(local)?;
+ let dest = self.stack[frame].get_local(local)?;
self.write_value_possibly_by_val(
src_val,
- |this, val| this.stack_mut()[frame].set_local(local, val),
+ |this, val| this.stack[frame].set_local(local, val),
dest,
dest_ty,
)
} else {
let dest_ptr = self.alloc_ptr(dest_ty)?.into();
let layout = self.layout_of(dest_ty)?;
- self.memory_mut().copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size, false)?;
+ self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size, false)?;
write_dest(self, Value::ByRef(dest_ptr, layout.align))?;
}
} else {
trace!("write_value_to_ptr: {:#?}, {}, {:#?}", value, dest_ty, layout);
match value {
Value::ByRef(ptr, align) => {
- self.memory_mut().copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size, false)
+ self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size, false)
}
Value::Scalar(scalar) => {
let signed = match layout.abi {
_ => bug!("write_value_to_ptr: invalid ByVal layout: {:#?}", layout),
}
};
- self.memory_mut().write_scalar(dest, dest_align, scalar, layout.size, signed)
+ self.memory.write_scalar(dest, dest_align, scalar, layout.size, signed)
}
Value::ScalarPair(a_val, b_val) => {
trace!("write_value_to_ptr valpair: {:#?}", layout);
let b_offset = a_size.abi_align(b.align(&self));
let b_ptr = dest.ptr_offset(b_offset, &self)?.into();
// TODO: What about signedess?
- self.memory_mut().write_scalar(a_ptr, dest_align, a_val, a_size, false)?;
- self.memory_mut().write_scalar(b_ptr, dest_align, b_val, b_size, false)
+ self.memory.write_scalar(a_ptr, dest_align, a_val, a_size, false)?;
+ self.memory.write_scalar(b_ptr, dest_align, b_val, b_size, false)
}
}
}
ptr_align: Align,
pointee_ty: Ty<'tcx>,
) -> EvalResult<'tcx, Value> {
- let ptr_size = self.memory().pointer_size();
- let p: Scalar = self.memory().read_ptr_sized(ptr, ptr_align)?.into();
+ let ptr_size = self.memory.pointer_size();
+ let p: Scalar = self.memory.read_ptr_sized(ptr, ptr_align)?.into();
if self.type_is_sized(pointee_ty) {
Ok(p.to_value())
} else {
let extra = ptr.offset(ptr_size, self)?;
match self.tcx.struct_tail(pointee_ty).sty {
ty::TyDynamic(..) => Ok(p.to_value_with_vtable(
- self.memory().read_ptr_sized(extra, ptr_align)?.to_ptr()?,
+ self.memory.read_ptr_sized(extra, ptr_align)?.to_ptr()?,
)),
ty::TySlice(..) | ty::TyStr => {
let len = self
- .memory()
+ .memory
.read_ptr_sized(extra, ptr_align)?
.to_bits(ptr_size)?;
Ok(p.to_value_with_len(len as u64, self.tcx.tcx))
) -> EvalResult<'tcx> {
match ty.sty {
ty::TyBool => {
- self.memory().read_scalar(ptr, ptr_align, Size::from_bytes(1))?.to_bool()?;
+ self.memory.read_scalar(ptr, ptr_align, Size::from_bytes(1))?.to_bool()?;
}
ty::TyChar => {
- let c = self.memory().read_scalar(ptr, ptr_align, Size::from_bytes(4))?.to_bits(Size::from_bytes(4))? as u32;
+ let c = self.memory.read_scalar(ptr, ptr_align, Size::from_bytes(4))?.to_bits(Size::from_bytes(4))? as u32;
match ::std::char::from_u32(c) {
Some(..) => (),
None => return err!(InvalidChar(c as u128)),
}
ty::TyFnPtr(_) => {
- self.memory().read_ptr_sized(ptr, ptr_align)?;
+ self.memory.read_ptr_sized(ptr, ptr_align)?;
},
ty::TyRef(_, rty, _) |
ty::TyRawPtr(ty::TypeAndMut { ty: rty, .. }) => {
if let layout::Abi::Scalar(ref scalar) = self.layout_of(ty)?.abi {
let size = scalar.value.size(self);
- self.memory().read_scalar(ptr, ptr_align, size)?;
+ self.memory.read_scalar(ptr, ptr_align, size)?;
}
}
pub fn try_read_value(&self, ptr: Scalar, ptr_align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
let layout = self.layout_of(ty)?;
- self.memory().check_align(ptr, ptr_align)?;
+ self.memory.check_align(ptr, ptr_align)?;
if layout.size.bytes() == 0 {
return Ok(Some(Value::Scalar(Scalar::undef())));
match layout.abi {
layout::Abi::Scalar(..) => {
- let scalar = self.memory().read_scalar(ptr, ptr_align, layout.size)?;
+ let scalar = self.memory.read_scalar(ptr, ptr_align, layout.size)?;
Ok(Some(Value::Scalar(scalar)))
}
layout::Abi::ScalarPair(ref a, ref b) => {
let a_ptr = ptr;
let b_offset = a_size.abi_align(b.align(self));
let b_ptr = ptr.offset(b_offset, self)?.into();
- let a_val = self.memory().read_scalar(a_ptr, ptr_align, a_size)?;
- let b_val = self.memory().read_scalar(b_ptr, ptr_align, b_size)?;
+ let a_val = self.memory.read_scalar(a_ptr, ptr_align, a_size)?;
+ let b_val = self.memory.read_scalar(b_ptr, ptr_align, b_size)?;
Ok(Some(Value::ScalarPair(a_val, b_val)))
}
_ => Ok(None),
}
pub fn frame(&self) -> &Frame<'mir, 'tcx> {
- self.stack().last().expect("no call frames exist")
+ self.stack.last().expect("no call frames exist")
}
pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx> {
- self.stack_mut().last_mut().expect("no call frames exist")
+ self.stack.last_mut().expect("no call frames exist")
}
pub(super) fn mir(&self) -> &'mir mir::Mir<'tcx> {
}
pub fn substs(&self) -> &'tcx Substs<'tcx> {
- if let Some(frame) = self.stack().last() {
+ if let Some(frame) = self.stack.last() {
frame.instance.substs
} else {
Substs::empty()
}
write!(msg, ":").unwrap();
- match self.stack()[frame].get_local(local) {
+ match self.stack[frame].get_local(local) {
Err(err) => {
if let EvalErrorKind::DeadLocal = err.kind {
write!(msg, " is dead").unwrap();
}
trace!("{}", msg);
- self.memory().dump_allocs(allocs);
+ self.memory.dump_allocs(allocs);
}
Place::Ptr { ptr, align, .. } => {
match ptr {
Scalar::Ptr(ptr) => {
trace!("by align({}) ref:", align.abi());
- self.memory().dump_alloc(ptr.alloc_id);
+ self.memory.dump_alloc(ptr.alloc_id);
}
ptr => trace!(" integral by ref: {:?}", ptr),
}
where
F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>,
{
- let val = self.stack()[frame].get_local(local)?;
+ let val = self.stack[frame].get_local(local)?;
let new_val = f(self, val)?;
- self.stack_mut()[frame].set_local(local, new_val)?;
+ self.stack[frame].set_local(local, new_val)?;
// FIXME(solson): Run this when setting to Undef? (See previous version of this code.)
- // if let Value::ByRef(ptr) = self.stack()[frame].get_local(local) {
- // self.memory().deallocate(ptr)?;
+ // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) {
+ // self.memory.deallocate(ptr)?;
// }
Ok(())
}