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
/**
* 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>,
}
/**
* 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,
}
/**
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
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);
}
/**
* 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>),
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
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
}
*/
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,
}
}
+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.
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}))
--- /dev/null
+// 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;
+ }
+ }
+}