//! generate the actual methods on tcx which find and execute the provider,
//! manage the caches, and so forth.
-use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
+use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams};
+use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::config::QueryVTable;
use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo};
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, FatalError};
use rustc_session::Session;
use rustc_span::{Span, DUMMY_SP};
+use std::borrow::Borrow;
use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::fmt::Debug;
use super::QueryConfig;
-pub struct QueryState<K> {
+pub struct QueryState<K, D: DepKind> {
#[cfg(parallel_compiler)]
- active: Sharded<FxHashMap<K, QueryResult>>,
+ active: Sharded<FxHashMap<K, QueryResult<D>>>,
#[cfg(not(parallel_compiler))]
- active: Lock<FxHashMap<K, QueryResult>>,
+ active: Lock<FxHashMap<K, QueryResult<D>>>,
}
/// Indicates the state of a query for a given key in a query map.
-enum QueryResult {
+enum QueryResult<D: DepKind> {
/// An already executing query. The query job can be used to await for its completion.
- Started(QueryJob),
+ Started(QueryJob<D>),
/// The query panicked. Queries trying to wait on this will raise a fatal error which will
/// silently panic.
Poisoned,
}
-impl<K> QueryState<K>
+impl<K, D> QueryState<K, D>
where
K: Eq + Hash + Clone + Debug,
+ D: DepKind,
{
pub fn all_inactive(&self) -> bool {
#[cfg(parallel_compiler)]
pub fn try_collect_active_jobs<Qcx: Copy>(
&self,
qcx: Qcx,
- make_query: fn(Qcx, K) -> QueryStackFrame,
- jobs: &mut QueryMap,
+ make_query: fn(Qcx, K) -> QueryStackFrame<D>,
+ jobs: &mut QueryMap<D>,
) -> Option<()> {
#[cfg(parallel_compiler)]
{
}
}
-impl<K> Default for QueryState<K> {
- fn default() -> QueryState<K> {
+impl<K, D: DepKind> Default for QueryState<K, D> {
+ fn default() -> QueryState<K, D> {
QueryState { active: Default::default() }
}
}
/// A type representing the responsibility to execute the job in the `job` field.
/// This will poison the relevant query if dropped.
-struct JobOwner<'tcx, K>
+struct JobOwner<'tcx, K, D: DepKind>
where
K: Eq + Hash + Clone,
{
- state: &'tcx QueryState<K>,
+ state: &'tcx QueryState<K, D>,
key: K,
id: QueryJobId,
}
#[cold]
#[inline(never)]
-fn mk_cycle<Qcx, V, R>(
+fn mk_cycle<Qcx, V, R, D: DepKind>(
qcx: Qcx,
- cycle_error: CycleError,
+ cycle_error: CycleError<D>,
handler: HandleCycleError,
cache: &dyn crate::query::QueryStorage<Value = V, Stored = R>,
) -> R
where
- Qcx: QueryContext,
- V: std::fmt::Debug + Value<Qcx::DepContext>,
+ Qcx: QueryContext + crate::query::HasDepContext<DepKind = D>,
+ V: std::fmt::Debug + Value<Qcx::DepContext, Qcx::DepKind>,
R: Clone,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
fn handle_cycle_error<Tcx, V>(
tcx: Tcx,
- cycle_error: &CycleError,
+ cycle_error: &CycleError<Tcx::DepKind>,
mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
handler: HandleCycleError,
) -> V
where
Tcx: DepContext,
- V: Value<Tcx>,
+ V: Value<Tcx, Tcx::DepKind>,
{
use HandleCycleError::*;
match handler {
}
}
-impl<'tcx, K> JobOwner<'tcx, K>
+impl<'tcx, K, D: DepKind> JobOwner<'tcx, K, D>
where
K: Eq + Hash + Clone,
{
#[inline(always)]
fn try_start<'b, Qcx>(
qcx: &'b Qcx,
- state: &'b QueryState<K>,
+ state: &'b QueryState<K, Qcx::DepKind>,
span: Span,
key: K,
- ) -> TryGetJob<'b, K>
+ ) -> TryGetJob<'b, K, D>
where
- Qcx: QueryContext,
+ Qcx: QueryContext + crate::query::HasDepContext<DepKind = D>,
{
#[cfg(parallel_compiler)]
let mut state_lock = state.active.get_shard_by_value(&key).lock();
}
}
-impl<'tcx, K> Drop for JobOwner<'tcx, K>
+impl<'tcx, K, D> Drop for JobOwner<'tcx, K, D>
where
K: Eq + Hash + Clone,
+ D: DepKind,
{
#[inline(never)]
#[cold]
}
#[derive(Clone)]
-pub(crate) struct CycleError {
+pub(crate) struct CycleError<D: DepKind> {
/// The query and related span that uses the cycle.
- pub usage: Option<(Span, QueryStackFrame)>,
- pub cycle: Vec<QueryInfo>,
+ pub usage: Option<(Span, QueryStackFrame<D>)>,
+ pub cycle: Vec<QueryInfo<D>>,
}
/// The result of `try_start`.
-enum TryGetJob<'tcx, K>
+enum TryGetJob<'tcx, K, D>
where
K: Eq + Hash + Clone,
+ D: DepKind,
{
/// The query is not yet started. Contains a guard to the cache eventually used to start it.
- NotYetStarted(JobOwner<'tcx, K>),
+ NotYetStarted(JobOwner<'tcx, K, D>),
/// The query was already completed.
/// Returns the result of the query and its dep-node index
JobCompleted(TimingGuard<'tcx>),
/// Trying to execute the query resulted in a cycle.
- Cycle(CycleError),
+ Cycle(CycleError<D>),
}
/// Checks if the query is already computed and in the cache.
/// which will be used if the query is not in the cache and we need
/// to compute it.
#[inline]
-pub fn try_get_cached<'a, Tcx, C, R, OnHit>(
+pub fn try_get_cached<Tcx, C, R, OnHit>(
tcx: Tcx,
- cache: &'a C,
+ cache: &C,
key: &C::Key,
// `on_hit` can be called while holding a lock to the query cache
on_hit: OnHit,
fn try_execute_query<Qcx, C>(
qcx: Qcx,
- state: &QueryState<C::Key>,
+ state: &QueryState<C::Key, Qcx::DepKind>,
cache: &C,
span: Span,
key: C::Key,
where
C: QueryCache,
C::Key: Clone + DepNodeParams<Qcx::DepContext>,
- C::Value: Value<Qcx::DepContext>,
+ C::Value: Value<Qcx::DepContext, Qcx::DepKind>,
+ C::Stored: Debug + std::borrow::Borrow<C::Value>,
Qcx: QueryContext,
{
- match JobOwner::<'_, C::Key>::try_start(&qcx, state, span, key.clone()) {
+ match JobOwner::<'_, C::Key, Qcx::DepKind>::try_start(&qcx, state, span, key.clone()) {
TryGetJob::NotYetStarted(job) => {
- let (result, dep_node_index) = execute_job(qcx, key, dep_node, query, job.id);
+ let (result, dep_node_index) = execute_job(qcx, key.clone(), dep_node, query, job.id);
+ if query.feedable {
+ // We may have put a value inside the cache from inside the execution.
+ // Verify that it has the same hash as what we have now, to ensure consistency.
+ let _ = cache.lookup(&key, |cached_result, _| {
+ let hasher = query.hash_result.expect("feedable forbids no_hash");
+ let old_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| hasher(&mut hcx, cached_result.borrow()));
+ let new_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| hasher(&mut hcx, &result));
+ debug_assert_eq!(
+ old_hash, new_hash,
+ "Computed query value for {:?}({:?}) is inconsistent with fed value,\ncomputed={:#?}\nfed={:#?}",
+ query.dep_kind, key, result, cached_result,
+ );
+ });
+ }
let result = job.complete(cache, result, dep_node_index);
(result, Some(dep_node_index))
}
if std::intrinsics::unlikely(
try_verify || qcx.dep_context().sess().opts.unstable_opts.incremental_verify_ich,
) {
- incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query);
+ incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result);
}
return Some((result, dep_node_index));
//
// See issue #82920 for an example of a miscompilation that would get turned into
// an ICE by this check
- incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query);
+ incremental_verify_ich(*qcx.dep_context(), &result, dep_node, query.hash_result);
Some((result, dep_node_index))
}
-#[instrument(skip(qcx, result, query), level = "debug")]
-fn incremental_verify_ich<Qcx, K, V: Debug>(
- qcx: Qcx::DepContext,
+#[instrument(skip(tcx, result, hash_result), level = "debug")]
+pub(crate) fn incremental_verify_ich<Tcx, V: Debug>(
+ tcx: Tcx,
result: &V,
- dep_node: &DepNode<Qcx::DepKind>,
- query: &QueryVTable<Qcx, K, V>,
-) where
- Qcx: QueryContext,
+ dep_node: &DepNode<Tcx::DepKind>,
+ hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
+) -> Fingerprint
+where
+ Tcx: DepContext,
{
assert!(
- qcx.dep_graph().is_green(dep_node),
+ tcx.dep_graph().is_green(dep_node),
"fingerprint for green query instance not loaded from cache: {:?}",
dep_node,
);
- let new_hash = query.hash_result.map_or(Fingerprint::ZERO, |f| {
- qcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
+ let new_hash = hash_result.map_or(Fingerprint::ZERO, |f| {
+ tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
});
- let old_hash = qcx.dep_graph().prev_fingerprint_of(dep_node);
+ let old_hash = tcx.dep_graph().prev_fingerprint_of(dep_node);
if Some(new_hash) != old_hash {
incremental_verify_ich_failed(
- qcx.sess(),
+ tcx.sess(),
DebugArg::from(&dep_node),
DebugArg::from(&result),
);
}
+
+ new_hash
}
// This DebugArg business is largely a mirror of std::fmt::ArgumentV1, which is
Ensure,
}
-pub fn get_query<Q, Qcx>(qcx: Qcx, span: Span, key: Q::Key, mode: QueryMode) -> Option<Q::Stored>
+pub fn get_query<Q, Qcx, D>(qcx: Qcx, span: Span, key: Q::Key, mode: QueryMode) -> Option<Q::Stored>
where
+ D: DepKind,
Q: QueryConfig<Qcx>,
Q::Key: DepNodeParams<Qcx::DepContext>,
- Q::Value: Value<Qcx::DepContext>,
+ Q::Value: Value<Qcx::DepContext, D>,
Qcx: QueryContext,
{
let query = Q::make_vtable(qcx, &key);
Some(result)
}
-pub fn force_query<Q, Qcx>(qcx: Qcx, key: Q::Key, dep_node: DepNode<Qcx::DepKind>)
+pub fn force_query<Q, Qcx, D>(qcx: Qcx, key: Q::Key, dep_node: DepNode<Qcx::DepKind>)
where
+ D: DepKind,
Q: QueryConfig<Qcx>,
Q::Key: DepNodeParams<Qcx::DepContext>,
- Q::Value: Value<Qcx::DepContext>,
+ Q::Value: Value<Qcx::DepContext, D>,
Qcx: QueryContext,
{
// We may be concurrently trying both execute and force a query.