about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock16
-rw-r--r--Cargo.toml2
-rw-r--r--src/librustc_data_structures/Cargo.toml2
-rw-r--r--src/librustc_infer/infer/mod.rs61
-rw-r--r--src/librustc_infer/infer/region_constraints/leak_check.rs21
-rw-r--r--src/librustc_infer/infer/region_constraints/mod.rs249
-rw-r--r--src/librustc_infer/infer/type_variable.rs16
7 files changed, 196 insertions, 171 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6f442e8d2b9..45c298ea8bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -987,7 +987,17 @@ dependencies = [
 [[package]]
 name = "ena"
 version = "0.13.1"
-source = "git+https://github.com/Marwes/ena?branch=detach_undo_log#9b977ea7f209a35f46d65d33cdd74b8f4931fb8a"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8944dc8fa28ce4a38f778bd46bf7d923fe73eed5a439398507246c8e017e6f36"
+dependencies = [
+ "log",
+]
+
+[[package]]
+name = "ena"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
 dependencies = [
  "log",
 ]
@@ -3234,7 +3244,7 @@ dependencies = [
  "bitflags",
  "cfg-if",
  "crossbeam-utils 0.7.2",
- "ena",
+ "ena 0.13.1",
  "indexmap",
  "jobserver",
  "lazy_static 1.4.0",
@@ -3680,7 +3690,7 @@ dependencies = [
  "bitflags",
  "cfg-if",
  "crossbeam-utils 0.7.2",
- "ena",
+ "ena 0.14.0",
  "graphviz",
  "indexmap",
  "jobserver",
diff --git a/Cargo.toml b/Cargo.toml
index 9b143dcc8d8..7b5e0fa1c28 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,7 +65,5 @@ rustc-std-workspace-core = { path = 'src/tools/rustc-std-workspace-core' }
 rustc-std-workspace-alloc = { path = 'src/tools/rustc-std-workspace-alloc' }
 rustc-std-workspace-std = { path = 'src/tools/rustc-std-workspace-std' }
 
-ena = { version = "0.13.1", git = "https://github.com/Marwes/ena", branch = "detach_undo_log" }
-
 [patch."https://github.com/rust-lang/rust-clippy"]
 clippy_lints = { path = "src/tools/clippy/clippy_lints" }
diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml
index 6d7022acc78..e257ada0629 100644
--- a/src/librustc_data_structures/Cargo.toml
+++ b/src/librustc_data_structures/Cargo.toml
@@ -10,7 +10,7 @@ path = "lib.rs"
 doctest = false
 
 [dependencies]
-ena = "0.13.1"
+ena = "0.14"
 indexmap = "1"
 log = "0.4"
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
diff --git a/src/librustc_infer/infer/mod.rs b/src/librustc_infer/infer/mod.rs
index 6d76f15998a..ed99b47f80c 100644
--- a/src/librustc_infer/infer/mod.rs
+++ b/src/librustc_infer/infer/mod.rs
@@ -45,7 +45,9 @@ use self::free_regions::RegionRelations;
 use self::lexical_region_resolve::LexicalRegionResolutions;
 use self::outlives::env::OutlivesEnvironment;
 use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound};
-use self::region_constraints::{RegionConstraintCollector, RegionSnapshot};
+use self::region_constraints::{
+    RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
+};
 use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 
 pub mod at;
@@ -161,7 +163,7 @@ pub struct InferCtxtInner<'tcx> {
     /// `resolve_regions_and_report_errors` is invoked, this gets set to `None`
     /// -- further attempts to perform unification, etc., may fail if new
     /// region constraints would've been added.
-    region_constraints: Option<RegionConstraintCollector<'tcx>>,
+    region_constraints: Option<RegionConstraintStorage<'tcx>>,
 
     /// A set of constraints that regionck must validate. Each
     /// constraint has the form `T:'a`, meaning "some type `T` must
@@ -206,7 +208,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
             const_unification_table: ut::UnificationStorage::new(),
             int_unification_table: ut::UnificationStorage::new(),
             float_unification_table: ut::UnificationStorage::new(),
-            region_constraints: Some(RegionConstraintCollector::new()),
+            region_constraints: Some(RegionConstraintStorage::new()),
             region_obligations: vec![],
         }
     }
