about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/dep_graph/dep_node.rs50
-rw-r--r--src/librustc/dep_graph/mod.rs3
-rw-r--r--src/librustc/query/mod.rs45
-rw-r--r--src/librustc/ty/query/config.rs9
-rw-r--r--src/librustc/ty/query/mod.rs112
-rw-r--r--src/librustc/ty/query/plumbing.rs106
-rw-r--r--src/librustc_macros/src/query.rs73
7 files changed, 161 insertions, 237 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index eb7e2871bfc..156f412e090 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -360,33 +360,9 @@ rustc_dep_node_append!([define_dep_nodes!][ <'tcx>
     [anon] TraitSelect,
 
     [] CompileCodegenUnit(Symbol),
-
-    [eval_always] Analysis(CrateNum),
 ]);
 
-pub trait RecoverKey<'tcx>: Sized {
-    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self>;
-}
-
-impl RecoverKey<'tcx> for CrateNum {
-    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
-        dep_node.extract_def_id(tcx).map(|id| id.krate)
-    }
-}
-
-impl RecoverKey<'tcx> for DefId {
-    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
-        dep_node.extract_def_id(tcx)
-    }
-}
-
-impl RecoverKey<'tcx> for DefIndex {
-    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
-        dep_node.extract_def_id(tcx).map(|id| id.index)
-    }
-}
-
-trait DepNodeParams<'tcx>: fmt::Debug {
+pub(crate) trait DepNodeParams<'tcx>: fmt::Debug + Sized {
     const CAN_RECONSTRUCT_QUERY_KEY: bool;
 
     /// This method turns the parameters of a DepNodeConstructor into an opaque
@@ -400,6 +376,14 @@ trait DepNodeParams<'tcx>: fmt::Debug {
     fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String {
         format!("{:?}", self)
     }
+
+    /// This method tries to recover the query key from the given `DepNode`,
+    /// something which is needed when forcing `DepNode`s during red-green
+    /// evaluation. The query system will only call this method if
+    /// `CAN_RECONSTRUCT_QUERY_KEY` is `true`.
+    /// It is always valid to return `None` here, in which case incremental
+    /// compilation will treat the query as having changed instead of forcing it.
+    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self>;
 }
 
 impl<'tcx, T> DepNodeParams<'tcx> for T
@@ -420,6 +404,10 @@ where
     default fn to_debug_str(&self, _: TyCtxt<'tcx>) -> String {
         format!("{:?}", *self)
     }
