Skip to content

Commit 28f19f6

Browse files
committed
Address review comments
1 parent ab168e6 commit 28f19f6

File tree

2 files changed

+64
-9
lines changed

2 files changed

+64
-9
lines changed

compiler/rustc_query_system/src/dep_graph/graph.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -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

11271183
impl<K> Default for TaskDeps<K> {

compiler/rustc_query_system/src/query/plumbing.rs

+7-8
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22
//! generate the actual methods on tcx which find and execute the provider,
33
//! manage the caches, and so forth.
44
5-
use crate::dep_graph::DepKind;
6-
use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams, TaskDeps};
5+
use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
76
use crate::query::caches::QueryCache;
87
use crate::query::config::{QueryDescription, QueryVtable};
98
use crate::query::job::{
@@ -516,12 +515,12 @@ where
516515
if query.cache_on_disk {
517516
let prof_timer = tcx.dep_context().profiler().incr_cache_loading();
518517

519-
let mut deps = TaskDeps::default();
520-
deps.read_allowed = false;
521-
let deps = Lock::new(deps);
522-
let result = CTX::DepKind::with_deps(Some(&deps), || {
523-
query.try_load_from_disk(tcx, prev_dep_node_index)
524-
});
518+
// The call to `with_query_deserialization` enforces that no new `DepNodes`
519+
// are created during deserialization. See the docs of that method for more
520+
// details.
521+
let result = dep_graph
522+
.with_query_deserialization(|| query.try_load_from_disk(tcx, prev_dep_node_index));
523+
525524
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
526525

527526
if let Some(result) = result {

0 commit comments

Comments
 (0)