@@ -243,8 +245,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
         ut::UnificationTable::with_log(&mut self.const_unification_table, &mut self.undo_log)
     }
 
-    pub fn unwrap_region_constraints(&mut self) -> &mut RegionConstraintCollector<'tcx> {
-        self.region_constraints.as_mut().expect("region constraints already solved")
+    pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'tcx, '_> {
+        self.region_constraints
+            .as_mut()
+            .expect("region constraints already solved")
+            .with_log(&mut self.undo_log)
     }
 }
 
@@ -258,6 +263,14 @@ pub(crate) enum UndoLog<'tcx> {
     ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
     IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
     FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
+    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
+    RegionUnificationTable(sv::UndoLog<ut::Delegate<ty::RegionVid>>),
+}
+
+impl<'tcx> From<region_constraints::UndoLog<'tcx>> for UndoLog<'tcx> {
+    fn from(l: region_constraints::UndoLog<'tcx>) -> Self {
+        UndoLog::RegionConstraintCollector(l)
+    }
 }
 
 impl<'tcx> From<sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>> for UndoLog<'tcx> {
@@ -308,6 +321,12 @@ impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::FloatVid>>> for UndoLog<'tcx> {
     }
 }
 
+impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::RegionVid>>> for UndoLog<'tcx> {
+    fn from(l: sv::UndoLog<ut::Delegate<ty::RegionVid>>) -> Self {
+        Self::RegionUnificationTable(l)
+    }
+}
+
 pub(crate) type UnificationTable<'a, 'tcx, T> =
     ut::UnificationTable<ut::InPlace<T, &'a mut ut::UnificationStorage<T>, &'a mut Logs<'tcx>>>;
 
@@ -316,6 +335,7 @@ struct RollbackView<'tcx, 'a> {
     const_unification_table: &'a mut ut::UnificationStorage<ty::ConstVid<'tcx>>,
     int_unification_table: &'a mut ut::UnificationStorage<ty::IntVid>,
     float_unification_table: &'a mut ut::UnificationStorage<ty::FloatVid>,
+    region_constraints: &'a mut RegionConstraintStorage<'tcx>,
 }
 
 impl<'tcx> Rollback<UndoLog<'tcx>> for RollbackView<'tcx, '_> {
@@ -325,6 +345,10 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for RollbackView<'tcx, '_> {
             UndoLog::ConstUnificationTable(undo) => self.const_unification_table.reverse(undo),
             UndoLog::IntUnificationTable(undo) => self.int_unification_table.reverse(undo),
             UndoLog::FloatUnificationTable(undo) => self.float_unification_table.reverse(undo),
+            UndoLog::RegionConstraintCollector(undo) => self.region_constraints.reverse(undo),
+            UndoLog::RegionUnificationTable(undo) => {
+                self.region_constraints.unification_table.reverse(undo)
+            }
         }
     }
 }
@@ -408,6 +432,16 @@ impl<'tcx> Snapshots<UndoLog<'tcx>> for Logs<'tcx> {
 }
 
 impl<'tcx> Logs<'tcx> {
+    pub(crate) fn region_constraints(
+        &self,
+        after: usize,
+    ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
+        self.logs[after..].iter().filter_map(|log| match log {
+            UndoLog::RegionConstraintCollector(log) => Some(log),
+            _ => None,
+        })
+    }
+
     fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
         // Failures here may indicate a failure to follow a stack discipline.
         assert!(self.logs.len() >= snapshot.undo_len);
@@ -1004,7 +1038,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             const_snapshot: _,
             int_snapshot: _,
             float_snapshot: _,
-            region_constraints_snapshot,
+            region_constraints_snapshot: _,
             region_obligations_snapshot,
             universe,
             was_in_snapshot,
@@ -1023,6 +1057,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             const_unification_table,
             int_unification_table,
             float_unification_table,
+            region_constraints,
             ..
         } = inner;
         inner.undo_log.rollback_to(
@@ -1031,11 +1066,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 const_unification_table,
                 int_unification_table,
                 float_unification_table,
+                region_constraints: region_constraints.as_mut().unwrap(),
             },
             undo_snapshot,
         );
         inner.projection_cache.rollback_to(projection_cache_snapshot);
