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.rs | 381 |
1 files changed, 292 insertions, 89 deletions
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs index 6becc8e41e..a6402e8701 100644 --- a/crates/span/src/hygiene.rs +++ b/crates/span/src/hygiene.rs @@ -21,44 +21,287 @@ //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. use std::fmt; -#[cfg(not(feature = "ra-salsa"))] -use crate::InternId; -#[cfg(feature = "ra-salsa")] -use ra_salsa::{InternId, InternValue}; - use crate::{Edition, MacroCallId}; -/// Interned [`SyntaxContextData`]. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct SyntaxContextId(InternId); +// Recursive expansion of interned macro +// ====================================== -impl fmt::Debug for SyntaxContextId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if f.alternate() { - write!(f, "{}", self.0.as_u32()) - } else { - f.debug_tuple("SyntaxContextId").field(&self.0).finish() +/// A syntax context describes a hierarchy tracking order of macro definitions. +#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub struct SyntaxContext( + salsa::Id, + std::marker::PhantomData<&'static salsa::plumbing::interned::Value<SyntaxContext>>, +); + +/// The underlying data interned by Salsa. +#[derive(Clone, Eq, Debug)] +pub struct SyntaxContextUnderlyingData { + pub outer_expn: Option<MacroCallId>, + pub outer_transparency: Transparency, + pub edition: Edition, + pub parent: SyntaxContext, + pub opaque: SyntaxContext, + pub opaque_and_semitransparent: SyntaxContext, +} + +const _: () = { + use salsa::plumbing as zalsa_; + use salsa::plumbing::interned as zalsa_struct_; + + impl PartialEq for SyntaxContextUnderlyingData { + fn eq(&self, other: &Self) -> bool { + self.outer_expn == other.outer_expn + && self.outer_transparency == other.outer_transparency + && self.edition == other.edition + && self.parent == other.parent } } -} -#[cfg(feature = "ra-salsa")] -impl ra_salsa::InternKey for SyntaxContextId { - fn from_intern_id(v: ra_salsa::InternId) -> Self { - SyntaxContextId(v) + impl std::hash::Hash for SyntaxContextUnderlyingData { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.outer_expn.hash(state); + self.outer_transparency.hash(state); + self.edition.hash(state); + self.parent.hash(state); + } + } + /// Key to use during hash lookups. Each field is some type that implements `Lookup<T>` + /// for the owned type. This permits interning with an `&str` when a `String` is required and so forth. + #[derive(Hash)] + struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>); + + impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike<StructKey<'db, T0, T1, T2, T3>> + for SyntaxContextUnderlyingData + where + Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>, + Transparency: zalsa_::interned::HashEqLike<T1>, + Edition: zalsa_::interned::HashEqLike<T2>, + SyntaxContext: zalsa_::interned::HashEqLike<T3>, + { + fn hash<H: std::hash::Hasher>(&self, h: &mut H) { + zalsa_::interned::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h); + zalsa_::interned::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h); + zalsa_::interned::HashEqLike::<T2>::hash(&self.edition, &mut *h); + zalsa_::interned::HashEqLike::<T3>::hash(&self.parent, &mut *h); + } + fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool { + zalsa_::interned::HashEqLike::<T0>::eq(&self.outer_expn, &data.0) + && zalsa_::interned::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1) + && zalsa_::interned::HashEqLike::<T2>::eq(&self.edition, &data.2) + && zalsa_::interned::HashEqLike::<T3>::eq(&self.parent, &data.3) + } + } + impl zalsa_struct_::Configuration for SyntaxContext { + const DEBUG_NAME: &'static str = "SyntaxContextData"; + type Fields<'a> = SyntaxContextUnderlyingData; + type Struct<'a> = SyntaxContext; + fn struct_from_id<'db>(id: salsa::Id) -> Self::Struct<'db> { + SyntaxContext(id, std::marker::PhantomData) + } + fn deref_struct(s: Self::Struct<'_>) -> salsa::Id { + s.0 + } } - fn as_intern_id(&self) -> ra_salsa::InternId { - self.0 + impl SyntaxContext { + pub fn ingredient<Db>(db: &Db) -> &zalsa_struct_::IngredientImpl<Self> + where + Db: ?Sized + zalsa_::Database, + { + static CACHE: zalsa_::IngredientCache<zalsa_struct_::IngredientImpl<SyntaxContext>> = + zalsa_::IngredientCache::new(); + CACHE.get_or_create(db.as_dyn_database(), || { + 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 + } + } + impl zalsa_::FromId for SyntaxContext { + fn from_id(id: salsa::Id) -> Self { + Self(id, std::marker::PhantomData) + } + } + unsafe impl Send for SyntaxContext {} -impl fmt::Display for SyntaxContextId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0.as_u32()) + unsafe impl Sync for SyntaxContext {} + + impl zalsa_::SalsaStructInDb for SyntaxContext { + type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex; + + fn lookup_or_create_ingredient_index( + aux: &salsa::plumbing::Zalsa, + ) -> salsa::plumbing::IngredientIndices { + aux.add_or_lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>().into() + } + + #[inline] + fn cast(id: salsa::Id, type_id: std::any::TypeId) -> Option<Self> { + if type_id == std::any::TypeId::of::<SyntaxContext>() { + Some(<Self as salsa::plumbing::FromId>::from_id(id)) + } else { + None + } + } + } + + unsafe impl salsa::plumbing::Update for SyntaxContext { + unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { + if unsafe { *old_pointer } != new_value { + unsafe { *old_pointer = new_value }; + true + } else { + false + } + } + } + impl<'db> SyntaxContext { + pub fn new< + Db, + T0: zalsa_::interned::Lookup<Option<MacroCallId>> + std::hash::Hash, + T1: zalsa_::interned::Lookup<Transparency> + std::hash::Hash, + T2: zalsa_::interned::Lookup<Edition> + std::hash::Hash, + T3: zalsa_::interned::Lookup<SyntaxContext> + std::hash::Hash, + >( + db: &'db Db, + outer_expn: T0, + outer_transparency: T1, + edition: T2, + parent: T3, + opaque: impl FnOnce(SyntaxContext) -> SyntaxContext, + opaque_and_semitransparent: impl FnOnce(SyntaxContext) -> SyntaxContext, + ) -> Self + where + Db: ?Sized + salsa::Database, + Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>, + Transparency: zalsa_::interned::HashEqLike<T1>, + Edition: zalsa_::interned::HashEqLike<T2>, + SyntaxContext: zalsa_::interned::HashEqLike<T3>, + { + SyntaxContext::ingredient(db).intern( + db.as_dyn_database(), + StructKey::<'db>( + outer_expn, + outer_transparency, + edition, + parent, + std::marker::PhantomData, + ), + |id, data| SyntaxContextUnderlyingData { + outer_expn: zalsa_::interned::Lookup::into_owned(data.0), + outer_transparency: zalsa_::interned::Lookup::into_owned(data.1), + edition: zalsa_::interned::Lookup::into_owned(data.2), + parent: zalsa_::interned::Lookup::into_owned(data.3), + opaque: opaque(zalsa_::FromId::from_id(id)), + opaque_and_semitransparent: opaque_and_semitransparent( + zalsa_::FromId::from_id(id), + ), + }, + ) + } + + /// Invariant: Only [`SyntaxContext::ROOT`] has a [`None`] outer expansion. + // FIXME: The None case needs to encode the context crate id. We can encode that as the MSB of + // MacroCallId is reserved anyways so we can do bit tagging here just fine. + // The bigger issue is that this will cause interning to now create completely separate chains + // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. + pub fn outer_expn<Db>(self, db: &'db Db) -> Option<MacroCallId> + 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) + } + + 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) + } + + 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()); + } + 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; + } + 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. + pub fn opaque<Db>(self, db: &'db Db) -> SyntaxContext + where + Db: ?Sized + zalsa_::Database, + { + if self.is_root() { + return 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. + pub fn opaque_and_semitransparent<Db>(self, db: &'db Db) -> SyntaxContext + where + Db: ?Sized + zalsa_::Database, + { + if self.is_root() { + return 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 { + const MAX_ID: u32 = salsa::Id::MAX_U32 - 1; + + 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) } -} -impl SyntaxContextId { #[inline] pub fn remove_root_edition(&mut self) { if self.is_root() { @@ -68,75 +311,19 @@ impl SyntaxContextId { /// The root context, which is the parent of all other contexts. All [`FileId`]s have this context. pub const fn root(edition: Edition) -> Self { - SyntaxContextId(unsafe { InternId::new_unchecked(edition as u32) }) + let edition = edition as u32; + SyntaxContext( + salsa::Id::from_u32(SyntaxContext::MAX_ID - edition), + std::marker::PhantomData, + ) } - pub fn is_root(self) -> bool { - self.into_u32() <= Edition::LATEST as u32 - } - - /// Deconstruct a `SyntaxContextId` into a raw `u32`. - /// This should only be used for deserialization purposes for the proc-macro server. pub fn into_u32(self) -> u32 { self.0.as_u32() } - /// Constructs a `SyntaxContextId` from a raw `u32`. - /// This should only be used for serialization purposes for the proc-macro server. pub fn from_u32(u32: u32) -> Self { - Self(InternId::from(u32)) - } -} - -/// A syntax context describes a hierarchy tracking order of macro definitions. -#[derive(Copy, Clone, Hash, PartialEq, Eq)] -pub struct SyntaxContextData { - /// Invariant: Only [`SyntaxContextId::ROOT`] has a [`None`] outer expansion. - // FIXME: The None case needs to encode the context crate id. We can encode that as the MSB of - // MacroCallId is reserved anyways so we can do bit tagging here just fine. - // The bigger issue is that this will cause interning to now create completely separate chains - // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. - pub outer_expn: Option<MacroCallId>, - pub outer_transparency: Transparency, - pub edition: Edition, - pub parent: SyntaxContextId, - /// This context, but with all transparent and semi-transparent expansions filtered away. - pub opaque: SyntaxContextId, - /// This context, but with all transparent expansions filtered away. - pub opaque_and_semitransparent: SyntaxContextId, -} - -#[cfg(feature = "ra-salsa")] -impl InternValue for SyntaxContextData { - type Key = (SyntaxContextId, Option<MacroCallId>, Transparency, Edition); - - fn into_key(&self) -> Self::Key { - (self.parent, self.outer_expn, self.outer_transparency, self.edition) - } -} - -impl std::fmt::Debug for SyntaxContextData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("SyntaxContextData") - .field("outer_expn", &self.outer_expn) - .field("outer_transparency", &self.outer_transparency) - .field("parent", &self.parent) - .field("opaque", &self.opaque) - .field("opaque_and_semitransparent", &self.opaque_and_semitransparent) - .finish() - } -} - -impl SyntaxContextData { - pub fn root(edition: Edition) -> Self { - SyntaxContextData { - outer_expn: None, - outer_transparency: Transparency::Opaque, - parent: SyntaxContextId::root(edition), - opaque: SyntaxContextId::root(edition), - opaque_and_semitransparent: SyntaxContextId::root(edition), - edition, - } + Self(salsa::Id::from_u32(u32), std::marker::PhantomData) } } @@ -167,3 +354,19 @@ impl Transparency { matches!(self, Self::Opaque) } } + +impl fmt::Display for SyntaxContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0.as_u32()) + } +} + +impl std::fmt::Debug for SyntaxContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if f.alternate() { + write!(f, "{}", self.0.as_u32()) + } else { + f.debug_tuple("SyntaxContext").field(&self.0).finish() + } + } +} |