Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/span/src/hygiene.rs')
-rw-r--r--crates/span/src/hygiene.rs127
1 files changed, 73 insertions, 54 deletions
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index a2923cd223..b21102f2db 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -27,7 +27,10 @@ use crate::Edition;
#[cfg(feature = "salsa")]
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
pub struct SyntaxContext(
- salsa::Id,
+ /// # Invariant
+ ///
+ /// This is either a valid `salsa::Id` or a root `SyntaxContext`.
+ u32,
std::marker::PhantomData<&'static salsa::plumbing::interned::Value<SyntaxContext>>,
);
@@ -95,10 +98,11 @@ const _: () = {
type Fields<'a> = SyntaxContextData;
type Struct<'a> = SyntaxContext;
fn struct_from_id<'db>(id: salsa::Id) -> Self::Struct<'db> {
- SyntaxContext(id, std::marker::PhantomData)
+ SyntaxContext::from_salsa_id(id)
}
fn deref_struct(s: Self::Struct<'_>) -> salsa::Id {
- s.0
+ s.as_salsa_id()
+ .expect("`SyntaxContext::deref_structs()` called on a root `SyntaxContext`")
}
}
impl SyntaxContext {
@@ -108,19 +112,19 @@ const _: () = {
{
static CACHE: zalsa_::IngredientCache<zalsa_struct_::IngredientImpl<SyntaxContext>> =
zalsa_::IngredientCache::new();
- CACHE.get_or_create(db.as_dyn_database(), || {
+ CACHE.get_or_create(db.zalsa(), || {
db.zalsa().add_or_lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>()
})
}
}
impl zalsa_::AsId for SyntaxContext {
fn as_id(&self) -> salsa::Id {
- self.0
+ self.as_salsa_id().expect("`SyntaxContext::as_id()` called on a root `SyntaxContext`")
}
}
impl zalsa_::FromId for SyntaxContext {
fn from_id(id: salsa::Id) -> Self {
- Self(id, std::marker::PhantomData)
+ Self::from_salsa_id(id)
}
}
unsafe impl Send for SyntaxContext {}
@@ -210,44 +214,44 @@ const _: () = {
where
Db: ?Sized + zalsa_::Database,
{
- if self.is_root() {
- return None;
- }
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), self);
- std::clone::Clone::clone(&fields.outer_expn)
+ let id = self.as_salsa_id()?;
+ let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id);
+ fields.outer_expn
}
pub fn outer_transparency<Db>(self, db: &'db Db) -> Transparency
where
Db: ?Sized + zalsa_::Database,
{
- if self.is_root() {
- return Transparency::Opaque;
- }
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), self);
- std::clone::Clone::clone(&fields.outer_transparency)
+ let Some(id) = self.as_salsa_id() else { return Transparency::Opaque };
+ let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id);
+ fields.outer_transparency
}
pub fn edition<Db>(self, db: &'db Db) -> Edition
where
Db: ?Sized + zalsa_::Database,
{
- if self.is_root() {
- return Edition::from_u32(SyntaxContext::MAX_ID - self.0.as_u32());
+ match self.as_salsa_id() {
+ Some(id) => {
+ let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id);
+ fields.edition
+ }
+ None => Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()),
}
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), self);
- std::clone::Clone::clone(&fields.edition)
}
pub fn parent<Db>(self, db: &'db Db) -> SyntaxContext
where
Db: ?Sized + zalsa_::Database,
{
- if self.is_root() {
- return self;
+ match self.as_salsa_id() {
+ Some(id) => {
+ let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id);
+ fields.parent
+ }
+ None => self,
}
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), self);
- std::clone::Clone::clone(&fields.parent)
}
/// This context, but with all transparent and semi-transparent expansions filtered away.
@@ -255,11 +259,13 @@ const _: () = {
where
Db: ?Sized + zalsa_::Database,
{
- if self.is_root() {
- return self;
+ match self.as_salsa_id() {
+ Some(id) => {
+ let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id);
+ fields.opaque
+ }
+ None => self,
}
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), self);
- std::clone::Clone::clone(&fields.opaque)
}
/// This context, but with all transparent expansions filtered away.
@@ -267,33 +273,19 @@ const _: () = {
where
Db: ?Sized + zalsa_::Database,
{
- if self.is_root() {
- return self;
+ match self.as_salsa_id() {
+ Some(id) => {
+ let fields = SyntaxContext::ingredient(db).data(db.as_dyn_database(), id);
+ fields.opaque_and_semitransparent
+ }
+ None => self,
}
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), self);
- std::clone::Clone::clone(&fields.opaque_and_semitransparent)
- }
-
- pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- salsa::with_attached_database(|db| {
- let fields = SyntaxContext::ingredient(db).fields(db.as_dyn_database(), this);
- let mut f = f.debug_struct("SyntaxContextData");
- let f = f.field("outer_expn", &fields.outer_expn);
- let f = f.field("outer_transparency", &fields.outer_expn);
- let f = f.field("edition", &fields.edition);
- let f = f.field("parent", &fields.parent);
- let f = f.field("opaque", &fields.opaque);
- let f = f.field("opaque_and_semitransparent", &fields.opaque_and_semitransparent);
- f.finish()
- })
- .unwrap_or_else(|| {
- f.debug_tuple("SyntaxContextData").field(&zalsa_::AsId::as_id(&this)).finish()
- })
}
}
};
impl SyntaxContext {
+ #[inline]
pub fn is_root(self) -> bool {
(SyntaxContext::MAX_ID - Edition::LATEST as u32) <= self.into_u32()
&& self.into_u32() <= (SyntaxContext::MAX_ID - Edition::Edition2015 as u32)
@@ -307,9 +299,11 @@ impl SyntaxContext {
}
/// The root context, which is the parent of all other contexts. All [`FileId`]s have this context.
+ #[inline]
pub const fn root(edition: Edition) -> Self {
let edition = edition as u32;
- SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition)
+ // SAFETY: Roots are valid `SyntaxContext`s
+ unsafe { SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition) }
}
}
@@ -317,12 +311,34 @@ impl SyntaxContext {
impl SyntaxContext {
const MAX_ID: u32 = salsa::Id::MAX_U32 - 1;
+ #[inline]
pub const fn into_u32(self) -> u32 {
- self.0.as_u32()
+ self.0
}
- pub const fn from_u32(u32: u32) -> Self {
- Self(salsa::Id::from_u32(u32), std::marker::PhantomData)
+ /// # Safety
+ ///
+ /// The ID must be a valid `SyntaxContext`.
+ #[inline]
+ pub const unsafe fn from_u32(u32: u32) -> Self {
+ // INVARIANT: Our precondition.
+ Self(u32, std::marker::PhantomData)
+ }
+
+ #[inline]
+ fn as_salsa_id(self) -> Option<salsa::Id> {
+ if self.is_root() {
+ None
+ } else {
+ // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`.
+ unsafe { Some(salsa::Id::from_u32(self.0)) }
+ }
+ }
+
+ #[inline]
+ fn from_salsa_id(id: salsa::Id) -> Self {
+ // SAFETY: This comes from a Salsa ID.
+ unsafe { Self::from_u32(id.as_u32()) }
}
}
#[cfg(not(feature = "salsa"))]
@@ -342,7 +358,10 @@ impl SyntaxContext {
self.0
}
- pub const fn from_u32(u32: u32) -> Self {
+ /// # Safety
+ ///
+ /// None. This is always safe to call without the `salsa` feature.
+ pub const unsafe fn from_u32(u32: u32) -> Self {
Self(u32)
}
}