-        inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot);
         inner.region_obligations.truncate(region_obligations_snapshot);
     }
 
@@ -1048,7 +1083,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             const_snapshot: _,
             int_snapshot: _,
             float_snapshot: _,
-            region_constraints_snapshot,
+            region_constraints_snapshot: _,
             region_obligations_snapshot: _,
             universe: _,
             was_in_snapshot,
@@ -1062,7 +1097,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         let mut inner = self.inner.borrow_mut();
         inner.undo_log.commit(undo_snapshot);
         inner.projection_cache.commit(projection_cache_snapshot);
-        inner.unwrap_region_constraints().commit(region_constraints_snapshot);
     }
 
     /// Executes `f` and commit the bindings.
@@ -1135,7 +1169,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         self.inner
             .borrow_mut()
             .unwrap_region_constraints()
-            .region_constraints_added_in_snapshot(&snapshot.region_constraints_snapshot)
+            .region_constraints_added_in_snapshot(&snapshot.undo_snapshot)
     }
 
     pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) {
@@ -1466,6 +1500,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             .region_constraints
             .take()
             .expect("regions already resolved")
+            .with_log(&mut inner.undo_log)
             .into_infos_and_data();
 
         let region_rels = &RegionRelations::new(
@@ -1527,12 +1562,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// called. This is used only during NLL processing to "hand off" ownership
     /// of the set of region variables into the NLL region context.
     pub fn take_region_var_origins(&self) -> VarInfos {
-        let (var_infos, data) = self
-            .inner
-            .borrow_mut()
+        let mut inner = self.inner.borrow_mut();
+        let (var_infos, data) = inner
             .region_constraints
             .take()
             .expect("regions already resolved")
+            .with_log(&mut inner.undo_log)
             .into_infos_and_data();
         assert!(data.is_empty());
         var_infos
diff --git a/src/librustc_infer/infer/region_constraints/leak_check.rs b/src/librustc_infer/infer/region_constraints/leak_check.rs
index 18e86162eb5..0178fa2eae6 100644
--- a/src/librustc_infer/infer/region_constraints/leak_check.rs
+++ b/src/librustc_infer/infer/region_constraints/leak_check.rs
@@ -1,9 +1,10 @@
 use super::*;
 use crate::infer::{CombinedSnapshot, PlaceholderMap};
+use rustc_data_structures::undo_log::UndoLogs;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::relate::RelateResult;
 
-impl<'tcx> RegionConstraintCollector<'tcx> {
+impl<'tcx> RegionConstraintCollector<'tcx, '_> {
     /// Searches region constraints created since `snapshot` that
     /// affect one of the placeholders in `placeholder_map`, returning
     /// an error if any of the placeholders are related to another
@@ -31,7 +32,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
     ) -> RelateResult<'tcx, ()> {
         debug!("leak_check(placeholders={:?})", placeholder_map);
 
-        assert!(self.in_snapshot());
+        assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
 
         // Go through each placeholder that we created.
         for &placeholder_region in placeholder_map.values() {
@@ -45,7 +46,11 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
             // in some way. This means any region that either outlives
             // or is outlived by a placeholder.
             let mut taint_set = TaintSet::new(TaintDirections::both(), placeholder_region);
-            taint_set.fixed_point(tcx, &self.undo_log, &self.data.verifys);
+            taint_set.fixed_point(
+                tcx,
+                self.undo_log.region_constraints(0),
+                &self.storage.data.verifys,
+            );
             let tainted_regions = taint_set.into_set();
 
             // Report an error if two placeholders in the same universe
@@ -88,19 +93,21 @@ impl<'tcx> TaintSet<'tcx> {
         TaintSet { directions, regions }
     }
 
-    fn fixed_point(
+    fn fixed_point<'a>(
         &mut self,
         tcx: TyCtxt<'tcx>,
-        undo_log: &[UndoLog<'tcx>],
+        undo_log: impl IntoIterator<Item = &'a UndoLog<'tcx>> + Clone,
         verifys: &[Verify<'tcx>],
-    ) {
+    ) where
+        'tcx: 'a,
+    {
         let mut prev_len = 0;
         while prev_len < self.len() {
             debug!("tainted: prev_len = {:?} new_len = {:?}", prev_len, self.len());
 
             prev_len = self.len();
 
-            for undo_entry in undo_log {
+            for undo_entry in undo_log.clone() {
                 match undo_entry {
                     &AddConstraint(Constraint::VarSubVar(a, b)) => {
                         self.add_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b)));
diff --git a/src/librustc_infer/infer/region_constraints/mod.rs b/src/librustc_infer/infer/region_constraints/mod.rs
index 2be6ec4481c..7b660ce4365 100644
--- a/src/librustc_infer/infer/region_constraints/mod.rs
+++ b/src/librustc_infer/infer/region_constraints/mod.rs
@@ -4,11 +4,13 @@ use self::CombineMapType::*;
 use self::UndoLog::*;
 
 use super::unify_key;
-use super::{MiscVariable, RegionVariableOrigin, SubregionOrigin};
+use super::{Logs, MiscVariable, RegionVariableOrigin, Rollback, Snapshot, SubregionOrigin};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
+use rustc_data_structures::undo_log::UndoLogs;
 use rustc_data_structures::unify as ut;
+use rustc_data_structures::unify::UnifyKey;
 use rustc_hir::def_id::DefId;
 use rustc_index::vec::IndexVec;
 use rustc_middle::ty::ReStatic;
@@ -26,7 +28,7 @@ mod leak_check;
 pub use rustc_middle::infer::MemberConstraint;
 
 #[derive(Default)]
-pub struct RegionConstraintCollector<'tcx> {
+pub struct RegionConstraintStorage<'tcx> {
     /// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
     var_infos: IndexVec<RegionVid, RegionVariableInfo>,
 
@@ -42,20 +44,6 @@ pub struct RegionConstraintCollector<'tcx> {
     /// exist). This prevents us from making many such regions.
     glbs: CombineMap<'tcx>,
 
-    /// The undo log records actions that might later be undone.
-    ///
-    /// Note: `num_open_snapshots` is used to track if we are actively
-    /// snapshotting. When the `start_snapshot()` method is called, we
-    /// increment `num_open_snapshots` to indicate that we are now actively
-    /// snapshotting. The reason for this is that otherwise we end up adding
-    /// entries for things like the lower bound on a variable and so forth,
-    /// which can never be rolled back.
-    undo_log: Vec<UndoLog<'tcx>>,
-
-    /// The number of open snapshots, i.e., those that haven't been committed or
-    /// rolled back.
-    num_open_snapshots: usize,
-
     /// When we add a R1 == R2 constriant, we currently add (a) edges
     /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this
     /// table. You can then call `opportunistic_resolve_var` early
@@ -64,13 +52,31 @@ pub struct RegionConstraintCollector<'tcx> {
     /// is iterating to a fixed point, because otherwise we sometimes
     /// would wind up with a fresh stream of region variables that
     /// have been equated but appear distinct.
-    unification_table: ut::UnificationTable<ut::InPlace<ty::RegionVid>>,
+    pub(super) unification_table: ut::UnificationStorage<ty::RegionVid>,
 
     /// a flag set to true when we perform any unifications; this is used
     /// to micro-optimize `take_and_reset_data`
     any_unifications: bool,
 }
 
+pub struct RegionConstraintCollector<'tcx, 'a> {
+    storage: &'a mut RegionConstraintStorage<'tcx>,
+    undo_log: &'a mut Logs<'tcx>,
+}
+
+impl std::ops::Deref for RegionConstraintCollector<'tcx, '_> {
+    type Target = RegionConstraintStorage<'tcx>;
+    fn deref(&self) -> &RegionConstraintStorage<'tcx> {
+        self.storage
+    }
+}
+
+impl std::ops::DerefMut for RegionConstraintCollector<'tcx, '_> {
+    fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> {
+        self.storage
+    }
+}
+
 pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>;
 
 /// The full set of region constraints gathered up by the collector.
@@ -258,13 +264,13 @@ pub enum VerifyBound<'tcx> {
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash)]
-struct TwoRegions<'tcx> {
+pub(crate) struct TwoRegions<'tcx> {
     a: Region<'tcx>,
     b: Region<'tcx>,
 }
 
 #[derive(Copy, Clone, PartialEq)]
-enum UndoLog<'tcx> {
+pub(crate) enum UndoLog<'tcx> {
     /// We added `RegionVid`.
     AddVar(RegionVid),
 
@@ -290,7 +296,7 @@ enum UndoLog<'tcx> {
 }
 
 #[derive(Copy, Clone, PartialEq)]
-enum CombineMapType {
+pub(crate) enum CombineMapType {
     Lub,
     Glb,
 }
@@ -304,8 +310,7 @@ pub struct RegionVariableInfo {
 }
 
 pub struct RegionSnapshot {
-    length: usize,
-    region_snapshot: ut::Snapshot<ut::InPlace<ty::RegionVid>>,
+    value_count: usize,
     any_unifications: bool,
 }
 
@@ -334,11 +339,48 @@ impl TaintDirections {
     }
 }
 
-impl<'tcx> RegionConstraintCollector<'tcx> {
+impl<'tcx> RegionConstraintStorage<'tcx> {
     pub fn new() -> Self {
         Self::default()
     }
 
+    pub(crate) fn with_log<'a>(
+        &'a mut self,
+        undo_log: &'a mut Logs<'tcx>,
+    ) -> RegionConstraintCollector<'tcx, 'a> {
+        RegionConstraintCollector { storage: self, undo_log }
+    }
+
+    fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) {
+        match undo_entry {
+            Purged => {
+                // nothing to do here
+            }
+            AddVar(vid) => {
+                self.var_infos.pop().unwrap();
+                assert_eq!(self.var_infos.len(), vid.index() as usize);
+            }
+            AddConstraint(ref constraint) => {
+                self.data.constraints.remove(constraint);
+            }
+            AddVerify(index) => {
+                self.data.verifys.pop();
+                assert_eq!(self.data.verifys.len(), index);
+            }
+            AddGiven(sub, sup) => {
+                self.data.givens.remove(&(sub, sup));
+            }
+            AddCombination(Glb, ref regions) => {
+                self.glbs.remove(regions);
+            }
+            AddCombination(Lub, ref regions) => {
+                self.lubs.remove(regions);
+            }
+        }
+    }
+}
+
+impl<'tcx> RegionConstraintCollector<'tcx, '_> {
     pub fn num_region_vars(&self) -> usize {
         self.var_infos.len()
     }
@@ -351,8 +393,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
     ///
     /// Not legal during a snapshot.
     pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) {
-        assert!(!self.in_snapshot());
-        (self.var_infos, self.data)
+        assert!(!UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
+        (mem::take(&mut self.storage.var_infos), mem::take(&mut self.storage.data))
     }
 
     /// Takes (and clears) the current set of constraints. Note that
@@ -368,21 +410,19 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
     ///
     /// Not legal during a snapshot.
     pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> {
-        assert!(!self.in_snapshot());
+        assert!(!UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
 
         // If you add a new field to `RegionConstraintCollector`, you
         // should think carefully about whether it needs to be cleared
         // or updated in some way.
-        let RegionConstraintCollector {
+        let RegionConstraintStorage {
             var_infos: _,
             data,
             lubs,
             glbs,
-            undo_log: _,
-            num_open_snapshots: _,
-            unification_table,
+            unification_table: _,
             any_unifications,
-        } = self;
+        } = self.storage;
 
         // Clear the tables of (lubs, glbs), so that we will create
         // fresh regions if we do a LUB operation. As it happens,
@@ -391,102 +431,38 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         lubs.clear();
         glbs.clear();
 
+        let data = mem::take(data);
+
         // Clear all unifications and recreate the variables a "now
         // un-unified" state. Note that when we unify `a` and `b`, we
         // also insert `a <= b` and a `b <= a` edges, so the
         // `RegionConstraintData` contains the relationship here.
         if *any_unifications {
-            unification_table.reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid });
             *any_unifications = false;
+            self.unification_table()
+                .reset_unifications(|vid| unify_key::RegionVidKey { min_vid: vid });
         }
 
-        mem::take(data)
+        data
     }
 
     pub fn data(&self) -> &RegionConstraintData<'tcx> {
         &self.data
     }
 
