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    pub fn set<R>(&self, f: impl FnOnce() -> R) -> R {
33        self.check_overwrite();
34        SESSION_GLOBALS.set(self, f)
35    }
36
37    fn check_overwrite(&self) {
38        Self::try_with(|prev| {
39            if let Some(prev) = prev
40                && !prev.maybe_eq(self)
41            {
42                overwrite_log();
43            }
44        });
45    }
46
47    /// Insert `source_map` into the session globals for the duration of the closure's execution.
48    #[deprecated(note = "does nothing")]
49    #[track_caller]
50    pub fn with_source_map<R>(_source_map: Arc<SourceMap>, f: impl FnOnce() -> R) -> R {
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    #[track_caller]
61    pub fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
62        debug_assert!(
63            SESSION_GLOBALS.is_set(),
64            "cannot access a scoped thread local variable without calling `set` first; \
65             did you forget to call `Session::enter`?"
66        );
67        SESSION_GLOBALS.with(f)
68    }
69
70    /// Calls the given closure with the current session globals if they have been set, otherwise
71    /// creates a new instance, sets it, and calls the closure with it.
72    #[inline]
73    #[track_caller]
74    pub fn with_or_default<R>(f: impl FnOnce(&Self) -> R) -> R {
75        if Self::is_set() { Self::with(f) } else { Self::default().set(|| Self::with(f)) }
76    }
77
78    /// Returns `true` if the session globals have been set.
79    #[inline]
80    pub fn is_set() -> bool {
81        SESSION_GLOBALS.is_set()
82    }
83
84    pub(crate) fn try_with<R>(f: impl FnOnce(Option<&Self>) -> R) -> R {
85        if SESSION_GLOBALS.is_set() { SESSION_GLOBALS.with(|g| f(Some(g))) } else { f(None) }
86    }
87
88    pub(crate) fn maybe_eq(&self, other: &Self) -> bool {
89        std::ptr::eq(self, other)
90    }
91}
92
93#[inline(never)]
94#[cold]
95fn overwrite_log() {
96    debug!(
97        "overwriting SESSION_GLOBALS; \
98         this might be due to manual incorrect usage of `SessionGlobals`, \
99         or entering multiple different nested `Session`s, which may cause unexpected behavior"
100    );
101}