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