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