]> git.lizzy.rs Git - rust.git/commitdiff
Remove infinite loop detector
authorDylan MacKenzie <ecstaticmorse@gmail.com>
Tue, 17 Mar 2020 23:08:13 +0000 (16:08 -0700)
committerDylan MacKenzie <ecstaticmorse@gmail.com>
Tue, 17 Mar 2020 23:09:53 +0000 (16:09 -0700)
src/librustc_mir/interpret/mod.rs
src/librustc_mir/interpret/snapshot.rs [deleted file]

index c3fd968276577241207408783545216fca5fe308..3063a99886b7bdb76a917614b5d339ac6b4bc472 100644 (file)
@@ -9,7 +9,6 @@
 mod operand;
 mod operator;
 mod place;
-pub(crate) mod snapshot; // for const_eval
 mod step;
 mod terminator;
 mod traits;
diff --git a/src/librustc_mir/interpret/snapshot.rs b/src/librustc_mir/interpret/snapshot.rs
deleted file mode 100644 (file)
index ee45179..0000000
+++ /dev/null
@@ -1,420 +0,0 @@
-//! 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.
-
-// This lives in `interpret` because it needs access to all sots of private state.  However,
-// it is not used by the general miri engine, just by CTFE.
-
-use std::hash::{Hash, Hasher};
-
-use rustc::ich::StableHashingContextProvider;
-use rustc::mir;
-use rustc::mir::interpret::{
-    AllocId, Allocation, InterpResult, Pointer, Relocations, Scalar, UndefMask,
-};
-
-use rustc::ty::layout::{Align, Size};
-use rustc::ty::{self, TyCtxt};
-use rustc_ast::ast::Mutability;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_index::vec::IndexVec;
-use rustc_macros::HashStable;
-use rustc_span::source_map::Span;
-
-use super::eval_context::{LocalState, StackPopCleanup};
-use super::{
-    Frame, Immediate, LocalValue, MemPlace, MemPlaceMeta, Memory, Operand, Place, ScalarMaybeUndef,
-};
-use crate::const_eval::CompileTimeInterpreter;
-
-#[derive(Default)]
-pub(crate) struct InfiniteLoopDetector<'mir, 'tcx> {
-    /// The set of all `InterpSnapshot` *hashes* observed by this detector.
-    ///
-    /// When a collision occurs in this table, we store the full snapshot in
-    /// `snapshots`.
-    hashes: FxHashSet<u64>,
-
-    /// The set of all `InterpSnapshot`s observed by this detector.
-    ///
-    /// An `InterpSnapshot` 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<InterpSnapshot<'mir, 'tcx>>,
-}
-
-impl<'mir, 'tcx> InfiniteLoopDetector<'mir, 'tcx> {
-    pub fn observe_and_analyze(
-        &mut self,
-        tcx: TyCtxt<'tcx>,
-        span: Span,
-        memory: &Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
-        stack: &[Frame<'mir, 'tcx>],
-    ) -> InterpResult<'tcx, ()> {
-        // Compute stack's hash before copying anything
-        let mut hcx = tcx.get_stable_hashing_context();
-        let mut hasher = StableHasher::new();
-        stack.hash_stable(&mut hcx, &mut hasher);
-        let hash = hasher.finish::<u64>();
-
-        // Check if we know that hash already
-        if self.hashes.is_empty() {
-            // FIXME(#49980): make this warning a lint
-            tcx.sess.span_warn(
-                span,
-                "Constant evaluating a complex constant, this might take some time",
-            );
-        }
-        if self.hashes.insert(hash) {
-            // No collision
-            return Ok(());
-        }
-
-        // We need to make a full copy. NOW things that to get really expensive.
-        info!("snapshotting the state of the interpreter");
-
-        if self.snapshots.insert(InterpSnapshot::new(memory, stack)) {
-            // Spurious collision or first cycle
-            return Ok(());
-        }
-
-        // Second cycle
-        throw_exhaust!(InfiniteLoop)
-    }
-}
-
-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;
-}
-
-macro_rules! __impl_snapshot_field {
-    ($field:ident, $ctx:expr) => {
-        $field.snapshot($ctx)
-    };
-    ($field:ident, $ctx:expr, $delegate:expr) => {
-        $delegate
-    };
-}
-
-// This assumes the type has two type parameters, first for the tag (set to `()`),
-// then for the id
-macro_rules! impl_snapshot_for {
-    (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<(), AllocIdSnapshot<'a>>;
-
-            #[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)?) ),* )
-                            )?
-                        }
-                    )*
-                }
-            }
-        }
-    };
-
-    (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<(), AllocIdSnapshot<'a>>;
-
-            #[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<T>
-where
-    Ctx: SnapshotContext<'a>,
-    T: Snapshot<'a, Ctx>,
-{
-    type Item = Option<<T as Snapshot<'a, Ctx>>::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<AllocationSnapshot<'a>>);
-
-impl<'a, Ctx> Snapshot<'a, Ctx> for AllocId
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = AllocIdSnapshot<'a>;
-
-    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
-        AllocIdSnapshot(ctx.resolve(self).map(|alloc| alloc.snapshot(ctx)))
-    }
-}
-
-impl_snapshot_for!(struct Pointer {
-    alloc_id,
-    offset -> *offset, // just copy offset verbatim
-    tag -> *tag, // just copy tag
-});
-
-impl<'a, Ctx> Snapshot<'a, Ctx> for Scalar
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = Scalar<(), AllocIdSnapshot<'a>>;
-
-    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
-        match self {
-            Scalar::Ptr(p) => Scalar::Ptr(p.snapshot(ctx)),
-            Scalar::Raw { size, data } => Scalar::Raw { data: *data, size: *size },
-        }
-    }
-}
-
-impl_snapshot_for!(
-    enum ScalarMaybeUndef {
-        Scalar(s),
-        Undef,
-    }
-);
-
-impl_snapshot_for!(
-    enum MemPlaceMeta {
-        Meta(s),
-        None,
-        Poison,
-    }
-);
-
-impl_snapshot_for!(struct MemPlace {
-    ptr,
-    meta,
-    align -> *align, // just copy alignment verbatim
-});
-
-impl<'a, Ctx> Snapshot<'a, Ctx> for Place
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = Place<(), AllocIdSnapshot<'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 },
-        }
-    }
-}
-
-impl_snapshot_for!(
-    enum Immediate {
-        Scalar(s),
-        ScalarPair(s, t),
-    }
-);
-
-impl_snapshot_for!(
-    enum Operand {
-        Immediate(v),
-        Indirect(m),
-    }
-);
-
-impl_snapshot_for!(
-    enum LocalValue {
-        Dead,
-        Uninitialized,
-        Live(v),
-    }
-);
-
-impl<'a, Ctx> Snapshot<'a, Ctx> for Relocations
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = Relocations<(), AllocIdSnapshot<'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: Relocations<(), AllocIdSnapshot<'a>>,
-    undef_mask: &'a UndefMask,
-    align: &'a Align,
-    size: &'a Size,
-    mutability: &'a Mutability,
-}
-
-impl<'a, Ctx> Snapshot<'a, Ctx> for &'a Allocation
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = AllocationSnapshot<'a>;
-
-    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
-        let Allocation { size, align, mutability, extra: (), .. } = self;
-
-        let all_bytes = 0..self.len();
-        // This 'inspect' is okay since following access respects undef and relocations. This does
-        // influence interpreter exeuction, but only to detect the error of cycles in evaluation
-        // dependencies.
-        let bytes = self.inspect_with_undef_and_ptr_outside_interpreter(all_bytes);
-
-        let undef_mask = self.undef_mask();
-        let relocations = self.relocations();
-
-        AllocationSnapshot {
-            bytes,
-            undef_mask,
-            align,
-            size,
-            mutability,
-            relocations: relocations.snapshot(ctx),
-        }
-    }
-}
-
-#[derive(Eq, PartialEq)]
-struct FrameSnapshot<'a, 'tcx> {
-    instance: ty::Instance<'tcx>,
-    span: Span,
-    return_to_block: &'a StackPopCleanup,
-    return_place: Option<Place<(), AllocIdSnapshot<'a>>>,
-    locals: IndexVec<mir::Local, LocalValue<(), AllocIdSnapshot<'a>>>,
-    block: Option<mir::BasicBlock>,
-    stmt: usize,
-}
-
-impl<'a, 'mir, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a Frame<'mir, 'tcx>
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = FrameSnapshot<'a, 'tcx>;
-
-    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
-        let Frame {
-            body: _,
-            instance,
-            span,
-            return_to_block,
-            return_place,
-            locals,
-            block,
-            stmt,
-            extra: _,
-        } = self;
-
-        FrameSnapshot {
-            instance: *instance,
-            span: *span,
-            return_to_block,
-            block: *block,
-            stmt: *stmt,
-            return_place: return_place.map(|r| r.snapshot(ctx)),
-            locals: locals.iter().map(|local| local.snapshot(ctx)).collect(),
-        }
-    }
-}
-
-impl<'a, 'tcx, Ctx> Snapshot<'a, Ctx> for &'a LocalState<'tcx>
-where
-    Ctx: SnapshotContext<'a>,
-{
-    type Item = LocalValue<(), AllocIdSnapshot<'a>>;
-
-    fn snapshot(&self, ctx: &'a Ctx) -> Self::Item {
-        let LocalState { value, layout: _ } = self;
-        value.snapshot(ctx)
-    }
-}
-
-impl<'b, 'mir, 'tcx> SnapshotContext<'b>
-    for Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>
-{
-    fn resolve(&'b self, id: &AllocId) -> Option<&'b Allocation> {
-        self.get_raw(*id).ok()
-    }
-}
-
-/// The virtual machine state during const-evaluation at a given point in time.
-/// We assume the `CompileTimeInterpreter` has no interesting extra state that
-/// is worth considering here.
-#[derive(HashStable)]
-struct InterpSnapshot<'mir, 'tcx> {
-    // Not hashing memory: Avoid hashing memory all the time during execution
-    #[stable_hasher(ignore)]
-    memory: Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
-    stack: Vec<Frame<'mir, 'tcx>>,
-}
-
-impl InterpSnapshot<'mir, 'tcx> {
-    fn new(
-        memory: &Memory<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>,
-        stack: &[Frame<'mir, 'tcx>],
-    ) -> Self {
-        InterpSnapshot { memory: memory.clone(), stack: stack.into() }
-    }
-
-    // Used to compare two snapshots
-    fn snapshot(&'b self) -> Vec<FrameSnapshot<'b, 'tcx>> {
-        // Start with the stack, iterate and recursively snapshot
-        self.stack.iter().map(|frame| frame.snapshot(&self.memory)).collect()
-    }
-}
-
-impl<'mir, 'tcx> Hash for InterpSnapshot<'mir, 'tcx> {
-    fn hash<H: Hasher>(&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::<u64>().hash(state)
-    }
-}
-
-impl<'mir, 'tcx> Eq for InterpSnapshot<'mir, 'tcx> {}
-
-impl<'mir, 'tcx> PartialEq for InterpSnapshot<'mir, 'tcx> {
-    fn eq(&self, other: &Self) -> bool {
-        // FIXME: This looks to be a *ridiculously expensive* comparison operation.
-        // Doesn't this make tons of copies?  Either `snapshot` is very badly named,
-        // or it does!
-        self.snapshot() == other.snapshot()
-    }
-}