@@ -171,6 +171,57 @@ impl<K: DepKind> DepGraph<K> {
171
171
K :: with_deps ( None , op)
172
172
}
173
173
174
+ /// Used to wrap the deserialization of a query result from disk,
175
+ /// This method enforces that no new `DepNodes` are created during
176
+ /// query result deserialization.
177
+ ///
178
+ /// Enforcing this makes the query dep graph simpler - all nodes
179
+ /// must be created during the query execution, and should be
180
+ /// created from inside the 'body' of a query (the implementation
181
+ /// provided by a particular compiler crate).
182
+ ///
183
+ /// Consider the case of three queries `A`, `B`, and `C`, where
184
+ /// `A` invokes `B` and `B` invokes `C`:
185
+ ///
186
+ /// `A -> B -> C`
187
+ ///
188
+ /// Suppose that decoding the result of query `B` required invoking
189
+ /// a query `D`. If we did not create a fresh `TaskDeps` when
190
+ /// decoding `B`, we might would still be using the `TaskDeps` for query `A`
191
+ /// (if we needed to re-execute `A`). This would cause us to create
192
+ /// a new edge `A -> D`. If this edge did not previously
193
+ /// exist in the `DepGraph`, then we could end up with a different
194
+ /// `DepGraph` at the end of compilation, even if there were no
195
+ /// meaningful changes to the overall program (e.g. a newline was added).
196
+ /// In addition, this edge might cause a subsequent compilation run
197
+ /// to try to force `D` before marking other necessary nodes green. If
198
+ /// `D` did not exist in the new compilation session, then we might
199
+ /// get an ICE. Normally, we would have tried (and failed) to mark
200
+ /// some other query green (e.g. `item_children`) which was used
201
+ /// to obtain `D`, which would prevent us from ever trying to force
202
+ /// a non-existent `D`.
203
+ ///
204
+ /// It might be possible to enforce that all `DepNode`s read during
205
+ /// deserialization already exist in the previous `DepGraph`. In
206
+ /// the above example, we would invoke `D` during the deserialization
207
+ /// of `B`. Since we correctly create a new `TaskDeps` from the decoding
208
+ /// of `B`, this would result in an edge `B -> D`. If that edge already
209
+ /// existed (with the same `DepPathHash`es), then it should be correct
210
+ /// to allow the invocation of the query to proceed during deserialization
211
+ /// of a query result. However, this would require additional complexity
212
+ /// in the query infrastructure, and is not currently needed by the
213
+ /// decoding of any query results. Should the need arise in the future,
214
+ /// we should consider extending the query system with this functionality.
215
+ pub fn with_query_deserialization < OP , R > ( & self , op : OP ) -> R
216
+ where
217
+ OP : FnOnce ( ) -> R ,
218
+ {
219
+ let mut deps = TaskDeps :: default ( ) ;
220
+ deps. read_allowed = false ;
221
+ let deps = Lock :: new ( deps) ;
222
+ K :: with_deps ( Some ( & deps) , op)
223
+ }
224
+
174
225
/// Starts a new dep-graph task. Dep-graph tasks are specified
175
226
/// using a free function (`task`) and **not** a closure -- this
176
227
/// is intentional because we want to exercise tight control over
@@ -1121,7 +1172,12 @@ pub struct TaskDeps<K> {
1121
1172
reads : EdgesVec ,
1122
1173
read_set : FxHashSet < DepNodeIndex > ,
1123
1174
phantom_data : PhantomData < DepNode < K > > ,
1124
- pub read_allowed : bool ,
1175
+ /// Whether or not we allow `DepGraph::read_index` to run.
1176
+ /// This is normally true, except inside `with_query_deserialization`,
1177
+ /// where it set to `false` to enforce that no new `DepNode` edges are
1178
+ /// created. See the documentation of `with_query_deserialization` for
1179
+ /// more details.
1180
+ read_allowed : bool ,
1125
1181
}
1126
1182
1127
1183
impl < K > Default for TaskDeps < K > {
0 commit comments