Skip to content

Commit 6c0f502

Browse files
Implement Clone, Eq and Hash for the heap and stack
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`.
1 parent db025c1 commit 6c0f502

File tree

4 files changed

+170
-8
lines changed

4 files changed

+170
-8
lines changed

src/librustc_mir/interpret/eval_context.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::Write;
2-
use std::mem;
2+
use std::hash::{Hash, Hasher};
3+
use std::{mem, ptr};
34

45
use rustc::hir::def_id::DefId;
56
use rustc::hir::def::Def;
@@ -44,15 +45,52 @@ pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
4445
pub(crate) terminators_remaining: usize,
4546
}
4647

47-
struct EvalState<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
48+
pub(crate) struct EvalState<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
4849
/// The virtual memory system.
4950
memory: Memory<'a, 'mir, 'tcx, M>,
5051

5152
/// The virtual call stack.
5253
stack: Vec<Frame<'mir, 'tcx>>,
5354
}
5455

56+
impl<'a, 'mir, 'tcx, M> Clone for EvalState<'a, 'mir, 'tcx, M>
57+
where M: Machine<'mir, 'tcx>,
58+
'tcx: 'a + 'mir,
59+
{
60+
fn clone(&self) -> Self {
61+
EvalState {
62+
memory: self.memory.clone(),
63+
stack: self.stack.clone(),
64+
}
65+
}
66+
}
67+
68+
impl<'a, 'mir, 'tcx, M> Eq for EvalState<'a, 'mir, 'tcx, M>
69+
where M: Machine<'mir, 'tcx>,
70+
'tcx: 'a + 'mir,
71+
{}
72+
73+
impl<'a, 'mir, 'tcx, M> PartialEq for EvalState<'a, 'mir, 'tcx, M>
74+
where M: Machine<'mir, 'tcx>,
75+
'tcx: 'a + 'mir,
76+
{
77+
fn eq(&self, other: &Self) -> bool {
78+
self.memory == other.memory
79+
&& self.stack == other.stack
80+
}
81+
}
82+
83+
impl<'a, 'mir, 'tcx, M> Hash for EvalState<'a, 'mir, 'tcx, M>
84+
where M: Machine<'mir, 'tcx>,
85+
'tcx: 'a + 'mir,
86+
{
87+
fn hash<H: Hasher>(&self, state: &mut H) {
88+
self.memory.hash(state);
89+
self.stack.hash(state);
90+
}
91+
}
5592
/// A stack frame.
93+
#[derive(Clone)]
5694
pub struct Frame<'mir, 'tcx: 'mir> {
5795
////////////////////////////////////////////////////////////////////////////////
5896
// Function and callsite information
@@ -94,6 +132,52 @@ pub struct Frame<'mir, 'tcx: 'mir> {
94132
pub stmt: usize,
95133
}
96134

135+
impl<'mir, 'tcx: 'mir> Eq for Frame<'mir, 'tcx> {}
136+
137+
impl<'mir, 'tcx: 'mir> PartialEq for Frame<'mir, 'tcx> {
138+
fn eq(&self, other: &Self) -> bool {
139+
let Frame {
140+
mir,
141+
instance: _,
142+
span: _,
143+
return_to_block,
144+
return_place,
145+
locals,
146+
block,
147+
stmt,
148+
} = self;
149+
150+
ptr::eq(mir, &other.mir)
151+
&& *return_to_block == other.return_to_block // TODO: Are these two necessary?
152+
&& *return_place == other.return_place
153+
&& *locals == other.locals
154+
&& *block == other.block
155+
&& *stmt == other.stmt
156+
}
157+
}
158+
159+
impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> {
160+
fn hash<H: Hasher>(&self, state: &mut H) {
161+
let Frame {
162+
mir,
163+
instance: _,
164+
span: _,
165+
return_to_block,
166+
return_place,
167+
locals,
168+
block,
169+
stmt,
170+
} = self;
171+
172+
(mir as *const _ as usize).hash(state);
173+
return_to_block.hash(state);
174+
return_place.hash(state);
175+
locals.hash(state);
176+
block.hash(state);
177+
stmt.hash(state);
178+
}
179+
}
180+
97181
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
98182
pub enum StackPopCleanup {
99183
/// The stackframe existed to compute the initial value of a static/constant, make sure it

src/librustc_mir/interpret/machine.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//! This separation exists to ensure that no fancy miri features like
33
//! interpreting common C functions leak into CTFE.
44
5+
use std::hash::Hash;
6+
57
use rustc::mir::interpret::{AllocId, EvalResult, Scalar, Pointer, AccessKind, GlobalId};
68
use super::{EvalContext, Place, ValTy, Memory};
79

@@ -15,7 +17,7 @@ use syntax::ast::Mutability;
1517
/// and some use case dependent behaviour can instead be applied
1618
pub trait Machine<'mir, 'tcx>: Sized {
1719
/// Additional data that can be accessed via the Memory
18-
type MemoryData;
20+
type MemoryData: Clone + Eq + Hash;
1921

2022
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
2123
type MemoryKinds: ::std::fmt::Debug + PartialEq + Copy + Clone;

src/librustc_mir/interpret/memory.rs

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::collections::VecDeque;
2+
use std::hash::{Hash, Hasher};
23
use std::ptr;
34

45
use rustc::hir::def_id::DefId;
@@ -9,7 +10,7 @@ use rustc::ty::layout::{self, Align, TargetDataLayout, Size};
910
use rustc::mir::interpret::{Pointer, AllocId, Allocation, AccessKind, Value,
1011
EvalResult, Scalar, EvalErrorKind, GlobalId, AllocType};
1112
pub use rustc::mir::interpret::{write_target_uint, write_target_int, read_target_uint};
12-
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
13+
use rustc_data_structures::fx::{FxHashSet, FxHashMap, FxHasher};
1314

1415
use syntax::ast::Mutability;
1516

@@ -19,7 +20,7 @@ use super::{EvalContext, Machine};
1920
// Allocations and pointers
2021
////////////////////////////////////////////////////////////////////////////////
2122

22-
#[derive(Debug, PartialEq, Copy, Clone)]
23+
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
2324
pub enum MemoryKind<T> {
2425
/// Error if deallocated except during a stack pop
2526
Stack,
@@ -47,6 +48,81 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
4748
pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
4849
}
4950

51+
impl<'a, 'mir, 'tcx, M> Clone for Memory<'a, 'mir, 'tcx, M>
52+
where M: Machine<'mir, 'tcx>,
53+
'tcx: 'a + 'mir,
54+
{
55+
fn clone(&self) -> Self {
56+
Memory {
57+
data: self.data.clone(),
58+
alloc_kind: self.alloc_kind.clone(),
59+
alloc_map: self.alloc_map.clone(),
60+
cur_frame: self.cur_frame.clone(),
61+
tcx: self.tcx.clone(),
62+
}
63+
}
64+
}
65+
66+
impl<'a, 'mir, 'tcx, M> Eq for Memory<'a, 'mir, 'tcx, M>
67+
where M: Machine<'mir, 'tcx>,
68+
'tcx: 'a + 'mir,
69+
{}
70+
71+
impl<'a, 'mir, 'tcx, M> PartialEq for Memory<'a, 'mir, 'tcx, M>
72+
where M: Machine<'mir, 'tcx>,
73+
'tcx: 'a + 'mir,
74+
{
75+
fn eq(&self, other: &Self) -> bool {
76+
let Memory {
77+
data,
78+
alloc_kind,
79+
alloc_map,
80+
cur_frame,
81+
tcx,
82+
} = self;
83+
84+
*data == other.data
85+
&& *alloc_kind == other.alloc_kind
86+
&& *alloc_map == other.alloc_map
87+
&& *cur_frame == other.cur_frame
88+
&& ptr::eq(tcx, &other.tcx)
89+
}
90+
}
91+
92+
impl<'a, 'mir, 'tcx, M> Hash for Memory<'a, 'mir, 'tcx, M>
93+
where M: Machine<'mir, 'tcx>,
94+
'tcx: 'a + 'mir,
95+
{
96+
fn hash<H: Hasher>(&self, state: &mut H) {
97+
let Memory {
98+
data,
99+
alloc_kind: _,
100+
alloc_map: _,
101+
cur_frame,
102+
tcx,
103+
} = self;
104+
105+
data.hash(state);
106+
cur_frame.hash(state);
107+
(tcx as *const _ as usize).hash(state);
108+
109+
// We ignore some fields which don't change between evaluation steps.
110+
111+
// Since HashMaps which contain the same items may have different
112+
// iteration orders, we use a commutative operation (in this case
113+
// addition, but XOR would also work), to combine the hash of each
114+
// `Allocation`.
115+
self.allocations()
116+
.map(|allocs| {
117+
let mut h = FxHasher::default();
118+
allocs.hash(&mut h);
119+
h.finish()
120+
})
121+
.fold(0u64, |hash, x| hash.wrapping_add(x))
122+
.hash(state);
123+
}
124+
}
125+
50126
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
51127
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
52128
Memory {
@@ -866,7 +942,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
866942

867943
for i in 0..size.bytes() {
868944
let defined = undef_mask.get(src.offset + Size::from_bytes(i));
869-
945+
870946
for j in 0..repeat {
871947
dest_allocation.undef_mask.set(
872948
dest.offset + Size::from_bytes(i + (size.bytes() * j)),

src/librustc_mir/interpret/place.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc::mir::interpret::{GlobalId, Value, Scalar, EvalResult, Pointer};
77
use super::{EvalContext, Machine, ValTy};
88
use interpret::memory::HasMemory;
99

10-
#[derive(Copy, Clone, Debug)]
10+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
1111
pub enum Place {
1212
/// A place referring to a value allocated in the `Memory` system.
1313
Ptr {
@@ -24,7 +24,7 @@ pub enum Place {
2424
Local { frame: usize, local: mir::Local },
2525
}
2626

27-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
2828
pub enum PlaceExtra {
2929
None,
3030
Length(u64),

0 commit comments

Comments
 (0)