Remove tls::with_related_context.

This function gets the current `ImplicitCtxt` and checks that its `tcx`
matches the passed-in `tcx`. It's an extra bit of sanity checking: when
you already have a `tcx`, and you need access to the non-`tcx` parts of
`ImplicitCtxt`, check that your `tcx` matches the one in `ImplicitCtxt`.

However, it's only used in two places: `start_query` and
`current_query_job`. The non-checked alternatives (`with_context`,
`with_context_opt`) are used in more places, including some where a
`tcx` is available. And things would have to go catastrophically wrong
for the check to fail -- e.g. if we somehow end up with multiple
`TyCtxt`s. In my opinion it's just an extra case to understand in
`tls.rs` that adds little value.

This commit removes it. This avoids the need for `tcx` parameters in a
couple of places. The commit also adjusts how `start_query` sets up its
`ImplicitCtxt` to more closely match how similar functions do it, i.e.
with `..icx.clone()` for the unchanged fields.
This commit is contained in:
Nicholas Nethercote
2026-03-03 09:42:13 +11:00
parent d2218f5f5c
commit e8a28e78a9
3 changed files with 18 additions and 50 deletions
@@ -1,5 +1,3 @@
use std::{mem, ptr};
use rustc_data_structures::sync;
use super::{GlobalCtxt, TyCtxt};
@@ -89,29 +87,6 @@ pub fn with_context<F, R>(f: F) -> R
with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
}
/// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument
/// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime
/// as the `TyCtxt` passed in.
/// This will panic if you pass it a `TyCtxt` which is different from the current
/// `ImplicitCtxt`'s `tcx` field.
#[inline]
pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R
where
F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R,
{
with_context(|context| {
// The two gcx have different invariant lifetimes, so we need to erase them for the comparison.
assert!(ptr::eq(
context.tcx.gcx as *const _ as *const (),
tcx.gcx as *const _ as *const ()
));
let context: &ImplicitCtxt<'_, '_> = unsafe { mem::transmute(context) };
f(context)
})
}
/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
/// Panics if there is no `ImplicitCtxt` available.
#[inline]
+5 -6
View File
@@ -228,7 +228,7 @@ fn cycle_error<'tcx, C: QueryCache>(
.ok()
.expect("failed to collect active queries");
let error = find_cycle_in_stack(try_execute, job_map, &current_query_job(tcx), span);
let error = find_cycle_in_stack(try_execute, job_map, &current_query_job(), span);
(mk_cycle(query, tcx, error.lift()), None)
}
@@ -305,7 +305,7 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>(
}
}
let current_job_id = current_query_job(tcx);
let current_job_id = current_query_job();
match state_lock.entry(key_hash, equivalent_key(&key), |(k, _)| sharded::make_hash(k)) {
Entry::Vacant(entry) => {
@@ -422,8 +422,7 @@ fn execute_job_non_incr<'tcx, C: QueryCache>(
let prof_timer = tcx.prof.query_provider();
// Call the query provider.
let value =
start_query(tcx, job_id, query.depth_limit, || (query.invoke_provider_fn)(tcx, key));
let value = start_query(job_id, query.depth_limit, || (query.invoke_provider_fn)(tcx, key));
let dep_node_index = tcx.dep_graph.next_virtual_depnode_index();
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
@@ -457,7 +456,7 @@ fn execute_job_incr<'tcx, C: QueryCache>(
// The diagnostics for this query will be promoted to the current session during
// `try_mark_green()`, so we can ignore them here.
if let Some(ret) = start_query(tcx, job_id, false, || try {
if let Some(ret) = start_query(job_id, false, || try {
let (prev_index, dep_node_index) = dep_graph_data.try_mark_green(tcx, dep_node)?;
let value = load_from_disk_or_invoke_provider_green(
tcx,
@@ -476,7 +475,7 @@ fn execute_job_incr<'tcx, C: QueryCache>(
let prof_timer = tcx.prof.query_provider();
let (result, dep_node_index) = start_query(tcx, job_id, query.depth_limit, || {
let (result, dep_node_index) = start_query(job_id, query.depth_limit, || {
if query.anon {
// Call the query provider inside an anon task.
return dep_graph_data.with_anon_task_inner(tcx, query.dep_kind, || {
+13 -19
View File
@@ -61,37 +61,31 @@ pub(crate) fn next_job_id<'tcx>(tcx: TyCtxt<'tcx>) -> QueryJobId {
}
#[inline]
pub(crate) fn current_query_job<'tcx>(tcx: TyCtxt<'tcx>) -> Option<QueryJobId> {
tls::with_related_context(tcx, |icx| icx.query)
pub(crate) fn current_query_job() -> Option<QueryJobId> {
tls::with_context(|icx| icx.query)
}
/// Executes a job by changing the `ImplicitCtxt` to point to the
/// new query job while it executes.
/// Executes a job by changing the `ImplicitCtxt` to point to the new query job while it executes.
#[inline(always)]
pub(crate) fn start_query<'tcx, R>(
tcx: TyCtxt<'tcx>,
token: QueryJobId,
pub(crate) fn start_query<R>(
job_id: QueryJobId,
depth_limit: bool,
compute: impl FnOnce() -> R,
) -> R {
// The `TyCtxt` stored in TLS has the same global interner lifetime
// as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
// when accessing the `ImplicitCtxt`.
tls::with_related_context(tcx, move |current_icx| {
if depth_limit && !tcx.recursion_limit().value_within_limit(current_icx.query_depth) {
depth_limit_error(tcx, token);
tls::with_context(move |icx| {
if depth_limit && !icx.tcx.recursion_limit().value_within_limit(icx.query_depth) {
depth_limit_error(icx.tcx, job_id);
}
// Update the `ImplicitCtxt` to point to our new query job.
let new_icx = ImplicitCtxt {
tcx,
query: Some(token),
query_depth: current_icx.query_depth + depth_limit as usize,
task_deps: current_icx.task_deps,
let icx = ImplicitCtxt {
query: Some(job_id),
query_depth: icx.query_depth + if depth_limit { 1 } else { 0 },
..icx.clone()
};
// Use the `ImplicitCtxt` while we execute the query.
tls::enter_context(&new_icx, compute)
tls::enter_context(&icx, compute)
})
}