solar_interface/
globals.rs

1use crate::SourceMap;
2use solar_data_structures::{defer, sync::Lock};
3use std::sync::Arc;
4
5scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals);
6
7/// Per-session global variables.
8///
9/// This struct is stored in thread-local storage in such a way that it is accessible without any
10/// kind of handle to all threads within the compilation session, but is not accessible outside the
11/// session.
12pub struct SessionGlobals {
13    pub(crate) symbol_interner: crate::symbol::Interner,
14    /// A reference to the source map in the `Session`. It's an `Option`
15    /// because it can't be initialized until `Session` is created, which
16    /// happens after `SessionGlobals`. `set_source_map` does the
17    /// initialization.
18    ///
19    /// This field should only be used in places where the `Session` is truly
20    /// not available, such as `<Span as Debug>::fmt`.
21    pub(crate) source_map: Lock<Option<Arc<SourceMap>>>,
22}
23
24impl Default for SessionGlobals {
25    fn default() -> Self {
26        Self::new()
27    }
28}
29
30impl SessionGlobals {
31    /// Creates a new session globals object.
32    pub fn new() -> Self {
33        Self { symbol_interner: crate::symbol::Interner::fresh(), source_map: Lock::new(None) }
34    }
35
36    /// Sets this instance as the global instance for the duration of the closure.
37    #[inline]
38    pub fn set<R>(&self, f: impl FnOnce() -> R) -> R {
39        if SESSION_GLOBALS.is_set() {
40            panic_overwrite();
41        }
42        SESSION_GLOBALS.set(self, f)
43    }
44
45    /// Insert `source_map` into the session globals for the duration of the closure's execution.
46    pub fn with_source_map<R>(source_map: Arc<SourceMap>, f: impl FnOnce() -> R) -> R {
47        let prev = Self::with(|g| g.source_map.lock().replace(source_map));
48        let _clear = defer(|| {
49            Self::with(|g| *g.source_map.lock() = prev);
50        });
51        f()
52    }
53
54    /// Calls the given closure with the current session globals.
55    ///
56    /// # Panics
57    ///
58    /// Panics if `set` has not previously been called.
59    #[inline]
60    pub fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
61        #[cfg(debug_assertions)]
62        if !SESSION_GLOBALS.is_set() {
63            let msg = if rayon::current_thread_index().is_some() {
64                "cannot access a scoped thread local variable without calling `set` first;\n\
65                 did you forget to call `Session::enter_parallel`?"
66            } else {
67                "cannot access a scoped thread local variable without calling `set` first;\n\
68                 did you forget to call `Session::enter`, or `Session::enter_parallel` \
69                 if using Rayon?"
70            };
71            panic!("{msg}");
72        }
73        SESSION_GLOBALS.with(f)
74    }
75
76    /// Calls the given closure with the current session globals if they have been set, otherwise
77    /// creates a new instance, sets it, and calls the closure with it.
78    #[inline]
79    pub fn with_or_default<R>(f: impl FnOnce(&Self) -> R) -> R {
80        if Self::is_set() {
81            Self::with(f)
82        } else {
83            Self::new().set(|| Self::with(f))
84        }
85    }
86
87    /// Returns `true` if the session globals have been set.
88    #[inline]
89    pub fn is_set() -> bool {
90        SESSION_GLOBALS.is_set()
91    }
92}
93
94#[cold]
95#[inline(never)]
96const fn panic_overwrite() -> ! {
97    panic!(
98        "SESSION_GLOBALS should never be overwritten! \
99         Use another thread if you need another SessionGlobals"
100    );
101}