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