1//! Helper functions that serve as the immediate implementation of
2//! `tcx.$query(..)` and its variations.
34use std::ops::Deref;
56use rustc_hir::def_id::LocalDefId;
7use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
89use crate::dep_graph;
10use crate::dep_graph::DepNodeKey;
11use crate::query::erase::{self, Erasable, Erased};
12use crate::query::{IntoQueryKey, QueryCache, QueryMode, QueryVTable};
13use crate::ty::{self, TyCtxt};
1415#[derive(#[automatically_derived]
impl<'tcx> ::core::marker::Copy for TyCtxtAt<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for TyCtxtAt<'tcx> {
#[inline]
fn clone(&self) -> TyCtxtAt<'tcx> {
let _: ::core::clone::AssertParamIsClone<TyCtxt<'tcx>>;
let _: ::core::clone::AssertParamIsClone<Span>;
*self
}
}Clone)]
16pub struct TyCtxtAt<'tcx> {
17pub tcx: TyCtxt<'tcx>,
18pub span: Span,
19}
2021impl<'tcx> Dereffor TyCtxtAt<'tcx> {
22type Target = TyCtxt<'tcx>;
23#[inline(always)]
24fn deref(&self) -> &Self::Target {
25&self.tcx
26 }
27}
2829#[derive(#[automatically_derived]
impl<'tcx> ::core::marker::Copy for TyCtxtEnsureOk<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for TyCtxtEnsureOk<'tcx> {
#[inline]
fn clone(&self) -> TyCtxtEnsureOk<'tcx> {
let _: ::core::clone::AssertParamIsClone<TyCtxt<'tcx>>;
*self
}
}Clone)]
30#[must_use]
31pub struct TyCtxtEnsureOk<'tcx> {
32pub tcx: TyCtxt<'tcx>,
33}
3435#[derive(#[automatically_derived]
impl<'tcx> ::core::marker::Copy for TyCtxtEnsureResult<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for TyCtxtEnsureResult<'tcx> {
#[inline]
fn clone(&self) -> TyCtxtEnsureResult<'tcx> {
let _: ::core::clone::AssertParamIsClone<TyCtxt<'tcx>>;
*self
}
}Clone)]
36#[must_use]
37pub struct TyCtxtEnsureResult<'tcx> {
38pub tcx: TyCtxt<'tcx>,
39}
4041#[derive(#[automatically_derived]
impl<'tcx> ::core::marker::Copy for TyCtxtEnsureDone<'tcx> { }Copy, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for TyCtxtEnsureDone<'tcx> {
#[inline]
fn clone(&self) -> TyCtxtEnsureDone<'tcx> {
let _: ::core::clone::AssertParamIsClone<TyCtxt<'tcx>>;
*self
}
}Clone)]
42#[must_use]
43pub struct TyCtxtEnsureDone<'tcx> {
44pub tcx: TyCtxt<'tcx>,
45}
4647impl<'tcx> TyCtxtEnsureOk<'tcx> {
48pub fn typeck(self, def_id: impl IntoQueryKey<LocalDefId>) {
49self.typeck_root(
50self.tcx.typeck_root_def_id(def_id.into_query_key().to_def_id()).expect_local(),
51 )
52 }
53}
5455impl<'tcx> TyCtxt<'tcx> {
56pub fn typeck(self, def_id: impl IntoQueryKey<LocalDefId>) -> &'tcx ty::TypeckResults<'tcx> {
57self.typeck_root(
58self.typeck_root_def_id(def_id.into_query_key().to_def_id()).expect_local(),
59 )
60 }
6162/// Returns a transparent wrapper for `TyCtxt` which uses
63 /// `span` as the location of queries performed through it.
64#[inline(always)]
65pub fn at(self, span: Span) -> TyCtxtAt<'tcx> {
66TyCtxtAt { tcx: self, span }
67 }
6869/// FIXME: `ensure_ok`'s effects are subtle. Is this comment fully accurate?
70 ///
71 /// Wrapper that calls queries in a special "ensure OK" mode, for callers
72 /// that don't need the return value and just want to invoke a query for
73 /// its potential side-effect of emitting fatal errors.
74 ///
75 /// This can be more efficient than a normal query call, because if the
76 /// query's inputs are all green, the call can return immediately without
77 /// needing to obtain a value (by decoding one from disk or by executing
78 /// the query).
79 ///
80 /// (As with all query calls, execution is also skipped if the query result
81 /// is already cached in memory.)
82 ///
83 /// ## WARNING
84 /// A subsequent normal call to the same query might still cause it to be
85 /// executed! This can occur when the inputs are all green, but the query's
86 /// result is not cached on disk, so the query must be executed to obtain a
87 /// return value.
88 ///
89 /// Therefore, this call mode is not appropriate for callers that want to
90 /// ensure that the query is _never_ executed in the future.
91#[inline(always)]
92pub fn ensure_ok(self) -> TyCtxtEnsureOk<'tcx> {
93TyCtxtEnsureOk { tcx: self }
94 }
9596/// This is a variant of `ensure_ok` only usable with queries that return
97 /// `Result<_, ErrorGuaranteed>`. Queries calls through this function will
98 /// return `Result<(), ErrorGuaranteed>`. I.e. the error status is returned
99 /// but nothing else. As with `ensure_ok`, this can be more efficient than
100 /// a normal query call.
101#[inline(always)]
102pub fn ensure_result(self) -> TyCtxtEnsureResult<'tcx> {
103TyCtxtEnsureResult { tcx: self }
104 }
105106/// Wrapper that calls queries where callers don't need the return value and
107 /// just want to guarantee that the query won't be executed in the future.
108 ///
109 /// This is useful for queries that read from a [`Steal`] value, to ensure
110 /// that they are executed before the query that will steal the value.
111 ///
112 /// Currently this causes the query to be executed normally, but this behavior may change.
113 ///
114 /// [`Steal`]: rustc_data_structures::steal::Steal
115#[inline(always)]
116pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> {
117TyCtxtEnsureDone { tcx: self }
118 }
119}
120121/// Checks whether there is already a value for this key in the in-memory
122/// query cache, returning that value if present.
123///
124/// (Also performs some associated bookkeeping, if a value was found.)
125#[inline(always)]
126fn try_get_cached<'tcx, C>(tcx: TyCtxt<'tcx>, cache: &C, key: C::Key) -> Option<C::Value>
127where
128C: QueryCache,
129{
130match cache.lookup(&key) {
131Some((value, index)) => {
132tcx.prof.query_cache_hit(index.into());
133tcx.dep_graph.read_index(index);
134Some(value)
135 }
136None => None,
137 }
138}
139140/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)`
141/// for all queries.
142#[inline(always)]
143pub(crate) fn query_get_at<'tcx, C>(
144 tcx: TyCtxt<'tcx>,
145 span: Span,
146 query: &'tcx QueryVTable<'tcx, C>,
147 key: C::Key,
148) -> C::Value
149where
150C: QueryCache,
151{
152match try_get_cached(tcx, &query.cache, key) {
153Some(value) => value,
154None => (query.execute_query_fn)(tcx, span, key, QueryMode::Get).unwrap(),
155 }
156}
157158/// Implementation of `tcx.ensure_ok().$query(..)` for all queries.
159#[inline]
160pub(crate) fn query_ensure_ok<'tcx, C>(
161 tcx: TyCtxt<'tcx>,
162 query: &'tcx QueryVTable<'tcx, C>,
163 key: C::Key,
164) where
165C: QueryCache,
166{
167match try_get_cached(tcx, &query.cache, key) {
168Some(_value) => {}
169None => {
170 (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::EnsureOk);
171 }
172 }
173}
174175/// Implementation of `tcx.ensure_result().$query(..)` for queries that
176/// return `Result<_, ErrorGuaranteed>`.
177#[inline]
178pub(crate) fn query_ensure_result<'tcx, C, T>(
179 tcx: TyCtxt<'tcx>,
180 query: &'tcx QueryVTable<'tcx, C>,
181 key: C::Key,
182) -> Result<(), ErrorGuaranteed>
183where
184C: QueryCache<Value = Erased<Result<T, ErrorGuaranteed>>>,
185Result<T, ErrorGuaranteed>: Erasable,
186{
187let convert = |value: Erased<Result<T, ErrorGuaranteed>>| -> Result<(), ErrorGuaranteed> {
188match erase::restore_val(value) {
189Ok(_) => Ok(()),
190Err(guar) => Err(guar),
191 }
192 };
193194match try_get_cached(tcx, &query.cache, key) {
195Some(value) => convert(value),
196None => {
197match (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::EnsureOk) {
198// We executed the query. Convert the successful result.
199Some(res) => convert(res),
200201// Reaching here means we didn't execute the query, but we can just assume the
202 // query succeeded, because it was green in the incremental cache. If it is green,
203 // that means that the previous compilation that wrote to the incremental cache
204 // compiles successfully. That is only possible if the cache entry was `Ok(())`, so
205 // we emit that here, without actually encoding the `Result` in the cache or
206 // loading it from there.
207None => Ok(()),
208 }
209 }
210 }
211}
212213/// "Feeds" a feedable query by adding a given key/value pair to its in-memory cache.
214/// Called by macro-generated methods of [`rustc_middle::ty::TyCtxtFeed`].
215pub(crate) fn query_feed<'tcx, C>(
216 tcx: TyCtxt<'tcx>,
217 query: &'tcx QueryVTable<'tcx, C>,
218 key: C::Key,
219 value: C::Value,
220) where
221C: QueryCache,
222 C::Key: DepNodeKey<'tcx>,
223{
224let format_value = query.format_value;
225226// Check whether the in-memory cache already has a value for this key.
227match try_get_cached(tcx, &query.cache, key) {
228Some(old) => {
229// The query already has a cached value for this key.
230 // That's OK if both values are the same, i.e. they have the same hash,
231 // so now we check their hashes.
232if let Some(hash_value_fn) = query.hash_value_fn {
233let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| {
234 (hash_value_fn(hcx, &old), hash_value_fn(hcx, &value))
235 });
236if old_hash != value_hash {
237// We have an inconsistency. This can happen if one of the two
238 // results is tainted by errors. In this case, delay a bug to
239 // ensure compilation is doomed, and keep the `old` value.
240tcx.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!(
241"Trying to feed an already recorded value for query {query:?} key={key:?}:\n\
242 old value: {old}\nnew value: {value}",
243 old = format_value(&old),
244 value = format_value(&value),
245 ));
246 }
247 } else {
248// The query is `no_hash`, so we have no way to perform a sanity check.
249 // If feeding the same value multiple times needs to be supported,
250 // the query should not be marked `no_hash`.
251crate::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!(
252"Trying to feed an already recorded value for query {query:?} key={key:?}:\n\
253 old value: {old}\nnew value: {value}",
254 old = format_value(&old),
255 value = format_value(&value),
256 )257 }
258 }
259None => {
260// There is no cached value for this key, so feed the query by
261 // adding the provided value to the cache.
262let dep_node = dep_graph::DepNode::construct(tcx, query.dep_kind, &key);
263let dep_node_index = tcx.dep_graph.with_feed_task(
264dep_node,
265tcx,
266&value,
267query.hash_value_fn,
268query.format_value,
269 );
270query.cache.complete(key, value, dep_node_index);
271 }
272 }
273}