-    fn in_snapshot(&self) -> bool {
-        self.num_open_snapshots > 0
-    }
-
     pub fn start_snapshot(&mut self) -> RegionSnapshot {
-        let length = self.undo_log.len();
-        debug!("RegionConstraintCollector: start_snapshot({})", length);
-        self.num_open_snapshots += 1;
+        debug!("RegionConstraintCollector: start_snapshot");
         RegionSnapshot {
-            length,
-            region_snapshot: self.unification_table.snapshot(),
+            value_count: self.unification_table.len(),
             any_unifications: self.any_unifications,
         }
     }
 
-    fn assert_open_snapshot(&self, snapshot: &RegionSnapshot) {
-        assert!(self.undo_log.len() >= snapshot.length);
-        assert!(self.num_open_snapshots > 0);
-    }
-
-    pub fn commit(&mut self, snapshot: RegionSnapshot) {
-        debug!("RegionConstraintCollector: commit({})", snapshot.length);
-        self.assert_open_snapshot(&snapshot);
-
-        if self.num_open_snapshots == 1 {
-            // The root snapshot. It's safe to clear the undo log because
-            // there's no snapshot further out that we might need to roll back
-            // to.
-            assert!(snapshot.length == 0);
-            self.undo_log.clear();
-        }
-
-        self.num_open_snapshots -= 1;
-
-        self.unification_table.commit(snapshot.region_snapshot);
-    }
-
     pub fn rollback_to(&mut self, snapshot: RegionSnapshot) {
         debug!("RegionConstraintCollector: rollback_to({:?})", snapshot);
-        self.assert_open_snapshot(&snapshot);
-
-        while self.undo_log.len() > snapshot.length {
-            let undo_entry = self.undo_log.pop().unwrap();
-            self.rollback_undo_entry(undo_entry);
-        }
-
-        self.num_open_snapshots -= 1;
-
-        self.unification_table.rollback_to(snapshot.region_snapshot);
         self.any_unifications = snapshot.any_unifications;
     }
 
