]> git.lizzy.rs Git - rust.git/commitdiff
Introduce snapshot_vec abstraction
authorNiko Matsakis <niko@alum.mit.edu>
Tue, 22 Jul 2014 11:40:51 +0000 (07:40 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Fri, 29 Aug 2014 01:15:23 +0000 (21:15 -0400)
src/librustc/lib.rs
src/librustc/middle/typeck/infer/mod.rs
src/librustc/middle/typeck/infer/unify.rs
src/librustc/util/snapshot_vec.rs [new file with mode: 0644]

index 9b3cc2b6a0a0002ed2ed0646d7f1cb5005076f65..ebce1a2a69b1431a0daa75716273982ded478683 100644 (file)
@@ -136,6 +136,7 @@ pub mod util {
     pub mod common;
     pub mod ppaux;
     pub mod nodemap;
+    pub mod snapshot_vec;
 }
 
 pub mod lib {
index c6312ec4663d62d177da244bf353687d030cc842..e672b8b310f5dc0be9801aec02321920dc9bd3f3 100644 (file)
@@ -555,13 +555,13 @@ fn rollback_to(&self, snapshot: CombinedSnapshot) {
 
         self.type_unification_table
             .borrow_mut()
-            .rollback_to(self.tcx, type_snapshot);
+            .rollback_to(type_snapshot);
         self.int_unification_table
             .borrow_mut()
-            .rollback_to(self.tcx, int_snapshot);
+            .rollback_to(int_snapshot);
         self.float_unification_table
             .borrow_mut()
-            .rollback_to(self.tcx, float_snapshot);
+            .rollback_to(float_snapshot);
         self.region_vars
             .rollback_to(region_vars_snapshot);
     }
index 33bdded52341f304b7f8a3716f57de9388edf9d8..fd722d369663871a67af60f707d319d17f66902f 100644 (file)
@@ -16,9 +16,9 @@
 use middle::typeck::infer::InferCtxt;
 use std::cell::RefCell;
 use std::fmt::Show;
-use std::mem;
 use syntax::ast;
 use util::ppaux::Repr;
+use util::snapshot_vec as sv;
 
 /**
  * This trait is implemented by any type that can serve as a type
@@ -82,13 +82,8 @@ pub struct UnificationTable<K,V> {
     /**
      * Indicates the current value of each key.
      */
-    values: Vec<VarValue<K,V>>,
 
-    /**
-     * When a snapshot is active, logs each change made to the table
-     * so that they can be unrolled.
-     */
-    undo_log: Vec<UndoLog<K,V>>,
+    values: sv::SnapshotVec<VarValue<K,V>,(),Delegate>,
 }
 
 /**
@@ -96,29 +91,9 @@ pub struct UnificationTable<K,V> {
  * made during the snapshot may either be *committed* or *rolled back*.
  */
 pub struct Snapshot<K> {
-    // Ensure that this snapshot is keyed to the table type.
-    marker1: marker::CovariantType<K>,
-
-    // Snapshots are tokens that should be created/consumed linearly.
-    marker2: marker::NoCopy,
-
-    // Length of the undo log at the time the snapshot was taken.
-    length: uint,
-}
-
-#[deriving(PartialEq)]
-enum UndoLog<K,V> {
-    /// Indicates where a snapshot started.
-    OpenSnapshot,
-
-    /// Indicates a snapshot that has been committed.
-    CommittedSnapshot,
-
-    /// New variable with given index was created.
-    NewVar(uint),
-
-    /// Variable with given index was changed *from* the given value.
-    SetVar(uint, VarValue<K,V>),
+    // Link snapshot to the key type `K` of the table.
+    marker: marker::CovariantType<K>,
+    snapshot: sv::Snapshot,
 }
 
 /**
@@ -131,6 +106,8 @@ pub struct Node<K,V> {
     pub rank: uint,
 }
 
+pub struct Delegate;
+
 // We can't use V:LatticeValue, much as I would like to,
 // because frequently the pattern is that V=Bounds<U> for some
 // other type parameter U, and we have no way to say
@@ -139,77 +116,26 @@ pub struct Node<K,V> {
 impl<V:PartialEq+Clone+Repr,K:UnifyKey<V>> UnificationTable<K,V> {
     pub fn new() -> UnificationTable<K,V> {
         UnificationTable {
-            values: Vec::new(),
-            undo_log: Vec::new()
+            values: sv::SnapshotVec::new(Delegate),
         }
     }
 
-    pub fn in_snapshot(&self) -> bool {
-        /*! True if a snapshot has been started. */
-
-        self.undo_log.len() > 0
-    }
-
     /**
      * Starts a new snapshot. Each snapshot must be either
      * rolled back or committed in a "LIFO" (stack) order.
      */
     pub fn snapshot(&mut self) -> Snapshot<K> {
-        let length = self.undo_log.len();
-        debug!("{}: snapshot at length {}",
-               UnifyKey::tag(None::<K>),
-               length);
-        self.undo_log.push(OpenSnapshot);
-        Snapshot { length: length,
-                   marker1: marker::CovariantType,
-                   marker2: marker::NoCopy }
-    }
-
-    fn assert_open_snapshot(&self, snapshot: &Snapshot<K>) {
-        // Or else there was a failure to follow a stack discipline:
-        assert!(self.undo_log.len() > snapshot.length);
-
-        // Invariant established by start_snapshot():
-        assert!(*self.undo_log.get(snapshot.length) == OpenSnapshot);
+        Snapshot { marker: marker::CovariantType::<K>,
+                   snapshot: self.values.start_snapshot() }
     }
 
     /**
      * Reverses all changes since the last snapshot. Also
      * removes any keys that have been created since then.
      */
