rustc_middle/ty/context/
tls.rs

1use std::{mem, ptr};
2
3use rustc_data_structures::sync::{self, Lock};
4use rustc_errors::DiagInner;
5use thin_vec::ThinVec;
6
7use super::{GlobalCtxt, TyCtxt};
8use crate::dep_graph::TaskDepsRef;
9use crate::query::plumbing::QueryJobId;
10
11/// This is the implicit state of rustc. It contains the current
12/// `TyCtxt` and query. It is updated when creating a local interner or
13/// executing a new query. Whenever there's a `TyCtxt` value available
14/// you should also have access to an `ImplicitCtxt` through the functions
15/// in this module.
16#[derive(Clone)]
17pub struct ImplicitCtxt<'a, 'tcx> {
18    /// The current `TyCtxt`.
19    pub tcx: TyCtxt<'tcx>,
20
21    /// The current query job, if any. This is updated by `JobOwner::start` in
22    /// `ty::query::plumbing` when executing a query.
23    pub query: Option<QueryJobId>,
24
25    /// Where to store diagnostics for the current query job, if any.
26    /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
27    pub diagnostics: Option<&'a Lock<ThinVec<DiagInner>>>,
28
29    /// Used to prevent queries from calling too deeply.
30    pub query_depth: usize,
31
32    /// The current dep graph task. This is used to add dependencies to queries
33    /// when executing them.
34    pub task_deps: TaskDepsRef<'a>,
35}
36
37impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> {
38    pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self {
39        let tcx = TyCtxt { gcx };
40        ImplicitCtxt {
41            tcx,
42            query: None,
43            diagnostics: None,
44            query_depth: 0,
45            task_deps: TaskDepsRef::Ignore,
46        }
47    }
48}
49
50// Import the thread-local variable from Rayon, which is preserved for Rayon jobs.
51use rayon_core::tlv::TLV;
52
53#[inline]
54fn erase(context: &ImplicitCtxt<'_, '_>) -> *const () {
55    context as *const _ as *const ()
56}
57
58#[inline]
59unsafe fn downcast<'a, 'tcx>(context: *const ()) -> &'a ImplicitCtxt<'a, 'tcx> {
60    unsafe { &*(context as *const ImplicitCtxt<'a, 'tcx>) }
61}
62
63/// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`.
64#[inline]
65pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R
66where
67    F: FnOnce() -> R,
68{
69    TLV.with(|tlv| {
70        let old = tlv.replace(erase(context));
71        let _reset = rustc_data_structures::defer(move || tlv.set(old));
72        f()
73    })
74}
75
76/// Allows access to the current `ImplicitCtxt` in a closure if one is available.
77#[inline]
78#[track_caller]
79pub fn with_context_opt<F, R>(f: F) -> R
80where
81    F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R,
82{
83    let context = TLV.get();
84    if context.is_null() {
85        f(None)
86    } else {
87        // We could get an `ImplicitCtxt` pointer from another thread.
88        // Ensure that `ImplicitCtxt` is `DynSync`.
89        sync::assert_dyn_sync::<ImplicitCtxt<'_, '_>>();
90
91        unsafe { f(Some(downcast(context))) }
92    }
93}
94
95/// Allows access to the current `ImplicitCtxt`.
96/// Panics if there is no `ImplicitCtxt` available.
97#[inline]
98pub fn with_context<F, R>(f: F) -> R
99where
100    F: for<'a, 'tcx> FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R,
101{
102    with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
103}
104
105/// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument
106/// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime
107/// as the `TyCtxt` passed in.
108/// This will panic if you pass it a `TyCtxt` which is different from the current
109/// `ImplicitCtxt`'s `tcx` field.
110#[inline]
111pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R
112where
113    F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R,
114{
115    with_context(|context| {
116        // The two gcx have different invariant lifetimes, so we need to erase them for the comparison.
117        assert!(ptr::eq(
118            context.tcx.gcx as *const _ as *const (),
119            tcx.gcx as *const _ as *const ()
120        ));
121
122        let context: &ImplicitCtxt<'_, '_> = unsafe { mem::transmute(context) };
123
124        f(context)
125    })
126}
127
128/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
129/// Panics if there is no `ImplicitCtxt` available.
130#[inline]
131pub fn with<F, R>(f: F) -> R
132where
133    F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R,
134{
135    with_context(|context| f(context.tcx))
136}
137
138/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
139/// The closure is passed None if there is no `ImplicitCtxt` available.
140#[inline]
141#[track_caller]
142pub fn with_opt<F, R>(f: F) -> R
143where
144    F: for<'tcx> FnOnce(Option<TyCtxt<'tcx>>) -> R,
145{
146    with_context_opt(
147        #[track_caller]
148        |opt_context| f(opt_context.map(|context| context.tcx)),
149    )
150}