-    fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) {
-        match undo_entry {
-            Purged => {
-                // nothing to do here
-            }
-            AddVar(vid) => {
-                self.var_infos.pop().unwrap();
-                assert_eq!(self.var_infos.len(), vid.index() as usize);
-            }
-            AddConstraint(ref constraint) => {
-                self.data.constraints.remove(constraint);
-            }
-            AddVerify(index) => {
-                self.data.verifys.pop();
-                assert_eq!(self.data.verifys.len(), index);
-            }
-            AddGiven(sub, sup) => {
-                self.data.givens.remove(&(sub, sup));
-            }
-            AddCombination(Glb, ref regions) => {
-                self.glbs.remove(regions);
-            }
-            AddCombination(Lub, ref regions) => {
-                self.lubs.remove(regions);
-            }
-        }
-    }
-
     pub fn new_region_var(
         &mut self,
         universe: ty::UniverseIndex,
@@ -494,11 +470,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
     ) -> RegionVid {
         let vid = self.var_infos.push(RegionVariableInfo { origin, universe });
 
-        let u_vid = self.unification_table.new_key(unify_key::RegionVidKey { min_vid: vid });
+        let u_vid = self.unification_table().new_key(unify_key::RegionVidKey { min_vid: vid });
         assert_eq!(vid, u_vid);
-        if self.in_snapshot() {
-            self.undo_log.push(AddVar(vid));
-        }
+        self.undo_log.push(AddVar(vid));
         debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin);
         vid
     }
