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