Skip to main content

rustc_middle/query/
inner.rs

1//! Helper functions that serve as the immediate implementation of
2//! `tcx.$query(..)` and its variations.
3
4use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
5
6use crate::dep_graph;
7use crate::dep_graph::{DepKind, DepNodeKey};
8use crate::query::erase::{self, Erasable, Erased};
9use crate::query::plumbing::QueryVTable;
10use crate::query::{EnsureMode, QueryCache, QueryMode};
11use crate::ty::TyCtxt;
12
13/// Checks whether there is already a value for this key in the in-memory
14/// query cache, returning that value if present.
15///
16/// (Also performs some associated bookkeeping, if a value was found.)
17#[inline(always)]
18fn try_get_cached<'tcx, C>(tcx: TyCtxt<'tcx>, cache: &C, key: C::Key) -> Option<C::Value>
19where
20    C: QueryCache,
21{
22    match cache.lookup(&key) {
23        Some((value, index)) => {
24            tcx.prof.query_cache_hit(index.into());
25            tcx.dep_graph.read_index(index);
26            Some(value)
27        }
28        None => None,
29    }
30}
31
32/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)`
33/// for all queries.
34#[inline(always)]
35pub(crate) fn query_get_at<'tcx, C>(
36    tcx: TyCtxt<'tcx>,
37    span: Span,
38    query: &'tcx QueryVTable<'tcx, C>,
39    key: C::Key,
40) -> C::Value
41where
42    C: QueryCache,
43{
44    match try_get_cached(tcx, &query.cache, key) {
45        Some(value) => value,
46        None => (query.execute_query_fn)(tcx, span, key, QueryMode::Get).unwrap(),
47    }
48}
49
50/// Shared implementation of `tcx.ensure_ok().$query(..)` and
51/// `tcx.ensure_done().$query(..)` for all queries.
52#[inline]
53pub(crate) fn query_ensure_ok_or_done<'tcx, C>(
54    tcx: TyCtxt<'tcx>,
55    query: &'tcx QueryVTable<'tcx, C>,
56    key: C::Key,
57    ensure_mode: EnsureMode,
58) where
59    C: QueryCache,
60{
61    match try_get_cached(tcx, &query.cache, key) {
62        Some(_value) => {}
63        None => {
64            (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::Ensure { ensure_mode });
65        }
66    }
67}
68
69/// Implementation of `tcx.ensure_result().$query(..)` for queries that
70/// return `Result<_, ErrorGuaranteed>`.
71#[inline]
72pub(crate) fn query_ensure_result<'tcx, C, T>(
73    tcx: TyCtxt<'tcx>,
74    query: &'tcx QueryVTable<'tcx, C>,
75    key: C::Key,
76) -> Result<(), ErrorGuaranteed>
77where
78    C: QueryCache<Value = Erased<Result<T, ErrorGuaranteed>>>,
79    Result<T, ErrorGuaranteed>: Erasable,
80{
81    match try_get_cached(tcx, &query.cache, key) {
82        Some(value) => erase::restore_val(value).map(drop),
83        None => (query.execute_query_fn)(
84            tcx,
85            DUMMY_SP,
86            key,
87            QueryMode::Ensure { ensure_mode: EnsureMode::Ok },
88        )
89        .map(erase::restore_val)
90        .map(|value| value.map(drop))
91        // Either we actually executed the query, which means we got a full `Result`,
92        // or we can just assume the query succeeded, because it was green in the
93        // incremental cache. If it is green, that means that the previous compilation
94        // that wrote to the incremental cache compiles successfully. That is only
95        // possible if the cache entry was `Ok(())`, so we emit that here, without
96        // actually encoding the `Result` in the cache or loading it from there.
97        .unwrap_or(Ok(())),
98    }
99}
100
101/// Common implementation of query feeding, used by `define_feedable!`.
102pub(crate) fn query_feed<'tcx, C>(
103    tcx: TyCtxt<'tcx>,
104    dep_kind: DepKind,
105    query_vtable: &QueryVTable<'tcx, C>,
106    key: C::Key,
107    value: C::Value,
108) where
109    C: QueryCache,
110    C::Key: DepNodeKey<'tcx>,
111{
112    let format_value = query_vtable.format_value;
113
114    // Check whether the in-memory cache already has a value for this key.
115    match try_get_cached(tcx, &query_vtable.cache, key) {
116        Some(old) => {
117            // The query already has a cached value for this key.
118            // That's OK if both values are the same, i.e. they have the same hash,
119            // so now we check their hashes.
120            if let Some(hash_value_fn) = query_vtable.hash_value_fn {
121                let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| {
122                    (hash_value_fn(hcx, &old), hash_value_fn(hcx, &value))
123                });
124                if old_hash != value_hash {
125                    // We have an inconsistency. This can happen if one of the two
126                    // results is tainted by errors. In this case, delay a bug to
127                    // ensure compilation is doomed, and keep the `old` value.
128                    tcx.dcx().delayed_bug(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Trying to feed an already recorded value for query {2:?} key={3:?}:\nold value: {0}\nnew value: {1}",
                format_value(&old), format_value(&value), dep_kind, key))
    })format!(
129                        "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
130                        old value: {old}\nnew value: {value}",
131                        old = format_value(&old),
132                        value = format_value(&value),
133                    ));
134                }
135            } else {
136                // The query is `no_hash`, so we have no way to perform a sanity check.
137                // If feeding the same value multiple times needs to be supported,
138                // the query should not be marked `no_hash`.
139                crate::util::bug::bug_fmt(format_args!("Trying to feed an already recorded value for query {2:?} key={3:?}:\nold value: {0}\nnew value: {1}",
        format_value(&old), format_value(&value), dep_kind, key))bug!(
140                    "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
141                    old value: {old}\nnew value: {value}",
142                    old = format_value(&old),
143                    value = format_value(&value),
144                )
145            }
146        }
147        None => {
148            // There is no cached value for this key, so feed the query by
149            // adding the provided value to the cache.
150            let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key);
151            let dep_node_index = tcx.dep_graph.with_feed_task(
152                dep_node,
153                tcx,
154                &value,
155                query_vtable.hash_value_fn,
156                query_vtable.format_value,
157            );
158            query_vtable.cache.complete(key, value, dep_node_index);
159        }
160    }
161}