Skip to content

Commit b857763

Browse files
committed
Recreate DefIds when a cached query gets replayed
1 parent df27092 commit b857763

File tree

6 files changed

+94
-18
lines changed

6 files changed

+94
-18
lines changed

compiler/rustc_middle/src/query/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1844,6 +1844,7 @@ rustc_queries! {
18441844
desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) }
18451845
separate_provide_extern
18461846
feedable
1847+
cache_on_disk_if { def_id.is_local() }
18471848
}
18481849

18491850
query inhabited_predicate_adt(key: DefId) -> ty::inhabitedness::InhabitedPredicate<'tcx> {

compiler/rustc_middle/src/ty/context.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate};
3939
use rustc_index::IndexVec;
4040
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
4141
use rustc_query_system::cache::WithDepNode;
42-
use rustc_query_system::dep_graph::DepNodeIndex;
42+
use rustc_query_system::dep_graph::{DepNodeIndex, TaskDepsRef};
4343
use rustc_query_system::ich::StableHashingContext;
44+
use rustc_query_system::query::DefIdInfo;
4445
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
4546
use rustc_session::config::CrateType;
4647
use rustc_session::cstore::{CrateStoreDyn, Untracked};
@@ -1807,10 +1808,9 @@ impl<'tcx> TyCtxt<'tcx> {
18071808
// decode the on-disk cache.
18081809
//
18091810
// Any LocalDefId which is used within queries, either as key or result, either:
1810-
// - has been created before the construction of the TyCtxt;
1811+
// - has been created before the construction of the TyCtxt,
1812+
// - has been created when marking a query as green (recreating definitions it created in the actual run),
18111813
// - has been created by this call to `create_def`.
1812-
// As a consequence, this LocalDefId is always re-created before it is needed by the incr.
1813-
// comp. engine itself.
18141814
//
18151815
// This call also writes to the value of `source_span` and `expn_that_defined` queries.
18161816
// This is fine because:
@@ -1820,9 +1820,30 @@ impl<'tcx> TyCtxt<'tcx> {
18201820

18211821
// This function modifies `self.definitions` using a side-effect.
18221822
// We need to ensure that these side effects are re-run by the incr. comp. engine.
1823-
// Depending on the forever-red node will tell the graph that the calling query
1824-
// needs to be re-evaluated.
1825-
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
1823+
tls::with_context(|icx| {
1824+
match icx.task_deps {
1825+
// Always gets rerun anyway, so nothing to replay
1826+
TaskDepsRef::EvalAlways => {}
1827+
// Top-level queries like the resolver get rerun every time anyway
1828+
TaskDepsRef::Ignore => {}
1829+
TaskDepsRef::Forbid => bug!(
1830+
"cannot create definition {parent:?}, {name:?}, {def_kind:?} without being able to register task dependencies"
1831+
),
1832+
TaskDepsRef::Allow(_) => {
1833+
icx.side_effects
1834+
.as_ref()
1835+
.unwrap()
1836+
.lock()
1837+
.definitions
1838+
.push(DefIdInfo { parent, data });
1839+
}
1840+
TaskDepsRef::Replay { prev_side_effects, created_def_ids } => {
1841+
let index = created_def_ids.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1842+
let prev_info = &prev_side_effects.definitions[index];
1843+
assert_eq!(*prev_info, DefIdInfo { parent, data });
1844+
}
1845+
}
1846+
});
18261847

18271848
let feed = TyCtxtFeed { tcx: self, key: def_id };
18281849
feed.def_kind(def_kind);

compiler/rustc_query_impl/src/plumbing.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ use rustc_middle::ty::{self, TyCtxt, TyEncoder};
2323
use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext};
2424
use rustc_query_system::ich::StableHashingContext;
2525
use rustc_query_system::query::{
26-
QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame,
27-
force_query,
26+
DefIdInfo, QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffects,
27+
QueryStackFrame, force_query,
2828
};
2929
use rustc_query_system::{LayoutOfDepth, QueryOverflow};
3030
use rustc_serialize::{Decodable, Encodable};
@@ -176,11 +176,15 @@ impl QueryContext for QueryCtxt<'_> {
176176
#[tracing::instrument(level = "trace", skip(self))]
177177
fn apply_side_effects(self, side_effects: QuerySideEffects) {
178178
let dcx = self.dep_context().sess().dcx();
179-
let QuerySideEffects { diagnostics } = side_effects;
179+
let QuerySideEffects { diagnostics, definitions } = side_effects;
180180

181181
for diagnostic in diagnostics {
182182
dcx.emit_diagnostic(diagnostic);
183183
}
184+
185+
for DefIdInfo { parent, data } in definitions {
186+
self.tcx.untracked().definitions.write().create_def(parent, data);
187+
}
184188
}
185189
}
186190

