@@ -177,6 +177,62 @@ impl<K: DepKind> DepGraph<K> {
177
177
K :: with_deps ( None , op)
178
178
}
179
179
180
+ /// Used to wrap the deserialization of a query result from disk,
181
+ /// This method enforces that no new `DepNodes` are created during
182
+ /// query result deserialization.
183
+ ///
184
+ /// Enforcing this makes the query dep graph simpler - all nodes
185
+ /// must be created during the query execution, and should be
186
+ /// created from inside the 'body' of a query (the implementation
187
+ /// provided by a particular compiler crate).
188
+ ///
189
+ /// Consider the case of three queries `A`, `B`, and `C`, where
190
+ /// `A` invokes `B` and `B` invokes `C`:
191
+ ///
192
+ /// `A -> B -> C`
193
+ ///
194
+ /// Suppose that decoding the result of query `B` required re-computing
195
+ /// the query `C`. If we did not create a fresh `TaskDeps` when
196
+ /// decoding `B`, we would still be using the `TaskDeps` for query `A`
197
+ /// (if we needed to re-execute `A`). This would cause us to create
198
+ /// a new edge `A -> C`. If this edge did not previously
199
+ /// exist in the `DepGraph`, then we could end up with a different
200
+ /// `DepGraph` at the end of compilation, even if there were no
201
+ /// meaningful changes to the overall program (e.g. a newline was added).
202
+ /// In addition, this edge might cause a subsequent compilation run
203
+ /// to try to force `C` before marking other necessary nodes green. If
204
+ /// `C` did not exist in the new compilation session, then we could
205
+ /// get an ICE. Normally, we would have tried (and failed) to mark
206
+ /// some other query green (e.g. `item_children`) which was used
207
+ /// to obtain `C`, which would prevent us from ever trying to force
208
+ /// a non-existent `D`.
209
+ ///
210
+ /// It might be possible to enforce that all `DepNode`s read during
211
+ /// deserialization already exist in the previous `DepGraph`. In
212
+ /// the above example, we would invoke `D` during the deserialization
213
+ /// of `B`. Since we correctly create a new `TaskDeps` from the decoding
214
+ /// of `B`, this would result in an edge `B -> D`. If that edge already
215
+ /// existed (with the same `DepPathHash`es), then it should be correct
216
+ /// to allow the invocation of the query to proceed during deserialization
217
+ /// of a query result. We would merely assert that the dep-graph fragment
218
+ /// that would have been added by invoking `C` while decoding `B`
219
+ /// is equivalent to the dep-graph fragment that we already instantiated for B
220
+ /// (at the point where we successfully marked B as green).
221
+ ///
222
+ /// However, this would require additional complexity
223
+ /// in the query infrastructure, and is not currently needed by the
224
+ /// decoding of any query results. Should the need arise in the future,
225
+ /// we should consider extending the query system with this functionality.
226
+ pub fn with_query_deserialization < OP , R > ( & self , op : OP ) -> R
227
+ where
228
+ OP : FnOnce ( ) -> R ,
229
+ {
230
+ let mut deps = TaskDeps :: default ( ) ;
231
+ deps. read_allowed = false ;
232
+ let deps = Lock :: new ( deps) ;
233
+ K :: with_deps ( Some ( & deps) , op)
234
+ }
235
+
180
236
/// Starts a new dep-graph task. Dep-graph tasks are specified
181
237
/// using a free function (`task`) and **not** a closure -- this
182
238
/// is intentional because we want to exercise tight control over
@@ -257,6 +313,7 @@ impl<K: DepKind> DepGraph<K> {
257
313
reads : SmallVec :: new ( ) ,
258
314
read_set : Default :: default ( ) ,
259
315
phantom_data : PhantomData ,
316
+ read_allowed : true ,
260
317
} ) )
261
318
} ;
262
319
let result = K :: with_deps ( task_deps. as_ref ( ) , || task ( cx, arg) ) ;
@@ -368,6 +425,11 @@ impl<K: DepKind> DepGraph<K> {
368
425
if let Some ( task_deps) = task_deps {
369
426
let mut task_deps = task_deps. lock ( ) ;
370
427
let task_deps = & mut * task_deps;
428
+
429
+ if !task_deps. read_allowed {
430
+ panic ! ( "Illegal read of: {:?}" , dep_node_index) ;
431
+ }
432
+
371
433
if cfg ! ( debug_assertions) {
372
434
data. current . total_read_count . fetch_add ( 1 , Relaxed ) ;
373
435
}
@@ -1129,6 +1191,12 @@ pub struct TaskDeps<K> {
1129
1191
reads : EdgesVec ,
1130
1192
read_set : FxHashSet < DepNodeIndex > ,
1131
1193
phantom_data : PhantomData < DepNode < K > > ,
1194
+ /// Whether or not we allow `DepGraph::read_index` to run.
1195
+ /// This is normally true, except inside `with_query_deserialization`,
1196
+ /// where it set to `false` to enforce that no new `DepNode` edges are
1197
+ /// created. See the documentation of `with_query_deserialization` for
1198
+ /// more details.
1199
+ read_allowed : bool ,
1132
1200
}
1133
1201
1134
1202
impl < K > Default for TaskDeps < K > {
@@ -1139,6 +1207,7 @@ impl<K> Default for TaskDeps<K> {
1139
1207
reads : EdgesVec :: new ( ) ,
1140
1208
read_set : FxHashSet :: default ( ) ,
1141
1209
phantom_data : PhantomData ,
1210
+ read_allowed : true ,
1142
1211
}
1143
1212
}
1144
1213
}
0 commit comments