diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index 8c7e49b51f9b2..714c98469db4d 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -9,8 +9,7 @@ //! The functions in this file should fall back to the default set in their //! origin crate when the `TyCtxt` is not present in TLS. -use rustc_errors::{Diagnostic, TRACK_DIAGNOSTIC}; -use rustc_middle::dep_graph::{DepNodeExt, TaskDepsRef}; +use rustc_middle::dep_graph::DepNodeExt; use rustc_middle::ty::tls; use rustc_query_system::dep_graph::dep_node::default_dep_kind_debug; use rustc_query_system::dep_graph::{DepContext, DepKind, DepNode}; @@ -26,26 +25,6 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { }) } -/// This is a callback from `rustc_errors` as it cannot access the implicit state -/// in `rustc_middle` otherwise. It is used when diagnostic messages are -/// emitted and stores them in the current query, if there is one. -fn track_diagnostic(diagnostic: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { - tls::with_context_opt(|icx| { - if let Some(icx) = icx { - if let Some(diagnostics) = icx.diagnostics { - diagnostics.lock().extend(Some(diagnostic.clone())); - } - - // Diagnostics are tracked, we can ignore the dependency. - let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; - return tls::enter_context(&icx, move || (*f)(diagnostic)); - } - - // In any other case, invoke diagnostics anyway. - (*f)(diagnostic); - }) -} - /// This is a callback from `rustc_hir` as it cannot access the implicit state /// in `rustc_middle` otherwise. fn def_id_debug(def_id: rustc_hir::def_id::DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -103,5 +82,5 @@ pub fn setup_callbacks() { .swap(&(dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); rustc_query_system::dep_graph::dep_node::DEP_NODE_DEBUG .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _)); - TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _)); + rustc_errors::TRACK_DIAGNOSTIC.swap(&(rustc_query_system::tls::track_diagnostic as _)); } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 32fba6ade88df..6b8deac90fe5b 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -450,11 +450,10 @@ pub fn try_print_query_stack( // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `DiagCtxt` may be in a weird // state if it was responsible for triggering the panic. - let i = ty::tls::with_context_opt(|icx| { - if let Some(icx) = icx { + let i = ty::tls::with_opt(|tcx| { + if let Some(tcx) = tcx { ty::print::with_no_queries!(print_query_stack( - QueryCtxt::new(icx.tcx), - icx.query, + QueryCtxt::new(tcx), dcx, num_frames, file, diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index dc0da165af67d..256a06a703f22 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -1,4 +1,4 @@ -use crate::ty::{self, TyCtxt}; +use crate::ty::TyCtxt; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_query_system::ich::StableHashingContext; use rustc_session::Session; @@ -24,27 +24,6 @@ pub type DepKindStruct<'tcx> = rustc_query_system::dep_graph::DepKindStruct(task_deps: TaskDepsRef<'_>, op: OP) -> R - where - OP: FnOnce() -> R, - { - ty::tls::with_context(|icx| { - let icx = ty::tls::ImplicitCtxt { task_deps, ..icx.clone() }; - - ty::tls::enter_context(&icx, op) - }) - } - - fn read_deps(op: OP) - where - OP: for<'a> FnOnce(TaskDepsRef<'a>), - { - ty::tls::with_context_opt(|icx| { - let Some(icx) = icx else { return }; - op(icx.task_deps) - }) - } - const DEP_KIND_NULL: DepKind = dep_kinds::Null; const DEP_KIND_RED: DepKind = dep_kinds::Red; const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index a41d4f1ad5897..64f07fd401bf9 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -16,7 +16,6 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::hir_id::OwnerId; use rustc_query_system::dep_graph::DepNodeIndex; use rustc_query_system::dep_graph::SerializedDepNodeIndex; -pub(crate) use rustc_query_system::query::QueryJobId; use rustc_query_system::query::*; use rustc_query_system::HandleCycleError; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 6807eacb7f177..74079fdc9cbc3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2,8 +2,6 @@ #![allow(rustc::usage_of_ty_tykind)] -pub mod tls; - use crate::arena::Arena; use crate::dep_graph::{DepGraph, DepKindStruct}; use crate::infer::canonical::{CanonicalParamEnvCache, CanonicalVarInfo, CanonicalVarInfos}; @@ -662,8 +660,7 @@ impl<'tcx> GlobalCtxt<'tcx> { where F: FnOnce(TyCtxt<'tcx>) -> R, { - let icx = tls::ImplicitCtxt::new(self); - tls::enter_context(&icx, || f(icx.tcx)) + rustc_query_system::tls::create_and_enter_context(self, || f(TyCtxt { gcx: self })) } pub fn finish(&self) -> FileEncodeResult { @@ -671,6 +668,32 @@ impl<'tcx> GlobalCtxt<'tcx> { } } +pub mod tls { + use super::TyCtxt; + + /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. + /// Panics if there is no `ImplicitCtxt` available. + #[inline] + pub fn with(f: F) -> R + where + F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R, + { + rustc_query_system::tls::with(|gcx| f(TyCtxt { gcx: unsafe { &*gcx.cast() } })) + } + + /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. + /// The closure is passed None if there is no `ImplicitCtxt` available. + #[inline] + pub fn with_opt(f: F) -> R + where + F: for<'tcx> FnOnce(Option>) -> R, + { + rustc_query_system::tls::with_opt(|opt_context| { + f(opt_context.map(|gcx| TyCtxt { gcx: unsafe { &*gcx.cast() } })) + }) + } +} + impl<'tcx> TyCtxt<'tcx> { /// Expects a body and returns its codegen attributes. /// diff --git a/compiler/rustc_middle/src/ty/context/tls.rs b/compiler/rustc_middle/src/ty/context/tls.rs deleted file mode 100644 index 9de77b9fda11a..0000000000000 --- a/compiler/rustc_middle/src/ty/context/tls.rs +++ /dev/null @@ -1,155 +0,0 @@ -use super::{GlobalCtxt, TyCtxt}; - -use crate::dep_graph::TaskDepsRef; -use crate::query::plumbing::QueryJobId; -use rustc_data_structures::sync::{self, Lock}; -use rustc_errors::Diagnostic; -#[cfg(not(parallel_compiler))] -use std::cell::Cell; -use std::mem; -use std::ptr; -use thin_vec::ThinVec; - -/// This is the implicit state of rustc. It contains the current -/// `TyCtxt` and query. It is updated when creating a local interner or -/// executing a new query. Whenever there's a `TyCtxt` value available -/// you should also have access to an `ImplicitCtxt` through the functions -/// in this module. -#[derive(Clone)] -pub struct ImplicitCtxt<'a, 'tcx> { - /// The current `TyCtxt`. - pub tcx: TyCtxt<'tcx>, - - /// The current query job, if any. This is updated by `JobOwner::start` in - /// `ty::query::plumbing` when executing a query. - pub query: Option, - - /// Where to store diagnostics for the current query job, if any. - /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query. - pub diagnostics: Option<&'a Lock>>, - - /// Used to prevent queries from calling too deeply. - pub query_depth: usize, - - /// The current dep graph task. This is used to add dependencies to queries - /// when executing them. - pub task_deps: TaskDepsRef<'a>, -} - -impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> { - pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self { - let tcx = TyCtxt { gcx }; - ImplicitCtxt { - tcx, - query: None, - diagnostics: None, - query_depth: 0, - task_deps: TaskDepsRef::Ignore, - } - } -} - -// Import the thread-local variable from Rayon, which is preserved for Rayon jobs. -#[cfg(parallel_compiler)] -use rayon_core::tlv::TLV; - -// Otherwise define our own -#[cfg(not(parallel_compiler))] -thread_local! { - /// A thread local variable that stores a pointer to the current `ImplicitCtxt`. - static TLV: Cell<*const ()> = const { Cell::new(ptr::null()) }; -} - -#[inline] -fn erase(context: &ImplicitCtxt<'_, '_>) -> *const () { - context as *const _ as *const () -} - -#[inline] -unsafe fn downcast<'a, 'tcx>(context: *const ()) -> &'a ImplicitCtxt<'a, 'tcx> { - &*(context as *const ImplicitCtxt<'a, 'tcx>) -} - -/// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`. -#[inline] -pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R -where - F: FnOnce() -> R, -{ - TLV.with(|tlv| { - let old = tlv.replace(erase(context)); - let _reset = rustc_data_structures::defer(move || tlv.set(old)); - f() - }) -} - -/// Allows access to the current `ImplicitCtxt` in a closure if one is available. -#[inline] -pub fn with_context_opt(f: F) -> R -where - F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R, -{ - let context = TLV.get(); - if context.is_null() { - f(None) - } else { - // We could get an `ImplicitCtxt` pointer from another thread. - // Ensure that `ImplicitCtxt` is `DynSync`. - sync::assert_dyn_sync::>(); - - unsafe { f(Some(downcast(context))) } - } -} - -/// Allows access to the current `ImplicitCtxt`. -/// Panics if there is no `ImplicitCtxt` available. -#[inline] -pub fn with_context(f: F) -> R -where - F: for<'a, 'tcx> FnOnce(&ImplicitCtxt<'a, 'tcx>) -> 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] -pub fn with(f: F) -> R -where - F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R, -{ - with_context(|context| f(context.tcx)) -} - -/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. -/// The closure is passed None if there is no `ImplicitCtxt` available. -#[inline] -pub fn with_opt(f: F) -> R -where - F: for<'tcx> FnOnce(Option>) -> R, -{ - with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx))) -} diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index a827717d9bbd1..899b81f9198c8 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -6,8 +6,6 @@ use crate::rustc_middle::dep_graph::DepContext; use crate::rustc_middle::ty::TyEncoder; use crate::QueryConfigRestored; use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher}; -use rustc_data_structures::sync::Lock; -use rustc_errors::Diagnostic; use rustc_index::Idx; use rustc_middle::dep_graph::dep_kinds; @@ -17,7 +15,6 @@ use rustc_middle::dep_graph::{ use rustc_middle::query::on_disk_cache::AbsoluteBytePos; use rustc_middle::query::on_disk_cache::{CacheDecoder, CacheEncoder, EncodedDepNodeIndex}; use rustc_middle::query::Key; -use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, print::with_no_queries, TyCtxt}; use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; use rustc_query_system::ich::StableHashingContext; @@ -31,7 +28,6 @@ use rustc_serialize::Encodable; use rustc_session::Limit; use rustc_span::def_id::LOCAL_CRATE; use std::num::NonZeroU64; -use thin_vec::ThinVec; #[derive(Copy, Clone)] pub struct QueryCtxt<'tcx> { @@ -75,11 +71,6 @@ impl QueryContext for QueryCtxt<'_> { ) } - #[inline] - fn current_query_job(self) -> Option { - tls::with_related_context(self.tcx, |icx| icx.query) - } - fn collect_active_jobs(self) -> QueryMap { let mut jobs = QueryMap::default(); @@ -119,37 +110,8 @@ impl QueryContext for QueryCtxt<'_> { } } - /// Executes a job by changing the `ImplicitCtxt` to point to the - /// new query job while it executes. It returns the diagnostics - /// captured during execution and the actual result. - #[inline(always)] - fn start_query( - self, - token: QueryJobId, - depth_limit: bool, - diagnostics: Option<&Lock>>, - 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(self.tcx, move |current_icx| { - if depth_limit && !self.recursion_limit().value_within_limit(current_icx.query_depth) { - self.depth_limit_error(token); - } - - // Update the `ImplicitCtxt` to point to our new query job. - let new_icx = ImplicitCtxt { - tcx: self.tcx, - query: Some(token), - diagnostics, - query_depth: current_icx.query_depth + depth_limit as usize, - task_deps: current_icx.task_deps, - }; - - // Use the `ImplicitCtxt` while we execute the query. - tls::enter_context(&new_icx, compute) - }) + fn recursion_limit(self) -> Limit { + self.tcx.recursion_limit() } fn depth_limit_error(self, job: QueryJobId) { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 1f09de0ed70c7..33b698379634a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -202,7 +202,7 @@ impl DepGraph { pub fn assert_ignored(&self) { if let Some(..) = self.data { - D::read_deps(|task_deps| { + crate::tls::read_deps(|task_deps| { assert_matches!( task_deps, TaskDepsRef::Ignore, @@ -216,7 +216,7 @@ impl DepGraph { where OP: FnOnce() -> R, { - D::with_deps(TaskDepsRef::Ignore, op) + crate::tls::with_deps(TaskDepsRef::Ignore, op) } /// Used to wrap the deserialization of a query result from disk, @@ -269,7 +269,7 @@ impl DepGraph { where OP: FnOnce() -> R, { - D::with_deps(TaskDepsRef::Forbid, op) + crate::tls::with_deps(TaskDepsRef::Forbid, op) } #[inline(always)] @@ -352,7 +352,7 @@ impl DepGraphData { - dep-node: {key:?}" ); - let with_deps = |task_deps| D::with_deps(task_deps, || task(cx, arg)); + let with_deps = |task_deps| crate::tls::with_deps(task_deps, || task(cx, arg)); let (result, edges) = if cx.dep_context().is_eval_always(key.kind) { (with_deps(TaskDepsRef::EvalAlways), EdgesVec::new()) } else { @@ -409,7 +409,7 @@ impl DepGraphData { debug_assert!(!cx.is_eval_always(dep_kind)); let task_deps = Lock::new(TaskDeps::default()); - let result = D::with_deps(TaskDepsRef::Allow(&task_deps), op); + let result = crate::tls::with_deps(TaskDepsRef::Allow(&task_deps), op); let task_deps = task_deps.into_inner(); let task_deps = task_deps.reads; @@ -460,7 +460,7 @@ impl DepGraph { #[inline] pub fn read_index(&self, dep_node_index: DepNodeIndex) { if let Some(ref data) = self.data { - D::read_deps(|task_deps| { + crate::tls::read_deps(|task_deps| { let mut task_deps = match task_deps { TaskDepsRef::Allow(deps) => deps.lock(), TaskDepsRef::EvalAlways => { @@ -568,7 +568,7 @@ impl DepGraph { } let mut edges = EdgesVec::new(); - D::read_deps(|task_deps| match task_deps { + crate::tls::read_deps(|task_deps| match task_deps { TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()), TaskDepsRef::EvalAlways => { edges.push(DepNodeIndex::FOREVER_RED_NODE); diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index feb69ecd07867..ee8f3df4c289b 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -81,16 +81,6 @@ pub trait DepContext: Copy { } pub trait Deps { - /// Execute the operation with provided dependencies. - fn with_deps(deps: TaskDepsRef<'_>, op: OP) -> R - where - OP: FnOnce() -> R; - - /// Access dependencies from current implicit context. - fn read_deps(op: OP) - where - OP: for<'a> FnOnce(TaskDepsRef<'a>); - /// We use this for most things when incr. comp. is turned off. const DEP_KIND_NULL: DepKind; diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index 9b66b9a48d905..0b02eaeb0c99e 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -19,6 +19,7 @@ pub mod dep_graph; mod error; pub mod ich; pub mod query; +pub mod tls; mod values; pub use error::HandleCycleError; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 3ef9de7da74b2..923842f47b367 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -607,11 +607,12 @@ pub fn report_cycle<'a>( pub fn print_query_stack( qcx: Qcx, - mut current_query: Option, dcx: &DiagCtxt, num_frames: Option, mut file: Option, ) -> usize { + let mut current_query = crate::tls::current_query_job(); + // Be careful relying on global state here: this code is called from // a panic hook, which means that the global `DiagCtxt` may be in a weird // state if it was responsible for triggering the panic. diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 9ff04c4e910d9..fbdd4763f4802 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -19,9 +19,9 @@ pub use self::config::{HashResult, QueryConfig}; use crate::dep_graph::DepKind; use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; use rustc_data_structures::stable_hasher::Hash64; -use rustc_data_structures::sync::Lock; use rustc_errors::Diagnostic; use rustc_hir::def::DefKind; +use rustc_session::Limit; use rustc_span::def_id::DefId; use rustc_span::Span; use thin_vec::ThinVec; @@ -106,9 +106,6 @@ impl QuerySideEffects { pub trait QueryContext: HasDepContext { fn next_job_id(self) -> QueryJobId; - /// Get the query information from the TLS context. - fn current_query_job(self) -> Option; - fn collect_active_jobs(self) -> QueryMap; /// Load side effects associated to the node in the previous session. @@ -124,16 +121,6 @@ pub trait QueryContext: HasDepContext { side_effects: QuerySideEffects, ); - /// Executes a job by changing the `ImplicitCtxt` to point to the - /// new query job while it executes. It returns the diagnostics - /// captured during execution and the actual result. - fn start_query( - self, - token: QueryJobId, - depth_limit: bool, - diagnostics: Option<&Lock>>, - compute: impl FnOnce() -> R, - ) -> R; - + fn recursion_limit(self) -> Limit; fn depth_limit_error(self, job: QueryJobId); } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 3bb2cc5634fe8..1080a823f8071 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -249,8 +249,11 @@ where Q: QueryConfig, Qcx: QueryContext, { - let error = - try_execute.find_cycle_in_stack(qcx.collect_active_jobs(), &qcx.current_query_job(), span); + let error = try_execute.find_cycle_in_stack( + qcx.collect_active_jobs(), + &crate::tls::current_query_job(), + span, + ); (mk_cycle(query, qcx, error), None) } @@ -334,7 +337,7 @@ where } } - let current_job_id = qcx.current_query_job(); + let current_job_id = crate::tls::current_query_job(); match state_lock.entry(key) { Entry::Vacant(entry) => { @@ -468,7 +471,8 @@ where } let prof_timer = qcx.dep_context().profiler().query_provider(); - let result = qcx.start_query(job_id, query.depth_limit(), None, || query.compute(qcx, key)); + let result = + crate::tls::start_query(qcx, job_id, query.depth_limit(), None, || query.compute(qcx, key)); let dep_node_index = qcx.dep_context().dep_graph().next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); @@ -505,7 +509,7 @@ where // 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) = qcx.start_query(job_id, false, None, || { + if let Some(ret) = crate::tls::start_query(qcx, job_id, query.depth_limit(), None, || { try_load_from_disk_and_cache_in_memory(query, dep_graph_data, qcx, &key, dep_node) }) { return ret; @@ -516,7 +520,7 @@ where let diagnostics = Lock::new(ThinVec::new()); let (result, dep_node_index) = - qcx.start_query(job_id, query.depth_limit(), Some(&diagnostics), || { + crate::tls::start_query(qcx, job_id, query.depth_limit(), Some(&diagnostics), || { if query.anon() { return dep_graph_data.with_anon_task(*qcx.dep_context(), query.dep_kind(), || { query.compute(qcx, key) diff --git a/compiler/rustc_query_system/src/tls.rs b/compiler/rustc_query_system/src/tls.rs new file mode 100644 index 0000000000000..67c0e79c3b216 --- /dev/null +++ b/compiler/rustc_query_system/src/tls.rs @@ -0,0 +1,207 @@ +use crate::dep_graph::TaskDepsRef; +use crate::query::{QueryContext, QueryJobId}; +use rustc_data_structures::sync::{self, Lock}; +use rustc_errors::Diagnostic; + +#[cfg(not(parallel_compiler))] +use std::cell::Cell; +use thin_vec::ThinVec; + +#[derive(Copy, Clone)] +struct GCXPointer(*const ()); + +unsafe impl sync::DynSync for GCXPointer {} + +/// This is the implicit state of rustc. It contains the current +/// `TyCtxt` and query. It is updated when creating a local interner or +/// executing a new query. Whenever there's a `TyCtxt` value available +/// you should also have access to an `ImplicitCtxt` through the functions +/// in this module. +#[derive(Clone)] +struct ImplicitCtxt<'a> { + /// Pointer to the current `GlobalCtxt`. + gcx: GCXPointer, + + /// The current query job, if any. This is updated by `JobOwner::start` in + /// `ty::query::plumbing` when executing a query. + query: Option, + + /// Where to store diagnostics for the current query job, if any. + /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query. + diagnostics: Option<&'a Lock>>, + + /// Used to prevent queries from calling too deeply. + query_depth: usize, + + /// The current dep graph task. This is used to add dependencies to queries + /// when executing them. + task_deps: TaskDepsRef<'a>, +} + +pub fn create_and_enter_context(gcx: &GCX, f: impl FnOnce() -> R) -> R { + let icx = ImplicitCtxt { + gcx: GCXPointer((gcx as *const GCX).cast()), + query: None, + diagnostics: None, + query_depth: 0, + task_deps: TaskDepsRef::Ignore, + }; + enter_context(&icx, f) +} + +// Import the thread-local variable from Rayon, which is preserved for Rayon jobs. +#[cfg(parallel_compiler)] +use rayon_core::tlv::TLV; + +// Otherwise define our own +#[cfg(not(parallel_compiler))] +thread_local! { + /// A thread local variable that stores a pointer to the current `ImplicitCtxt`. + static TLV: Cell<*const ()> = const { Cell::new(ptr::null()) }; +} + +#[inline] +fn erase(context: &ImplicitCtxt<'_>) -> *const () { + context as *const _ as *const () +} + +#[inline] +unsafe fn downcast<'a>(context: *const ()) -> &'a ImplicitCtxt<'a> { + &*(context as *const ImplicitCtxt<'a>) +} + +/// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`. +#[inline] +fn enter_context<'a, F, R>(context: &ImplicitCtxt<'a>, f: F) -> R +where + F: FnOnce() -> R, +{ + TLV.with(|tlv| { + let old = tlv.replace(erase(context)); + let _reset = rustc_data_structures::defer(move || tlv.set(old)); + f() + }) +} + +/// Allows access to the current `ImplicitCtxt` in a closure if one is available. +#[inline] +fn with_context_opt(f: F) -> R +where + F: for<'a> FnOnce(Option<&ImplicitCtxt<'a>>) -> R, +{ + let context = TLV.get(); + if context.is_null() { + f(None) + } else { + // We could get an `ImplicitCtxt` pointer from another thread. + // Ensure that `ImplicitCtxt` is `DynSync`. + sync::assert_dyn_sync::>(); + + unsafe { f(Some(downcast(context))) } + } +} + +/// Allows access to the current `ImplicitCtxt`. +/// Panics if there is no `ImplicitCtxt` available. +#[inline] +fn with_context(f: F) -> R +where + F: for<'a> FnOnce(&ImplicitCtxt<'a>) -> R, +{ + with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls"))) +} + +/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. +/// Panics if there is no `ImplicitCtxt` available. +#[inline] +pub fn with(f: F) -> R +where + F: for<'tcx> FnOnce(*const ()) -> R, +{ + with_context(|context| f(context.gcx.0)) +} + +/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`. +/// The closure is passed None if there is no `ImplicitCtxt` available. +#[inline] +pub fn with_opt(f: F) -> R +where + F: for<'tcx> FnOnce(Option<*const ()>) -> R, +{ + with_context_opt(|opt_context| f(opt_context.map(|context| context.gcx.0))) +} + +/// This is a callback from `rustc_ast` as it cannot access the implicit state +/// in `rustc_middle` otherwise. It is used to when diagnostic messages are +/// emitted and stores them in the current query, if there is one. +pub fn track_diagnostic(diagnostic: Diagnostic, f: &mut dyn FnMut(Diagnostic)) { + with_context_opt(|icx| { + if let Some(icx) = icx { + if let Some(ref diagnostics) = icx.diagnostics { + diagnostics.lock().extend(Some(diagnostic.clone())); + } + + // Diagnostics are tracked, we can ignore the dependency. + let icx = ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; + return enter_context(&icx, move || (*f)(diagnostic)); + } + + // In any other case, invoke diagnostics anyway. + (*f)(diagnostic); + }) +} + +/// Execute the operation with provided dependencies. +pub fn with_deps(task_deps: TaskDepsRef<'_>, op: OP) -> R +where + OP: FnOnce() -> R, +{ + crate::tls::with_context(|icx| { + let icx = crate::tls::ImplicitCtxt { task_deps, ..icx.clone() }; + crate::tls::enter_context(&icx, op) + }) +} + +/// Access dependencies from current implicit context. +pub fn read_deps(op: OP) +where + OP: FnOnce(TaskDepsRef<'_>), +{ + crate::tls::with_context_opt(|icx| if let Some(icx) = icx { op(icx.task_deps) } else { return }) +} + +/// Get the query information from the TLS context. +#[inline(always)] +pub fn current_query_job() -> Option { + with_context_opt(|icx| icx?.query) +} + +/// Executes a job by changing the `ImplicitCtxt` to point to the +/// new query job while it executes. It returns the diagnostics +/// captured during execution and the actual result. +#[inline(always)] +pub fn start_query( + qcx: QCX, + token: QueryJobId, + depth_limit: bool, + diagnostics: Option<&Lock>>, + compute: impl FnOnce() -> R, +) -> R { + with_context(move |current_icx| { + if depth_limit && !qcx.recursion_limit().value_within_limit(current_icx.query_depth) { + qcx.depth_limit_error(token); + } + + // Update the `ImplicitCtxt` to point to our new query job. + let new_icx = ImplicitCtxt { + gcx: current_icx.gcx, + query: Some(token), + diagnostics, + query_depth: current_icx.query_depth + depth_limit as usize, + task_deps: current_icx.task_deps, + }; + + // Use the `ImplicitCtxt` while we execute the query. + enter_context(&new_icx, || rustc_data_structures::stack::ensure_sufficient_stack(compute)) + }) +}