Skip to main content

rustc_middle/ty/context/
tls.rs

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