compiler/rustc_query_system/src/dep_graph/graph.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1111
use rustc_data_structures::profiling::{QueryInvocationId, SelfProfilerRef};
1212
use rustc_data_structures::sharded::{self, Sharded};
1313
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
14-
use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc};
14+
use rustc_data_structures::sync::{AtomicU32, AtomicU64, AtomicUsize, Lock, Lrc};
1515
use rustc_data_structures::unord::UnordMap;
1616
use rustc_index::IndexVec;
1717
use rustc_macros::{Decodable, Encodable};
@@ -224,6 +224,15 @@ impl<D: Deps> DepGraph<D> {
224224
D::with_deps(TaskDepsRef::Ignore, op)
225225
}
226226

227+
pub(crate) fn with_replay<R>(
228+
&self,
229+
prev_side_effects: &QuerySideEffects,
230+
created_def_ids: &AtomicUsize,
231+
op: impl FnOnce() -> R,
232+
) -> R {
233+
D::with_deps(TaskDepsRef::Replay { prev_side_effects, created_def_ids }, op)
234+
}
235+
227236
/// Used to wrap the deserialization of a query result from disk,
228237
/// This method enforces that no new `DepNodes` are created during
229238
/// query result deserialization.
@@ -278,6 +287,7 @@ impl<D: Deps> DepGraph<D> {
278287
}
279288

