]>
Commit | Line | Data |
---|---|---|
f9f354fc XL |
1 | use std::marker::PhantomData; |
2 | ||
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; | |
17df50a5 | 6 | use rustc_middle::infer::unify_key::RegionVidKey; |
f9f354fc XL |
7 | use rustc_middle::ty; |
8 | ||
9 | use crate::{ | |
10 | infer::{region_constraints, type_variable, InferCtxtInner}, | |
11 | traits, | |
12 | }; | |
13 | ||
14 | pub struct Snapshot<'tcx> { | |
15 | pub(crate) undo_len: usize, | |
16 | _marker: PhantomData<&'tcx ()>, | |
17 | } | |
18 | ||
6a06907d | 19 | /// Records the "undo" data for a single operation that affects some form of inference variable. |
5099ac24 | 20 | #[derive(Clone)] |
f9f354fc XL |
21 | pub(crate) enum UndoLog<'tcx> { |
22 | TypeVariables(type_variable::UndoLog<'tcx>), | |
23 | ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>), | |
24 | IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), | |
25 | FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), | |
26 | RegionConstraintCollector(region_constraints::UndoLog<'tcx>), | |
17df50a5 | 27 | RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), |
f9f354fc XL |
28 | ProjectionCache(traits::UndoLog<'tcx>), |
29 | PushRegionObligation, | |
30 | } | |
31 | ||
32 | macro_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 | ||
44 | // Upcast from a single kind of "undoable action" to the general enum | |
45 | impl_from! { | |
46 | RegionConstraintCollector(region_constraints::UndoLog<'tcx>), | |
47 | TypeVariables(type_variable::UndoLog<'tcx>), | |
48 | ||
49 | TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>), | |
50 | TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>), | |
51 | TypeVariables(sv::UndoLog<type_variable::Delegate>), | |
52 | TypeVariables(type_variable::Instantiate), | |
53 | ||
54 | IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), | |
55 | ||
56 | FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), | |
57 | ||
58 | ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>), | |
59 | ||
17df50a5 | 60 | RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), |
f9f354fc XL |
61 | ProjectionCache(traits::UndoLog<'tcx>), |
62 | } | |
63 | ||
64 | /// The Rollback trait defines how to rollback a particular action. | |
65 | impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> { | |
66 | fn reverse(&mut self, undo: UndoLog<'tcx>) { | |
67 | match undo { | |
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::ProjectionCache(undo) => self.projection_cache.reverse(undo), | |
79 | UndoLog::PushRegionObligation => { | |
80 | self.region_obligations.pop(); | |
81 | } | |
82 | } | |
83 | } | |
84 | } | |
85 | ||
86 | /// The combined undo log for all the various unification tables. For each change to the storage | |
87 | /// for any kind of inference variable, we record an UndoLog entry in the vector here. | |
5099ac24 | 88 | #[derive(Clone)] |
f9f354fc XL |
89 | pub(crate) struct InferCtxtUndoLogs<'tcx> { |
90 | logs: Vec<UndoLog<'tcx>>, | |
91 | num_open_snapshots: usize, | |
92 | } | |
93 | ||
94 | impl Default for InferCtxtUndoLogs<'_> { | |
95 | fn default() -> Self { | |
96 | Self { logs: Default::default(), num_open_snapshots: Default::default() } | |
97 | } | |
98 | } | |
99 | ||
100 | /// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any | |
94222f64 | 101 | /// action that is convertable into an UndoLog (per the From impls above). |
f9f354fc XL |
102 | impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx> |
103 | where | |
104 | UndoLog<'tcx>: From<T>, | |
105 | { | |
106 | #[inline] | |
107 | fn num_open_snapshots(&self) -> usize { | |
108 | self.num_open_snapshots | |
109 | } | |
110 | ||
111 | #[inline] | |
112 | fn push(&mut self, undo: T) { | |
113 | if self.in_snapshot() { | |
114 | self.logs.push(undo.into()) | |
115 | } | |
116 | } | |
117 | ||
118 | fn clear(&mut self) { | |
119 | self.logs.clear(); | |
120 | self.num_open_snapshots = 0; | |
121 | } | |
122 | ||
123 | fn extend<J>(&mut self, undos: J) | |
124 | where | |
125 | Self: Sized, | |
126 | J: IntoIterator<Item = T>, | |
127 | { | |
128 | if self.in_snapshot() { | |
129 | self.logs.extend(undos.into_iter().map(UndoLog::from)) | |
130 | } | |
131 | } | |
132 | } | |
133 | ||
134 | impl<'tcx> InferCtxtInner<'tcx> { | |
135 | pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) { | |
136 | debug!("rollback_to({})", snapshot.undo_len); | |
137 | self.undo_log.assert_open_snapshot(&snapshot); | |
138 | ||
139 | while self.undo_log.logs.len() > snapshot.undo_len { | |
140 | let undo = self.undo_log.logs.pop().unwrap(); | |
141 | self.reverse(undo); | |
142 | } | |
143 | ||
144 | if self.undo_log.num_open_snapshots == 1 { | |
145 | // The root snapshot. It's safe to clear the undo log because | |
146 | // there's no snapshot further out that we might need to roll back | |
147 | // to. | |
148 | assert!(snapshot.undo_len == 0); | |
149 | self.undo_log.logs.clear(); | |
150 | } | |
151 | ||
152 | self.undo_log.num_open_snapshots -= 1; | |
153 | } | |
154 | ||
155 | pub fn commit(&mut self, snapshot: Snapshot<'tcx>) { | |
156 | debug!("commit({})", snapshot.undo_len); | |
157 | ||
158 | if self.undo_log.num_open_snapshots == 1 { | |
159 | // The root snapshot. It's safe to clear the undo log because | |
160 | // there's no snapshot further out that we might need to roll back | |
161 | // to. | |
162 | assert!(snapshot.undo_len == 0); | |
163 | self.undo_log.logs.clear(); | |
164 | } | |
165 | ||
166 | self.undo_log.num_open_snapshots -= 1; | |
167 | } | |
168 | } | |
169 | ||
170 | impl<'tcx> InferCtxtUndoLogs<'tcx> { | |
f9f354fc XL |
171 | pub fn start_snapshot(&mut self) -> Snapshot<'tcx> { |
172 | self.num_open_snapshots += 1; | |
173 | Snapshot { undo_len: self.logs.len(), _marker: PhantomData } | |
174 | } | |
175 | ||
176 | pub(crate) fn region_constraints_in_snapshot( | |
177 | &self, | |
178 | s: &Snapshot<'tcx>, | |
179 | ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone { | |
180 | self.logs[s.undo_len..].iter().filter_map(|log| match log { | |
181 | UndoLog::RegionConstraintCollector(log) => Some(log), | |
182 | _ => None, | |
183 | }) | |
184 | } | |
185 | ||
186 | pub(crate) fn region_constraints( | |
187 | &self, | |
188 | ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone { | |
189 | self.logs.iter().filter_map(|log| match log { | |
190 | UndoLog::RegionConstraintCollector(log) => Some(log), | |
191 | _ => None, | |
192 | }) | |
193 | } | |
194 | ||
195 | fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) { | |
196 | // Failures here may indicate a failure to follow a stack discipline. | |
197 | assert!(self.logs.len() >= snapshot.undo_len); | |
198 | assert!(self.num_open_snapshots > 0); | |
199 | } | |
f9f354fc XL |
200 | } |
201 | ||
202 | impl<'tcx> std::ops::Index<usize> for InferCtxtUndoLogs<'tcx> { | |
203 | type Output = UndoLog<'tcx>; | |
204 | ||
205 | fn index(&self, key: usize) -> &Self::Output { | |
206 | &self.logs[key] | |
207 | } | |
208 | } | |
209 | ||
210 | impl<'tcx> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'tcx> { | |
211 | fn index_mut(&mut self, key: usize) -> &mut Self::Output { | |
212 | &mut self.logs[key] | |
213 | } | |
214 | } |