]> git.lizzy.rs Git - rust.git/commitdiff
Implement Clone, Eq and Hash for the heap and stack
authorDylan MacKenzie <ecstaticmorse@gmail.com>
Fri, 22 Jun 2018 04:40:14 +0000 (21:40 -0700)
committerDylan MacKenzie <ecstaticmorse@gmail.com>
Wed, 4 Jul 2018 21:36:07 +0000 (14:36 -0700)
I use a pattern binding in each custom impl, so that adding fields to
`Memory` or `Frame` will cause a compiler error instead of causing e.g.
`PartialEq` to become invalid. This may be too cute.

This adds several requirements to `Machine::MemoryData`. These can be
removed if we don't want this associated type to be part of the equality
of `Memory`.

src/librustc_mir/interpret/eval_context.rs
src/librustc_mir/interpret/machine.rs
src/librustc_mir/interpret/memory.rs
src/librustc_mir/interpret/place.rs

index fe95b9319f3203d0bb05c39009d41a59b9ce7817..50987af409f4063638b8b319f63ec0e537eab9d8 100644 (file)
@@ -1,5 +1,6 @@
 use std::fmt::Write;
-use std::mem;
+use std::hash::{Hash, Hasher};
+use std::{mem, ptr};
 
 use rustc::hir::def_id::DefId;
 use rustc::hir::def::Def;
@@ -44,7 +45,7 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
     pub(crate) terminators_remaining: usize,
 }
 
-struct EvalState<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
+pub(crate) struct EvalState<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
     /// The virtual memory system.
     memory: Memory<'a, 'mir, 'tcx, M>,
 
@@ -52,7 +53,44 @@ struct EvalState<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
     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> {
     ////////////////////////////////////////////////////////////////////////////////
     // Function and callsite information
@@ -94,6 +132,52 @@ pub struct Frame<'mir, 'tcx: 'mir> {
     pub stmt: usize,
 }
 
