use crate::SourceMap;
use std::sync::Arc;
scoped_tls::scoped_thread_local!(static SESSION_GLOBALS: SessionGlobals);
pub(crate) struct SessionGlobals {
pub(crate) symbol_interner: crate::symbol::Interner,
pub(crate) source_map: Arc<SourceMap>,
}
impl Default for SessionGlobals {
fn default() -> Self {
Self::new(Default::default())
}
}
impl SessionGlobals {
pub(crate) fn new(source_map: Arc<SourceMap>) -> Self {
Self { symbol_interner: crate::symbol::Interner::fresh(), source_map }
}
pub(crate) fn set<R>(&self, f: impl FnOnce() -> R) -> R {
self.check_overwrite();
SESSION_GLOBALS.set(self, f)
}
fn check_overwrite(&self) {
Self::try_with(|prev| {
if let Some(prev) = prev
&& !prev.maybe_eq(self)
{
overwrite_log();
}
});
}
#[inline]
#[track_caller]
pub(crate) fn with<R>(f: impl FnOnce(&Self) -> R) -> R {
debug_assert!(
SESSION_GLOBALS.is_set(),
"cannot access a scoped thread local variable without calling `set` first; \
did you forget to call `Session::enter`?"
);
SESSION_GLOBALS.with(f)
}
#[inline]
#[track_caller]
pub(crate) fn with_or_default<R>(f: impl FnOnce(&Self) -> R) -> R {
if Self::is_set() { Self::with(f) } else { Self::default().set(|| Self::with(f)) }
}
#[inline]
pub(crate) fn is_set() -> bool {
SESSION_GLOBALS.is_set()
}
pub(crate) fn try_with<R>(f: impl FnOnce(Option<&Self>) -> R) -> R {
if SESSION_GLOBALS.is_set() { SESSION_GLOBALS.with(|g| f(Some(g))) } else { f(None) }
}
pub(crate) fn maybe_eq(&self, other: &Self) -> bool {
std::ptr::eq(self, other)
}
}
#[inline(never)]
#[cold]
fn overwrite_log() {
debug!(
"overwriting SESSION_GLOBALS; \
this might be due to manual incorrect usage of `SessionGlobals`, \
or entering multiple different nested `Session`s, which may cause unexpected behavior"
);
}