-    pub fn rollback_to(&mut self, tcx: &ty::ctxt, snapshot: Snapshot<K>) {
-        debug!("{}: rollback_to({})",
-               UnifyKey::tag(None::<K>),
-               snapshot.length);
-
-        self.assert_open_snapshot(&snapshot);
-
-        while self.undo_log.len() > snapshot.length + 1 {
-            match self.undo_log.pop().unwrap() {
-                OpenSnapshot => {
-                    // This indicates a failure to obey the stack discipline.
-                    tcx.sess.bug("Cannot rollback an uncommitted snapshot");
-                }
-
-                CommittedSnapshot => {
-                    // This occurs when there are nested snapshots and
-                    // the inner is committed but outer is rolled back.
-                }
-
-                NewVar(i) => {
-                    assert!(self.values.len() == i);
-                    self.values.pop();
-                }
-
-                SetVar(i, v) => {
-                    *self.values.get_mut(i) = v;
-                }
-            }
-        }
-
-        let v = self.undo_log.pop().unwrap();
-        assert!(v == OpenSnapshot);
-        assert!(self.undo_log.len() == snapshot.length);
+    pub fn rollback_to(&mut self, snapshot: Snapshot<K>) {
+        debug!("{}: rollback_to()", UnifyKey::tag(None::<K>));
+        self.values.rollback_to(snapshot.snapshot);
     }
 
     /**
@@ -217,28 +143,12 @@ pub fn rollback_to(&mut self, tcx: &ty::ctxt, snapshot: Snapshot<K>) {
      * can still be undone if there is a snapshot further out.
      */
     pub fn commit(&mut self, snapshot: Snapshot<K>) {
-        debug!("{}: commit({})",
-               UnifyKey::tag(None::<K>),
-               snapshot.length);
-
-        self.assert_open_snapshot(&snapshot);
-
-        if snapshot.length == 0 {
-            // The root snapshot.
-            self.undo_log.truncate(0);
-        } else {
-            *self.undo_log.get_mut(snapshot.length) = CommittedSnapshot;
-        }
+        debug!("{}: commit()", UnifyKey::tag(None::<K>));
+        self.values.commit(snapshot.snapshot);
     }
 
     pub fn new_key(&mut self, value: V) -> K {
-        let index = self.values.len();
-
-        if self.in_snapshot() {
-            self.undo_log.push(NewVar(index));
-        }
-
-        self.values.push(Root(value, 0));
+        let index = self.values.push(Root(value, 0));
         let k = UnifyKey::from_index(index);
         debug!("{}: created new key: {}",
                UnifyKey::tag(None::<K>),
@@ -246,20 +156,6 @@ pub fn new_key(&mut self, value: V) -> K {
         k
     }
 
-    fn swap_value(&mut self,
-                  index: uint,
-                  new_value: VarValue<K,V>)
-                  -> VarValue<K,V>
-    {
-        /*!
-         * Primitive operation to swap a value in the var array.
-         * Caller should update the undo log if we are in a snapshot.
-         */
-
-        let loc = self.values.get_mut(index);
-        mem::replace(loc, new_value)
-    }
-
     pub fn get(&mut self, tcx: &ty::ctxt, vid: K) -> Node<K,V> {
         /*!
          * Find the root node for `vid`. This uses the standard
@@ -274,15 +170,7 @@ pub fn get(&mut self, tcx: &ty::ctxt, vid: K) -> Node<K,V> {
                 let node: Node<K,V> = self.get(tcx, redirect.clone());
                 if node.key != redirect {
                     // Path compression
-                    let old_value =
-                        self.swap_value(index, Redirect(node.key.clone()));
-
-                    // If we are in a snapshot, record this compression,
-                    // because it's possible that the unification which
-                    // caused it will be rolled back later.
-                    if self.in_snapshot() {
-                        self.undo_log.push(SetVar(index, old_value));
-                    }
+                    self.values.set(index, Redirect(node.key.clone()));
                 }
                 node
             }
@@ -310,15 +198,12 @@ pub fn set(&mut self,
          */
 
         assert!(self.is_root(&key));
-        assert!(self.in_snapshot());
 
         debug!("Updating variable {} to {}",
                key.repr(tcx),
                new_value.repr(tcx));
 
-        let index = key.index();
-        let old_value = self.swap_value(index, new_value);
-        self.undo_log.push(SetVar(index, old_value));
+        self.values.set(key.index(), new_value);
     }
 
     pub fn unify(&mut self,
@@ -359,6 +244,12 @@ pub fn unify(&mut self,
     }
 }
 
+impl<K,V> sv::SnapshotVecDelegate<VarValue<K,V>,()> for Delegate {
+    fn reverse(&mut self, _: &mut Vec<VarValue<K,V>>, _: ()) {
+        fail!("Nothing to reverse");
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Code to handle simple keys like ints, floats---anything that
 // doesn't have a subtyping relationship we need to worry about.
@@ -373,7 +264,8 @@ pub trait SimplyUnifiable : Clone + PartialEq + Repr {
 
 pub fn err<V:SimplyUnifiable>(a_is_expected: bool,
                               a_t: V,
-                              b_t: V) -> ures {
+                              b_t: V)
+                              -> ures {
     if a_is_expected {
         Err(SimplyUnifiable::to_type_err(
             ty::expected_found {expected: a_t, found: b_t}))
diff --git a/src/librustc/util/snapshot_vec.rs b/src/librustc/util/snapshot_vec.rs
new file mode 100644 (file)
index 0000000..60e50c8
--- /dev/null
@@ -0,0 +1,195 @@
+// Copyright 2014 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+/*!
+ * A utility class for implementing "snapshottable" things; a
+ * snapshottable data structure permits you to take a snapshot (via
+ * `start_snapshot`) and then, after making some changes, elect either
+ * to rollback to the start of the snapshot or commit those changes.
+ *
+ * This vector is intended to be used as part of an abstraction, not
+ * serve as a complete abstraction on its own. As such, while it will
+ * roll back most changes on its own, it also supports a `get_mut`
+ * operation that gives you an abitrary mutable pointer into the
+ * vector. To ensure that any changes you make this with this pointer
+ * are rolled back, you must invoke `record` to record any changes you
+ * make and also supplying a delegate capable of reversing those
+ * changes.
+ */
+
+use std::kinds::marker;
+use std::mem;
+
+#[deriving(PartialEq)]
+enum UndoLog<T,U> {
+    /// Indicates where a snapshot started.
+    OpenSnapshot,
+
+    /// Indicates a snapshot that has been committed.
+    CommittedSnapshot,
+
+    /// New variable with given index was created.
+    NewElem(uint),
+
+    /// Variable with given index was changed *from* the given value.
+    SetElem(uint, T),
+
+    /// Extensible set of actions
+    Other(U)
+}
+
+pub struct SnapshotVec<T,U,D> {
+    values: Vec<T>,
+    undo_log: Vec<UndoLog<T,U>>,
+    delegate: D
+}
+
+pub struct Snapshot {
+    // Snapshots are tokens that should be created/consumed linearly.
+    marker: marker::NoCopy,
+
+    // Length of the undo log at the time the snapshot was taken.
+    length: uint,
+}
+
+pub trait SnapshotVecDelegate<T,U> {
+    fn reverse(&mut self, values: &mut Vec<T>, action: U);
+}
+
+impl<T,U,D:SnapshotVecDelegate<T,U>> SnapshotVec<T,U,D> {
+    pub fn new(delegate: D) -> SnapshotVec<T,U,D> {
+        SnapshotVec {
+            values: Vec::new(),
+            undo_log: Vec::new(),
+            delegate: delegate
+        }
+    }
+
+    fn in_snapshot(&self) -> bool {
+        !self.undo_log.is_empty()
+    }
+
+    pub fn record(&mut self, action: U) {
+        if self.in_snapshot() {
+            self.undo_log.push(Other(action));
+        }
+    }
+
+    pub fn push(&mut self, elem: T) -> uint {
+        let len = self.values.len();
+        self.values.push(elem);
+
+        if self.in_snapshot() {
+            self.undo_log.push(NewElem(len));
+        }
+
+        len
+    }
+
+    pub fn get<'a>(&'a self, index: uint) -> &'a T {
+        self.values.get(index)
+    }
+
+    pub fn get_mut<'a>(&'a mut self, index: uint) -> &'a mut T {
+        /*!
+         * Returns a mutable pointer into the vec; whatever changes
+         * you make here cannot be undone automatically, so you should
+         * be sure call `record()` with some sort of suitable undo
+         * action.
+         */
+
+        self.values.get_mut(index)
+    }
+
+    pub fn set(&mut self, index: uint, new_elem: T) {
+        /*!
+         * Updates the element at the given index. The old value will
+         * saved (and perhaps restored) if a snapshot is active.
+         */
+
+        let old_elem = mem::replace(self.values.get_mut(index), new_elem);
+        if self.in_snapshot() {
+            self.undo_log.push(SetElem(index, old_elem));
+        }
+    }
+
+    pub fn start_snapshot(&mut self) -> Snapshot {
+        let length = self.undo_log.len();
+        self.undo_log.push(OpenSnapshot);
+        Snapshot { length: length,
+                   marker: marker::NoCopy }
+    }
+
+    fn assert_open_snapshot(&self, snapshot: &Snapshot) {
+        // Or else there was a failure to follow a stack discipline:
+        assert!(self.undo_log.len() > snapshot.length);
+
+        // Invariant established by start_snapshot():
+        assert!(
+            match *self.undo_log.get(snapshot.length) {
+                OpenSnapshot => true,
+                _ => false
+            });
+    }
+
+    pub fn rollback_to(&mut self, snapshot: Snapshot) {
+        debug!("rollback_to({})", snapshot.length);
+
+        self.assert_open_snapshot(&snapshot);
+
+        while self.undo_log.len() > snapshot.length + 1 {
+            match self.undo_log.pop().unwrap() {
+                OpenSnapshot => {
+                    // This indicates a failure to obey the stack discipline.
+                    fail!("Cannot rollback an uncommited snapshot");
+                }
+
+                CommittedSnapshot => {
+                    // This occurs when there are nested snapshots and
+                    // the inner is commited but outer is rolled back.
+                }
+
+                NewElem(i) => {
+                    self.values.pop();
+                    assert!(self.values.len() == i);
+                }
+
+                SetElem(i, v) => {
+                    *self.values.get_mut(i) = v;
+                }
+
+                Other(u) => {
+                    self.delegate.reverse(&mut self.values, u);
+                }
+            }
+        }
+
+        let v = self.undo_log.pop().unwrap();
+        assert!(match v { OpenSnapshot => true, _ => false });
+        assert!(self.undo_log.len() == snapshot.length);
+    }
+
+    /**
+     * Commits all changes since the last snapshot. Of course, they
+     * can still be undone if there is a snapshot further out.
+     */
+    pub fn commit(&mut self, snapshot: Snapshot) {
+        debug!("commit({})", snapshot.length);
+
+        self.assert_open_snapshot(&snapshot);
+
+        if snapshot.length == 0 {
+            // The root snapshot.
+            self.undo_log.truncate(0);
+        } else {
+            *self.undo_log.get_mut(snapshot.length) = CommittedSnapshot;
+        }
+    }
+}