@@ -171,6 +171,57 @@ impl<K: DepKind> DepGraph<K> {
171171 K :: with_deps ( None , op)
172172 }
173173
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+
174225 /// Starts a new dep-graph task. Dep-graph tasks are specified
175226 /// using a free function (`task`) and **not** a closure -- this
176227 /// is intentional because we want to exercise tight control over
@@ -1121,7 +1172,12 @@ pub struct TaskDeps<K> {
11211172 reads : EdgesVec ,
11221173 read_set : FxHashSet < DepNodeIndex > ,
11231174 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 ,
11251181}
11261182
11271183impl < K > Default for TaskDeps < K > {
0 commit comments