+
+    default fn recover(_: TyCtxt<'tcx>, _: &DepNode) -> Option<Self> {
+        None
+    }
 }
 
 impl<'tcx> DepNodeParams<'tcx> for DefId {
@@ -432,6 +420,10 @@ impl<'tcx> DepNodeParams<'tcx> for DefId {
     fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
         tcx.def_path_str(*self)
     }
+
+    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
+        dep_node.extract_def_id(tcx)
+    }
 }
 
 impl<'tcx> DepNodeParams<'tcx> for DefIndex {
@@ -444,6 +436,10 @@ impl<'tcx> DepNodeParams<'tcx> for DefIndex {
     fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
         tcx.def_path_str(DefId::local(*self))
     }
+
+    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
+        dep_node.extract_def_id(tcx).map(|id| id.index)
+    }
 }
 
 impl<'tcx> DepNodeParams<'tcx> for CrateNum {
@@ -457,6 +453,10 @@ impl<'tcx> DepNodeParams<'tcx> for CrateNum {
     fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
         tcx.crate_name(*self).to_string()
     }
+
+    fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
+        dep_node.extract_def_id(tcx).map(|id| id.krate)
+    }
 }
 
 impl<'tcx> DepNodeParams<'tcx> for (DefId, DefId) {
diff --git a/src/librustc/dep_graph/mod.rs b/src/librustc/dep_graph/mod.rs
index eb377d20f59..1fbd90743f4 100644
--- a/src/librustc/dep_graph/mod.rs
+++ b/src/librustc/dep_graph/mod.rs
@@ -6,7 +6,8 @@ mod query;
 mod safe;
 mod serialized;
 
-pub use self::dep_node::{label_strs, DepConstructor, DepKind, DepNode, RecoverKey, WorkProductId};
+pub(crate) use self::dep_node::DepNodeParams;
+pub use self::dep_node::{label_strs, DepConstructor, DepKind, DepNode, WorkProductId};
 pub use self::graph::WorkProductFileKind;
 pub use self::graph::{hash_result, DepGraph, DepNodeColor, DepNodeIndex, TaskDeps, WorkProduct};
 pub use self::prev::PreviousDepGraph;
diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs
index b7f4f432838..11e9acf3a39 100644
--- a/src/librustc/query/mod.rs
+++ b/src/librustc/query/mod.rs
@@ -1,4 +1,4 @@
-use crate::dep_graph::{DepKind, DepNode, RecoverKey, SerializedDepNodeIndex};
+use crate::dep_graph::SerializedDepNodeIndex;
 use crate::mir;
 use crate::mir::interpret::{GlobalId, LitToConstInput};
 use crate::traits;
@@ -60,6 +60,11 @@ rustc_queries! {
             cache_on_disk_if { key.is_local() }
         }
 
+        query analysis(key: CrateNum) -> Result<(), ErrorReported> {
+            eval_always
+            desc { "running analysis passes on this crate" }
+        }
+
         /// Maps from the `DefId` of an item (trait/struct/enum/fn) to its
         /// associated generics.
         query generics_of(key: DefId) -> &'tcx ty::Generics {
@@ -195,7 +200,6 @@ rustc_queries! {
             // queries). Making it anonymous avoids hashing the result, which
             // may save a bit of time.
             anon
-            no_force
             desc { "erasing regions from `{:?}`", ty }
         }
 
@@ -204,7 +208,6 @@ rustc_queries! {
         }
 
         query program_clauses_for_env(_: traits::Environment<'tcx>) -> Clauses<'tcx> {
-            no_force
             desc { "generating chalk-style clauses for environment" }
         }
 
@@ -247,7 +250,6 @@ rustc_queries! {
         /// To avoid cycles within the predicates of a single item we compute
         /// per-type-parameter predicates for resolving `T::AssocTy`.
         query type_param_predicates(key: (DefId, DefId)) -> ty::GenericPredicates<'tcx> {
-            no_force
             desc { |tcx| "computing the bounds for type parameter `{}`", {
                 let id = tcx.hir().as_local_hir_id(key.1).unwrap();
                 tcx.hir().ty_param_name(id)
@@ -503,7 +505,6 @@ rustc_queries! {
         /// form to be used outside of const eval.
         query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
             -> ConstEvalRawResult<'tcx> {
-            no_force
             desc { |tcx|
                 "const-evaluating `{}`",
                 tcx.def_path_str(key.value.instance.def.def_id())
@@ -520,7 +521,6 @@ rustc_queries! {
         /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`.
         query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>)
             -> ConstEvalResult<'tcx> {
-            no_force
             desc { |tcx|
                 "const-evaluating + checking `{}`",
                 tcx.def_path_str(key.value.instance.def.def_id())
@@ -535,7 +535,6 @@ rustc_queries! {
         query const_field(
             key: ty::ParamEnvAnd<'tcx, (&'tcx ty::Const<'tcx>, mir::Field)>
         ) -> ConstValue<'tcx> {
-            no_force
             desc { "extract field of const" }
         }
 
@@ -544,19 +543,16 @@ rustc_queries! {
         query destructure_const(
             key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>>
         ) -> mir::DestructuredConst<'tcx> {
-            no_force
             desc { "destructure constant" }
         }
 
         query const_caller_location(key: (rustc_span::Symbol, u32, u32)) -> ConstValue<'tcx> {
-            no_force
             desc { "get a &core::panic::Location referring to a span" }
         }
 
         query lit_to_const(
             key: LitToConstInput<'tcx>
         ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
-            no_force
             desc { "converting literal to const" }
         }
     }
@@ -587,7 +583,6 @@ rustc_queries! {
         query region_scope_tree(_: DefId) -> &'tcx region::ScopeTree {}
 
         query mir_shims(key: ty::InstanceDef<'tcx>) -> &'tcx mir::BodyAndCache<'tcx> {
-            no_force
             desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) }
         }
 
@@ -595,7 +590,6 @@ rustc_queries! {
         /// given instance from the local crate. In particular, it will also
         /// look up the correct symbol name of instances from upstream crates.
         query symbol_name(key: ty::Instance<'tcx>) -> ty::SymbolName {
-            no_force
             desc { "computing the symbol for `{}`", key }
             cache_on_disk_if { true }
         }
@@ -642,7 +636,6 @@ rustc_queries! {
     Other {
         query vtable_methods(key: ty::PolyTraitRef<'tcx>)
                             -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] {
-            no_force
             desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) }
         }
     }
@@ -651,7 +644,6 @@ rustc_queries! {
         query codegen_fulfill_obligation(
             key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
         ) -> Option<Vtable<'tcx, ()>> {
-            no_force
             cache_on_disk_if { true }
             desc { |tcx|
                 "checking if `{}` fulfills its obligations",
@@ -683,22 +675,18 @@ rustc_queries! {
         /// Trait selection queries. These are best used by invoking `ty.is_copy_modulo_regions()`,
         /// `ty.is_copy()`, etc, since that will prune the environment where possible.
         query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-            no_force
             desc { "computing whether `{}` is `Copy`", env.value }
         }
         /// Query backing `TyS::is_sized`.
         query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-            no_force
             desc { "computing whether `{}` is `Sized`", env.value }
         }
         /// Query backing `TyS::is_freeze`.
         query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-            no_force
             desc { "computing whether `{}` is freeze", env.value }
         }
         /// Query backing `TyS::needs_drop`.
         query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
-            no_force
             desc { "computing whether `{}` needs drop", env.value }
         }
 
@@ -712,7 +700,6 @@ rustc_queries! {
         query layout_raw(
             env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
         ) -> Result<&'tcx ty::layout::LayoutDetails, ty::layout::LayoutError<'tcx>> {
-            no_force
             desc { "computing layout of `{}`", env.value }
         }
     }
@@ -768,7 +755,6 @@ rustc_queries! {
 
     TypeChecking {
         query specializes(_: (DefId, DefId)) -> bool {
-            no_force
             desc { "computing whether impls specialize one another" }
         }
         query in_scope_traits_map(_: DefIndex)
@@ -853,7 +839,6 @@ rustc_queries! {
         ///       (like `Clone::clone` for example).
         query upstream_drop_glue_for(substs: SubstsRef<'tcx>) -> Option<CrateNum> {
             desc { "available upstream drop-glue for `{:?}`", substs }
-            no_force
         }
     }
 
@@ -898,7 +883,6 @@ rustc_queries! {
     TypeChecking {
         query implementations_of_trait(_: (CrateNum, DefId))
             -> &'tcx [DefId] {
-            no_force
             desc { "looking up implementations of a trait in a crate" }
         }
         query all_trait_implementations(_: CrateNum)
@@ -1065,7 +1049,6 @@ rustc_queries! {
         }
         query is_codegened_item(_: DefId) -> bool {}
         query codegen_unit(_: Symbol) -> Arc<CodegenUnit<'tcx>> {
-            no_force
             desc { "codegen_unit" }
         }
         query backend_optimization_level(_: CrateNum) -> OptLevel {
@@ -1088,7 +1071,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
             NoSolution,
         > {
-            no_force
             desc { "normalizing `{:?}`", goal }
         }
 
@@ -1096,7 +1078,6 @@ rustc_queries! {
         query normalize_ty_after_erasing_regions(
             goal: ParamEnvAnd<'tcx, Ty<'tcx>>
         ) -> Ty<'tcx> {
-            no_force
             desc { "normalizing `{:?}`", goal }
         }
 
@@ -1106,7 +1087,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Vec<OutlivesBound<'tcx>>>>,
             NoSolution,
         > {
-            no_force
             desc { "computing implied outlives bounds for `{:?}`", goal }
         }
 
@@ -1117,7 +1097,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>,
             NoSolution,
         > {
-            no_force
             desc { "computing dropck types for `{:?}`", goal }
         }
 
@@ -1126,7 +1105,6 @@ rustc_queries! {
         query evaluate_obligation(
             goal: CanonicalPredicateGoal<'tcx>
         ) -> Result<traits::EvaluationResult, traits::OverflowError> {
-            no_force
             desc { "evaluating trait selection obligation `{}`", goal.value.value }
         }
 
@@ -1137,7 +1115,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
             NoSolution,
         > {
-            no_force
             desc { "evaluating `type_op_ascribe_user_type` `{:?}`", goal }
         }
 
@@ -1148,7 +1125,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
             NoSolution,
         > {
-            no_force
             desc { "evaluating `type_op_eq` `{:?}`", goal }
         }
 
@@ -1159,7 +1135,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
             NoSolution,
         > {
-            no_force
             desc { "evaluating `type_op_subtype` `{:?}`", goal }
         }
 
@@ -1170,7 +1145,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ()>>,
             NoSolution,
         > {
-            no_force
             desc { "evaluating `type_op_prove_predicate` `{:?}`", goal }
         }
 
@@ -1181,7 +1155,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, Ty<'tcx>>>,
             NoSolution,
         > {
-            no_force
             desc { "normalizing `{:?}`", goal }
         }
 
@@ -1192,7 +1165,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::Predicate<'tcx>>>,
             NoSolution,
         > {
-            no_force
             desc { "normalizing `{:?}`", goal }
         }
 
@@ -1203,7 +1175,6 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::PolyFnSig<'tcx>>>,
             NoSolution,
         > {
-            no_force
             desc { "normalizing `{:?}`", goal }
         }
 
@@ -1214,12 +1185,10 @@ rustc_queries! {
             &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, ty::FnSig<'tcx>>>,
             NoSolution,
         > {
-            no_force
             desc { "normalizing `{:?}`", goal }
         }
 
         query substitute_normalize_and_test_predicates(key: (DefId, SubstsRef<'tcx>)) -> bool {
-            no_force
             desc { |tcx|
                 "testing substituted normalized predicates:`{}`",
                 tcx.def_path_str(key.0)
@@ -1229,7 +1198,6 @@ rustc_queries! {
         query method_autoderef_steps(
             goal: CanonicalTyGoal<'tcx>
         ) -> MethodAutoderefStepsResult<'tcx> {
-            no_force
             desc { "computing autoderef types for `{:?}`", goal }
         }
     }
@@ -1243,7 +1211,6 @@ rustc_queries! {
         // Get an estimate of the size of an InstanceDef based on its MIR for CGU partitioning.
         query instance_def_size_estimate(def: ty::InstanceDef<'tcx>)
             -> usize {
-            no_force
             desc { |tcx| "estimating size for `{}`", tcx.def_path_str(def.def_id()) }
         }
 
diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs
index e0e1ca374d9..178c2362def 100644
--- a/src/librustc/ty/query/config.rs
+++ b/src/librustc/ty/query/config.rs
@@ -2,11 +2,10 @@ use crate::dep_graph::SerializedDepNodeIndex;
 use crate::dep_graph::{DepKind, DepNode};
 use crate::ty::query::caches::QueryCache;
 use crate::ty::query::plumbing::CycleError;
-use crate::ty::query::queries;
 use crate::ty::query::{Query, QueryState};
 use crate::ty::TyCtxt;
 use rustc_data_structures::profiling::ProfileCategory;
-use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_hir::def_id::DefId;
 
 use crate::ich::StableHashingContext;
 use rustc_data_structures::fingerprint::Fingerprint;
@@ -87,9 +86,3 @@ where
         bug!("QueryDescription::load_from_disk() called for an unsupported query.")
     }
 }
-
-impl<'tcx> QueryDescription<'tcx> for queries::analysis<'tcx> {
-    fn describe(_tcx: TyCtxt<'_>, _: CrateNum) -> Cow<'static, str> {
-        "running analysis passes on this crate".into()
-    }
-}
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 8614fd5cdca..8adb828fbeb 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -1,4 +1,4 @@
-use crate::dep_graph::{self, DepConstructor, DepNode};
+use crate::dep_graph::{self, DepConstructor, DepNode, DepNodeParams};
 use crate::hir::exports::Export;
 use crate::infer::canonical::{self, Canonical};
 use crate::lint::LintLevelMap;
@@ -60,8 +60,8 @@ use std::sync::Arc;
 
 #[macro_use]
 mod plumbing;
+pub use self::plumbing::CycleError;
 use self::plumbing::*;
-pub use self::plumbing::{force_from_dep_node, CycleError};
 
 mod stats;
 pub use self::stats::print_stats;
@@ -104,9 +104,105 @@ pub use self::profiling_support::{IntoSelfProfilingString, QueryKeyStringBuilder
 // Queries marked with `fatal_cycle` do not need the latter implementation,
 // as they will raise an fatal error on query cycles instead.
 
-rustc_query_append! { [define_queries!][ <'tcx>
-    Other {
-        /// Runs analysis passes on the crate.
-        [eval_always] fn analysis: Analysis(CrateNum) -> Result<(), ErrorReported>,
-    },
-]}
+rustc_query_append! { [define_queries!][<'tcx>] }
+
+/// The red/green evaluation system will try to mark a specific DepNode in the
+/// dependency graph as green by recursively trying to mark the dependencies of
+/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode`
+/// where we don't know if it is red or green and we therefore actually have
+/// to recompute its value in order to find out. Since the only piece of
+/// information that we have at that point is the `DepNode` we are trying to
+/// re-evaluate, we need some way to re-run a query from just that. This is what
+/// `force_from_dep_node()` implements.
+///
+/// In the general case, a `DepNode` consists of a `DepKind` and an opaque
+/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint
+/// is usually constructed by computing a stable hash of the query-key that the
+/// `DepNode` corresponds to. Consequently, it is not in general possible to go
+/// back from hash to query-key (since hash functions are not reversible). For
+/// this reason `force_from_dep_node()` is expected to fail from time to time
+/// because we just cannot find out, from the `DepNode` alone, what the
+/// corresponding query-key is and therefore cannot re-run the query.
+///
+/// The system deals with this case letting `try_mark_green` fail which forces
+/// the root query to be re-evaluated.
+///
+/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless.
+/// Fortunately, we can use some contextual information that will allow us to
+/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we
+/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a
+/// valid `DefPathHash`. Since we also always build a huge table that maps every
+/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have
+/// everything we need to re-run the query.
+///
+/// Take the `mir_validated` query as an example. Like many other queries, it
+/// just has a single parameter: the `DefId` of the item it will compute the
+/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode`
+/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode`
+/// is actually a `DefPathHash`, and can therefore just look up the corresponding
+/// `DefId` in `tcx.def_path_hash_to_def_id`.
+///
+/// When you implement a new query, it will likely have a corresponding new
+/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As
+/// a rule of thumb, if your query takes a `DefId` or `DefIndex` as sole parameter,
+/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just
+/// add it to the "We don't have enough information to reconstruct..." group in
+/// the match below.
+pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool {
+    use crate::dep_graph::DepKind;
+
+    // We must avoid ever having to call `force_from_dep_node()` for a
+    // `DepNode::codegen_unit`:
+    // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we
+    // would always end up having to evaluate the first caller of the
+    // `codegen_unit` query that *is* reconstructible. This might very well be
+    // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just
+    // to re-trigger calling the `codegen_unit` query with the right key. At
+    // that point we would already have re-done all the work we are trying to
+    // avoid doing in the first place.
+    // The solution is simple: Just explicitly call the `codegen_unit` query for
+    // each CGU, right after partitioning. This way `try_mark_green` will always
+    // hit the cache instead of having to go through `force_from_dep_node`.
+    // This assertion makes sure, we actually keep applying the solution above.
+    debug_assert!(
+        dep_node.kind != DepKind::codegen_unit,
+        "calling force_from_dep_node() on DepKind::codegen_unit"
+    );
+
+    if !dep_node.kind.can_reconstruct_query_key() {
+        return false;
+    }
+
+    rustc_dep_node_force!([dep_node, tcx]
+        // These are inputs that are expected to be pre-allocated and that
+        // should therefore always be red or green already.
+        DepKind::AllLocalTraitImpls |
+        DepKind::CrateMetadata |
+        DepKind::HirBody |
+        DepKind::Hir |
+
+        // These are anonymous nodes.
+        DepKind::TraitSelect |
+
+        // We don't have enough information to reconstruct the query key of
+        // these.
+        DepKind::CompileCodegenUnit => {
+            bug!("force_from_dep_node: encountered {:?}", dep_node)
+        }
+    );
+
+    false
+}
+
+impl DepNode {
+    /// Check whether the query invocation corresponding to the given
+    /// DepNode is eligible for on-disk-caching. If so, this is method
+    /// will execute the query corresponding to the given DepNode.
+    /// Also, as a sanity check, it expects that the corresponding query
+    /// invocation has been marked as green already.
+    pub fn try_load_from_on_disk_cache<'tcx>(&self, tcx: TyCtxt<'tcx>) {
+        use crate::dep_graph::DepKind;
+
+        rustc_dep_node_try_load_from_on_disk_cache!(self, tcx)
+    }
+}
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index c94909549df..acf67f52dce 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -2,7 +2,7 @@
 //! generate the actual methods on tcx which find and execute the provider,
 //! manage the caches, and so forth.
 
-use crate::dep_graph::{DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex};
+use crate::dep_graph::{DepNode, DepNodeIndex, SerializedDepNodeIndex};
 use crate::ty::query::caches::QueryCache;
 use crate::ty::query::config::{QueryAccessors, QueryDescription};
 use crate::ty::query::job::{QueryInfo, QueryJob, QueryJobId, QueryShardJobId};
@@ -720,7 +720,7 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[allow(dead_code)]
-    fn force_query<Q: QueryDescription<'tcx> + 'tcx>(
+    pub(super) fn force_query<Q: QueryDescription<'tcx> + 'tcx>(
         self,
         key: Q::Key,
         span: Span,
@@ -1162,105 +1162,3 @@ macro_rules! define_provider_struct {
         }
     };
 }
-
-/// The red/green evaluation system will try to mark a specific DepNode in the
-/// dependency graph as green by recursively trying to mark the dependencies of
-/// that `DepNode` as green. While doing so, it will sometimes encounter a `DepNode`
-/// where we don't know if it is red or green and we therefore actually have
-/// to recompute its value in order to find out. Since the only piece of
-/// information that we have at that point is the `DepNode` we are trying to
-/// re-evaluate, we need some way to re-run a query from just that. This is what
-/// `force_from_dep_node()` implements.
-///
-/// In the general case, a `DepNode` consists of a `DepKind` and an opaque
-/// GUID/fingerprint that will uniquely identify the node. This GUID/fingerprint
-/// is usually constructed by computing a stable hash of the query-key that the
-/// `DepNode` corresponds to. Consequently, it is not in general possible to go
-/// back from hash to query-key (since hash functions are not reversible). For
-/// this reason `force_from_dep_node()` is expected to fail from time to time
-/// because we just cannot find out, from the `DepNode` alone, what the
-/// corresponding query-key is and therefore cannot re-run the query.
-///
-/// The system deals with this case letting `try_mark_green` fail which forces
-/// the root query to be re-evaluated.
-///
-/// Now, if `force_from_dep_node()` would always fail, it would be pretty useless.
-/// Fortunately, we can use some contextual information that will allow us to
-/// reconstruct query-keys for certain kinds of `DepNode`s. In particular, we
-/// enforce by construction that the GUID/fingerprint of certain `DepNode`s is a
-/// valid `DefPathHash`. Since we also always build a huge table that maps every
-/// `DefPathHash` in the current codebase to the corresponding `DefId`, we have
-/// everything we need to re-run the query.
-///
-/// Take the `mir_validated` query as an example. Like many other queries, it
-/// just has a single parameter: the `DefId` of the item it will compute the
-/// validated MIR for. Now, when we call `force_from_dep_node()` on a `DepNode`
-/// with kind `MirValidated`, we know that the GUID/fingerprint of the `DepNode`
-/// is actually a `DefPathHash`, and can therefore just look up the corresponding
-/// `DefId` in `tcx.def_path_hash_to_def_id`.
-///
-/// When you implement a new query, it will likely have a corresponding new
-/// `DepKind`, and you'll have to support it here in `force_from_dep_node()`. As
-/// a rule of thumb, if your query takes a `DefId` or `DefIndex` as sole parameter,
-/// then `force_from_dep_node()` should not fail for it. Otherwise, you can just
-/// add it to the "We don't have enough information to reconstruct..." group in
-/// the match below.
-pub fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: &DepNode) -> bool {
-    use crate::dep_graph::RecoverKey;
-
-    // We must avoid ever having to call `force_from_dep_node()` for a
-    // `DepNode::codegen_unit`:
-    // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we
-    // would always end up having to evaluate the first caller of the
-    // `codegen_unit` query that *is* reconstructible. This might very well be
-    // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just
-    // to re-trigger calling the `codegen_unit` query with the right key. At
-    // that point we would already have re-done all the work we are trying to
-    // avoid doing in the first place.
-    // The solution is simple: Just explicitly call the `codegen_unit` query for
-    // each CGU, right after partitioning. This way `try_mark_green` will always
-    // hit the cache instead of having to go through `force_from_dep_node`.
-    // This assertion makes sure, we actually keep applying the solution above.
-    debug_assert!(
-        dep_node.kind != DepKind::codegen_unit,
-        "calling force_from_dep_node() on DepKind::codegen_unit"
-    );
-
-    if !dep_node.kind.can_reconstruct_query_key() {
-        return false;
-    }
-
-    rustc_dep_node_force!([dep_node, tcx]
-        // These are inputs that are expected to be pre-allocated and that
-        // should therefore always be red or green already.
-        DepKind::AllLocalTraitImpls |
-        DepKind::CrateMetadata |
-        DepKind::HirBody |
-        DepKind::Hir |
-
-        // These are anonymous nodes.
-        DepKind::TraitSelect |
-
-        // We don't have enough information to reconstruct the query key of
-        // these.
-        DepKind::CompileCodegenUnit => {
-            bug!("force_from_dep_node: encountered {:?}", dep_node)
-        }
-
-        DepKind::Analysis => {
-            let def_id = if let Some(def_id) = dep_node.extract_def_id(tcx) {
-                def_id
-            } else {
-                // Return from the whole function.
-                return false
-            };
-            tcx.force_query::<crate::ty::query::queries::analysis<'_>>(
-                def_id.krate,
-                DUMMY_SP,
-                *dep_node
-            );
-        }
-    );
-
-    true
-}
diff --git a/src/librustc_macros/src/query.rs b/src/librustc_macros/src/query.rs
index 97b800decc5..56b7be2f7e2 100644
--- a/src/librustc_macros/src/query.rs
+++ b/src/librustc_macros/src/query.rs
@@ -51,9 +51,6 @@ enum QueryModifier {
     /// Don't hash the result, instead just mark a query red if it runs
     NoHash,
 
-    /// Don't force the query
-    NoForce,
-
     /// Generate a dep node based on the dependencies of the query
     Anon,
 
@@ -118,8 +115,6 @@ impl Parse for QueryModifier {
             Ok(QueryModifier::CycleDelayBug)
         } else if modifier == "no_hash" {
             Ok(QueryModifier::NoHash)
-        } else if modifier == "no_force" {
-            Ok(QueryModifier::NoForce)
         } else if modifier == "anon" {
             Ok(QueryModifier::Anon)
         } else if modifier == "eval_always" {
@@ -222,9 +217,6 @@ struct QueryModifiers {
     /// Don't hash the result, instead just mark a query red if it runs
     no_hash: bool,
 
-    /// Don't force the query
-    no_force: bool,
-
     /// Generate a dep node based on the dependencies of the query
     anon: bool,
 
@@ -241,7 +233,6 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
     let mut fatal_cycle = false;
     let mut cycle_delay_bug = false;
     let mut no_hash = false;
-    let mut no_force = false;
     let mut anon = false;
     let mut eval_always = false;
     for modifier in query.modifiers.0.drain(..) {
@@ -288,12 +279,6 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
                 }
                 no_hash = true;
             }
-            QueryModifier::NoForce => {
-                if no_force {
-                    panic!("duplicate modifier `no_force` for query `{}`", query.name);
-                }
-                no_force = true;
-            }
             QueryModifier::Anon => {
                 if anon {
                     panic!("duplicate modifier `anon` for query `{}`", query.name);
@@ -316,7 +301,6 @@ fn process_modifiers(query: &mut Query) -> QueryModifiers {
         fatal_cycle,
         cycle_delay_bug,
         no_hash,
-        no_force,
         anon,
         eval_always,
     }
@@ -425,7 +409,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
     let mut dep_node_def_stream = quote! {};
     let mut dep_node_force_stream = quote! {};
     let mut try_load_from_on_disk_cache_stream = quote! {};
-    let mut no_force_queries = Vec::new();
     let mut cached_queries = quote! {};
 
     for group in groups.0 {
@@ -444,19 +427,19 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
                 cached_queries.extend(quote! {
                     #name,
                 });
-            }
 
-            if modifiers.cache.is_some() && !modifiers.no_force {
                 try_load_from_on_disk_cache_stream.extend(quote! {
                     DepKind::#name => {
-                        debug_assert!(tcx.dep_graph
-                                         .node_color(self)
-                                         .map(|c| c.is_green())
-                                         .unwrap_or(false));
-
-                        let key = RecoverKey::recover(tcx, self).unwrap();
-                        if queries::#name::cache_on_disk(tcx, key, None) {
-                            let _ = tcx.#name(key);
+                        if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY {
+                            debug_assert!($tcx.dep_graph
+                                            .node_color($dep_node)
+                                            .map(|c| c.is_green())
+                                            .unwrap_or(false));
+
+                            let key = <#arg as DepNodeParams>::recover($tcx, $dep_node).unwrap();
+                            if queries::#name::cache_on_disk($tcx, key, None) {
+                                let _ = $tcx.#name(key);
+                            }
                         }
                     }
                 });
@@ -501,24 +484,21 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
                 [#attribute_stream] #name(#arg),
             });
 
-            if modifiers.no_force {
-                no_force_queries.push(name.clone());
-            } else {
-                // Add a match arm to force the query given the dep node
-                dep_node_force_stream.extend(quote! {
-                    DepKind::#name => {
-                        if let Some(key) = RecoverKey::recover($tcx, $dep_node) {
+            // Add a match arm to force the query given the dep node
+            dep_node_force_stream.extend(quote! {
+                DepKind::#name => {
+                    if <#arg as DepNodeParams>::CAN_RECONSTRUCT_QUERY_KEY {
+                        if let Some(key) = <#arg as DepNodeParams>::recover($tcx, $dep_node) {
                             $tcx.force_query::<crate::ty::query::queries::#name<'_>>(
                                 key,
                                 DUMMY_SP,
                                 *$dep_node
                             );
-                        } else {
-                            return false;
+                            return true;
                         }
                     }
-                });
-            }
+                }
+            });
 
             add_query_description_impl(&query, modifiers, &mut query_description_stream);
         }
@@ -528,12 +508,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
         });
     }
 
-    // Add an arm for the no force queries to panic when trying to force them
-    for query in no_force_queries {
-        dep_node_force_stream.extend(quote! {
-            DepKind::#query |
-        });
-    }
     dep_node_force_stream.extend(quote! {
         DepKind::Null => {
             bug!("Cannot force dep node: {:?}", $dep_node)
@@ -577,14 +551,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
 
         #query_description_stream
 
-        impl DepNode {
-            /// Check whether the query invocation corresponding to the given
-            /// DepNode is eligible for on-disk-caching. If so, this is method
-            /// will execute the query corresponding to the given DepNode.
-            /// Also, as a sanity check, it expects that the corresponding query
-            /// invocation has been marked as green already.
-            pub fn try_load_from_on_disk_cache(&self, tcx: TyCtxt<'_>) {
-                match self.kind {
+        macro_rules! rustc_dep_node_try_load_from_on_disk_cache {
+            ($dep_node:expr, $tcx:expr) => {
+                match $dep_node.kind {
                     #try_load_from_on_disk_cache_stream
                     _ => (),
                 }