rustc_middle/query/
inner.rs

1//! Helper functions that serve as the immediate implementation of
2//! `tcx.$query(..)` and its variations.
3
4use std::fmt::Debug;
5
6use rustc_data_structures::fingerprint::Fingerprint;
7use rustc_query_system::dep_graph::{DepKind, DepNodeParams};
8use rustc_query_system::ich::StableHashingContext;
9use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached};
10use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
11
12use crate::dep_graph;
13use crate::query::IntoQueryParam;
14use crate::query::erase::{self, Erase, EraseType};
15use crate::ty::TyCtxt;
16
17/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)`
18/// for all queries.
19#[inline(always)]
20pub(crate) fn query_get_at<'tcx, Cache>(
21    tcx: TyCtxt<'tcx>,
22    execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
23    query_cache: &Cache,
24    span: Span,
25    key: Cache::Key,
26) -> Cache::Value
27where
28    Cache: QueryCache,
29{
30    let key = key.into_query_param();
31    match try_get_cached(tcx, query_cache, &key) {
32        Some(value) => value,
33        None => execute_query(tcx, span, key, QueryMode::Get).unwrap(),
34    }
35}
36
37/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries,
38/// and `tcx.ensure_done().$query(..)` for all queries.
39#[inline]
40pub(crate) fn query_ensure<'tcx, Cache>(
41    tcx: TyCtxt<'tcx>,
42    execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
43    query_cache: &Cache,
44    key: Cache::Key,
45    check_cache: bool,
46) where
47    Cache: QueryCache,
48{
49    let key = key.into_query_param();
50    if try_get_cached(tcx, query_cache, &key).is_none() {
51        execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache });
52    }
53}
54
55/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that
56/// have the `return_result_from_ensure_ok` modifier.
57#[inline]
58pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>(
59    tcx: TyCtxt<'tcx>,
60    execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
61    query_cache: &Cache,
62    key: Cache::Key,
63    check_cache: bool,
64) -> Result<(), ErrorGuaranteed>
65where
66    Cache: QueryCache<Value = Erase<Result<T, ErrorGuaranteed>>>,
67    Result<T, ErrorGuaranteed>: EraseType,
68{
69    let key = key.into_query_param();
70    if let Some(res) = try_get_cached(tcx, query_cache, &key) {
71        erase::restore(res).map(drop)
72    } else {
73        execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
74            .map(erase::restore)
75            .map(|res| res.map(drop))
76            // Either we actually executed the query, which means we got a full `Result`,
77            // or we can just assume the query succeeded, because it was green in the
78            // incremental cache. If it is green, that means that the previous compilation
79            // that wrote to the incremental cache compiles successfully. That is only
80            // possible if the cache entry was `Ok(())`, so we emit that here, without
81            // actually encoding the `Result` in the cache or loading it from there.
82            .unwrap_or(Ok(()))
83    }
84}
85
86/// Common implementation of query feeding, used by `define_feedable!`.
87pub(crate) fn query_feed<'tcx, Cache, Value>(
88    tcx: TyCtxt<'tcx>,
89    dep_kind: DepKind,
90    hasher: Option<fn(&mut StableHashingContext<'_>, &Value) -> Fingerprint>,
91    cache: &Cache,
92    key: Cache::Key,
93    erased: Erase<Value>,
94) where
95    Cache: QueryCache<Value = Erase<Value>>,
96    Cache::Key: DepNodeParams<TyCtxt<'tcx>>,
97    Value: EraseType + Debug,
98{
99    let value = erase::restore::<Value>(erased);
100
101    match try_get_cached(tcx, cache, &key) {
102        Some(old) => {
103            let old = erase::restore::<Value>(old);
104            if let Some(hasher) = hasher {
105                let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx
106                    .with_stable_hashing_context(|mut hcx| {
107                        (hasher(&mut hcx, &value), hasher(&mut hcx, &old))
108                    });
109                if old_hash != value_hash {
110                    // We have an inconsistency. This can happen if one of the two
111                    // results is tainted by errors. In this case, delay a bug to
112                    // ensure compilation is doomed, and keep the `old` value.
113                    tcx.dcx().delayed_bug(format!(
114                        "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
115                        old value: {old:?}\nnew value: {value:?}",
116                    ));
117                }
118            } else {
119                // The query is `no_hash`, so we have no way to perform a sanity check.
120                // If feeding the same value multiple times needs to be supported,
121                // the query should not be marked `no_hash`.
122                bug!(
123                    "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
124                    old value: {old:?}\nnew value: {value:?}",
125                )
126            }
127        }
128        None => {
129            let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key);
130            let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher);
131            cache.complete(key, erased, dep_node_index);
132        }
133    }
134}