@@ -520,19 +494,30 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
     pub fn pop_placeholders(&mut self, placeholders: &FxHashSet<ty::Region<'tcx>>) {
         debug!("pop_placeholders(placeholders={:?})", placeholders);
 
-        assert!(self.in_snapshot());
+        assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log));
 
         let constraints_to_kill: Vec<usize> = self
             .undo_log
+            .logs
             .iter()
             .enumerate()
             .rev()
-            .filter(|&(_, undo_entry)| kill_constraint(placeholders, undo_entry))
+            .filter(|&(_, undo_entry)| match undo_entry {
+                super::UndoLog::RegionConstraintCollector(undo_entry) => {
+                    kill_constraint(placeholders, undo_entry)
+                }
+                _ => false,
+            })
             .map(|(index, _)| index)
             .collect();
 
         for index in constraints_to_kill {
-            let undo_entry = mem::replace(&mut self.undo_log[index], Purged);
+            let undo_entry = match &mut self.undo_log.logs[index] {
+                super::UndoLog::RegionConstraintCollector(undo_entry) => {
+                    mem::replace(undo_entry, Purged)
+                }
+                _ => unreachable!(),
+            };
             self.rollback_undo_entry(undo_entry);
         }
 
@@ -566,12 +551,9 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         // never overwrite an existing (constraint, origin) - only insert one if it isn't
         // present in the map yet. This prevents origins from outside the snapshot being
         // replaced with "less informative" origins e.g., during calls to `can_eq`
