Introduce snapshot_vec abstraction
This commit is contained in:
parent
790d9c4708
commit
d6e5797e41
@ -136,6 +136,7 @@ pub mod util {
|
||||
pub mod common;
|
||||
pub mod ppaux;
|
||||
pub mod nodemap;
|
||||
pub mod snapshot_vec;
|
||||
}
|
||||
|
||||
pub mod lib {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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}))
|
||||
|
195
src/librustc/util/snapshot_vec.rs
Normal file
195
src/librustc/util/snapshot_vec.rs
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user