From 7fa42beef8d385e5eccf7ad86dcf022dfca1c0ac Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 28 Jul 2018 17:07:40 +0200 Subject: [PATCH 01/15] Remove unused alloc_id_recursion_tracker --- src/librustc/ich/hcx.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/librustc/ich/hcx.rs b/src/librustc/ich/hcx.rs index 371f631737c98..1acc58948f413 100644 --- a/src/librustc/ich/hcx.rs +++ b/src/librustc/ich/hcx.rs @@ -15,7 +15,6 @@ use hir::map::definitions::Definitions; use ich::{self, CachingSourceMapView, Fingerprint}; use middle::cstore::CrateStore; use ty::{TyCtxt, fast_reject}; -use mir::interpret::AllocId; use session::Session; use std::cmp::Ord; @@ -60,8 +59,6 @@ pub struct StableHashingContext<'a> { // CachingSourceMapView, so we initialize it lazily. raw_source_map: &'a SourceMap, caching_source_map: Option>, - - pub(super) alloc_id_recursion_tracker: FxHashSet, } #[derive(PartialEq, Eq, Clone, Copy)] @@ -105,7 +102,6 @@ impl<'a> StableHashingContext<'a> { hash_spans: hash_spans_initial, hash_bodies: true, node_id_hashing_mode: NodeIdHashingMode::HashDefPath, - alloc_id_recursion_tracker: Default::default(), } } From 030077401d6b7c2f923b24271dc5dc80044b089a Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 28 Jul 2018 19:11:47 +0200 Subject: [PATCH 02/15] Promote EvalSnapshot to newtype --- src/librustc_mir/interpret/eval_context.rs | 27 +++++++++++++++++----- src/librustc_mir/interpret/step.rs | 2 +- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 6e144ba7ed2ee..467634f28969a 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -196,8 +196,24 @@ impl<'tcx> LocalValue { } /// The virtual machine state during const-evaluation at a given point in time. -type EvalSnapshot<'a, 'mir, 'tcx, M> - = (M, Vec>, Memory<'a, 'mir, 'tcx, M>); +#[derive(Eq, PartialEq, Hash)] +pub(crate) struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { + machine: M, + memory: Memory<'a, 'mir, 'tcx, M>, + stack: Vec>, +} + +impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn new<'b>(machine: &M, memory: &Memory<'a, 'mir, 'tcx, M>, stack: &[Frame<'mir, 'tcx>]) -> Self { + EvalSnapshot { + machine: machine.clone(), + memory: memory.clone(), + stack: stack.into(), + } + } +} pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The set of all `EvalSnapshot` *hashes* observed by this detector. @@ -238,13 +254,12 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> pub fn observe_and_analyze( &mut self, machine: &M, - stack: &Vec>, memory: &Memory<'a, 'mir, 'tcx, M>, + stack: &[Frame<'mir, 'tcx>], ) -> EvalResult<'tcx, ()> { - let snapshot = (machine, stack, memory); let mut fx = FxHasher::default(); - snapshot.hash(&mut fx); + (machine, memory, stack).hash(&mut fx); let hash = fx.finish(); if self.hashes.insert(hash) { @@ -252,7 +267,7 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> return Ok(()) } - if self.snapshots.insert((machine.clone(), stack.clone(), memory.clone())) { + if self.snapshots.insert(EvalSnapshot::new(machine, memory, stack)) { // Spurious collision or first cycle return Ok(()) } diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 114ef093ec2fd..f3d655c2651a9 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -73,7 +73,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { "Constant evaluating a complex constant, this might take some time"); } - self.loop_detector.observe_and_analyze(&self.machine, &self.stack, &self.memory) + self.loop_detector.observe_and_analyze(&self.machine, &self.memory, &self.stack[..]) } pub fn run(&mut self) -> EvalResult<'tcx> { From a083aa02edf0a3cb963dc71aea1509afe6e179a0 Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sun, 5 Aug 2018 00:01:11 +0200 Subject: [PATCH 03/15] Implement Hash in terms of HashStable for EvalSnapshot --- src/librustc/ich/impls_ty.rs | 2 +- src/librustc_data_structures/stable_hasher.rs | 17 +++++ src/librustc_mir/const_eval.rs | 2 + src/librustc_mir/interpret/eval_context.rs | 66 ++++++++++++++----- src/librustc_mir/interpret/machine.rs | 6 +- src/librustc_mir/interpret/memory.rs | 34 +--------- src/librustc_mir/interpret/operand.rs | 10 +++ src/librustc_mir/interpret/place.rs | 20 ++++++ 8 files changed, 105 insertions(+), 52 deletions(-) diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index 6250e12f43043..677343a85defe 100644 --- a/src/librustc/ich/impls_ty.rs +++ b/src/librustc/ich/impls_ty.rs @@ -412,7 +412,7 @@ impl<'a> HashStable> for mir::interpret::AllocId { ty::tls::with_opt(|tcx| { trace!("hashing {:?}", *self); let tcx = tcx.expect("can't hash AllocIds during hir lowering"); - let alloc_kind = tcx.alloc_map.lock().get(*self).expect("no value for AllocId"); + let alloc_kind = tcx.alloc_map.lock().get(*self); alloc_kind.hash_stable(hcx, hasher); }); } diff --git a/src/librustc_data_structures/stable_hasher.rs b/src/librustc_data_structures/stable_hasher.rs index 1024e69cc2b0e..c70a0abe8c7e4 100644 --- a/src/librustc_data_structures/stable_hasher.rs +++ b/src/librustc_data_structures/stable_hasher.rs @@ -281,6 +281,23 @@ impl HashStable for (T1, T2, T3) } } +impl HashStable for (T1, T2, T3, T4) + where T1: HashStable, + T2: HashStable, + T3: HashStable, + T4: HashStable, +{ + fn hash_stable(&self, + ctx: &mut CTX, + hasher: &mut StableHasher) { + let (ref _0, ref _1, ref _2, ref _3) = *self; + _0.hash_stable(ctx, hasher); + _1.hash_stable(ctx, hasher); + _2.hash_stable(ctx, hasher); + _3.hash_stable(ctx, hasher); + } +} + impl, CTX> HashStable for [T] { default fn hash_stable(&self, ctx: &mut CTX, diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index 70addf2c907e6..92ddd8777f733 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -196,6 +196,8 @@ impl<'tcx> Into> for ConstEvalError { } } +impl_stable_hash_for!(struct CompileTimeEvaluator {}); + #[derive(Clone, Debug)] enum ConstEvalError { NeedsRfc(String), diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 467634f28969a..8d4f3baf3a9b7 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -15,6 +15,7 @@ use std::mem; use rustc::hir::def_id::DefId; use rustc::hir::def::Def; use rustc::hir::map::definitions::DefPathData; +use rustc::ich::{StableHashingContext, StableHashingContextProvider}; use rustc::mir; use rustc::ty::layout::{ self, Size, Align, HasDataLayout, LayoutOf, TyLayout @@ -22,8 +23,9 @@ use rustc::ty::layout::{ use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::query::TyCtxtAt; -use rustc_data_structures::fx::{FxHashSet, FxHasher}; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::IndexVec; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use rustc::mir::interpret::{ GlobalId, Scalar, FrameInfo, EvalResult, EvalErrorKind, @@ -134,12 +136,12 @@ impl<'mir, 'tcx: 'mir> PartialEq for Frame<'mir, 'tcx> { } } -impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> { - fn hash(&self, state: &mut H) { +impl<'a, 'mir, 'tcx: 'mir> HashStable> for Frame<'mir, 'tcx> { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Frame { - mir: _, + mir, instance, - span: _, + span, return_to_block, return_place, locals, @@ -147,12 +149,8 @@ impl<'mir, 'tcx: 'mir> Hash for Frame<'mir, 'tcx> { stmt, } = self; - instance.hash(state); - return_to_block.hash(state); - return_place.hash(state); - locals.hash(state); - block.hash(state); - stmt.hash(state); + (mir, instance, span, return_to_block).hash_stable(hcx, hasher); + (return_place, locals, block, stmt).hash_stable(hcx, hasher); } } @@ -168,6 +166,15 @@ pub enum StackPopCleanup { None { cleanup: bool }, } +impl<'a> HashStable> for StackPopCleanup { + fn hash_stable(&self, hcx: &mut StableHashingContext<'b>, hasher: &mut StableHasher) { + match self { + StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher), + StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher), + } + } +} + // State of a local variable #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub enum LocalValue { @@ -195,9 +202,14 @@ impl<'tcx> LocalValue { } } +impl_stable_hash_for!(enum self::LocalValue { + Dead, + Live(x), +}); + /// The virtual machine state during const-evaluation at a given point in time. -#[derive(Eq, PartialEq, Hash)] -pub(crate) struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { +#[derive(Eq, PartialEq)] +struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { machine: M, memory: Memory<'a, 'mir, 'tcx, M>, stack: Vec>, @@ -215,6 +227,27 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> } } +impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn hash(&self, state: &mut H) { + // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2) + let mut hcx = self.memory.tcx.get_stable_hashing_context(); + let mut hasher = StableHasher::::new(); + self.hash_stable(&mut hcx, &mut hasher); + hasher.finish().hash(state) + } +} + +impl<'a, 'b, 'mir, 'tcx, M> HashStable> for EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'b>, hasher: &mut StableHasher) { + let EvalSnapshot{ machine, memory, stack } = self; + (machine, &memory.data, stack).hash_stable(hcx, hasher); + } +} + pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The set of all `EvalSnapshot` *hashes* observed by this detector. /// @@ -258,9 +291,10 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> stack: &[Frame<'mir, 'tcx>], ) -> EvalResult<'tcx, ()> { - let mut fx = FxHasher::default(); - (machine, memory, stack).hash(&mut fx); - let hash = fx.finish(); + let mut hcx = memory.tcx.get_stable_hashing_context(); + let mut hasher = StableHasher::::new(); + (machine, stack).hash_stable(&mut hcx, &mut hasher); + let hash = hasher.finish(); if self.hashes.insert(hash) { // No collision diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 57af63d63d9c4..61963f6d3d354 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -15,17 +15,19 @@ use std::hash::Hash; use rustc::hir::def_id::DefId; +use rustc::ich::StableHashingContext; use rustc::mir::interpret::{Allocation, EvalResult, Scalar}; use rustc::mir; use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt}; +use rustc_data_structures::stable_hasher::HashStable; use super::{EvalContext, PlaceTy, OpTy}; /// 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<'mir, 'tcx>: Clone + Eq + Hash { +pub trait Machine<'mir, 'tcx>: Clone + Eq + Hash + for<'a> HashStable> { /// Additional data that can be accessed via the Memory - type MemoryData: Clone + Eq + Hash; + type MemoryData: Clone + Eq + Hash + for<'a> HashStable>; /// Additional memory kinds a machine wishes to distinguish from the builtin ones type MemoryKinds: ::std::fmt::Debug + Copy + Clone + Eq + Hash; diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 59bebbb87a775..4a291f164ccda 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -17,7 +17,6 @@ //! short-circuiting the empty case! use std::collections::VecDeque; -use std::hash::{Hash, Hasher}; use std::ptr; use rustc::ty::{self, Instance, query::TyCtxtAt}; @@ -26,7 +25,7 @@ use rustc::mir::interpret::{Pointer, AllocId, Allocation, ConstValue, ScalarMayb EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic, truncate}; pub use rustc::mir::interpret::{write_target_uint, read_target_uint}; -use rustc_data_structures::fx::{FxHashSet, FxHashMap, FxHasher}; +use rustc_data_structures::fx::{FxHashSet, FxHashMap}; use syntax::ast::Mutability; @@ -91,37 +90,6 @@ impl<'a, 'mir, 'tcx, M> PartialEq for Memory<'a, 'mir, 'tcx, M> } } -impl<'a, 'mir, 'tcx, M> Hash for Memory<'a, 'mir, 'tcx, M> - where M: Machine<'mir, 'tcx>, - 'tcx: 'a + 'mir, -{ - fn hash(&self, state: &mut H) { - let Memory { - data, - alloc_map: _, - tcx: _, - } = self; - - data.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.alloc_map.iter() - .map(|(&id, alloc)| { - let mut h = FxHasher::default(); - id.hash(&mut h); - alloc.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 { diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 6f9e0cf3e37b6..9970816dc2a94 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -81,6 +81,11 @@ impl<'tcx> Value { } } +impl_stable_hash_for!(enum ::interpret::Value { + Scalar(x), + ScalarPair(x, y), +}); + // ScalarPair needs a type to interpret, so we often have a value and a type together // as input for binary and cast operations. #[derive(Copy, Clone, Debug)] @@ -126,6 +131,11 @@ impl Operand { } } +impl_stable_hash_for!(enum ::interpret::Operand { + Immediate(x), + Indirect(x), +}); + #[derive(Copy, Clone, Debug)] pub struct OpTy<'tcx> { crate op: Operand, // ideally we'd make this private, but const_prop needs this diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 5bf6b2b46b7a6..6236a4784fb70 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -14,10 +14,12 @@ use std::convert::TryFrom; +use rustc::ich::StableHashingContext; use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, Size, Align, LayoutOf, TyLayout, HasDataLayout}; use rustc_data_structures::indexed_vec::Idx; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use rustc::mir::interpret::{ GlobalId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic @@ -37,6 +39,12 @@ pub struct MemPlace { pub extra: Option, } +impl_stable_hash_for!(struct ::interpret::MemPlace { + ptr, + align, + extra, +}); + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum Place { /// A place referring to a value allocated in the `Memory` system. @@ -50,6 +58,18 @@ pub enum Place { }, } +impl<'a> HashStable> for Place { + fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + match self { + Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher), + + Place::Local { frame, local } => { + frame.hash_stable(hcx, hasher); + local.hash_stable(hcx, hasher); + }, + } + } +} #[derive(Copy, Clone, Debug)] pub struct PlaceTy<'tcx> { place: Place, From 015f470daa3020ebe92abf61fa4010b56bde006d Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 4 Aug 2018 17:41:03 +0200 Subject: [PATCH 04/15] Move EvalSnapshot into its own module --- src/librustc_mir/interpret/eval_context.rs | 44 +------------------- src/librustc_mir/interpret/mod.rs | 1 + src/librustc_mir/interpret/snapshot.rs | 47 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 42 deletions(-) create mode 100644 src/librustc_mir/interpret/snapshot.rs diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 8d4f3baf3a9b7..b6a84ed8404e4 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -9,7 +9,6 @@ // except according to those terms. use std::fmt::Write; -use std::hash::{Hash, Hasher}; use std::mem; use rustc::hir::def_id::DefId; @@ -40,6 +39,8 @@ use super::{ Memory, Machine }; +use super::snapshot::EvalSnapshot; + pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. pub machine: M, @@ -207,47 +208,6 @@ impl_stable_hash_for!(enum self::LocalValue { Live(x), }); -/// The virtual machine state during const-evaluation at a given point in time. -#[derive(Eq, PartialEq)] -struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { - machine: M, - memory: Memory<'a, 'mir, 'tcx, M>, - stack: Vec>, -} - -impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> - where M: Machine<'mir, 'tcx>, -{ - fn new<'b>(machine: &M, memory: &Memory<'a, 'mir, 'tcx, M>, stack: &[Frame<'mir, 'tcx>]) -> Self { - EvalSnapshot { - machine: machine.clone(), - memory: memory.clone(), - stack: stack.into(), - } - } -} - -impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M> - where M: Machine<'mir, 'tcx>, -{ - fn hash(&self, state: &mut H) { - // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2) - let mut hcx = self.memory.tcx.get_stable_hashing_context(); - let mut hasher = StableHasher::::new(); - self.hash_stable(&mut hcx, &mut hasher); - hasher.finish().hash(state) - } -} - -impl<'a, 'b, 'mir, 'tcx, M> HashStable> for EvalSnapshot<'a, 'mir, 'tcx, M> - where M: Machine<'mir, 'tcx>, -{ - fn hash_stable(&self, hcx: &mut StableHashingContext<'b>, hasher: &mut StableHasher) { - let EvalSnapshot{ machine, memory, stack } = self; - (machine, &memory.data, stack).hash_stable(hcx, hasher); - } -} - pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// The set of all `EvalSnapshot` *hashes* observed by this detector. /// diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 462c4b8889dd1..1e8de02923240 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -17,6 +17,7 @@ mod operand; mod machine; mod memory; mod operator; +mod snapshot; mod step; mod terminator; mod traits; diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs new file mode 100644 index 0000000000000..45574da741025 --- /dev/null +++ b/src/librustc_mir/interpret/snapshot.rs @@ -0,0 +1,47 @@ +use std::hash::{Hash, Hasher}; + +use rustc::ich::{StableHashingContext, StableHashingContextProvider}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; + +use super::{Frame, Memory, Machine}; + +/// The virtual machine state during const-evaluation at a given point in time. +#[derive(Eq, PartialEq)] +pub struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { + machine: M, + memory: Memory<'a, 'mir, 'tcx, M>, + stack: Vec>, +} + +impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + pub fn new(machine: &M, memory: &Memory<'a, 'mir, 'tcx, M>, stack: &[Frame<'mir, 'tcx>]) -> Self { + EvalSnapshot { + machine: machine.clone(), + memory: memory.clone(), + stack: stack.into(), + } + } +} + +impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn hash(&self, state: &mut H) { + // Implement in terms of hash stable, so that k1 == k2 -> hash(k1) == hash(k2) + let mut hcx = self.memory.tcx.get_stable_hashing_context(); + let mut hasher = StableHasher::::new(); + self.hash_stable(&mut hcx, &mut hasher); + hasher.finish().hash(state) + } +} + +impl<'a, 'b, 'mir, 'tcx, M> HashStable> for EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn hash_stable(&self, hcx: &mut StableHashingContext<'b>, hasher: &mut StableHasher) { + let EvalSnapshot{ machine, memory, stack } = self; + (machine, &memory.data, stack).hash_stable(hcx, hasher); + } +} From 2f5c3fde7c5443b8afceaa65aec2b1f62f33e12e Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 11 Aug 2018 19:08:14 +0200 Subject: [PATCH 05/15] Make vaious allocation related types generic on the allocation id --- src/librustc/mir/interpret/mod.rs | 12 ++++++------ src/librustc/mir/interpret/value.rs | 8 ++++---- src/librustc_mir/interpret/eval_context.rs | 8 ++++---- src/librustc_mir/interpret/operand.rs | 17 +++++++++-------- src/librustc_mir/interpret/place.rs | 12 ++++++------ 5 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index d40dbae09d2cb..9b7087ca03483 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -134,8 +134,8 @@ impl PointerArithmetic for T {} #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] -pub struct Pointer { - pub alloc_id: AllocId, +pub struct Pointer { + pub alloc_id: Id, pub offset: Size, } @@ -543,16 +543,16 @@ impl Allocation { impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {} #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)] -pub struct Relocations(SortedMap); +pub struct Relocations(SortedMap); -impl Relocations { - pub fn new() -> Relocations { +impl Relocations { + pub fn new() -> Self { Relocations(SortedMap::new()) } // The caller must guarantee that the given relocations are already sorted // by address and contain no duplicates. - pub fn from_presorted(r: Vec<(Size, AllocId)>) -> Relocations { + pub fn from_presorted(r: Vec<(Size, Id)>) -> Self { Relocations(SortedMap::from_presorted_elements(r)) } } diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 11a4f8b884e7f..9982da483ce09 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -326,7 +326,7 @@ impl From for Scalar { /// size. Like a range of bytes in an `Allocation`, a `Scalar` can either represent the raw bytes /// of a simple value or a pointer into another `Allocation` #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] -pub enum Scalar { +pub enum Scalar { /// The raw bytes of a simple value. Bits { /// The first `size` bytes are the value. @@ -338,12 +338,12 @@ pub enum Scalar { /// A pointer into an `Allocation`. An `Allocation` in the `memory` module has a list of /// relocations, but a `Scalar` is only large enough to contain one, so we just represent the /// relocation and its associated offset together as a `Pointer` here. - Ptr(Pointer), + Ptr(Pointer), } #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] -pub enum ScalarMaybeUndef { - Scalar(Scalar), +pub enum ScalarMaybeUndef { + Scalar(Scalar), Undef, } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index b6a84ed8404e4..02d87bdb7dcf4 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -26,7 +26,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use rustc::mir::interpret::{ - GlobalId, Scalar, FrameInfo, + GlobalId, Scalar, FrameInfo, AllocId, EvalResult, EvalErrorKind, ScalarMaybeUndef, truncate, sign_extend, @@ -98,7 +98,7 @@ pub struct Frame<'mir, 'tcx: 'mir> { /// The locals are stored as `Option`s. /// `None` represents a local that is currently dead, while a live local /// can either directly contain `Scalar` or refer to some part of an `Allocation`. - pub locals: IndexVec, + pub locals: IndexVec>, //////////////////////////////////////////////////////////////////////////////// // Current position within the function @@ -178,13 +178,13 @@ impl<'a> HashStable> for StackPopCleanup { // State of a local variable #[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub enum LocalValue { +pub enum LocalValue { Dead, // Mostly for convenience, we re-use the `Operand` type here. // This is an optimization over just always having a pointer here; // we can thus avoid doing an allocation when the local just stores // immediate values *and* never has its address taken. - Live(Operand), + Live(Operand), } impl<'tcx> LocalValue { diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 9970816dc2a94..4093a6304b360 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -17,9 +17,10 @@ use std::convert::TryInto; use rustc::{mir, ty}; use rustc::ty::layout::{self, Size, LayoutOf, TyLayout, HasDataLayout, IntegerExt}; use rustc_data_structures::indexed_vec::Idx; - use rustc::mir::interpret::{ - GlobalId, ConstValue, Scalar, EvalResult, Pointer, ScalarMaybeUndef, EvalErrorKind + GlobalId, AllocId, + ConstValue, Pointer, Scalar, ScalarMaybeUndef, + EvalResult, EvalErrorKind }; use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; @@ -31,9 +32,9 @@ use super::{EvalContext, Machine, MemPlace, MPlaceTy, MemoryKind}; /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// defined on `Value`, and do not have to work with a `Place`. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum Value { - Scalar(ScalarMaybeUndef), - ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef), +pub enum Value { + Scalar(ScalarMaybeUndef), + ScalarPair(ScalarMaybeUndef, ScalarMaybeUndef), } impl<'tcx> Value { @@ -106,9 +107,9 @@ impl<'tcx> ::std::ops::Deref for ValTy<'tcx> { /// or still in memory. The latter is an optimization, to delay reading that chunk of /// memory and to avoid having to store arbitrary-sized data here. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum Operand { - Immediate(Value), - Indirect(MemPlace), +pub enum Operand { + Immediate(Value), + Indirect(MemPlace), } impl Operand { diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 6236a4784fb70..fd7324c2d17b1 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -22,21 +22,21 @@ use rustc_data_structures::indexed_vec::Idx; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use rustc::mir::interpret::{ - GlobalId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic + GlobalId, AllocId, Scalar, EvalResult, Pointer, ScalarMaybeUndef, PointerArithmetic }; use super::{EvalContext, Machine, Value, ValTy, Operand, OpTy, MemoryKind}; #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub struct MemPlace { +pub struct MemPlace { /// A place may have an integral pointer for ZSTs, and since it might /// be turned back into a reference before ever being dereferenced. /// However, it may never be undef. - pub ptr: Scalar, + pub ptr: Scalar, pub align: Align, /// Metadata for unsized places. Interpretation is up to the type. /// Must not be present for sized types, but can be missing for unsized types /// (e.g. `extern type`). - pub extra: Option, + pub extra: Option>, } impl_stable_hash_for!(struct ::interpret::MemPlace { @@ -46,9 +46,9 @@ impl_stable_hash_for!(struct ::interpret::MemPlace { }); #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] -pub enum Place { +pub enum Place { /// A place referring to a value allocated in the `Memory` system. - Ptr(MemPlace), + Ptr(MemPlace), /// To support alloc-free locals, we are able to write directly to a local. /// (Without that optimization, we'd just always be a `MemPlace`.) From adb1965c12587bcc24edd62e9ecf1c240f8196cd Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 18 Aug 2018 12:14:18 +0200 Subject: [PATCH 06/15] Introduce Snapshot and SnapshotContext traits --- src/librustc_mir/interpret/snapshot.rs | 266 ++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 45574da741025..2bb184cd9bc55 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -1,9 +1,273 @@ use std::hash::{Hash, Hasher}; use rustc::ich::{StableHashingContext, StableHashingContextProvider}; +use rustc::mir; +use rustc::mir::interpret::{AllocId, Pointer, Scalar, ScalarMaybeUndef, Relocations, Allocation, UndefMask}; +use rustc::ty; +use rustc::ty::layout::Align; +use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; +use syntax::ast::Mutability; +use syntax::source_map::Span; -use super::{Frame, Memory, Machine}; +use super::eval_context::{LocalValue, StackPopCleanup}; +use super::{Frame, Memory, Machine, Operand, MemPlace, Place, PlaceExtra, Value}; + +trait SnapshotContext<'a> { + type To; + type From; + fn resolve(&'a self, id: &Self::From) -> Option<&'a Self::To>; +} + +trait Snapshot<'a, Ctx: SnapshotContext<'a>> { + type Item; + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item; +} + +#[derive(Eq, PartialEq)] +struct AllocIdSnapshot<'a>(Option>); + +impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = AllocIdSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + AllocIdSnapshot(ctx.resolve(self).map(|alloc| alloc.snapshot(ctx))) + } +} + +type PointerSnapshot<'a> = Pointer>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for Pointer + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = PointerSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + let Pointer{ alloc_id, offset } = self; + + Pointer { + alloc_id: alloc_id.snapshot(ctx), + offset: *offset, + } + } +} + +type ScalarSnapshot<'a> = Scalar>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = ScalarSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)), + Scalar::Bits{ size, bits } => Scalar::Bits{ + size: *size, + bits: *bits, + }, + } + } +} + +type ScalarMaybeUndefSnapshot<'a> = ScalarMaybeUndef>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for ScalarMaybeUndef + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = ScalarMaybeUndefSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.snapshot(ctx)), + ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef, + } + } +} + +type MemPlaceSnapshot<'a> = MemPlace>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for MemPlace + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = MemPlaceSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + let MemPlace{ ptr, extra, align } = self; + + MemPlaceSnapshot{ + ptr: ptr.snapshot(ctx), + extra: extra.snapshot(ctx), + align: *align, + } + } +} + +type PlaceSnapshot<'a> = Place>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for Place + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = PlaceSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + Place::Ptr(p) => Place::Ptr(p.snapshot(ctx)), + + Place::Local{ frame, local } => Place::Local{ + frame: *frame, + local: *local, + }, + } + } +} + +type PlaceExtraSnapshot<'a> = PlaceExtra>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for PlaceExtra + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = PlaceExtraSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + PlaceExtra::Vtable(p) => PlaceExtra::Vtable(p.snapshot(ctx)), + PlaceExtra::Length(l) => PlaceExtra::Length(*l), + PlaceExtra::None => PlaceExtra::None, + } + } +} + +type ValueSnapshot<'a> = Value>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for Value + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = ValueSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + Value::Scalar(s) => Value::Scalar(s.snapshot(ctx)), + Value::ScalarPair(a, b) => Value::ScalarPair(a.snapshot(ctx), b.snapshot(ctx)), + } + } +} + +type OperandSnapshot<'a> = Operand>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for Operand + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = OperandSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + Operand::Immediate(v) => Operand::Immediate(v.snapshot(ctx)), + Operand::Indirect(m) => Operand::Indirect(m.snapshot(ctx)), + } + } +} + +type LocalValueSnapshot<'a> = LocalValue>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for LocalValue + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = LocalValueSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + LocalValue::Live(v) => LocalValue::Live(v.snapshot(ctx)), + LocalValue::Dead => LocalValue::Dead, + } + } +} + +type RelocationsSnapshot<'a> = Relocations>; + +impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = RelocationsSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + Relocations::from_presorted(self.iter().map(|(size, id)| (*size, id.snapshot(ctx))).collect()) + } +} + +#[derive(Eq, PartialEq)] +struct AllocationSnapshot<'a> { + bytes: &'a [u8], + relocations: RelocationsSnapshot<'a>, + undef_mask: &'a UndefMask, + align: &'a Align, + runtime_mutability: &'a Mutability, +} + +impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = AllocationSnapshot<'a>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + let Allocation { bytes, relocations, undef_mask, align, runtime_mutability } = self; + + AllocationSnapshot { + bytes, + undef_mask, + align, + runtime_mutability, + relocations: relocations.snapshot(ctx), + } + } +} + +#[derive(Eq, PartialEq)] +struct FrameSnapshot<'a, 'tcx> { + instance: &'a ty::Instance<'tcx>, + span: &'a Span, + return_to_block: &'a StackPopCleanup, + return_place: PlaceSnapshot<'a>, + locals: IndexVec>, + block: &'a mir::BasicBlock, + stmt: usize, +} + +impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx> + where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, +{ + type Item = FrameSnapshot<'a, 'tcx>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + let Frame { + mir: _, + instance, + span, + return_to_block, + return_place, + locals, + block, + stmt, + } = self; + + FrameSnapshot { + instance, + span, + return_to_block, + block, + stmt: *stmt, + return_place: return_place.snapshot(ctx), + locals: locals.iter().map(|local| local.snapshot(ctx)).collect(), + } + } +} + +#[derive(Eq, PartialEq)] +struct MemorySnapshot<'a, 'mir: 'a, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx> + 'a> { + data: &'a M::MemoryData, +} /// The virtual machine state during const-evaluation at a given point in time. #[derive(Eq, PartialEq)] From bf6ba974dede851b59afb9aa031ce0c548b754c4 Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 18 Aug 2018 12:32:37 +0200 Subject: [PATCH 07/15] Impl SnapshotContext for Memory --- src/librustc_mir/interpret/snapshot.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 2bb184cd9bc55..d23122825aac8 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -269,6 +269,16 @@ struct MemorySnapshot<'a, 'mir: 'a, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx> + 'a data: &'a M::MemoryData, } +impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + type To = Allocation; + type From = AllocId; + fn resolve(&'b self, id: &Self::From) -> Option<&'b Self::To> { + self.get(*id).ok() + } +} + /// The virtual machine state during const-evaluation at a given point in time. #[derive(Eq, PartialEq)] pub struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { From 927c709eb99e5243975db2ddc6c40cb06a6f472c Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 18 Aug 2018 14:10:46 +0200 Subject: [PATCH 08/15] Impl Eq and PartialEq for EvalSnapshot in terms of the Snapshot trait --- src/librustc_mir/interpret/eval_context.rs | 26 --------- src/librustc_mir/interpret/memory.rs | 21 ------- src/librustc_mir/interpret/snapshot.rs | 67 +++++++++++++++------- 3 files changed, 45 insertions(+), 69 deletions(-) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index 02d87bdb7dcf4..cc6d6d433f84e 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -111,32 +111,6 @@ 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; - - // Some of these are constant during evaluation, but are included - // anyways for correctness. - *instance == other.instance - && *return_to_block == other.return_to_block - && *return_place == other.return_place - && *locals == other.locals - && *block == other.block - && *stmt == other.stmt - } -} - impl<'a, 'mir, 'tcx: 'mir> HashStable> for Frame<'mir, 'tcx> { fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { let Frame { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 4a291f164ccda..9e61de92936bd 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -69,27 +69,6 @@ impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout } } -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_map, - tcx: _, - } = self; - - *data == other.data - && *alloc_map == other.alloc_map - } -} - 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 { diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index d23122825aac8..66364a7390c73 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -11,7 +11,7 @@ use syntax::ast::Mutability; use syntax::source_map::Span; use super::eval_context::{LocalValue, StackPopCleanup}; -use super::{Frame, Memory, Machine, Operand, MemPlace, Place, PlaceExtra, Value}; +use super::{Frame, Memory, Machine, Operand, MemPlace, Place, Value}; trait SnapshotContext<'a> { type To; @@ -24,6 +24,20 @@ trait Snapshot<'a, Ctx: SnapshotContext<'a>> { fn snapshot(&self, ctx: &'a Ctx) -> Self::Item; } +impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option + where Ctx: SnapshotContext<'a>, + T: Snapshot<'a, Ctx> +{ + type Item = Option<>::Item>; + + fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { + match self { + Some(x) => Some(x.snapshot(ctx)), + None => None, + } + } +} + #[derive(Eq, PartialEq)] struct AllocIdSnapshot<'a>(Option>); @@ -124,22 +138,6 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Place } } -type PlaceExtraSnapshot<'a> = PlaceExtra>; - -impl<'a, Ctx> Snapshot<'a, Ctx> for PlaceExtra - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = PlaceExtraSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - PlaceExtra::Vtable(p) => PlaceExtra::Vtable(p.snapshot(ctx)), - PlaceExtra::Length(l) => PlaceExtra::Length(*l), - PlaceExtra::None => PlaceExtra::None, - } - } -} - type ValueSnapshot<'a> = Value>; impl<'a, Ctx> Snapshot<'a, Ctx> for Value @@ -203,7 +201,7 @@ struct AllocationSnapshot<'a> { relocations: RelocationsSnapshot<'a>, undef_mask: &'a UndefMask, align: &'a Align, - runtime_mutability: &'a Mutability, + mutability: &'a Mutability, } impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation @@ -212,20 +210,20 @@ 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, runtime_mutability } = self; + let Allocation { bytes, relocations, undef_mask, align, mutability } = self; AllocationSnapshot { bytes, undef_mask, align, - runtime_mutability, + mutability, relocations: relocations.snapshot(ctx), } } } #[derive(Eq, PartialEq)] -struct FrameSnapshot<'a, 'tcx> { +struct FrameSnapshot<'a, 'tcx: 'a> { instance: &'a ty::Instance<'tcx>, span: &'a Span, return_to_block: &'a StackPopCleanup, @@ -269,6 +267,15 @@ struct MemorySnapshot<'a, 'mir: 'a, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx> + 'a data: &'a M::MemoryData, } +impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn snapshot<'b: 'a>(&'b self) -> MemorySnapshot<'b, 'mir, 'tcx, M> { + let Memory { data, .. } = self; + MemorySnapshot { data } + } +} + impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, { @@ -280,7 +287,6 @@ impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M> } /// The virtual machine state during const-evaluation at a given point in time. -#[derive(Eq, PartialEq)] pub struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { machine: M, memory: Memory<'a, 'mir, 'tcx, M>, @@ -297,6 +303,11 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> stack: stack.into(), } } + + fn snapshot<'b: 'a>(&'b self) -> (&'b M, MemorySnapshot<'b, 'mir, 'tcx, M>, Vec>) { + let EvalSnapshot{ machine, memory, stack } = self; + (&machine, memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect()) + } } impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M> @@ -319,3 +330,15 @@ impl<'a, 'b, 'mir, 'tcx, M> HashStable> for EvalSnapsho (machine, &memory.data, stack).hash_stable(hcx, hasher); } } + +impl<'a, 'mir, 'tcx, M> Eq for EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{} + +impl<'a, 'mir, 'tcx, M> PartialEq for EvalSnapshot<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, +{ + fn eq(&self, other: &Self) -> bool { + self.snapshot() == other.snapshot() + } +} From 806ecabab1921e0e2228a0f37bad4b5baf0fd4fd Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 18 Aug 2018 14:48:14 +0200 Subject: [PATCH 09/15] Add regression test for #52475 --- src/test/ui/consts/const-eval/issue-52475.rs | 25 +++++++++++ .../ui/consts/const-eval/issue-52475.stderr | 42 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 src/test/ui/consts/const-eval/issue-52475.rs create mode 100644 src/test/ui/consts/const-eval/issue-52475.stderr diff --git a/src/test/ui/consts/const-eval/issue-52475.rs b/src/test/ui/consts/const-eval/issue-52475.rs new file mode 100644 index 0000000000000..1c1e6535e69dc --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-52475.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(const_let)] + +fn main() { + let _ = [(); { + //~^ WARNING Constant evaluating a complex constant, this might take some time + //~| ERROR could not evaluate repeat length + let mut x = &0; + let mut n = 0; + while n < 5 { //~ ERROR constant contains unimplemented expression type + n = (n + 1) % 5; + x = &0; // Materialize a new AllocId + } + 0 + }]; +} diff --git a/src/test/ui/consts/const-eval/issue-52475.stderr b/src/test/ui/consts/const-eval/issue-52475.stderr new file mode 100644 index 0000000000000..f45587f3f7580 --- /dev/null +++ b/src/test/ui/consts/const-eval/issue-52475.stderr @@ -0,0 +1,42 @@ +error[E0019]: constant contains unimplemented expression type + --> $DIR/issue-52475.rs:19:9 + | +LL | / while n < 5 { //~ ERROR constant contains unimplemented expression type +LL | | n = (n + 1) % 5; +LL | | x = &0; // Materialize a new AllocId +LL | | } + | |_________^ + +warning: Constant evaluating a complex constant, this might take some time + --> $DIR/issue-52475.rs:14:18 + | +LL | let _ = [(); { + | __________________^ +LL | | //~^ WARNING Constant evaluating a complex constant, this might take some time +LL | | //~| ERROR could not evaluate repeat length +LL | | let mut x = &0; +... | +LL | | 0 +LL | | }]; + | |_____^ + +error[E0080]: could not evaluate repeat length + --> $DIR/issue-52475.rs:14:18 + | +LL | let _ = [(); { + | __________________^ +LL | | //~^ WARNING Constant evaluating a complex constant, this might take some time +LL | | //~| ERROR could not evaluate repeat length +LL | | let mut x = &0; +... | +LL | | n = (n + 1) % 5; + | | ----------- duplicate interpreter state observed here, const evaluation will never terminate +... | +LL | | 0 +LL | | }]; + | |_____^ + +error: aborting due to 2 previous errors + +Some errors occurred: E0019, E0080. +For more information about an error, try `rustc --explain E0019`. From 0a84ff07bd13a1c2261b4f2db2e1042487431dfe Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sun, 26 Aug 2018 16:28:35 +0200 Subject: [PATCH 10/15] Add an info log when snapshotting the constant evaluation context --- src/librustc_mir/interpret/eval_context.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index cc6d6d433f84e..de76064b7cd3c 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -235,6 +235,8 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> return Ok(()) } + info!("snapshotting the state of the interpreter"); + if self.snapshots.insert(EvalSnapshot::new(machine, memory, stack)) { // Spurious collision or first cycle return Ok(()) From 4eb8d94cd99856f44c24e65b8ebc7ce3bde9a4ea Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sun, 26 Aug 2018 18:47:42 +0200 Subject: [PATCH 11/15] Add a convenience macro to reduce code duplication --- src/librustc_mir/interpret/snapshot.rs | 200 +++++++++++-------------- 1 file changed, 86 insertions(+), 114 deletions(-) diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 66364a7390c73..02b3c696e1f89 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -14,9 +14,7 @@ use super::eval_context::{LocalValue, StackPopCleanup}; use super::{Frame, Memory, Machine, Operand, MemPlace, Place, Value}; trait SnapshotContext<'a> { - type To; - type From; - fn resolve(&'a self, id: &Self::From) -> Option<&'a Self::To>; + fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>; } trait Snapshot<'a, Ctx: SnapshotContext<'a>> { @@ -24,6 +22,52 @@ trait Snapshot<'a, Ctx: SnapshotContext<'a>> { fn snapshot(&self, ctx: &'a Ctx) -> Self::Item; } +macro_rules! __impl_snapshot_field { + ($field:ident, $ctx:expr) => ($field.snapshot($ctx)); + ($field:ident, $ctx:expr, $delegate:expr) => ($delegate); +} + +macro_rules! impl_snapshot_for { + // FIXME(mark-i-m): Some of these should be `?` rather than `*`. + (enum $enum_name:ident { $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)*),* ) )* ),* $(,)* }) => { + impl<'a, Ctx> self::Snapshot<'a, Ctx> for $enum_name + where Ctx: self::SnapshotContext<'a>, + { + type Item = $enum_name>; + + #[inline] + fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item { + match *self { + $( + $enum_name::$variant $( ( $(ref $field),* ) )* => + $enum_name::$variant $( ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* ), )* + )* + } + } + } + }; + + // FIXME(mark-i-m): same here. + (struct $struct_name:ident { $($field:ident $(-> $delegate:expr)*),* $(,)* }) => { + impl<'a, Ctx> self::Snapshot<'a, Ctx> for $struct_name + where Ctx: self::SnapshotContext<'a>, + { + type Item = $struct_name>; + + #[inline] + fn snapshot(&self, __ctx: &'a Ctx) -> Self::Item { + let $struct_name { + $(ref $field),* + } = *self; + + $struct_name { + $( $field: __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* + } + } + } + }; +} + impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option where Ctx: SnapshotContext<'a>, T: Snapshot<'a, Ctx> @@ -42,7 +86,7 @@ impl<'a, Ctx, T> Snapshot<'a, Ctx> for Option struct AllocIdSnapshot<'a>(Option>); impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, + where Ctx: SnapshotContext<'a>, { type Item = AllocIdSnapshot<'a>; @@ -51,34 +95,20 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId } } -type PointerSnapshot<'a> = Pointer>; - -impl<'a, Ctx> Snapshot<'a, Ctx> for Pointer - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = PointerSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - let Pointer{ alloc_id, offset } = self; - - Pointer { - alloc_id: alloc_id.snapshot(ctx), - offset: *offset, - } - } -} - -type ScalarSnapshot<'a> = Scalar>; +impl_snapshot_for!(struct Pointer { + alloc_id, + offset -> *offset, +}); impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, + where Ctx: SnapshotContext<'a>, { - type Item = ScalarSnapshot<'a>; + type Item = Scalar>; fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { match self { Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)), - Scalar::Bits{ size, bits } => Scalar::Bits{ + Scalar::Bits{ size, bits } => Scalar::Bits { size: *size, bits: *bits, }, @@ -86,45 +116,21 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar } } -type ScalarMaybeUndefSnapshot<'a> = ScalarMaybeUndef>; - -impl<'a, Ctx> Snapshot<'a, Ctx> for ScalarMaybeUndef - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = ScalarMaybeUndefSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - ScalarMaybeUndef::Scalar(s) => ScalarMaybeUndef::Scalar(s.snapshot(ctx)), - ScalarMaybeUndef::Undef => ScalarMaybeUndef::Undef, - } - } -} - -type MemPlaceSnapshot<'a> = MemPlace>; - -impl<'a, Ctx> Snapshot<'a, Ctx> for MemPlace - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = MemPlaceSnapshot<'a>; +impl_snapshot_for!(enum ScalarMaybeUndef { + Scalar(s), + Undef, +}); - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - let MemPlace{ ptr, extra, align } = self; - - MemPlaceSnapshot{ - ptr: ptr.snapshot(ctx), - extra: extra.snapshot(ctx), - align: *align, - } - } -} - -type PlaceSnapshot<'a> = Place>; +impl_snapshot_for!(struct MemPlace { + ptr, + extra, + align -> *align, +}); impl<'a, Ctx> Snapshot<'a, Ctx> for Place - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, + where Ctx: SnapshotContext<'a>, { - type Item = PlaceSnapshot<'a>; + type Item = Place>; fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { match self { @@ -138,57 +144,25 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Place } } -type ValueSnapshot<'a> = Value>; - -impl<'a, Ctx> Snapshot<'a, Ctx> for Value - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = ValueSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - Value::Scalar(s) => Value::Scalar(s.snapshot(ctx)), - Value::ScalarPair(a, b) => Value::ScalarPair(a.snapshot(ctx), b.snapshot(ctx)), - } - } -} +impl_snapshot_for!(enum Value { + Scalar(s), + ScalarPair(s, t), +}); -type OperandSnapshot<'a> = Operand>; +impl_snapshot_for!(enum Operand { + Immediate(v), + Indirect(m), +}); -impl<'a, Ctx> Snapshot<'a, Ctx> for Operand - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = OperandSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - Operand::Immediate(v) => Operand::Immediate(v.snapshot(ctx)), - Operand::Indirect(m) => Operand::Indirect(m.snapshot(ctx)), - } - } -} - -type LocalValueSnapshot<'a> = LocalValue>; - -impl<'a, Ctx> Snapshot<'a, Ctx> for LocalValue - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, -{ - type Item = LocalValueSnapshot<'a>; - - fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - match self { - LocalValue::Live(v) => LocalValue::Live(v.snapshot(ctx)), - LocalValue::Dead => LocalValue::Dead, - } - } -} - -type RelocationsSnapshot<'a> = Relocations>; +impl_snapshot_for!(enum LocalValue { + Live(v), + Dead, +}); impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, + where Ctx: SnapshotContext<'a>, { - type Item = RelocationsSnapshot<'a>; + type Item = Relocations>; fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { Relocations::from_presorted(self.iter().map(|(size, id)| (*size, id.snapshot(ctx))).collect()) @@ -198,14 +172,14 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations #[derive(Eq, PartialEq)] struct AllocationSnapshot<'a> { bytes: &'a [u8], - relocations: RelocationsSnapshot<'a>, + relocations: Relocations>, undef_mask: &'a UndefMask, align: &'a Align, mutability: &'a Mutability, } impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, + where Ctx: SnapshotContext<'a>, { type Item = AllocationSnapshot<'a>; @@ -227,14 +201,14 @@ struct FrameSnapshot<'a, 'tcx: 'a> { instance: &'a ty::Instance<'tcx>, span: &'a Span, return_to_block: &'a StackPopCleanup, - return_place: PlaceSnapshot<'a>, - locals: IndexVec>, + return_place: Place>, + locals: IndexVec>>, block: &'a mir::BasicBlock, stmt: usize, } impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx> - where Ctx: SnapshotContext<'a, To=Allocation, From=AllocId>, + where Ctx: SnapshotContext<'a>, { type Item = FrameSnapshot<'a, 'tcx>; @@ -279,9 +253,7 @@ impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M> impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, { - type To = Allocation; - type From = AllocId; - fn resolve(&'b self, id: &Self::From) -> Option<&'b Self::To> { + fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> { self.get(*id).ok() } } From add9ee24db86ea25a286fbd604f1ba8aadf90969 Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sun, 26 Aug 2018 19:34:40 +0200 Subject: [PATCH 12/15] Keep lines shorter than 100 characters --- src/librustc_mir/interpret/eval_context.rs | 11 +++++-- src/librustc_mir/interpret/place.rs | 5 ++- src/librustc_mir/interpret/snapshot.rs | 36 +++++++++++++++++----- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index de76064b7cd3c..ac34de490bba2 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -112,7 +112,11 @@ pub struct Frame<'mir, 'tcx: 'mir> { } impl<'a, 'mir, 'tcx: 'mir> HashStable> for Frame<'mir, 'tcx> { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + let Frame { mir, instance, @@ -142,7 +146,10 @@ pub enum StackPopCleanup { } impl<'a> HashStable> for StackPopCleanup { - fn hash_stable(&self, hcx: &mut StableHashingContext<'b>, hasher: &mut StableHasher) { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { match self { StackPopCleanup::Goto(ref block) => block.hash_stable(hcx, hasher), StackPopCleanup::None { cleanup } => cleanup.hash_stable(hcx, hasher), diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index fd7324c2d17b1..0d165ef4803e7 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -59,7 +59,10 @@ pub enum Place { } impl<'a> HashStable> for Place { - fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { + fn hash_stable( + &self, hcx: &mut StableHashingContext<'a>, + hasher: &mut StableHasher) { + match self { Place::Ptr(mem_place) => mem_place.hash_stable(hcx, hasher), diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 02b3c696e1f89..72cc6220470da 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -2,7 +2,10 @@ use std::hash::{Hash, Hasher}; use rustc::ich::{StableHashingContext, StableHashingContextProvider}; use rustc::mir; -use rustc::mir::interpret::{AllocId, Pointer, Scalar, ScalarMaybeUndef, Relocations, Allocation, UndefMask}; +use rustc::mir::interpret::{ + AllocId, Pointer, Scalar, ScalarMaybeUndef, Relocations, Allocation, UndefMask +}; + use rustc::ty; use rustc::ty::layout::Align; use rustc_data_structures::indexed_vec::IndexVec; @@ -29,7 +32,10 @@ macro_rules! __impl_snapshot_field { macro_rules! impl_snapshot_for { // FIXME(mark-i-m): Some of these should be `?` rather than `*`. - (enum $enum_name:ident { $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)*),* ) )* ),* $(,)* }) => { + (enum $enum_name:ident { + $( $variant:ident $( ( $($field:ident $(-> $delegate:expr)*),* ) )* ),* $(,)* + }) => { + impl<'a, Ctx> self::Snapshot<'a, Ctx> for $enum_name where Ctx: self::SnapshotContext<'a>, { @@ -40,7 +46,9 @@ macro_rules! impl_snapshot_for { match *self { $( $enum_name::$variant $( ( $(ref $field),* ) )* => - $enum_name::$variant $( ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* ), )* + $enum_name::$variant $( + ( $( __impl_snapshot_field!($field, __ctx $(, $delegate)*) ),* ), + )* )* } } @@ -165,7 +173,9 @@ impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations type Item = Relocations>; fn snapshot(&self, ctx: &'a Ctx) -> Self::Item { - Relocations::from_presorted(self.iter().map(|(size, id)| (*size, id.snapshot(ctx))).collect()) + Relocations::from_presorted(self.iter() + .map(|(size, id)| (*size, id.snapshot(ctx))) + .collect()) } } @@ -268,7 +278,11 @@ pub struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, { - pub fn new(machine: &M, memory: &Memory<'a, 'mir, 'tcx, M>, stack: &[Frame<'mir, 'tcx>]) -> Self { + pub fn new( + machine: &M, + memory: &Memory<'a, 'mir, 'tcx, M>, + stack: &[Frame<'mir, 'tcx>]) -> Self { + EvalSnapshot { machine: machine.clone(), memory: memory.clone(), @@ -276,7 +290,8 @@ impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> } } - fn snapshot<'b: 'a>(&'b self) -> (&'b M, MemorySnapshot<'b, 'mir, 'tcx, M>, Vec>) { + fn snapshot<'b: 'a>(&'b self) + -> (&'b M, MemorySnapshot<'b, 'mir, 'tcx, M>, Vec>) { let EvalSnapshot{ machine, memory, stack } = self; (&machine, memory.snapshot(), stack.iter().map(|frame| frame.snapshot(memory)).collect()) } @@ -294,10 +309,15 @@ impl<'a, 'mir, 'tcx, M> Hash for EvalSnapshot<'a, 'mir, 'tcx, M> } } -impl<'a, 'b, 'mir, 'tcx, M> HashStable> for EvalSnapshot<'a, 'mir, 'tcx, M> +impl<'a, 'b, 'mir, 'tcx, M> HashStable> + for EvalSnapshot<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, { - fn hash_stable(&self, hcx: &mut StableHashingContext<'b>, hasher: &mut StableHasher) { + fn hash_stable( + &self, + hcx: &mut StableHashingContext<'b>, + hasher: &mut StableHasher) { + let EvalSnapshot{ machine, memory, stack } = self; (machine, &memory.data, stack).hash_stable(hcx, hasher); } From a26ceb3be4e771799b58383116f4d70f15e7c937 Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Thu, 30 Aug 2018 21:58:28 +0200 Subject: [PATCH 13/15] Use EvalContext's TyCtx for the purpose of hashing the evaluation context --- src/librustc_mir/interpret/eval_context.rs | 3 ++- src/librustc_mir/interpret/step.rs | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index ac34de490bba2..bc03ef8c025c1 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -227,12 +227,13 @@ impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> pub fn observe_and_analyze( &mut self, + tcx: &TyCtxt<'b, 'tcx, 'tcx>, machine: &M, memory: &Memory<'a, 'mir, 'tcx, M>, stack: &[Frame<'mir, 'tcx>], ) -> EvalResult<'tcx, ()> { - let mut hcx = memory.tcx.get_stable_hashing_context(); + let mut hcx = tcx.get_stable_hashing_context(); let mut hasher = StableHasher::::new(); (machine, stack).hash_stable(&mut hcx, &mut hasher); let hash = hasher.finish(); diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index f3d655c2651a9..545333e879176 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -73,7 +73,12 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { "Constant evaluating a complex constant, this might take some time"); } - self.loop_detector.observe_and_analyze(&self.machine, &self.memory, &self.stack[..]) + self.loop_detector.observe_and_analyze( + &self.tcx, + &self.machine, + &self.memory, + &self.stack[..], + ) } pub fn run(&mut self) -> EvalResult<'tcx> { From 61a999a531d03c73626716e2b48d259e13f3b6af Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Sat, 1 Sep 2018 21:16:22 +0200 Subject: [PATCH 14/15] Move InfiniteLoopDetector to snapshot.rs --- src/librustc_mir/interpret/eval_context.rs | 71 +------------------- src/librustc_mir/interpret/snapshot.rs | 77 ++++++++++++++++++++-- 2 files changed, 75 insertions(+), 73 deletions(-) diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index bc03ef8c025c1..f7277f8d27610 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -14,7 +14,7 @@ use std::mem; use rustc::hir::def_id::DefId; use rustc::hir::def::Def; use rustc::hir::map::definitions::DefPathData; -use rustc::ich::{StableHashingContext, StableHashingContextProvider}; +use rustc::ich::StableHashingContext; use rustc::mir; use rustc::ty::layout::{ self, Size, Align, HasDataLayout, LayoutOf, TyLayout @@ -22,7 +22,6 @@ use rustc::ty::layout::{ use rustc::ty::subst::{Subst, Substs}; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::query::TyCtxtAt; -use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use rustc::mir::interpret::{ @@ -39,7 +38,7 @@ use super::{ Memory, Machine }; -use super::snapshot::EvalSnapshot; +use super::snapshot::InfiniteLoopDetector; pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { /// Stores the `Machine` instance. @@ -189,72 +188,6 @@ impl_stable_hash_for!(enum self::LocalValue { Live(x), }); -pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { - /// The set of all `EvalSnapshot` *hashes* observed by this detector. - /// - /// When a collision occurs in this table, we store the full snapshot in - /// `snapshots`. - hashes: FxHashSet, - - /// The set of all `EvalSnapshot`s observed by this detector. - /// - /// An `EvalSnapshot` will only be fully cloned once it has caused a - /// collision in `hashes`. As a result, the detector must observe at least - /// *two* full cycles of an infinite loop before it triggers. - snapshots: FxHashSet>, -} - -impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M> - where M: Machine<'mir, 'tcx>, - 'tcx: 'a + 'mir, -{ - fn default() -> Self { - InfiniteLoopDetector { - hashes: FxHashSet::default(), - snapshots: FxHashSet::default(), - } - } -} - -impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> - where M: Machine<'mir, 'tcx>, - 'tcx: 'a + 'mir, -{ - /// Returns `true` if the loop detector has not yet observed a snapshot. - pub fn is_empty(&self) -> bool { - self.hashes.is_empty() - } - - pub fn observe_and_analyze( - &mut self, - tcx: &TyCtxt<'b, 'tcx, 'tcx>, - machine: &M, - memory: &Memory<'a, 'mir, 'tcx, M>, - stack: &[Frame<'mir, 'tcx>], - ) -> EvalResult<'tcx, ()> { - - let mut hcx = tcx.get_stable_hashing_context(); - let mut hasher = StableHasher::::new(); - (machine, stack).hash_stable(&mut hcx, &mut hasher); - let hash = hasher.finish(); - - if self.hashes.insert(hash) { - // No collision - return Ok(()) - } - - info!("snapshotting the state of the interpreter"); - - if self.snapshots.insert(EvalSnapshot::new(machine, memory, stack)) { - // Spurious collision or first cycle - return Ok(()) - } - - // Second cycle - Err(EvalErrorKind::InfiniteLoop.into()) - } -} - impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a EvalContext<'a, 'mir, 'tcx, M> { #[inline] fn data_layout(&self) -> &layout::TargetDataLayout { diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 72cc6220470da..316c80d18c11c 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -3,11 +3,14 @@ use std::hash::{Hash, Hasher}; use rustc::ich::{StableHashingContext, StableHashingContextProvider}; use rustc::mir; use rustc::mir::interpret::{ - AllocId, Pointer, Scalar, ScalarMaybeUndef, Relocations, Allocation, UndefMask + AllocId, Pointer, Scalar, ScalarMaybeUndef, + Relocations, Allocation, UndefMask, + EvalResult, EvalErrorKind, }; -use rustc::ty; +use rustc::ty::{self, TyCtxt}; use rustc::ty::layout::Align; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableHasherResult}; use syntax::ast::Mutability; @@ -16,6 +19,72 @@ use syntax::source_map::Span; use super::eval_context::{LocalValue, StackPopCleanup}; use super::{Frame, Memory, Machine, Operand, MemPlace, Place, Value}; +pub(super) struct InfiniteLoopDetector<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { + /// The set of all `EvalSnapshot` *hashes* observed by this detector. + /// + /// When a collision occurs in this table, we store the full snapshot in + /// `snapshots`. + hashes: FxHashSet, + + /// The set of all `EvalSnapshot`s observed by this detector. + /// + /// An `EvalSnapshot` will only be fully cloned once it has caused a + /// collision in `hashes`. As a result, the detector must observe at least + /// *two* full cycles of an infinite loop before it triggers. + snapshots: FxHashSet>, +} + +impl<'a, 'mir, 'tcx, M> Default for InfiniteLoopDetector<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, + 'tcx: 'a + 'mir, +{ + fn default() -> Self { + InfiniteLoopDetector { + hashes: FxHashSet::default(), + snapshots: FxHashSet::default(), + } + } +} + +impl<'a, 'mir, 'tcx, M> InfiniteLoopDetector<'a, 'mir, 'tcx, M> + where M: Machine<'mir, 'tcx>, + 'tcx: 'a + 'mir, +{ + /// Returns `true` if the loop detector has not yet observed a snapshot. + pub fn is_empty(&self) -> bool { + self.hashes.is_empty() + } + + pub fn observe_and_analyze( + &mut self, + tcx: &TyCtxt<'b, 'tcx, 'tcx>, + machine: &M, + memory: &Memory<'a, 'mir, 'tcx, M>, + stack: &[Frame<'mir, 'tcx>], + ) -> EvalResult<'tcx, ()> { + + let mut hcx = tcx.get_stable_hashing_context(); + let mut hasher = StableHasher::::new(); + (machine, stack).hash_stable(&mut hcx, &mut hasher); + let hash = hasher.finish(); + + if self.hashes.insert(hash) { + // No collision + return Ok(()) + } + + info!("snapshotting the state of the interpreter"); + + if self.snapshots.insert(EvalSnapshot::new(machine, memory, stack)) { + // Spurious collision or first cycle + return Ok(()) + } + + // Second cycle + Err(EvalErrorKind::InfiniteLoop.into()) + } +} + trait SnapshotContext<'a> { fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>; } @@ -269,7 +338,7 @@ impl<'a, 'b, 'mir, 'tcx, M> SnapshotContext<'b> for Memory<'a, 'mir, 'tcx, M> } /// The virtual machine state during const-evaluation at a given point in time. -pub struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { +struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { machine: M, memory: Memory<'a, 'mir, 'tcx, M>, stack: Vec>, @@ -278,7 +347,7 @@ pub struct EvalSnapshot<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { impl<'a, 'mir, 'tcx, M> EvalSnapshot<'a, 'mir, 'tcx, M> where M: Machine<'mir, 'tcx>, { - pub fn new( + fn new( machine: &M, memory: &Memory<'a, 'mir, 'tcx, M>, stack: &[Frame<'mir, 'tcx>]) -> Self { From 05cdf8dc3d490fbec2519de90b254f47ec7373e8 Mon Sep 17 00:00:00 2001 From: Bruno Dutra Date: Mon, 3 Sep 2018 20:03:14 +0200 Subject: [PATCH 15/15] Document snapshot.rs --- src/librustc/mir/interpret/mod.rs | 5 +++++ src/librustc_mir/interpret/snapshot.rs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 9b7087ca03483..ccc5bba1ad611 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -133,6 +133,11 @@ pub trait PointerArithmetic: layout::HasDataLayout { impl PointerArithmetic for T {} +/// Pointer is generic over the type that represents a reference to Allocations, +/// thus making it possible for the most convenient representation to be used in +/// each context. +/// +/// Defaults to the index based and loosely coupled AllocId. #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, RustcEncodable, RustcDecodable, Hash)] pub struct Pointer { pub alloc_id: Id, diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs index 316c80d18c11c..8aa053baae9f0 100644 --- a/src/librustc_mir/interpret/snapshot.rs +++ b/src/librustc_mir/interpret/snapshot.rs @@ -1,3 +1,7 @@ +//! This module contains the machinery necessary to detect infinite loops +//! during const-evaluation by taking snapshots of the state of the interpreter +//! at regular intervals. + use std::hash::{Hash, Hasher}; use rustc::ich::{StableHashingContext, StableHashingContextProvider}; @@ -89,6 +93,8 @@ trait SnapshotContext<'a> { fn resolve(&'a self, id: &AllocId) -> Option<&'a Allocation>; } +/// Taking a snapshot of the evaluation context produces a view of +/// the state of the interpreter that is invariant to `AllocId`s. trait Snapshot<'a, Ctx: SnapshotContext<'a>> { type Item; fn snapshot(&self, ctx: &'a Ctx) -> Self::Item;