+impl<'mir, 'tcx: 'mir> Eq for Frame<'mir, 'tcx> {}
+
+impl<'mir, 'tcx: 'mir> PartialEq for Frame<'mir, 'tcx> {
+    fn eq(&self, other: &Self) -> bool {
+        let Frame {
+            mir,
+            instance: _,
+            span: _,
+            return_to_block,
+            return_place,
+            locals,
+            block,
+            stmt,
+        } = self;
+
+        ptr::eq(mir, &other.mir)
+            && *return_to_block == other.return_to_block // TODO: Are these two necessary?
+            && *return_place == other.return_place
+            && *locals == other.locals
+            && *block == other.block
+            && *stmt == other.stmt
+    }
+}
+
+impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> {
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        let Frame {
+            mir,
+            instance: _,
+            span: _,
+            return_to_block,
+            return_place,
+            locals,
+            block,
+            stmt,
+        } = self;
+
+        (mir as *const _ as usize).hash(state);
+        return_to_block.hash(state);
+        return_place.hash(state);
+        locals.hash(state);
+        block.hash(state);
+        stmt.hash(state);
+    }
+}
+
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
 pub enum StackPopCleanup {
     /// The stackframe existed to compute the initial value of a static/constant, make sure it
index 4d04900320fe900553bfd28b67feb7ebb868effe..f6491d7f1a46903507b236ed17166a976bb3464f 100644 (file)
@@ -2,6 +2,8 @@
 //! This separation exists to ensure that no fancy miri features like
 //! interpreting common C functions leak into CTFE.
 
+use std::hash::Hash;
+
 use rustc::mir::interpret::{AllocId, EvalResult, Scalar, Pointer, AccessKind, GlobalId};
 use super::{EvalContext, Place, ValTy, Memory};
 
@@ -15,7 +17,7 @@
 /// and some use case dependent behaviour can instead be applied
 pub trait Machine<'mir, 'tcx>: Sized {
     /// Additional data that can be accessed via the Memory
-    type MemoryData;
+    type MemoryData: Clone + Eq + Hash;
 
     /// Additional memory kinds a machine wishes to distinguish from the builtin ones
     type MemoryKinds: ::std::fmt::Debug + PartialEq + Copy + Clone;
index 4d1188b0f152b843fcfb8c92af73e8a76dd40a71..98ab361c239ee7b5d9273b86fbac6feb8c9b5019 100644 (file)
@@ -1,4 +1,5 @@
 use std::collections::VecDeque;
+use std::hash::{Hash, Hasher};
 use std::ptr;
 
 use rustc::hir::def_id::DefId;
@@ -9,7 +10,7 @@
 use rustc::mir::interpret::{Pointer, AllocId, Allocation, AccessKind, Value,
                             EvalResult, Scalar, EvalErrorKind, GlobalId, AllocType};
 pub use rustc::mir::interpret::{write_target_uint, write_target_int, read_target_uint};
-use rustc_data_structures::fx::{FxHashSet, FxHashMap};
+use rustc_data_structures::fx::{FxHashSet, FxHashMap, FxHasher};
 
 use syntax::ast::Mutability;
 
@@ -19,7 +20,7 @@
 // Allocations and pointers
 ////////////////////////////////////////////////////////////////////////////////
 
-#[derive(Debug, PartialEq, Copy, Clone)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
 pub enum MemoryKind<T> {
     /// Error if deallocated except during a stack pop
     Stack,
@@ -47,6 +48,81 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
     pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
 }
 
+impl<'a, 'mir, 'tcx, M> Clone for Memory<'a, 'mir, 'tcx, M>
+    where M: Machine<'mir, 'tcx>,
+          'tcx: 'a + 'mir,
+{
+    fn clone(&self) -> Self {
+        Memory {
+            data: self.data.clone(),
+            alloc_kind: self.alloc_kind.clone(),
+            alloc_map: self.alloc_map.clone(),
+            cur_frame: self.cur_frame.clone(),
+            tcx: self.tcx.clone(),
+        }
+    }
+}
+
+impl<'a, 'mir, 'tcx, M> Eq for Memory<'a, 'mir, 'tcx, M>
+    where M: Machine<'mir, 'tcx>,
+          'tcx: 'a + 'mir,
+{}
+
+impl<'a, 'mir, 'tcx, M> PartialEq for Memory<'a, 'mir, 'tcx, M>
+    where M: Machine<'mir, 'tcx>,
+          'tcx: 'a + 'mir,
+{
+    fn eq(&self, other: &Self) -> bool {
+        let Memory {
+            data,
+            alloc_kind,
+            alloc_map,
+            cur_frame,
+            tcx,
+        } = self;
+
+        *data == other.data
+            && *alloc_kind == other.alloc_kind
+            && *alloc_map == other.alloc_map
+            && *cur_frame == other.cur_frame
+            && ptr::eq(tcx, &other.tcx)
+    }
+}
+
+impl<'a, 'mir, 'tcx, M> Hash for Memory<'a, 'mir, 'tcx, M>
+    where M: Machine<'mir, 'tcx>,
+          'tcx: 'a + 'mir,
+{
+    fn hash<H: Hasher>(&self, state: &mut H) {
+        let Memory {
+            data,
+            alloc_kind: _,
+            alloc_map: _,
+            cur_frame,
+            tcx,
+        } = self;
+
+        data.hash(state);
+        cur_frame.hash(state);
+        (tcx as *const _ as usize).hash(state);
+
+        // We ignore some fields which don't change between evaluation steps.
+
+        // Since HashMaps which contain the same items may have different
+        // iteration orders, we use a commutative operation (in this case
+        // addition, but XOR would also work), to combine the hash of each
+        // `Allocation`.
+        self.allocations()
+            .map(|allocs| {
+                let mut h = FxHasher::default();
+                allocs.hash(&mut h);
+                h.finish()
+            })
+            .fold(0u64, |hash, x| hash.wrapping_add(x))
+            .hash(state);
+    }
+}
+
 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
     pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
         Memory {
@@ -866,7 +942,7 @@ fn copy_undef_mask(
 
         for i in 0..size.bytes() {
             let defined = undef_mask.get(src.offset + Size::from_bytes(i));
-            
+
             for j in 0..repeat {
                 dest_allocation.undef_mask.set(
                     dest.offset + Size::from_bytes(i + (size.bytes() * j)),
index 95391bc8963e9f2e90f7698608ccd9213508f998..bde2e5945f5d47c0ba1fccee74d63b7bc62bd6f4 100644 (file)
@@ -7,7 +7,7 @@
 use super::{EvalContext, Machine, ValTy};
 use interpret::memory::HasMemory;
 
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 pub enum Place {
     /// A place referring to a value allocated in the `Memory` system.
     Ptr {
@@ -24,7 +24,7 @@ pub enum Place {
     Local { frame: usize, local: mir::Local },
 }
 
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
 pub enum PlaceExtra {
     None,
     Length(u64),