280289
#[inline(always)]
290+
/// A helper for `codegen_cranelift`.
281291
pub fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>(
282292
&self,
283293
key: DepNode,
@@ -477,6 +487,12 @@ impl<D: Deps> DepGraph<D> {
477487
return;
478488
}
479489
TaskDepsRef::Ignore => return,
490+
// We don't need to record dependencies when rerunning a query
491+
// because we have no disk cache entry to load. The dependencies
492+
// are preserved.
493+
// FIXME: assert that the dependencies don't change instead of
494+
// recording them.
495+
TaskDepsRef::Replay { .. } => return,
480496
TaskDepsRef::Forbid => {
481497
// Reading is forbidden in this context. ICE with a useful error message.
482498
panic_on_forbidden_read(data, dep_node_index)
@@ -583,6 +599,7 @@ impl<D: Deps> DepGraph<D> {
583599
edges.push(DepNodeIndex::FOREVER_RED_NODE);
584600
}
585601
TaskDepsRef::Ignore => {}
602+
TaskDepsRef::Replay { .. } => {}
586603
TaskDepsRef::Forbid => {
587604
panic!("Cannot summarize when dependencies are not recorded.")
588605
}
@@ -1284,6 +1301,18 @@ pub enum TaskDepsRef<'a> {
12841301
/// to ensure that the decoding process doesn't itself
12851302
/// require the execution of any queries.
12861303
Forbid,
1304+
/// Side effects from the previous run made available to
1305+
/// queries when they are reexecuted because their result was not
1306+
/// available in the cache. Whenever the query creates a new `DefId`,
1307+
/// it is checked against the entries in `QuerySideEffects::definitions`
1308+
/// to ensure that the new `DefId`s are the same as the ones that were
1309+
/// created the last time the query was executed.
1310+
Replay {
1311+
prev_side_effects: &'a QuerySideEffects,
1312+
/// Every new `DefId` is pushed here so we can check
1313+
/// that they match the cached ones.
1314+
created_def_ids: &'a AtomicUsize,
1315+
},
12871316
}
12881317

12891318
#[derive(Debug)]

compiler/rustc_query_system/src/query/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_errors::DiagInner;
1717
use rustc_hir::def::DefKind;
1818
use rustc_macros::{Decodable, Encodable};
1919
use rustc_span::Span;
20-
use rustc_span::def_id::DefId;
20+
use rustc_span::def_id::{DefId, LocalDefId};
2121
use thin_vec::ThinVec;
2222

2323
pub use self::config::{HashResult, QueryConfig};
@@ -76,20 +76,30 @@ pub struct QuerySideEffects {
7676
/// These diagnostics will be re-emitted if we mark
7777
/// the query as green.
7878
pub diagnostics: ThinVec<DiagInner>,
79+
/// Stores any `DefId`s that were created during query execution.
80+
/// These `DefId`s will be re-created when we mark the query as green.
81+
pub definitions: ThinVec<DefIdInfo>,
82+
}
83+
84+
#[derive(Debug, Clone, Encodable, Decodable, PartialEq)]
85+
pub struct DefIdInfo {
86+
pub parent: LocalDefId,
87+
pub data: rustc_hir::definitions::DefPathData,
7988
}
8089

8190
impl QuerySideEffects {
8291
/// Returns true if there might be side effects.
8392
#[inline]
8493
pub fn maybe_any(&self) -> bool {
85-
let QuerySideEffects { diagnostics } = self;
94+
let QuerySideEffects { diagnostics, definitions } = self;
8695
// Use `has_capacity` so that the destructor for `self.diagnostics` can be skipped
8796
// if `maybe_any` is known to be false.
88-
diagnostics.has_capacity()
97+
diagnostics.has_capacity() || definitions.has_capacity()
8998
}
9099
pub fn append(&mut self, other: QuerySideEffects) {
91-
let QuerySideEffects { diagnostics } = self;
100+
let QuerySideEffects { diagnostics, definitions } = self;
92101
diagnostics.extend(other.diagnostics);
102+
definitions.extend(other.definitions);
93103
}
94104
}
95105

compiler/rustc_query_system/src/query/plumbing.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_data_structures::fingerprint::Fingerprint;
1212
use rustc_data_structures::fx::FxHashMap;
1313
use rustc_data_structures::sharded::Sharded;
1414
use rustc_data_structures::stack::ensure_sufficient_stack;
15-
use rustc_data_structures::sync::Lock;
15+
use rustc_data_structures::sync::{AtomicUsize, Lock};
1616
use rustc_data_structures::{outline, sync};
1717
use rustc_errors::{Diag, FatalError, StashKey};
1818
use rustc_span::{DUMMY_SP, Span};
@@ -504,7 +504,7 @@ where
504504
let dep_node =
505505
dep_node_opt.get_or_insert_with(|| query.construct_dep_node(*qcx.dep_context(), &key));
506506

507-
// The diagnostics for this query will be promoted to the current session during
507+
// The side_effects for this query will be promoted to the current session during
508508
// `try_mark_green()`, so we can ignore them here.
509509
if let Some(ret) = qcx.start_query(job_id, false, None, || {
510510
try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node)
@@ -624,8 +624,19 @@ where
624624
// recompute.
625625
let prof_timer = qcx.dep_context().profiler().query_provider();
626626

627+
let prev_side_effects = qcx.load_side_effects(prev_dep_node_index);
628+
let created_def_ids = AtomicUsize::new(0);
627629
// The dep-graph for this computation is already in-place.
628-
let result = qcx.dep_context().dep_graph().with_ignore(|| query.compute(qcx, *key));
630+
let result =
631+
qcx.dep_context()
632+
.dep_graph()
633+
.with_replay(&prev_side_effects, &created_def_ids, || query.compute(qcx, *key));
634+
635+
// We want to verify that the `DefId`s created by the call to `query.compute` are consistent with
636+
// those from the previous compilation. We already checked at `DefId` creation time, that the
637+
// created `DefId`s have the same parent and `DefPathData` as the cached ones.
638+
// We check here that we have not missed any.
639+
assert_eq!(created_def_ids.into_inner(), prev_side_effects.definitions.len());
629640

630641
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
631642

0 commit comments

Comments
 (0)