Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/intern/src/intern.rs')
-rw-r--r--crates/intern/src/intern.rs46
1 files changed, 40 insertions, 6 deletions
diff --git a/crates/intern/src/intern.rs b/crates/intern/src/intern.rs
index 5d4d001185..b7acd6624b 100644
--- a/crates/intern/src/intern.rs
+++ b/crates/intern/src/intern.rs
@@ -1,8 +1,31 @@
//! Interning of single values.
+//!
+//! Interning supports two modes: GC and non-GC.
+//!
+//! In non-GC mode, you create [`Interned`]s, and can create `Copy` handles to them
+//! that can still be upgraded back to [`Interned`] ([`InternedRef`]) via [`Interned::as_ref`].
+//! Generally, letting the [`InternedRef`] to outlive the [`Interned`] is a soundness bug and can
+//! lead to UB. When all [`Interned`]s of some value are dropped, the value is freed (newer interns
+//! may re-create it, not necessarily in the same place).
+//!
+//! In GC mode, you generally operate on [`InternedRef`]s. They are `Copy` and comfortable. To intern
+//! a value you call [`Interned::new_gc`], which returns an [`InternedRef`]. Having all [`Interned`]s
+//! of some value be dropped will *not* immediately free the value. Instead, a mark-and-sweep GC can
+//! be initiated, which will free all values which have no live [`Interned`]s.
+//!
+//! Generally, in GC mode, you operate on [`InternedRef`], but when you need to store some long-term
+//! value (e.g. a Salsa query output), you convert it to an [`Interned`]. This ensures that an eventual
+//! GC will not free it as long as it is alive.
+//!
+//! Making mistakes is hard due to GC [`InternedRef`] wrappers not implementing `salsa::Update`, meaning
+//! Salsa will ensure you do not store them in queries or Salsa-interneds. However it's still *possible*
+//! without unsafe code (for example, by storing them in a `static`), which is why triggering GC is unsafe.
+//!
+//! For more information about GC see [`crate::gc`].
use std::{
fmt::{self, Debug, Display},
- hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
+ hash::{BuildHasher, Hash, Hasher},
ops::Deref,
ptr,
sync::OnceLock,
@@ -10,19 +33,16 @@ use std::{
use dashmap::{DashMap, SharedValue};
use hashbrown::raw::RawTable;
-use rustc_hash::FxHasher;
+use rustc_hash::FxBuildHasher;
use triomphe::{Arc, ArcBorrow};
-type InternMap<T> = DashMap<Arc<T>, (), BuildHasherDefault<FxHasher>>;
+type InternMap<T> = DashMap<Arc<T>, (), FxBuildHasher>;
type Guard<T> = dashmap::RwLockWriteGuard<'static, RawTable<(Arc<T>, SharedValue<()>)>>;
pub struct Interned<T: Internable> {
arc: Arc<T>,
}
-unsafe impl<T: Send + Sync + Internable> Send for Interned<T> {}
-unsafe impl<T: Send + Sync + Internable> Sync for Interned<T> {}
-
impl<T: Internable> Interned<T> {
#[inline]
pub fn new(obj: T) -> Self {
@@ -96,6 +116,7 @@ impl<T: Internable> Interned<T> {
/// The pointer should originate from an `Interned` or an `InternedRef`.
#[inline]
pub unsafe fn from_raw(ptr: *const T) -> Self {
+ // SAFETY: Our precondition.
Self { arc: unsafe { Arc::from_raw(ptr) } }
}
@@ -209,6 +230,7 @@ impl<'a, T: Internable> InternedRef<'a, T> {
/// The pointer needs to originate from `Interned` or `InternedRef`.
#[inline]
pub unsafe fn from_raw(ptr: *const T) -> Self {
+ // SAFETY: Our precondition.
Self { arc: unsafe { ArcBorrow::from_ptr(ptr) } }
}
@@ -228,6 +250,7 @@ impl<'a, T: Internable> InternedRef<'a, T> {
/// map also keeps a reference to the value.
#[inline]
pub unsafe fn decrement_refcount(self) {
+ // SAFETY: Our precondition.
unsafe { drop(Arc::from_raw(self.as_raw())) }
}
@@ -235,6 +258,17 @@ impl<'a, T: Internable> InternedRef<'a, T> {
pub(crate) fn strong_count(self) -> usize {
ArcBorrow::strong_count(&self.arc)
}
+
+ /// **Available only on GC mode**.
+ ///
+ /// Changes the attached lifetime, as in GC mode, the lifetime is more kind of a lint to prevent misuse
+ /// than actual soundness check.
+ #[inline]
+ pub fn change_lifetime<'b>(self) -> InternedRef<'b, T> {
+ const { assert!(T::USE_GC) };
+ // SAFETY: The lifetime on `InternedRef` is essentially advisory only for GCed types.
+ unsafe { std::mem::transmute::<InternedRef<'a, T>, InternedRef<'b, T>>(self) }
+ }
}
impl<T> Clone for InternedRef<'_, T> {