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
db_lt: $db_lt:lifetime,
// the salsa ID
id: $Id:path,
// Name user gave for `new`
new_fn: $new_fn:ident,
@ -55,7 +58,7 @@ macro_rules! setup_interned_struct_sans_lifetime {
$(#[$attr])*
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
$vis struct $Struct(
salsa::Id,
$Id,
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 Struct<'a> = $Struct;
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 {
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 {
fn as_id(&self) -> salsa::Id {
self.0
self.0.as_id()
}
}
impl $zalsa::FromId for $Struct {
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 LRU: bool = false;
const CONSTRUCTOR_NAME: bool = false;
const ID: bool = true;
}
struct StructMacro {

View file

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

View file

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

View file

@ -32,6 +32,7 @@ pub(crate) fn interned_sans_lifetime(
type InternedArgs = Options<InternedStruct>;
#[derive(Debug)]
struct InternedStruct;
impl crate::options::AllowedOptions for InternedStruct {
@ -56,6 +57,8 @@ impl crate::options::AllowedOptions for InternedStruct {
const LRU: bool = false;
const CONSTRUCTOR_NAME: bool = true;
const ID: bool = true;
}
impl SalsaStructAllowedOptions for InternedStruct {
@ -93,6 +96,7 @@ impl Macro {
let field_tys = salsa_struct.field_tys();
let field_indexed_tys = salsa_struct.field_indexed_tys();
let generate_debug_impl = salsa_struct.generate_debug_impl();
let id = salsa_struct.id();
let zalsa = self.hygiene.ident("zalsa");
let zalsa_struct = self.hygiene.ident("zalsa_struct");
@ -108,6 +112,7 @@ impl Macro {
vis: #vis,
Struct: #struct_ident,
db_lt: #db_lt,
id: #id,
new_fn: #new_fn,
field_options: [#(#field_options),*],
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
/// for any particular location are configured via the `AllowedOptions`
/// trait.
#[derive(Debug)]
pub(crate) struct Options<A: AllowedOptions> {
/// 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>`.
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.
phantom: PhantomData<A>,
}
@ -85,6 +92,7 @@ impl<A: AllowedOptions> Default for Options<A> {
phantom: Default::default(),
lru: Default::default(),
singleton: Default::default(),
id: Default::default(),
}
}
}
@ -102,6 +110,7 @@ pub(crate) trait AllowedOptions {
const RECOVERY_FN: bool;
const LRU: bool;
const CONSTRUCTOR_NAME: bool;
const ID: bool;
}
type Equals = syn::Token![=];
@ -252,7 +261,7 @@ impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
}
} else if ident == "constructor" {
if A::CONSTRUCTOR_NAME {
let _eq = Equals::parse(input)?;
let _eq: syn::token::Eq = Equals::parse(input)?;
let ident = syn::Ident::parse(input)?;
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",
));
}
} 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 {
return Err(syn::Error::new(
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.
///
/// 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 CONSTRUCTOR_NAME: bool = false;
const ID: bool = true;
}
struct Macro {

View file

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

View file

@ -2,23 +2,38 @@ use expect_test::expect;
use std::path::{Path, PathBuf};
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 {
data: String,
}
#[salsa::interned_sans_lifetime]
#[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedPair {
data: (InternedString, InternedString),
}
#[salsa::interned_sans_lifetime]
#[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedTwoFields {
data1: String,
data2: String,
}
#[salsa::interned_sans_lifetime]
#[salsa::interned_sans_lifetime(id = CustomSalsaIdWrapper)]
struct InternedVec {
data1: Vec<String>,
}