rustc_infer/infer/snapshot/
undo_log.rs

1use std::assert_matches::assert_matches;
2use std::marker::PhantomData;
3
4use rustc_data_structures::undo_log::{Rollback, UndoLogs};
5use rustc_data_structures::{snapshot_vec as sv, unify as ut};
6use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey};
7use tracing::debug;
8
9use crate::infer::unify_key::{ConstVidKey, RegionVidKey};
10use crate::infer::{InferCtxtInner, region_constraints, type_variable};
11use crate::traits;
12
13pub struct Snapshot<'tcx> {
14    pub(crate) undo_len: usize,
15    _marker: PhantomData<&'tcx ()>,
16}
17
18/// Records the "undo" data for a single operation that affects some form of inference variable.
19#[derive(Clone)]
20pub(crate) enum UndoLog<'tcx> {
21    DuplicateOpaqueType,
22    OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>),
23    TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
24    ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
25    IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
26    FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
27    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
28    RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
29    ProjectionCache(traits::UndoLog<'tcx>),
30    PushTypeOutlivesConstraint,
31    PushRegionAssumption,
32    PushHirTypeckPotentiallyRegionDependentGoal,
33}
34
35macro_rules! impl_from {
36    ($($ctor:ident ($ty:ty),)*) => {
37        $(
38        impl<'tcx> From<$ty> for UndoLog<'tcx> {
39            fn from(x: $ty) -> Self {
40                UndoLog::$ctor(x.into())
41            }
42        }
43        )*
44    }
45}
46
47// Upcast from a single kind of "undoable action" to the general enum
48impl_from! {
49    RegionConstraintCollector(region_constraints::UndoLog<'tcx>),
50
51    TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>),
52    IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
53    FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>),
54
55    ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'tcx>>>),
56
57    RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
58    ProjectionCache(traits::UndoLog<'tcx>),
59}
60
61/// The Rollback trait defines how to rollback a particular action.
62impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
63    fn reverse(&mut self, undo: UndoLog<'tcx>) {
64        match undo {
65            UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(),
66            UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx),
67            UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
68            UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
69            UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
70            UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
71            UndoLog::RegionConstraintCollector(undo) => {
72                self.region_constraint_storage.as_mut().unwrap().reverse(undo)
73            }
74            UndoLog::RegionUnificationTable(undo) => {
75                self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
76            }
77            UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo),
78            UndoLog::PushTypeOutlivesConstraint => {
79                let popped = self.region_obligations.pop();
80                assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
81            }
82            UndoLog::PushRegionAssumption => {
83                let popped = self.region_assumptions.pop();
84                assert_matches!(popped, Some(_), "pushed region assumption but could not pop it");
85            }
86            UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => {
87                let popped = self.hir_typeck_potentially_region_dependent_goals.pop();
88                assert_matches!(popped, Some(_), "pushed goal but could not pop it");
89            }
90        }
91    }
92}
93
94/// The combined undo log for all the various unification tables. For each change to the storage
95/// for any kind of inference variable, we record an UndoLog entry in the vector here.
96#[derive(Clone, Default)]
97pub(crate) struct InferCtxtUndoLogs<'tcx> {
98    logs: Vec<UndoLog<'tcx>>,
99    num_open_snapshots: usize,
100}
101
102/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
103/// action that is convertible into an UndoLog (per the From impls above).
104impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
105where
106    UndoLog<'tcx>: From<T>,
107{
108    #[inline]
109    fn num_open_snapshots(&self) -> usize {
110        self.num_open_snapshots
111    }
112
113    #[inline]
114    fn push(&mut self, undo: T) {
115        if self.in_snapshot() {
116            self.logs.push(undo.into())
117        }
118    }
119
120    fn clear(&mut self) {
121        self.logs.clear();
122        self.num_open_snapshots = 0;
123    }
124
125    fn extend<J>(&mut self, undos: J)
126    where
127        Self: Sized,
128        J: IntoIterator<Item = T>,
129    {
130        if self.in_snapshot() {
131            self.logs.extend(undos.into_iter().map(UndoLog::from))
132        }
133    }
134}
135
136impl<'tcx> InferCtxtInner<'tcx> {
137    pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) {
138        debug!("rollback_to({})", snapshot.undo_len);
139        self.undo_log.assert_open_snapshot(&snapshot);
140
141        while self.undo_log.logs.len() > snapshot.undo_len {
142            let undo = self.undo_log.logs.pop().unwrap();
143            self.reverse(undo);
144        }
145
146        self.type_variable_storage.finalize_rollback();
147
148        if self.undo_log.num_open_snapshots == 1 {
149            // After the root snapshot the undo log should be empty.
150            assert!(snapshot.undo_len == 0);
151            assert!(self.undo_log.logs.is_empty());
152        }
153
154        self.undo_log.num_open_snapshots -= 1;
155    }
156
157    pub fn commit(&mut self, snapshot: Snapshot<'tcx>) {
158        debug!("commit({})", snapshot.undo_len);
159
160        if self.undo_log.num_open_snapshots == 1 {
161            // The root snapshot. It's safe to clear the undo log because
162            // there's no snapshot further out that we might need to roll back
163            // to.
164            assert!(snapshot.undo_len == 0);
165            self.undo_log.logs.clear();
166        }
167
168        self.undo_log.num_open_snapshots -= 1;
169    }
170}
171
172impl<'tcx> InferCtxtUndoLogs<'tcx> {
173    pub(crate) fn start_snapshot(&mut self) -> Snapshot<'tcx> {
174        self.num_open_snapshots += 1;
175        Snapshot { undo_len: self.logs.len(), _marker: PhantomData }
176    }
177
178    pub(crate) fn region_constraints_in_snapshot(
179        &self,
180        s: &Snapshot<'tcx>,
181    ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone {
182        self.logs[s.undo_len..].iter().filter_map(|log| match log {
183            UndoLog::RegionConstraintCollector(log) => Some(log),
184            _ => None,
185        })
186    }
187
188    pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot<'tcx>) -> bool {
189        self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
190    }
191
192    fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) {
193        // Failures here may indicate a failure to follow a stack discipline.
194        assert!(self.logs.len() >= snapshot.undo_len);
195        assert!(self.num_open_snapshots > 0);
196    }
197}
198
199impl<'tcx> std::ops::Index<usize> for InferCtxtUndoLogs<'tcx> {
200    type Output = UndoLog<'tcx>;
201
202    fn index(&self, key: usize) -> &Self::Output {
203        &self.logs[key]
204    }
205}
206
207impl<'tcx> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'tcx> {
208    fn index_mut(&mut self, key: usize) -> &mut Self::Output {
209        &mut self.logs[key]
210    }
211}