solar_interface/
globals.rs

1use crate::SourceMap;
2use std::sync::Arc;
3
4scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals);
5
6/// Per-session global variables.
7///
8/// This struct is stored in thread-local storage in such a way that it is accessible without any
9/// kind of handle to all threads within the compilation session, but is not accessible outside the
10/// session.
11///
12/// These should only be used when `Session` is truly not available, such as `Symbol::intern` and
13/// `<Span as Debug>::fmt`.
14pub struct SessionGlobals {
15    pub(crate) symbol_interner: crate::symbol::Interner,
16    pub(crate) source_map: Arc<SourceMap>,
17}
18
19impl Default for SessionGlobals {
20    fn default() -> Self {
21        Self::new(Default::default())
22    }
23}
24
25impl SessionGlobals {
26    /// Creates a new session globals object.
27    pub fn new(source_map: Arc<SourceMap>) -> Self {
28        Self { symbol_interner: crate::symbol::Interner::fresh(), source_map }
29    }
30
31    /// Sets this instance as the global instance for the duration of the closure.
32    #[inline]
33    #[track_caller]
34    pub fn set<R>(&self, f: impl FnOnce() -> R) -> R {
35        if cfg!(debug_assertions) && SESSION_GLOBALS.is_set() {
36            check_overwrite(self);
37        }
38        SESSION_GLOBALS.set(self, f)
39    }
40
41    /// Insert `source_map` into the session globals for the duration of the closure's execution.
42    #[deprecated(note = "does nothing")]
43    #[track_caller]
44    pub fn with_source_map<R>(_source_map: Arc<SourceMap>, f: impl FnOnce() -> R) -> R {
45        f()
46    }
47
48    /// Calls the given closure with the current session globals.
49    ///
50    /// # Panics
51    ///
52    /// Panics if `set` has not previously been called.
53    #[inline]
54    #[track_caller]
55    pub fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
56        #[cfg(debug_assertions)]
57        if !SESSION_GLOBALS.is_set() {
58            let msg = if rayon::current_thread_index().is_some() {
59                "cannot access a scoped thread local variable without calling `set` first;\n\
60                 did you forget to call `Session::enter_parallel`?"
61            } else {
62                "cannot access a scoped thread local variable without calling `set` first;\n\
63                 did you forget to call `Session::enter`, or `Session::enter_parallel` \
64                 if using Rayon?"
65            };
66            panic!("{msg}");
67        }
68        SESSION_GLOBALS.with(f)
69    }
70
71    /// Calls the given closure with the current session globals if they have been set, otherwise
72    /// creates a new instance, sets it, and calls the closure with it.
73    #[inline]
74    #[track_caller]
75    pub fn with_or_default<R>(f: impl FnOnce(&Self) -> R) -> R {
76        if Self::is_set() { Self::with(f) } else { Self::default().set(|| Self::with(f)) }
77    }
78
79    /// Returns `true` if the session globals have been set.
80    #[inline]
81    pub fn is_set() -> bool {
82        SESSION_GLOBALS.is_set()
83    }
84
85    fn maybe_eq(&self, other: &Self) -> bool {
86        // Extra check for test usage of `enter`:
87        // we allow replacing empty source maps with eachother.
88        std::ptr::eq(self, other) || (self.is_default() && other.is_default())
89    }
90
91    fn is_default(&self) -> bool {
92        self.source_map.is_empty()
93    }
94}
95
96#[cold]
97#[inline(never)]
98#[cfg_attr(debug_assertions, track_caller)]
99fn check_overwrite(new: &SessionGlobals) {
100    SessionGlobals::with(|old| {
101        if !old.maybe_eq(new) {
102            panic!(
103                "SESSION_GLOBALS should never be overwritten!\n\
104                 This is likely either due to manual incorrect usage of `SessionGlobals`, \
105                 or entering multiple nested `Session`s, which is not supported"
106            );
107        }
108    })
109}