-        let in_snapshot = self.in_snapshot();
         let undo_log = &mut self.undo_log;
-        self.data.constraints.entry(constraint).or_insert_with(|| {
-            if in_snapshot {
-                undo_log.push(AddConstraint(constraint));
-            }
+        self.storage.data.constraints.entry(constraint).or_insert_with(|| {
+            undo_log.push(AddConstraint(constraint));
             origin
         });
     }
@@ -589,9 +571,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
 
         let index = self.data.verifys.len();
         self.data.verifys.push(verify);
-        if self.in_snapshot() {
-            self.undo_log.push(AddVerify(index));
-        }
+        self.undo_log.push(AddVerify(index));
     }
 
     pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) {
@@ -599,9 +579,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         if self.data.givens.insert((sub, sup)) {
             debug!("add_given({:?} <= {:?})", sub, sup);
 
-            if self.in_snapshot() {
-                self.undo_log.push(AddGiven(sub, sup));
-            }
+            self.undo_log.push(AddGiven(sub, sup));
         }
     }
 
@@ -619,7 +597,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
 
             if let (ty::ReVar(sub), ty::ReVar(sup)) = (*sub, *sup) {
                 debug!("make_eqregion: uniying {:?} with {:?}", sub, sup);
-                self.unification_table.union(sub, sup);
+                self.unification_table().union(sub, sup);
                 self.any_unifications = true;
             }
         }
