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