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