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::{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, Cache>(
36    tcx: TyCtxt<'tcx>,
37    execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
38    query_cache: &Cache,
39    span: Span,
40    key: Cache::Key,
41) -> Cache::Value
42where
43    Cache: QueryCache,
44{
45    match try_get_cached(tcx, query_cache, &key) {
46        Some(value) => value,
47        None => execute_query(tcx, span, key, QueryMode::Get).unwrap(),
48    }
49}
50
51/// Shared implementation of `tcx.ensure_ok().$query(..)` for most queries,
52/// and `tcx.ensure_done().$query(..)` for all queries.
53#[inline]
54pub(crate) fn query_ensure<'tcx, Cache>(
55    tcx: TyCtxt<'tcx>,
56    execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
57    query_cache: &Cache,
58    key: Cache::Key,
59    check_cache: bool,
60) where
61    Cache: QueryCache,
62{
63    if try_get_cached(tcx, query_cache, &key).is_none() {
64        execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache });
65    }
66}
67
68/// Shared implementation of `tcx.ensure_ok().$query(..)` for queries that
69/// have the `return_result_from_ensure_ok` modifier.
70#[inline]
71pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>(
72    tcx: TyCtxt<'tcx>,
73    execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
74    query_cache: &Cache,
75    key: Cache::Key,
76    check_cache: bool,
77) -> Result<(), ErrorGuaranteed>
78where
79    Cache: QueryCache<Value = Erased<Result<T, ErrorGuaranteed>>>,
80    Result<T, ErrorGuaranteed>: Erasable,
81{
82    if let Some(res) = try_get_cached(tcx, query_cache, &key) {
83        erase::restore_val(res).map(drop)
84    } else {
85        execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
86            .map(erase::restore_val)
87            .map(|res| res.map(drop))
88            // Either we actually executed the query, which means we got a full `Result`,
89            // or we can just assume the query succeeded, because it was green in the
90            // incremental cache. If it is green, that means that the previous compilation
91            // that wrote to the incremental cache compiles successfully. That is only
92            // possible if the cache entry was `Ok(())`, so we emit that here, without
93            // actually encoding the `Result` in the cache or loading it from there.
94            .unwrap_or(Ok(()))
95    }
96}
97
98/// Common implementation of query feeding, used by `define_feedable!`.
99pub(crate) fn query_feed<'tcx, Cache>(
100    tcx: TyCtxt<'tcx>,
101    dep_kind: DepKind,
102    query_vtable: &QueryVTable<'tcx, Cache>,
103    cache: &Cache,
104    key: Cache::Key,
105    value: Cache::Value,
106) where
107    Cache: QueryCache,
108    Cache::Key: DepNodeKey<TyCtxt<'tcx>>,
109{
110    let format_value = query_vtable.format_value;
111
112    // Check whether the in-memory cache already has a value for this key.
113    match try_get_cached(tcx, cache, &key) {
114        Some(old) => {
115            // The query already has a cached value for this key.
116            // That's OK if both values are the same, i.e. they have the same hash,
117            // so now we check their hashes.
118            if let Some(hasher_fn) = query_vtable.hash_result {
119                let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| {
120                    (hasher_fn(hcx, &old), hasher_fn(hcx, &value))
121                });
122                if old_hash != value_hash {
123                    // We have an inconsistency. This can happen if one of the two
124                    // results is tainted by errors. In this case, delay a bug to
125                    // ensure compilation is doomed, and keep the `old` value.
126                    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!(
127                        "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
128                        old value: {old}\nnew value: {value}",
129                        old = format_value(&old),
130                        value = format_value(&value),
131                    ));
132                }
133            } else {
134                // The query is `no_hash`, so we have no way to perform a sanity check.
135                // If feeding the same value multiple times needs to be supported,
136                // the query should not be marked `no_hash`.
137                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!(
138                    "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\
139                    old value: {old}\nnew value: {value}",
140                    old = format_value(&old),
141                    value = format_value(&value),
142                )
143            }
144        }
145        None => {
146            // There is no cached value for this key, so feed the query by
147            // adding the provided value to the cache.
148            let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key);
149            let dep_node_index = tcx.dep_graph.with_feed_task(
150                dep_node,
151                tcx,
152                &value,
153                query_vtable.hash_result,
154                query_vtable.format_value,
155            );
156            cache.complete(key, value, dep_node_index);
157        }
158    }
159}