hir_ty/next_solver/infer/snapshot/
undo_log.rs1use ena::snapshot_vec as sv;
4use ena::undo_log::{Rollback, UndoLogs};
5use ena::unify as ut;
6use rustc_type_ir::FloatVid;
7use rustc_type_ir::IntVid;
8use tracing::debug;
9
10use crate::next_solver::OpaqueTypeKey;
11use crate::next_solver::infer::opaque_types::OpaqueHiddenType;
12use crate::next_solver::infer::unify_key::ConstVidKey;
13use crate::next_solver::infer::unify_key::RegionVidKey;
14use crate::next_solver::infer::{InferCtxtInner, region_constraints, type_variable};
15
16pub struct Snapshot {
17 pub(crate) undo_len: usize,
18}
19
20#[derive(Clone)]
22pub(crate) enum UndoLog<'db> {
23 DuplicateOpaqueType,
24 OpaqueTypes(OpaqueTypeKey<'db>, Option<OpaqueHiddenType<'db>>),
25 TypeVariables(type_variable::UndoLog<'db>),
26 ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'db>>>),
27 IntUnificationTable(sv::UndoLog<ut::Delegate<IntVid>>),
28 FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>),
29 RegionConstraintCollector(region_constraints::UndoLog<'db>),
30 RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>),
31 PushTypeOutlivesConstraint,
32 PushRegionAssumption,
33}
34
35macro_rules! impl_from {
36 ($($ctor:ident ($ty:ty),)*) => {
37 $(
38 impl<'db> From<$ty> for UndoLog<'db> {
39 fn from(x: $ty) -> Self {
40 UndoLog::$ctor(x.into())
41 }
42 }
43 )*
44 }
45}
46
47impl_from! {
49 RegionConstraintCollector(region_constraints::UndoLog<'db>),
50
51 TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'db>>>),
52 TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidSubKey>>),
53 TypeVariables(type_variable::UndoLog<'db>),
54 IntUnificationTable(sv::UndoLog<ut::Delegate<IntVid>>),
55 FloatUnificationTable(sv::UndoLog<ut::Delegate<FloatVid>>),
56
57 ConstUnificationTable(sv::UndoLog<ut::Delegate<ConstVidKey<'db>>>),
58
59 RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'db>>>),
60}
61
62impl<'db> Rollback<UndoLog<'db>> for InferCtxtInner<'db> {
64 fn reverse(&mut self, undo: UndoLog<'db>) {
65 match undo {
66 UndoLog::DuplicateOpaqueType => self.opaque_type_storage.pop_duplicate_entry(),
67 UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx),
68 UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
69 UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
70 UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),
71 UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo),
72 UndoLog::RegionConstraintCollector(undo) => {
73 self.region_constraint_storage.as_mut().unwrap().reverse(undo)
74 }
75 UndoLog::RegionUnificationTable(undo) => {
76 self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo)
77 }
78 UndoLog::PushTypeOutlivesConstraint => {
79 let popped = self.region_obligations.pop();
80 assert!(popped.is_some(), "pushed region constraint but could not pop it");
81 }
82 UndoLog::PushRegionAssumption => {
83 let popped = self.region_assumptions.pop();
84 assert!(popped.is_some(), "pushed region assumption but could not pop it");
85 }
86 }
87 }
88}
89
90#[derive(Clone, Default)]
93pub(crate) struct InferCtxtUndoLogs<'db> {
94 logs: Vec<UndoLog<'db>>,
95 num_open_snapshots: usize,
96}
97
98impl<'db, T> UndoLogs<T> for InferCtxtUndoLogs<'db>
101where
102 UndoLog<'db>: From<T>,
103{
104 #[inline]
105 fn num_open_snapshots(&self) -> usize {
106 self.num_open_snapshots
107 }
108
109 #[inline]
110 fn push(&mut self, undo: T) {
111 if self.in_snapshot() {
112 self.logs.push(undo.into())
113 }
114 }
115
116 fn clear(&mut self) {
117 self.logs.clear();
118 self.num_open_snapshots = 0;
119 }
120
121 fn extend<J>(&mut self, undos: J)
122 where
123 Self: Sized,
124 J: IntoIterator<Item = T>,
125 {
126 if self.in_snapshot() {
127 self.logs.extend(undos.into_iter().map(UndoLog::from))
128 }
129 }
130}
131
132impl<'db> InferCtxtInner<'db> {
133 pub fn rollback_to(&mut self, snapshot: Snapshot) {
134 debug!("rollback_to({})", snapshot.undo_len);
135 self.undo_log.assert_open_snapshot(&snapshot);
136
137 while self.undo_log.logs.len() > snapshot.undo_len {
138 let undo = self.undo_log.logs.pop().unwrap();
139 self.reverse(undo);
140 }
141
142 self.type_variable_storage.finalize_rollback();
143
144 if self.undo_log.num_open_snapshots == 1 {
145 assert!(snapshot.undo_len == 0);
147 assert!(self.undo_log.logs.is_empty());
148 }
149
150 self.undo_log.num_open_snapshots -= 1;
151 }
152
153 pub fn commit(&mut self, snapshot: Snapshot) {
154 debug!("commit({})", snapshot.undo_len);
155
156 if self.undo_log.num_open_snapshots == 1 {
157 assert!(snapshot.undo_len == 0);
161 self.undo_log.logs.clear();
162 }
163
164 self.undo_log.num_open_snapshots -= 1;
165 }
166}
167
168impl<'db> InferCtxtUndoLogs<'db> {
169 pub(crate) fn start_snapshot(&mut self) -> Snapshot {
170 self.num_open_snapshots += 1;
171 Snapshot { undo_len: self.logs.len() }
172 }
173
174 pub(crate) fn region_constraints_in_snapshot(
175 &self,
176 s: &Snapshot,
177 ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'db>> + Clone {
178 self.logs[s.undo_len..].iter().filter_map(|log| match log {
179 UndoLog::RegionConstraintCollector(log) => Some(log),
180 _ => None,
181 })
182 }
183
184 pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot) -> bool {
185 self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..)))
186 }
187
188 fn assert_open_snapshot(&self, snapshot: &Snapshot) {
189 assert!(self.logs.len() >= snapshot.undo_len);
191 assert!(self.num_open_snapshots > 0);
192 }
193}
194
195impl<'db> std::ops::Index<usize> for InferCtxtUndoLogs<'db> {
196 type Output = UndoLog<'db>;
197
198 fn index(&self, key: usize) -> &Self::Output {
199 &self.logs[key]
200 }
201}
202
203impl<'db> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'db> {
204 fn index_mut(&mut self, key: usize) -> &mut Self::Output {
205 &mut self.logs[key]
206 }
207}