@@ -741,7 +719,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         tcx: TyCtxt<'tcx>,
         rid: RegionVid,
     ) -> ty::Region<'tcx> {
-        let vid = self.unification_table.probe_value(rid).min_vid;
+        let vid = self.unification_table().probe_value(rid).min_vid;
         tcx.mk_region(ty::ReVar(vid))
     }
 
@@ -769,9 +747,7 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         let c_universe = cmp::max(a_universe, b_universe);
         let c = self.new_region_var(c_universe, MiscVariable(origin.span()));
         self.combine_map(t).insert(vars, c);
-        if self.in_snapshot() {
-            self.undo_log.push(AddCombination(t, vars));
-        }
+        self.undo_log.push(AddCombination(t, vars));
         let new_r = tcx.mk_region(ReVar(c));
         for &old_r in &[a, b] {
             match t {
@@ -801,7 +777,8 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         &self,
         mark: &RegionSnapshot,
     ) -> (Range<RegionVid>, Vec<RegionVariableOrigin>) {
-        let range = self.unification_table.vars_since_snapshot(&mark.region_snapshot);
+        let range = RegionVid::from_index(mark.value_count as u32)
+            ..RegionVid::from_index(self.unification_table.len() as u32);
         (
             range.clone(),
             (range.start.index()..range.end.index())
@@ -810,10 +787,10 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
         )
     }
 
-    /// See `InferCtxt::region_constraints_added_in_snapshot`.
-    pub fn region_constraints_added_in_snapshot(&self, mark: &RegionSnapshot) -> Option<bool> {
-        self.undo_log[mark.length..]
-            .iter()
+    /// See [`RegionInference::region_constraints_added_in_snapshot`].
+    pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'_>) -> Option<bool> {
+        self.undo_log
+            .region_constraints(mark.undo_len)
             .map(|&elt| match elt {
                 AddConstraint(constraint) => Some(constraint.involves_placeholders()),
                 _ => None,
@@ -821,11 +798,15 @@ impl<'tcx> RegionConstraintCollector<'tcx> {
             .max()
             .unwrap_or(None)
     }
+
+    fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, ty::RegionVid> {
+        ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log)
+    }
 }
 
 impl fmt::Debug for RegionSnapshot {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "RegionSnapshot(length={})", self.length)
+        write!(f, "RegionSnapshot")
     }
 }
 
@@ -910,3 +891,9 @@ impl<'tcx> RegionConstraintData<'tcx> {
             && givens.is_empty()
     }
 }
+
+impl<'tcx> Rollback<UndoLog<'tcx>> for RegionConstraintStorage<'tcx> {
+    fn reverse(&mut self, undo: UndoLog<'tcx>) {
+        self.rollback_undo_entry(undo)
+    }
+}
diff --git a/src/librustc_infer/infer/type_variable.rs b/src/librustc_infer/infer/type_variable.rs
index 47f7d764136..3858c50b92c 100644
--- a/src/librustc_infer/infer/type_variable.rs
+++ b/src/librustc_infer/infer/type_variable.rs
@@ -344,23 +344,11 @@ impl<'tcx> TypeVariableTable<'tcx, '_> {
         sv::SnapshotVec::with_log(self.values, self.undo_log)
     }
 
-    fn eq_relations(
-        &mut self,
-    ) -> ut::UnificationTable<
-        ut::InPlace<
-            TyVidEqKey<'tcx>,
-            &mut ut::UnificationStorage<TyVidEqKey<'tcx>>,
-            &mut Logs<'tcx>,
-        >,
-    > {
+    fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> {
         ut::UnificationTable::with_log(self.eq_relations, self.undo_log)
     }
 
-    fn sub_relations(
-        &mut self,
-    ) -> ut::UnificationTable<
-        ut::InPlace<ty::TyVid, &mut ut::UnificationStorage<ty::TyVid>, &mut Logs<'tcx>>,
-    > {
+    fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> {
         ut::UnificationTable::with_log(self.sub_relations, self.undo_log)
     }