]> git.lizzy.rs Git - rust.git/blobdiff - compiler/rustc_query_system/src/query/plumbing.rs
Rollup merge of #106078 - nikic:filecheck-context, r=Mark-Simulacrum
[rust.git] / compiler / rustc_query_system / src / query / plumbing.rs
index f8d93a27d1c2bbbb3ceff184ae2702235eef9387..53844dab9db59490992e073f0b62bf742df3f223 100644 (file)
@@ -2,7 +2,8 @@
 //! 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};
@@ -19,6 +20,7 @@
 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)]
@@ -65,8 +68,8 @@ pub fn all_inactive(&self) -> bool {
     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)]
         {
@@ -100,34 +103,34 @@ pub fn try_collect_active_jobs<Qcx: Copy>(
     }
 }
 
-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);
@@ -137,13 +140,13 @@ fn mk_cycle<Qcx, V, R>(
 
 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 {
@@ -163,7 +166,7 @@ fn handle_cycle_error<Tcx, V>(
     }
 }
 
-impl<'tcx, K> JobOwner<'tcx, K>
+impl<'tcx, K, D: DepKind> JobOwner<'tcx, K, D>
 where
     K: Eq + Hash + Clone,
 {
@@ -178,12 +181,12 @@ impl<'tcx, K> JobOwner<'tcx, K>
     #[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();
@@ -278,9 +281,10 @@ fn complete<C>(self, cache: &C, result: C::Value, dep_node_index: DepNodeIndex)
     }
 }
 
-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]
@@ -306,19 +310,20 @@ fn drop(&mut self) {
 }
 
 #[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
@@ -327,7 +332,7 @@ enum TryGetJob<'tcx, K>
     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.
@@ -335,9 +340,9 @@ enum TryGetJob<'tcx, K>
 /// 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,
@@ -358,7 +363,7 @@ pub fn try_get_cached<'a, Tcx, C, R, 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,
@@ -368,12 +373,27 @@ fn try_execute_query<Qcx, C>(
 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))
         }
@@ -525,7 +545,7 @@ fn try_load_from_disk_and_cache_in_memory<Qcx, K, V>(
             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));
@@ -558,39 +578,42 @@ fn try_load_from_disk_and_cache_in_memory<Qcx, K, V>(
     //
     // 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
@@ -719,11 +742,12 @@ pub enum QueryMode {
     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);
@@ -752,11 +776,12 @@ pub fn get_query<Q, Qcx>(qcx: Qcx, span: Span, key: Q::Key, mode: QueryMode) ->
     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.