feature: add id paramater to interned structs

This commit is contained in:
David Barsky 2024-12-02 14:14:32 +00:00
parent 0f379579fc
commit d4740ed72c
10 changed files with 71 additions and 10 deletions

View file

@ -14,6 +14,9 @@ macro_rules! setup_interned_struct_sans_lifetime {
// Name of the `'db` lifetime that the user gave // Name of the `'db` lifetime that the user gave
db_lt: $db_lt:lifetime, db_lt: $db_lt:lifetime,
// the salsa ID
id: $Id:path,
// Name user gave for `new` // Name user gave for `new`
new_fn: $new_fn:ident, new_fn: $new_fn:ident,
@ -55,7 +58,7 @@ macro_rules! setup_interned_struct_sans_lifetime {
$(#[$attr])* $(#[$attr])*
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
$vis struct $Struct( $vis struct $Struct(
salsa::Id, $Id,
std::marker::PhantomData < &'static salsa::plumbing::interned::Value < $Struct > > std::marker::PhantomData < &'static salsa::plumbing::interned::Value < $Struct > >
); );
@ -96,10 +99,12 @@ macro_rules! setup_interned_struct_sans_lifetime {
type Data<'a> = StructData<'a>; type Data<'a> = StructData<'a>;
type Struct<'a> = $Struct; type Struct<'a> = $Struct;
fn struct_from_id<'db>(id: salsa::Id) -> Self::Struct<'db> { fn struct_from_id<'db>(id: salsa::Id) -> Self::Struct<'db> {
$Struct(id, std::marker::PhantomData) use $zalsa::FromId;
$Struct(<$Id>::from_id(id), std::marker::PhantomData)
} }
fn deref_struct(s: Self::Struct<'_>) -> salsa::Id { fn deref_struct(s: Self::Struct<'_>) -> salsa::Id {
s.0 use $zalsa::AsId;
s.0.as_id()
} }
} }
@ -118,13 +123,13 @@ macro_rules! setup_interned_struct_sans_lifetime {
impl $zalsa::AsId for $Struct { impl $zalsa::AsId for $Struct {
fn as_id(&self) -> salsa::Id { fn as_id(&self) -> salsa::Id {
self.0 self.0.as_id()
} }
} }
impl $zalsa::FromId for $Struct { impl $zalsa::FromId for $Struct {
fn from_id(id: salsa::Id) -> Self { fn from_id(id: salsa::Id) -> Self {
Self(id, std::marker::PhantomData) Self(<$Id>::from_id(id), std::marker::PhantomData)
} }
} }

View file

@ -42,6 +42,7 @@ impl AllowedOptions for Accumulator {
const RECOVERY_FN: bool = false; const RECOVERY_FN: bool = false;
const LRU: bool = false; const LRU: bool = false;
const CONSTRUCTOR_NAME: bool = false; const CONSTRUCTOR_NAME: bool = false;
const ID: bool = true;
} }
struct StructMacro { struct StructMacro {

View file

@ -55,6 +55,8 @@ impl crate::options::AllowedOptions for InputStruct {
const LRU: bool = false; const LRU: bool = false;
const CONSTRUCTOR_NAME: bool = true; const CONSTRUCTOR_NAME: bool = true;
const ID: bool = true;
} }
impl SalsaStructAllowedOptions for InputStruct { impl SalsaStructAllowedOptions for InputStruct {

View file

@ -56,6 +56,8 @@ impl crate::options::AllowedOptions for InternedStruct {
const LRU: bool = false; const LRU: bool = false;
const CONSTRUCTOR_NAME: bool = true; const CONSTRUCTOR_NAME: bool = true;
const ID: bool = true;
} }
impl SalsaStructAllowedOptions for InternedStruct { impl SalsaStructAllowedOptions for InternedStruct {

View file

@ -32,6 +32,7 @@ pub(crate) fn interned_sans_lifetime(
type InternedArgs = Options<InternedStruct>; type InternedArgs = Options<InternedStruct>;
#[derive(Debug)]
struct InternedStruct; struct InternedStruct;
impl crate::options::AllowedOptions for InternedStruct { impl crate::options::AllowedOptions for InternedStruct {
@ -56,6 +57,8 @@ impl crate::options::AllowedOptions for InternedStruct {
const LRU: bool = false; const LRU: bool = false;
const CONSTRUCTOR_NAME: bool = true; const CONSTRUCTOR_NAME: bool = true;
const ID: bool = true;
} }
impl SalsaStructAllowedOptions for InternedStruct { impl SalsaStructAllowedOptions for InternedStruct {
@ -93,6 +96,7 @@ impl Macro {
let field_tys = salsa_struct.field_tys(); let field_tys = salsa_struct.field_tys();
let field_indexed_tys = salsa_struct.field_indexed_tys(); let field_indexed_tys = salsa_struct.field_indexed_tys();
let generate_debug_impl = salsa_struct.generate_debug_impl(); let generate_debug_impl = salsa_struct.generate_debug_impl();
let id = salsa_struct.id();
let zalsa = self.hygiene.ident("zalsa"); let zalsa = self.hygiene.ident("zalsa");
let zalsa_struct = self.hygiene.ident("zalsa_struct"); let zalsa_struct = self.hygiene.ident("zalsa_struct");
@ -108,6 +112,7 @@ impl Macro {
vis: #vis, vis: #vis,
Struct: #struct_ident, Struct: #struct_ident,
db_lt: #db_lt, db_lt: #db_lt,
id: #id,
new_fn: #new_fn, new_fn: #new_fn,
field_options: [#(#field_options),*], field_options: [#(#field_options),*],
field_ids: [#(#field_ids),*], field_ids: [#(#field_ids),*],

View file

@ -7,6 +7,7 @@ use syn::{ext::IdentExt, spanned::Spanned};
/// are required and trailing commas are permitted. The options accepted /// are required and trailing commas are permitted. The options accepted
/// for any particular location are configured via the `AllowedOptions` /// for any particular location are configured via the `AllowedOptions`
/// trait. /// trait.
#[derive(Debug)]
pub(crate) struct Options<A: AllowedOptions> { pub(crate) struct Options<A: AllowedOptions> {
/// The `return_ref` option is used to signal that field/return type is "by ref" /// The `return_ref` option is used to signal that field/return type is "by ref"
/// ///
@ -66,6 +67,12 @@ pub(crate) struct Options<A: AllowedOptions> {
/// If this is `Some`, the value is the `<ident>`. /// If this is `Some`, the value is the `<ident>`.
pub constructor_name: Option<syn::Ident>, pub constructor_name: Option<syn::Ident>,
/// The `id = <path>` option is used to set a custom ID for interrned structs.
///
/// The custom ID needs to handle
/// If this is `Some`, the value is the `<ident>`.
pub id: Option<syn::Path>,
/// Remember the `A` parameter, which plays no role after parsing. /// Remember the `A` parameter, which plays no role after parsing.
phantom: PhantomData<A>, phantom: PhantomData<A>,
} }
@ -85,6 +92,7 @@ impl<A: AllowedOptions> Default for Options<A> {
phantom: Default::default(), phantom: Default::default(),
lru: Default::default(), lru: Default::default(),
singleton: Default::default(), singleton: Default::default(),
id: Default::default(),
} }
} }
} }
@ -102,6 +110,7 @@ pub(crate) trait AllowedOptions {
const RECOVERY_FN: bool; const RECOVERY_FN: bool;
const LRU: bool; const LRU: bool;
const CONSTRUCTOR_NAME: bool; const CONSTRUCTOR_NAME: bool;
const ID: bool;
} }
type Equals = syn::Token![=]; type Equals = syn::Token![=];
@ -252,7 +261,7 @@ impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
} }
} else if ident == "constructor" { } else if ident == "constructor" {
if A::CONSTRUCTOR_NAME { if A::CONSTRUCTOR_NAME {
let _eq = Equals::parse(input)?; let _eq: syn::token::Eq = Equals::parse(input)?;
let ident = syn::Ident::parse(input)?; let ident = syn::Ident::parse(input)?;
if let Some(old) = std::mem::replace(&mut options.constructor_name, Some(ident)) if let Some(old) = std::mem::replace(&mut options.constructor_name, Some(ident))
{ {
@ -267,6 +276,17 @@ impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
"`constructor` option not allowed here", "`constructor` option not allowed here",
)); ));
} }
} else if ident == "id" {
if A::ID {
let _eq = Equals::parse(input)?;
let path = syn::Path::parse(input)?;
options.id = Some(path);
} else {
return Err(syn::Error::new(
ident.span(),
"`id` option not allowed here",
));
}
} else { } else {
return Err(syn::Error::new( return Err(syn::Error::new(
ident.span(), ident.span(),

View file

@ -118,6 +118,13 @@ where
} }
} }
pub(crate) fn id(&self) -> syn::Path {
match &self.args.id {
Some(id) => id.clone(),
None => parse_quote!(salsa::Id),
}
}
/// Disallow `#[id]` attributes on the fields of this struct. /// Disallow `#[id]` attributes on the fields of this struct.
/// ///
/// If an `#[id]` field is found, return an error. /// If an `#[id]` field is found, return an error.

View file

@ -44,6 +44,8 @@ impl crate::options::AllowedOptions for TrackedFn {
const LRU: bool = true; const LRU: bool = true;
const CONSTRUCTOR_NAME: bool = false; const CONSTRUCTOR_NAME: bool = false;
const ID: bool = true;
} }
struct Macro { struct Macro {

View file

@ -50,6 +50,8 @@ impl crate::options::AllowedOptions for TrackedStruct {
const LRU: bool = false; const LRU: bool = false;
const CONSTRUCTOR_NAME: bool = true; const CONSTRUCTOR_NAME: bool = true;
const ID: bool = true;
} }
impl SalsaStructAllowedOptions for TrackedStruct { impl SalsaStructAllowedOptions for TrackedStruct {

View file

@ -2,23 +2,38 @@ use expect_test::expect;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use test_log::test; use test_log::test;
#[salsa::interned_sans_lifetime] #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
struct CustomSalsaIdWrapper(salsa::Id);
impl salsa::plumbing::AsId for CustomSalsaIdWrapper {
fn as_id(&self) -> salsa::Id {
self.0
}
}
impl salsa::plumbing::FromId for CustomSalsaIdWrapper {
fn from_id(id: salsa::Id) -> Self {
CustomSalsaIdWrapper(id)
}
}
#[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedString { struct InternedString {
data: String, data: String,
} }
#[salsa::interned_sans_lifetime] #[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedPair { struct InternedPair {
data: (InternedString, InternedString), data: (InternedString, InternedString),
} }
#[salsa::interned_sans_lifetime] #[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedTwoFields { struct InternedTwoFields {
data1: String, data1: String,
data2: String, data2: String,
} }
#[salsa::interned_sans_lifetime] #[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedVec { struct InternedVec {
data1: Vec<String>, data1: Vec<String>,
} }