Skip to content

Remove the no_force query attribute #69475

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 10, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions src/librustc/dep_graph/dep_node.rs
Original file line number Diff line number Diff line change
@@ -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>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment like:

    /// 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.

}

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) {
3 changes: 2 additions & 1 deletion src/librustc/dep_graph/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
45 changes: 6 additions & 39 deletions src/librustc/query/mod.rs
Original file line number Diff line number Diff line change
@@ -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,15 +583,13 @@ 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()) }
}

/// The `symbol_name` query provides the symbol name for calling a
/// 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,15 +1071,13 @@ rustc_queries! {
&'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
NoSolution,
> {
no_force
desc { "normalizing `{:?}`", goal }
}

/// Do not call this query directly: invoke `normalize_erasing_regions` instead.
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()) }
}

9 changes: 1 addition & 8 deletions src/librustc/ty/query/config.rs
Original file line number Diff line number Diff line change
@@ -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()
}
}
112 changes: 104 additions & 8 deletions src/librustc/ty/query/mod.rs
Original file line number Diff line number Diff line change
@@ -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)
}
}
106 changes: 2 additions & 104 deletions src/librustc/ty/query/plumbing.rs
Original file line number Diff line number Diff line change
@@ -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
}
73 changes: 21 additions & 52 deletions src/librustc_macros/src/query.rs
Original file line number Diff line number Diff line change
@@ -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
_ => (),
}