1 use std::marker::PhantomData;
3 use rustc_data_structures::snapshot_vec as sv;
4 use rustc_data_structures::undo_log::{Rollback, UndoLogs};
5 use rustc_data_structures::unify as ut;
9 infer::{region_constraints, type_variable, InferCtxtInner},
13 pub struct Snapshot<'tcx> {
14 pub(crate) undo_len: usize,
15 _marker: PhantomData<&'tcx ()>,
18 /// Records the 'undo' data fora single operation that affects some form of inference variable.
19 pub(crate) enum UndoLog<'tcx> {
20 TypeVariables(type_variable::UndoLog<'tcx>),
21 ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
22 IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
23 FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
24 RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
25 RegionUnificationTable(sv::UndoLog<ut::Delegate<ty::RegionVid>>),
26 ProjectionCache(traits::UndoLog<'tcx>),
30 macro_rules! impl_from {
31 ($($ctor: ident ($ty: ty),)*) => {
33 impl<'tcx> From<$ty> for UndoLog<'tcx> {
34 fn from(x: $ty) -> Self {
35 UndoLog::$ctor(x.into())
42 // Upcast from a single kind of "undoable action" to the general enum
44 RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
45 TypeVariables(type_variable::UndoLog<'tcx>),
47 TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
48 TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>),
49 TypeVariables(sv::UndoLog<type_variable::Delegate>),
50 TypeVariables(type_variable::Instantiate),
52 IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
54 FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
56 ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
58 RegionUnificationTable(sv::UndoLog<ut::Delegate<ty::RegionVid>>),
59 ProjectionCache(traits::UndoLog<'tcx>),
62 /// The Rollback trait defines how to rollback a particular action.
63 impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
64 fn reverse(&mut self, undo: UndoLog<'tcx>) {
66 UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
67 UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
68 UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
69 UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
70 UndoLog::RegionConstraintCollector(undo) => {
71 self.region_constraint_storage.as_mut().unwrap().reverse(undo)
73 UndoLog::RegionUnificationTable(undo) => {
74 self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
76 UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo),
77 UndoLog::PushRegionObligation => {
78 self.region_obligations.pop();
84 /// The combined undo log for all the various unification tables. For each change to the storage
85 /// for any kind of inference variable, we record an UndoLog entry in the vector here.
86 pub(crate) struct InferCtxtUndoLogs<'tcx> {
87 logs: Vec<UndoLog<'tcx>>,
88 num_open_snapshots: usize,
91 impl Default for InferCtxtUndoLogs<'_> {
92 fn default() -> Self {
93 Self { logs: Default::default(), num_open_snapshots: Default::default() }
97 /// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
98 /// action that is convertable into a UndoLog (per the From impls above).
99 impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
101 UndoLog<'tcx>: From<T>,
104 fn num_open_snapshots(&self) -> usize {
105 self.num_open_snapshots
109 fn push(&mut self, undo: T) {
110 if self.in_snapshot() {
111 self.logs.push(undo.into())
115 fn clear(&mut self) {
117 self.num_open_snapshots = 0;
120 fn extend<J>(&mut self, undos: J)
123 J: IntoIterator<Item = T>,
125 if self.in_snapshot() {
126 self.logs.extend(undos.into_iter().map(UndoLog::from))
131 impl<'tcx> InferCtxtInner<'tcx> {
132 pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) {
133 debug!("rollback_to({})", snapshot.undo_len);
134 self.undo_log.assert_open_snapshot(&snapshot);
136 while self.undo_log.logs.len() > snapshot.undo_len {
137 let undo = self.undo_log.logs.pop().unwrap();
141 if self.undo_log.num_open_snapshots == 1 {
142 // The root snapshot. It's safe to clear the undo log because
143 // there's no snapshot further out that we might need to roll back
145 assert!(snapshot.undo_len == 0);
146 self.undo_log.logs.clear();
149 self.undo_log.num_open_snapshots -= 1;
152 pub fn commit(&mut self, snapshot: Snapshot<'tcx>) {
153 debug!("commit({})", snapshot.undo_len);
155 if self.undo_log.num_open_snapshots == 1 {
156 // The root snapshot. It's safe to clear the undo log because
157 // there's no snapshot further out that we might need to roll back
159 assert!(snapshot.undo_len == 0);
160 self.undo_log.logs.clear();
163 self.undo_log.num_open_snapshots -= 1;
167 impl<'tcx> InferCtxtUndoLogs<'tcx> {
168 pub fn actions_since_snapshot(&self, snapshot: &Snapshot<'tcx>) -> &[UndoLog<'tcx>] {
169 &self.logs[snapshot.undo_len..]
172 pub fn start_snapshot(&mut self) -> Snapshot<'tcx> {
173 self.num_open_snapshots += 1;
174 Snapshot { undo_len: self.logs.len(), _marker: PhantomData }
177 pub(crate) fn region_constraints_in_snapshot(
180 ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
181 self.logs[s.undo_len..].iter().filter_map(|log| match log {
182 UndoLog::RegionConstraintCollector(log) => Some(log),
187 pub(crate) fn region_constraints(
189 ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
190 self.logs.iter().filter_map(|log| match log {
191 UndoLog::RegionConstraintCollector(log) => Some(log),
196 fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
197 // Failures here may indicate a failure to follow a stack discipline.
198 assert!(self.logs.len() >= snapshot.undo_len);
199 assert!(self.num_open_snapshots > 0);
203 impl<'tcx> std::ops::Index<usize> for InferCtxtUndoLogs<'tcx> {
204 type Output = UndoLog<'tcx>;
206 fn index(&self, key: usize) -> &Self::Output {
211 impl<'tcx> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'tcx> {
212 fn index_mut(&mut self, key: usize) -> &mut Self::Output {