Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/salsa-2022-macros/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/salsa-2022-macros/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"f52122185e9c1ed013834e800b45aa8481559ed2f01ed518ff0a7ae9ad8886bd","src/accumulator.rs":"4da510729f72652abdd9a09a5a962fa0cad25339b04838638d4a20133c0298e2","src/configuration.rs":"4d47f6ad909966ac201d3346925bf177997d875b1f858896e9fb4b25ac84b4fe","src/db.rs":"202d18bc346585376b5df00e5a42aace0ed6f0b06c0ab888e69f31ae0f02eca9","src/input.rs":"f5f5628b4b65f7f6be336bfd052fd052f23c9a517cc15052843a046b7c5ae7c9","src/interned.rs":"06b10fa5afd46f5e093ce5c242e9975be54355fb19560f429d1e8f494e68173b","src/jar.rs":"472f9a72af791b236e233136031ec3df642e39fd42ea6d554cdac7c9c844f6b1","src/lib.rs":"f3d2284a4c9f62365466a314e5072cd9eedb43fa005c4b688516bb6fa482358a","src/options.rs":"ee18e422f9589e07691895b7cc6f0baa8129ffd7293e0a59db6c03a6b1a6279d","src/salsa_struct.rs":"9a132d5fab49060e6c9cfb634e6cd62550736b826b67c8e23b4827e90926e052","src/tracked.rs":"e7e6af1e0ec5e73578419e0a36e8f7ff4f38c6e4d25ccffba045cb5c8193c942","src/tracked_fn.rs":"2ee1897ccd6880a56198911748ebaeabb9b534d6bc0c42fc3d7a03d2777c4a3a","src/tracked_struct.rs":"24a511419588071c5f26a96ba5980ccb031aeecc510eddb0e52238945d287f91"},"package":null}
|
||||
32
third-party/vendor/salsa-2022-macros/Cargo.toml
vendored
Normal file
32
third-party/vendor/salsa-2022-macros/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "salsa-2022-macros"
|
||||
version = "0.1.0"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
eyre = "0.6.5"
|
||||
heck = "0.4"
|
||||
proc-macro2 = "1.0"
|
||||
quote = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0"
|
||||
features = [
|
||||
"full",
|
||||
"extra-traits",
|
||||
"visit-mut",
|
||||
]
|
||||
162
third-party/vendor/salsa-2022-macros/src/accumulator.rs
vendored
Normal file
162
third-party/vendor/salsa-2022-macros/src/accumulator.rs
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
use syn::ItemStruct;
|
||||
|
||||
// #[salsa::accumulator(jar = Jar0)]
|
||||
// struct Accumulator(DataType);
|
||||
|
||||
pub(crate) fn accumulator(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let args = syn::parse_macro_input!(args as Args);
|
||||
let struct_impl = syn::parse_macro_input!(input as ItemStruct);
|
||||
accumulator_contents(&args, &struct_impl)
|
||||
.unwrap_or_else(syn::Error::into_compile_error)
|
||||
.into()
|
||||
}
|
||||
|
||||
type Args = crate::options::Options<Accumulator>;
|
||||
|
||||
struct Accumulator;
|
||||
|
||||
impl crate::options::AllowedOptions for Accumulator {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
|
||||
const SINGLETON: bool = false;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = false;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = false;
|
||||
}
|
||||
|
||||
fn accumulator_contents(
|
||||
args: &Args,
|
||||
struct_item: &syn::ItemStruct,
|
||||
) -> syn::Result<proc_macro2::TokenStream> {
|
||||
// We expect a single anonymous field.
|
||||
let data_ty = data_ty(struct_item)?;
|
||||
let struct_name = &struct_item.ident;
|
||||
let struct_ty = &parse_quote! {#struct_name};
|
||||
|
||||
let inherent_impl = inherent_impl(args, struct_ty, data_ty);
|
||||
let ingredients_for_impl = ingredients_for_impl(args, struct_name, data_ty);
|
||||
let struct_item_out = struct_item_out(args, struct_item, data_ty);
|
||||
let accumulator_impl = accumulator_impl(args, struct_ty, data_ty);
|
||||
|
||||
Ok(quote! {
|
||||
#inherent_impl
|
||||
#ingredients_for_impl
|
||||
#struct_item_out
|
||||
#accumulator_impl
|
||||
})
|
||||
}
|
||||
|
||||
fn data_ty(struct_item: &syn::ItemStruct) -> syn::Result<&syn::Type> {
|
||||
if let syn::Fields::Unnamed(fields) = &struct_item.fields {
|
||||
if fields.unnamed.len() != 1 {
|
||||
Err(syn::Error::new(
|
||||
struct_item.ident.span(),
|
||||
"accumulator structs should have only one anonymous field",
|
||||
))
|
||||
} else {
|
||||
Ok(&fields.unnamed[0].ty)
|
||||
}
|
||||
} else {
|
||||
Err(syn::Error::new(
|
||||
struct_item.ident.span(),
|
||||
"accumulator structs should have only one anonymous field",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn struct_item_out(
|
||||
_args: &Args,
|
||||
struct_item: &syn::ItemStruct,
|
||||
data_ty: &syn::Type,
|
||||
) -> syn::ItemStruct {
|
||||
let mut struct_item_out = struct_item.clone();
|
||||
struct_item_out.fields = syn::Fields::Unnamed(parse_quote! {
|
||||
(std::marker::PhantomData<#data_ty>)
|
||||
});
|
||||
struct_item_out
|
||||
}
|
||||
|
||||
fn inherent_impl(args: &Args, struct_ty: &syn::Type, data_ty: &syn::Type) -> syn::ItemImpl {
|
||||
let jar_ty = args.jar_ty();
|
||||
parse_quote! {
|
||||
impl #struct_ty {
|
||||
pub fn push<DB: ?Sized>(db: &DB, data: #data_ty)
|
||||
where
|
||||
DB: salsa::storage::HasJar<#jar_ty>,
|
||||
{
|
||||
let (jar, runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #struct_ty >>::ingredient(jar);
|
||||
ingredients.push(runtime, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ingredients_for_impl(
|
||||
args: &Args,
|
||||
struct_name: &syn::Ident,
|
||||
data_ty: &syn::Type,
|
||||
) -> syn::ItemImpl {
|
||||
let jar_ty = args.jar_ty();
|
||||
let debug_name = crate::literal(struct_name);
|
||||
parse_quote! {
|
||||
impl salsa::storage::IngredientsFor for #struct_name {
|
||||
type Ingredients = salsa::accumulator::AccumulatorIngredient<#data_ty>;
|
||||
type Jar = #jar_ty;
|
||||
|
||||
fn create_ingredients<DB>(routes: &mut salsa::routes::Routes<DB>) -> Self::Ingredients
|
||||
where
|
||||
DB: salsa::DbWithJar<Self::Jar> + salsa::storage::JarFromJars<Self::Jar>,
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
<_ as salsa::storage::HasIngredientsFor<Self>>::ingredient(jar)
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
<_ as salsa::storage::HasIngredientsFor<Self>>::ingredient_mut(jar)
|
||||
},
|
||||
);
|
||||
salsa::accumulator::AccumulatorIngredient::new(index, #debug_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn accumulator_impl(args: &Args, struct_ty: &syn::Type, data_ty: &syn::Type) -> syn::ItemImpl {
|
||||
let jar_ty = args.jar_ty();
|
||||
parse_quote! {
|
||||
impl salsa::accumulator::Accumulator for #struct_ty {
|
||||
type Data = #data_ty;
|
||||
type Jar = #jar_ty;
|
||||
|
||||
fn accumulator_ingredient<'db, Db>(
|
||||
db: &'db Db,
|
||||
) -> &'db salsa::accumulator::AccumulatorIngredient<Self::Data>
|
||||
where
|
||||
Db: ?Sized + salsa::storage::HasJar<Self::Jar>
|
||||
{
|
||||
let (jar, _) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor<#struct_ty>>::ingredient(jar);
|
||||
ingredients
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
94
third-party/vendor/salsa-2022-macros/src/configuration.rs
vendored
Normal file
94
third-party/vendor/salsa-2022-macros/src/configuration.rs
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
pub(crate) struct Configuration {
|
||||
pub(crate) jar_ty: syn::Type,
|
||||
pub(crate) salsa_struct_ty: syn::Type,
|
||||
pub(crate) key_ty: syn::Type,
|
||||
pub(crate) value_ty: syn::Type,
|
||||
pub(crate) cycle_strategy: CycleRecoveryStrategy,
|
||||
pub(crate) backdate_fn: syn::ImplItemMethod,
|
||||
pub(crate) execute_fn: syn::ImplItemMethod,
|
||||
pub(crate) recover_fn: syn::ImplItemMethod,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
pub(crate) fn to_impl(&self, self_ty: &syn::Type) -> syn::ItemImpl {
|
||||
let Configuration {
|
||||
jar_ty,
|
||||
salsa_struct_ty,
|
||||
key_ty,
|
||||
value_ty,
|
||||
cycle_strategy,
|
||||
backdate_fn,
|
||||
execute_fn,
|
||||
recover_fn,
|
||||
} = self;
|
||||
parse_quote! {
|
||||
impl salsa::function::Configuration for #self_ty {
|
||||
type Jar = #jar_ty;
|
||||
type SalsaStruct = #salsa_struct_ty;
|
||||
type Key = #key_ty;
|
||||
type Value = #value_ty;
|
||||
const CYCLE_STRATEGY: salsa::cycle::CycleRecoveryStrategy = #cycle_strategy;
|
||||
#backdate_fn
|
||||
#execute_fn
|
||||
#recover_fn
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum CycleRecoveryStrategy {
|
||||
Panic,
|
||||
Fallback,
|
||||
}
|
||||
|
||||
impl quote::ToTokens for CycleRecoveryStrategy {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
match self {
|
||||
CycleRecoveryStrategy::Panic => {
|
||||
tokens.extend(quote! {salsa::cycle::CycleRecoveryStrategy::Panic})
|
||||
}
|
||||
CycleRecoveryStrategy::Fallback => {
|
||||
tokens.extend(quote! {salsa::cycle::CycleRecoveryStrategy::Fallback})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an appropriate definition for `should_backdate_value` depending on
|
||||
/// whether this value is memoized or not.
|
||||
pub(crate) fn should_backdate_value_fn(should_backdate: bool) -> syn::ImplItemMethod {
|
||||
if should_backdate {
|
||||
parse_quote! {
|
||||
fn should_backdate_value(v1: &Self::Value, v2: &Self::Value) -> bool {
|
||||
salsa::function::should_backdate_value(v1, v2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
fn should_backdate_value(_v1: &Self::Value, _v2: &Self::Value) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an appropriate definition for `recover_from_cycle` for cases where
|
||||
/// the cycle recovery is panic.
|
||||
pub(crate) fn panic_cycle_recovery_fn() -> syn::ImplItemMethod {
|
||||
parse_quote! {
|
||||
fn recover_from_cycle(
|
||||
_db: &salsa::function::DynDb<Self>,
|
||||
_cycle: &salsa::Cycle,
|
||||
_key: Self::Key,
|
||||
) -> Self::Value {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn value_ty(sig: &syn::Signature) -> syn::Type {
|
||||
match &sig.output {
|
||||
syn::ReturnType::Default => parse_quote!(()),
|
||||
syn::ReturnType::Type(_, ty) => syn::Type::clone(ty),
|
||||
}
|
||||
}
|
||||
233
third-party/vendor/salsa-2022-macros/src/db.rs
vendored
Normal file
233
third-party/vendor/salsa-2022-macros/src/db.rs
vendored
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
use proc_macro2::Literal;
|
||||
use syn::{spanned::Spanned, Token};
|
||||
|
||||
// Source:
|
||||
//
|
||||
// #[salsa::db(Jar0, Jar1, Jar2)]
|
||||
// pub struct Database {
|
||||
// storage: salsa::Storage<Self>,
|
||||
// }
|
||||
|
||||
pub(crate) fn db(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let args = syn::parse_macro_input!(args as Args);
|
||||
let input = syn::parse_macro_input!(input as syn::ItemStruct);
|
||||
let storage = match find_storage_field(&input) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
let err = Literal::string(err);
|
||||
let error = quote_spanned!(input.ident.span() => compile_error!(#err));
|
||||
return quote! {
|
||||
#input
|
||||
#error
|
||||
}
|
||||
.into();
|
||||
}
|
||||
};
|
||||
|
||||
let as_salsa_database_impl = as_salsa_database_impl(&input);
|
||||
let has_jars_impl = has_jars_impl(&args, &input, &storage);
|
||||
let has_jars_dyn_impl = has_jars_dyn_impl(&input, &storage);
|
||||
let per_jar_impls = per_jar_impls(&args, &input, &storage);
|
||||
|
||||
quote! {
|
||||
#input
|
||||
#as_salsa_database_impl
|
||||
#has_jars_impl
|
||||
#has_jars_dyn_impl
|
||||
#(#per_jar_impls)*
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub struct Args {
|
||||
jar_paths: syn::punctuated::Punctuated<syn::Path, Token![,]>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Args {
|
||||
fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
jar_paths: syn::punctuated::Punctuated::parse_terminated(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn find_storage_field(input: &syn::ItemStruct) -> Result<syn::Ident, &'static str> {
|
||||
let storage = "storage";
|
||||
for field in input.fields.iter() {
|
||||
if let Some(i) = &field.ident {
|
||||
if i == storage {
|
||||
return Ok(i.clone());
|
||||
}
|
||||
} else {
|
||||
return Err(
|
||||
"database struct must be a braced struct (`{}`) with a field named storage",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Err("database has no field named `storage`")
|
||||
}
|
||||
|
||||
fn as_salsa_database_impl(input: &syn::ItemStruct) -> syn::ItemImpl {
|
||||
let db = &input.ident;
|
||||
parse_quote! {
|
||||
impl salsa::database::AsSalsaDatabase for #db {
|
||||
fn as_salsa_database(&self) -> &dyn salsa::Database {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_jars_impl(args: &Args, input: &syn::ItemStruct, storage: &syn::Ident) -> syn::ItemImpl {
|
||||
let jar_paths: Vec<&syn::Path> = args.jar_paths.iter().collect();
|
||||
let jar_field_names: Vec<_> = args
|
||||
.jar_paths
|
||||
.iter()
|
||||
.zip(0..)
|
||||
.map(|(p, i)| syn::LitInt::new(&format!("{}", i), p.span()))
|
||||
.collect();
|
||||
let db = &input.ident;
|
||||
parse_quote! {
|
||||
// ANCHOR: HasJars
|
||||
impl salsa::storage::HasJars for #db {
|
||||
type Jars = (#(#jar_paths,)*);
|
||||
// ANCHOR_END: HasJars
|
||||
|
||||
fn jars(&self) -> (&Self::Jars, &salsa::Runtime) {
|
||||
self.#storage.jars()
|
||||
}
|
||||
|
||||
fn jars_mut(&mut self) -> (&mut Self::Jars, &mut salsa::Runtime) {
|
||||
self.#storage.jars_mut()
|
||||
}
|
||||
|
||||
// ANCHOR: create_jars
|
||||
fn create_jars(routes: &mut salsa::routes::Routes<Self>) -> Box<Self::Jars> {
|
||||
unsafe {
|
||||
salsa::plumbing::create_jars_inplace::<#db>(|jars| {
|
||||
#(
|
||||
unsafe {
|
||||
let place = std::ptr::addr_of_mut!((*jars).#jar_field_names);
|
||||
<#jar_paths as salsa::jar::Jar>::init_jar(place, routes);
|
||||
}
|
||||
)*
|
||||
})
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: create_jars
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_jars_dyn_impl(input: &syn::ItemStruct, storage: &syn::Ident) -> syn::ItemImpl {
|
||||
let db = &input.ident;
|
||||
parse_quote! {
|
||||
impl salsa::storage::HasJarsDyn for #db {
|
||||
fn runtime(&self) -> &salsa::Runtime {
|
||||
self.#storage.runtime()
|
||||
}
|
||||
|
||||
fn runtime_mut(&mut self) ->&mut salsa::Runtime {
|
||||
self.#storage.runtime_mut()
|
||||
}
|
||||
|
||||
fn maybe_changed_after(
|
||||
&self,
|
||||
input: salsa::key::DependencyIndex,
|
||||
revision: salsa::Revision,
|
||||
) -> bool {
|
||||
let ingredient = self.#storage.ingredient(input.ingredient_index());
|
||||
ingredient.maybe_changed_after(self, input, revision)
|
||||
}
|
||||
|
||||
fn cycle_recovery_strategy(
|
||||
&self,
|
||||
ingredient_index: salsa::IngredientIndex,
|
||||
) -> salsa::cycle::CycleRecoveryStrategy {
|
||||
let ingredient = self.#storage.ingredient(ingredient_index);
|
||||
ingredient.cycle_recovery_strategy()
|
||||
}
|
||||
|
||||
fn origin(
|
||||
&self,
|
||||
index: salsa::DatabaseKeyIndex,
|
||||
) -> Option<salsa::runtime::local_state::QueryOrigin> {
|
||||
let ingredient = self.#storage.ingredient(index.ingredient_index());
|
||||
ingredient.origin(index.key_index())
|
||||
}
|
||||
|
||||
fn mark_validated_output(&self, executor: salsa::DatabaseKeyIndex, output: salsa::key::DependencyIndex) {
|
||||
let ingredient = self.#storage.ingredient(output.ingredient_index());
|
||||
ingredient.mark_validated_output(self, executor, output.key_index());
|
||||
}
|
||||
|
||||
fn remove_stale_output(&self, executor: salsa::DatabaseKeyIndex, stale_output: salsa::key::DependencyIndex) {
|
||||
let ingredient = self.#storage.ingredient(stale_output.ingredient_index());
|
||||
ingredient.remove_stale_output(self, executor, stale_output.key_index());
|
||||
}
|
||||
|
||||
fn salsa_struct_deleted(&self, ingredient: salsa::IngredientIndex, id: salsa::Id) {
|
||||
let ingredient = self.#storage.ingredient(ingredient);
|
||||
ingredient.salsa_struct_deleted(self, id);
|
||||
}
|
||||
|
||||
fn fmt_index(&self, index: salsa::key::DependencyIndex, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let ingredient = self.#storage.ingredient(index.ingredient_index());
|
||||
ingredient.fmt_index(index.key_index(), fmt)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn per_jar_impls(args: &Args, input: &syn::ItemStruct, storage: &syn::Ident) -> Vec<syn::ItemImpl> {
|
||||
let db = &input.ident;
|
||||
args.jar_paths
|
||||
.iter()
|
||||
.zip(0..)
|
||||
.flat_map(|(jar_path, jar_index)| {
|
||||
let jar_index = Literal::u32_unsuffixed(jar_index);
|
||||
vec![
|
||||
parse_quote! {
|
||||
impl salsa::storage::DbWithJar<#jar_path> for #db {
|
||||
fn as_jar_db<'db>(&'db self) -> &'db <#jar_path as salsa::jar::Jar<'db>>::DynDb
|
||||
where
|
||||
'db: 'db,
|
||||
{
|
||||
self as &'db <#jar_path as salsa::jar::Jar<'db>>::DynDb
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
parse_quote! {
|
||||
impl salsa::storage::HasJar<#jar_path> for #db {
|
||||
fn jar(&self) -> (&#jar_path, &salsa::Runtime) {
|
||||
let (__jars, __runtime) = self.#storage.jars();
|
||||
(&__jars.#jar_index, __runtime)
|
||||
}
|
||||
|
||||
fn jar_mut(&mut self) -> (&mut #jar_path, &mut salsa::Runtime) {
|
||||
let (__jars, __runtime) = self.#storage.jars_mut();
|
||||
(&mut __jars.#jar_index, __runtime)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
parse_quote! {
|
||||
impl salsa::storage::JarFromJars<#jar_path> for #db {
|
||||
fn jar_from_jars<'db>(jars: &Self::Jars) -> &#jar_path {
|
||||
&jars.#jar_index
|
||||
}
|
||||
|
||||
fn jar_from_jars_mut<'db>(jars: &mut Self::Jars) -> &mut #jar_path {
|
||||
&mut jars.#jar_index
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
321
third-party/vendor/salsa-2022-macros/src/input.rs
vendored
Normal file
321
third-party/vendor/salsa-2022-macros/src/input.rs
vendored
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
use crate::salsa_struct::{SalsaField, SalsaStruct, SalsaStructKind};
|
||||
use proc_macro2::{Literal, TokenStream};
|
||||
|
||||
/// For an entity struct `Foo` with fields `f1: T1, ..., fN: TN`, we generate...
|
||||
///
|
||||
/// * the "id struct" `struct Foo(salsa::Id)`
|
||||
/// * the entity ingredient, which maps the id fields to the `Id`
|
||||
/// * for each value field, a function ingredient
|
||||
pub(crate) fn input(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
match SalsaStruct::new(SalsaStructKind::Input, args, input)
|
||||
.and_then(|el| InputStruct(el).generate_input())
|
||||
{
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
struct InputStruct(SalsaStruct<Self>);
|
||||
|
||||
impl std::ops::Deref for InputStruct {
|
||||
type Target = SalsaStruct<Self>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::options::AllowedOptions for InputStruct {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
const SINGLETON: bool = true;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = true;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = true;
|
||||
}
|
||||
|
||||
impl InputStruct {
|
||||
fn generate_input(&self) -> syn::Result<TokenStream> {
|
||||
let id_struct = self.id_struct();
|
||||
let inherent_impl = self.input_inherent_impl();
|
||||
let ingredients_for_impl = self.input_ingredients();
|
||||
let as_id_impl = self.as_id_impl();
|
||||
let salsa_struct_in_db_impl = self.salsa_struct_in_db_impl();
|
||||
let as_debug_with_db_impl = self.as_debug_with_db_impl();
|
||||
|
||||
Ok(quote! {
|
||||
#id_struct
|
||||
#inherent_impl
|
||||
#ingredients_for_impl
|
||||
#as_id_impl
|
||||
#as_debug_with_db_impl
|
||||
#salsa_struct_in_db_impl
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate an inherent impl with methods on the entity type.
|
||||
fn input_inherent_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
let db_dyn_ty = self.db_dyn_ty();
|
||||
let input_index = self.input_index();
|
||||
|
||||
let field_indices = self.all_field_indices();
|
||||
let field_names = self.all_field_names();
|
||||
let field_vises = self.all_field_vises();
|
||||
let field_tys: Vec<_> = self.all_field_tys();
|
||||
let field_clones: Vec<_> = self.all_fields().map(SalsaField::is_clone_field).collect();
|
||||
let get_field_names: Vec<_> = self.all_get_field_names();
|
||||
let field_getters: Vec<syn::ImplItemMethod> = field_indices.iter().zip(&get_field_names).zip(&field_vises).zip(&field_tys).zip(&field_clones).map(|((((field_index, get_field_name), field_vis), field_ty), is_clone_field)|
|
||||
if !*is_clone_field {
|
||||
parse_quote! {
|
||||
#field_vis fn #get_field_name<'db>(self, __db: &'db #db_dyn_ty) -> &'db #field_ty
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#field_index.fetch(__runtime, self)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
#field_vis fn #get_field_name<'db>(self, __db: &'db #db_dyn_ty) -> #field_ty
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#field_index.fetch(__runtime, self).clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.collect();
|
||||
|
||||
// setters
|
||||
let set_field_names = self.all_set_field_names();
|
||||
let field_setters: Vec<syn::ImplItemMethod> = field_indices.iter()
|
||||
.zip(&set_field_names)
|
||||
.zip(&field_vises)
|
||||
.zip(&field_tys)
|
||||
.filter_map(|(((field_index, &set_field_name), field_vis), field_ty)| {
|
||||
let set_field_name = set_field_name?;
|
||||
Some(parse_quote! {
|
||||
#field_vis fn #set_field_name<'db>(self, __db: &'db mut #db_dyn_ty) -> salsa::setter::Setter<'db, #ident, #field_ty>
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient_mut(__jar);
|
||||
salsa::setter::Setter::new(__runtime, self, &mut __ingredients.#field_index)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let constructor_name = self.constructor_name();
|
||||
let singleton = self.0.is_isingleton();
|
||||
|
||||
let constructor: syn::ImplItemMethod = if singleton {
|
||||
parse_quote! {
|
||||
/// Creates a new singleton input
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If called when an instance already exists
|
||||
pub fn #constructor_name(__db: &#db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
let __id = __ingredients.#input_index.new_singleton_input(__runtime);
|
||||
#(
|
||||
__ingredients.#field_indices.store_new(__runtime, __id, #field_names, salsa::Durability::LOW);
|
||||
)*
|
||||
__id
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
pub fn #constructor_name(__db: &#db_dyn_ty, #(#field_names: #field_tys,)*) -> Self
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
let __id = __ingredients.#input_index.new_input(__runtime);
|
||||
#(
|
||||
__ingredients.#field_indices.store_new(__runtime, __id, #field_names, salsa::Durability::LOW);
|
||||
)*
|
||||
__id
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if singleton {
|
||||
let get: syn::ImplItemMethod = parse_quote! {
|
||||
#[track_caller]
|
||||
pub fn get(__db: &#db_dyn_ty) -> Self {
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#input_index.get_singleton_input(__runtime).expect("singleton input struct not yet initialized")
|
||||
}
|
||||
};
|
||||
|
||||
let try_get: syn::ImplItemMethod = parse_quote! {
|
||||
#[track_caller]
|
||||
pub fn try_get(__db: &#db_dyn_ty) -> Option<Self> {
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#input_index.get_singleton_input(__runtime)
|
||||
}
|
||||
};
|
||||
|
||||
parse_quote! {
|
||||
impl #ident {
|
||||
#constructor
|
||||
|
||||
#get
|
||||
|
||||
#try_get
|
||||
|
||||
#(#field_getters)*
|
||||
|
||||
#(#field_setters)*
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
impl #ident {
|
||||
#constructor
|
||||
|
||||
#(#field_getters)*
|
||||
|
||||
#(#field_setters)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
/// Generate the `IngredientsFor` impl for this entity.
|
||||
///
|
||||
/// The entity's ingredients include both the main entity ingredient along with a
|
||||
/// function ingredient for each of the value fields.
|
||||
fn input_ingredients(&self) -> syn::ItemImpl {
|
||||
use crate::literal;
|
||||
let ident = self.id_ident();
|
||||
let field_ty = self.all_field_tys();
|
||||
let jar_ty = self.jar_ty();
|
||||
let all_field_indices: Vec<Literal> = self.all_field_indices();
|
||||
let input_index: Literal = self.input_index();
|
||||
let debug_name_struct = literal(self.id_ident());
|
||||
let debug_name_fields: Vec<_> = self.all_field_names().into_iter().map(literal).collect();
|
||||
|
||||
parse_quote! {
|
||||
impl salsa::storage::IngredientsFor for #ident {
|
||||
type Jar = #jar_ty;
|
||||
type Ingredients = (
|
||||
#(
|
||||
salsa::input_field::InputFieldIngredient<#ident, #field_ty>,
|
||||
)*
|
||||
salsa::input::InputIngredient<#ident>,
|
||||
);
|
||||
|
||||
fn create_ingredients<DB>(
|
||||
routes: &mut salsa::routes::Routes<DB>,
|
||||
) -> Self::Ingredients
|
||||
where
|
||||
DB: salsa::DbWithJar<Self::Jar> + salsa::storage::JarFromJars<Self::Jar>,
|
||||
{
|
||||
(
|
||||
#(
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient(jar);
|
||||
&ingredients.#all_field_indices
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient_mut(jar);
|
||||
&mut ingredients.#all_field_indices
|
||||
},
|
||||
);
|
||||
salsa::input_field::InputFieldIngredient::new(index, #debug_name_fields)
|
||||
},
|
||||
)*
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient(jar);
|
||||
&ingredients.#input_index
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient_mut(jar);
|
||||
&mut ingredients.#input_index
|
||||
},
|
||||
);
|
||||
salsa::input::InputIngredient::new(index, #debug_name_struct)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For the entity, we create a tuple that contains the function ingredients
|
||||
/// for each "other" field and the entity ingredient. This is the index of
|
||||
/// the entity ingredient within that tuple.
|
||||
fn input_index(&self) -> Literal {
|
||||
Literal::usize_unsuffixed(self.all_fields().count())
|
||||
}
|
||||
|
||||
/// For the entity, we create a tuple that contains the function ingredients
|
||||
/// for each field and an entity ingredient. These are the indices
|
||||
/// of the function ingredients within that tuple.
|
||||
fn all_field_indices(&self) -> Vec<Literal> {
|
||||
self.all_fields()
|
||||
.zip(0..)
|
||||
.map(|(_, i)| Literal::usize_unsuffixed(i))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Names of setters of all fields that should be generated. Returns an optional Ident for the field name
|
||||
/// that is None when the field should not generate a setter.
|
||||
///
|
||||
/// Setters are not created for fields with #[id] tag so they'll be safe to include in debug formatting
|
||||
pub(crate) fn all_set_field_names(&self) -> Vec<Option<&syn::Ident>> {
|
||||
self.all_fields()
|
||||
.map(|ef| (!ef.has_id_attr).then(|| ef.set_name()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Implementation of `SalsaStructInDb`.
|
||||
fn salsa_struct_in_db_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
parse_quote! {
|
||||
impl<DB> salsa::salsa_struct::SalsaStructInDb<DB> for #ident
|
||||
where
|
||||
DB: ?Sized + salsa::DbWithJar<#jar_ty>,
|
||||
{
|
||||
fn register_dependent_fn(_db: &DB, _index: salsa::routes::IngredientIndex) {
|
||||
// Do nothing here, at least for now.
|
||||
// If/when we add ability to delete inputs, this would become relevant.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
195
third-party/vendor/salsa-2022-macros/src/interned.rs
vendored
Normal file
195
third-party/vendor/salsa-2022-macros/src/interned.rs
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
use crate::salsa_struct::{SalsaStruct, SalsaStructKind};
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
// #[salsa::interned(jar = Jar0, data = TyData0)]
|
||||
// #[derive(Eq, PartialEq, Hash, Debug, Clone)]
|
||||
// struct Ty0 {
|
||||
// field1: Type1,
|
||||
// #[id(ref)] field2: Type2,
|
||||
// ...
|
||||
// }
|
||||
|
||||
pub(crate) fn interned(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
match SalsaStruct::new(SalsaStructKind::Interned, args, input)
|
||||
.and_then(|el| InternedStruct(el).generate_interned())
|
||||
{
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
struct InternedStruct(SalsaStruct<Self>);
|
||||
|
||||
impl std::ops::Deref for InternedStruct {
|
||||
type Target = SalsaStruct<Self>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::options::AllowedOptions for InternedStruct {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
|
||||
const SINGLETON: bool = false;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = true;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = true;
|
||||
}
|
||||
|
||||
impl InternedStruct {
|
||||
fn generate_interned(&self) -> syn::Result<TokenStream> {
|
||||
self.validate_interned()?;
|
||||
let id_struct = self.id_struct();
|
||||
let data_struct = self.data_struct();
|
||||
let ingredients_for_impl = self.ingredients_for_impl();
|
||||
let as_id_impl = self.as_id_impl();
|
||||
let named_fields_impl = self.inherent_impl_for_named_fields();
|
||||
let salsa_struct_in_db_impl = self.salsa_struct_in_db_impl();
|
||||
let as_debug_with_db_impl = self.as_debug_with_db_impl();
|
||||
|
||||
Ok(quote! {
|
||||
#id_struct
|
||||
#data_struct
|
||||
#ingredients_for_impl
|
||||
#as_id_impl
|
||||
#named_fields_impl
|
||||
#salsa_struct_in_db_impl
|
||||
#as_debug_with_db_impl
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_interned(&self) -> syn::Result<()> {
|
||||
self.disallow_id_fields("interned")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If this is an interned struct, then generate methods to access each field,
|
||||
/// as well as a `new` method.
|
||||
fn inherent_impl_for_named_fields(&self) -> syn::ItemImpl {
|
||||
let vis = self.visibility();
|
||||
let id_ident = self.id_ident();
|
||||
let db_dyn_ty = self.db_dyn_ty();
|
||||
let jar_ty = self.jar_ty();
|
||||
|
||||
let field_getters: Vec<syn::ImplItemMethod> = self
|
||||
.all_fields()
|
||||
.map(|field| {
|
||||
let field_name = field.name();
|
||||
let field_ty = field.ty();
|
||||
let field_vis = field.vis();
|
||||
let field_get_name = field.get_name();
|
||||
if field.is_clone_field() {
|
||||
parse_quote! {
|
||||
#field_vis fn #field_get_name(self, db: &#db_dyn_ty) -> #field_ty {
|
||||
let (jar, runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #id_ident >>::ingredient(jar);
|
||||
std::clone::Clone::clone(&ingredients.data(runtime, self).#field_name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
#field_vis fn #field_get_name<'db>(self, db: &'db #db_dyn_ty) -> &'db #field_ty {
|
||||
let (jar, runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #id_ident >>::ingredient(jar);
|
||||
&ingredients.data(runtime, self).#field_name
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let field_names = self.all_field_names();
|
||||
let field_tys = self.all_field_tys();
|
||||
let data_ident = self.data_ident();
|
||||
let constructor_name = self.constructor_name();
|
||||
let new_method: syn::ImplItemMethod = parse_quote! {
|
||||
#vis fn #constructor_name(
|
||||
db: &#db_dyn_ty,
|
||||
#(#field_names: #field_tys,)*
|
||||
) -> Self {
|
||||
let (jar, runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #id_ident >>::ingredient(jar);
|
||||
ingredients.intern(runtime, #data_ident {
|
||||
#(#field_names,)*
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
parse_quote! {
|
||||
impl #id_ident {
|
||||
#(#field_getters)*
|
||||
|
||||
#new_method
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an impl of `salsa::storage::IngredientsFor`.
|
||||
///
|
||||
/// For a memoized type, the only ingredient is an `InternedIngredient`.
|
||||
fn ingredients_for_impl(&self) -> syn::ItemImpl {
|
||||
let id_ident = self.id_ident();
|
||||
let debug_name = crate::literal(id_ident);
|
||||
let jar_ty = self.jar_ty();
|
||||
let data_ident = self.data_ident();
|
||||
parse_quote! {
|
||||
impl salsa::storage::IngredientsFor for #id_ident {
|
||||
type Jar = #jar_ty;
|
||||
type Ingredients = salsa::interned::InternedIngredient<#id_ident, #data_ident>;
|
||||
|
||||
fn create_ingredients<DB>(
|
||||
routes: &mut salsa::routes::Routes<DB>,
|
||||
) -> Self::Ingredients
|
||||
where
|
||||
DB: salsa::storage::JarFromJars<Self::Jar>,
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
<_ as salsa::storage::HasIngredientsFor<Self>>::ingredient(jar)
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
<_ as salsa::storage::HasIngredientsFor<Self>>::ingredient_mut(jar)
|
||||
},
|
||||
);
|
||||
salsa::interned::InternedIngredient::new(index, #debug_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `SalsaStructInDb`.
|
||||
fn salsa_struct_in_db_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
parse_quote! {
|
||||
impl<DB> salsa::salsa_struct::SalsaStructInDb<DB> for #ident
|
||||
where
|
||||
DB: ?Sized + salsa::DbWithJar<#jar_ty>,
|
||||
{
|
||||
fn register_dependent_fn(_db: &DB, _index: salsa::routes::IngredientIndex) {
|
||||
// Do nothing here, at least for now.
|
||||
// If/when we add ability to delete inputs, this would become relevant.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
third-party/vendor/salsa-2022-macros/src/jar.rs
vendored
Normal file
172
third-party/vendor/salsa-2022-macros/src/jar.rs
vendored
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
use proc_macro2::Literal;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Field, FieldsUnnamed, Ident, ItemStruct, Path, Token};
|
||||
|
||||
use crate::options::Options;
|
||||
|
||||
// Source:
|
||||
//
|
||||
// #[salsa::jar(db = Jar0Db)]
|
||||
// pub struct Jar0(Entity0, Ty0, EntityComponent0, my_func);
|
||||
|
||||
pub(crate) fn jar(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let options = syn::parse_macro_input!(args as Args);
|
||||
let db_path = match options.db_path {
|
||||
Some(v) => v,
|
||||
None => panic!("no `db` specified"),
|
||||
};
|
||||
let input = syn::parse_macro_input!(input as ItemStruct);
|
||||
jar_struct_and_friends(&db_path, &input).into()
|
||||
}
|
||||
|
||||
type Args = Options<Jar>;
|
||||
|
||||
struct Jar;
|
||||
|
||||
impl crate::options::AllowedOptions for Jar {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
|
||||
const SINGLETON: bool = false;
|
||||
|
||||
const JAR: bool = false;
|
||||
|
||||
const DATA: bool = false;
|
||||
|
||||
const DB: bool = true;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = false;
|
||||
}
|
||||
|
||||
pub(crate) fn jar_struct_and_friends(
|
||||
jar_trait: &Path,
|
||||
input: &ItemStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let output_struct = jar_struct(input);
|
||||
|
||||
let jar_struct = &input.ident;
|
||||
|
||||
// for each field, we need to generate an impl of `HasIngredientsFor`
|
||||
let has_ingredients_for_impls: Vec<_> = input
|
||||
.fields
|
||||
.iter()
|
||||
.zip(0..)
|
||||
.map(|(field, index)| has_ingredients_for_impl(jar_struct, field, index))
|
||||
.collect();
|
||||
|
||||
let jar_impl = jar_impl(jar_struct, jar_trait, input);
|
||||
|
||||
quote! {
|
||||
#output_struct
|
||||
|
||||
#(#has_ingredients_for_impls)*
|
||||
|
||||
#jar_impl
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn has_ingredients_for_impl(
|
||||
jar_struct: &Ident,
|
||||
field: &Field,
|
||||
index: u32,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field_ty = &field.ty;
|
||||
let index = Literal::u32_unsuffixed(index);
|
||||
quote! {
|
||||
impl salsa::storage::HasIngredientsFor<#field_ty> for #jar_struct {
|
||||
fn ingredient(&self) -> &<#field_ty as salsa::storage::IngredientsFor>::Ingredients {
|
||||
&self.#index
|
||||
}
|
||||
|
||||
fn ingredient_mut(&mut self) -> &mut <#field_ty as salsa::storage::IngredientsFor>::Ingredients {
|
||||
&mut self.#index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn jar_impl(
|
||||
jar_struct: &Ident,
|
||||
jar_trait: &Path,
|
||||
input: &ItemStruct,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let field_tys: Vec<_> = input.fields.iter().map(|f| &f.ty).collect();
|
||||
let field_var_names: &Vec<_> = &input
|
||||
.fields
|
||||
.iter()
|
||||
.zip(0..)
|
||||
.map(|(f, i)| syn::LitInt::new(&format!("{}", i), f.ty.span()))
|
||||
.collect();
|
||||
// ANCHOR: init_jar
|
||||
quote! {
|
||||
unsafe impl<'salsa_db> salsa::jar::Jar<'salsa_db> for #jar_struct {
|
||||
type DynDb = dyn #jar_trait + 'salsa_db;
|
||||
|
||||
unsafe fn init_jar<DB>(place: *mut Self, routes: &mut salsa::routes::Routes<DB>)
|
||||
where
|
||||
DB: salsa::storage::JarFromJars<Self> + salsa::storage::DbWithJar<Self>,
|
||||
{
|
||||
#(
|
||||
unsafe {
|
||||
std::ptr::addr_of_mut!((*place).#field_var_names)
|
||||
.write(<#field_tys as salsa::storage::IngredientsFor>::create_ingredients(routes));
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: init_jar
|
||||
}
|
||||
|
||||
pub(crate) fn jar_struct(input: &ItemStruct) -> ItemStruct {
|
||||
let mut output_struct = input.clone();
|
||||
output_struct.fields = generate_fields(input).into();
|
||||
if output_struct.semi_token.is_none() {
|
||||
output_struct.semi_token = Some(Token);
|
||||
}
|
||||
output_struct
|
||||
}
|
||||
|
||||
fn generate_fields(input: &ItemStruct) -> FieldsUnnamed {
|
||||
// Generate the
|
||||
let mut output_fields = Punctuated::new();
|
||||
for field in input.fields.iter() {
|
||||
let mut field = field.clone();
|
||||
|
||||
// Convert to anonymous fields
|
||||
field.ident = None;
|
||||
|
||||
let field_ty = &field.ty;
|
||||
field.ty =
|
||||
syn::parse2(quote!(< #field_ty as salsa::storage::IngredientsFor >::Ingredients))
|
||||
.unwrap();
|
||||
|
||||
output_fields.push(field);
|
||||
}
|
||||
|
||||
let paren_token = match &input.fields {
|
||||
syn::Fields::Named(f) => syn::token::Paren {
|
||||
span: f.brace_token.span,
|
||||
},
|
||||
syn::Fields::Unnamed(f) => f.paren_token,
|
||||
syn::Fields::Unit => syn::token::Paren {
|
||||
span: input.ident.span(),
|
||||
},
|
||||
};
|
||||
|
||||
FieldsUnnamed {
|
||||
paren_token,
|
||||
unnamed: output_fields,
|
||||
}
|
||||
}
|
||||
73
third-party/vendor/salsa-2022-macros/src/lib.rs
vendored
Normal file
73
third-party/vendor/salsa-2022-macros/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
//! This crate provides salsa's macros and attributes.
|
||||
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
macro_rules! parse_quote {
|
||||
($($inp:tt)*) => {
|
||||
syn::parse2(quote!{$($inp)*}).unwrap_or_else(|err| {
|
||||
panic!("failed to parse at {}:{}:{}: {}", file!(), line!(), column!(), err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! parse_quote_spanned {
|
||||
($($inp:tt)*) => {
|
||||
syn::parse2(quote_spanned!{$($inp)*}).unwrap_or_else(|err| {
|
||||
panic!("failed to parse at {}:{}:{}: {}", file!(), line!(), column!(), err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a single Ident to Literal: useful when &'static str is needed.
|
||||
pub(crate) fn literal(ident: &proc_macro2::Ident) -> proc_macro2::Literal {
|
||||
proc_macro2::Literal::string(&ident.to_string())
|
||||
}
|
||||
|
||||
mod accumulator;
|
||||
mod configuration;
|
||||
mod db;
|
||||
mod input;
|
||||
mod interned;
|
||||
mod jar;
|
||||
mod options;
|
||||
mod salsa_struct;
|
||||
mod tracked;
|
||||
mod tracked_fn;
|
||||
mod tracked_struct;
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn accumulator(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
accumulator::accumulator(args, input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn jar(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
jar::jar(args, input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn db(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
db::db(args, input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn interned(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
interned::interned(args, input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn input(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
input::input(args, input)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
tracked::tracked(args, input)
|
||||
}
|
||||
280
third-party/vendor/salsa-2022-macros/src/options.rs
vendored
Normal file
280
third-party/vendor/salsa-2022-macros/src/options.rs
vendored
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use syn::{ext::IdentExt, spanned::Spanned};
|
||||
|
||||
/// "Options" are flags that can be supplied to the various salsa related
|
||||
/// macros. They are listed like `(ref, no_eq, foo=bar)` etc. The commas
|
||||
/// are required and trailing commas are permitted. The options accepted
|
||||
/// for any particular location are configured via the `AllowedOptions`
|
||||
/// trait.
|
||||
pub(crate) struct Options<A: AllowedOptions> {
|
||||
/// The `return_ref` option is used to signal that field/return type is "by ref"
|
||||
///
|
||||
/// If this is `Some`, the value is the `ref` identifier.
|
||||
pub return_ref: Option<syn::Ident>,
|
||||
|
||||
/// The `no_eq` option is used to signal that a given field does not implement
|
||||
/// the `Eq` trait and cannot be compared for equality.
|
||||
///
|
||||
/// If this is `Some`, the value is the `no_eq` identifier.
|
||||
pub no_eq: Option<syn::Ident>,
|
||||
|
||||
/// The `singleton` option is used on input with only one field
|
||||
/// It allows the creation of convenient methods
|
||||
pub singleton: Option<syn::Ident>,
|
||||
|
||||
/// The `specify` option is used to signal that a tracked function can
|
||||
/// have its value externally specified (at least some of the time).
|
||||
///
|
||||
/// If this is `Some`, the value is the `specify` identifier.
|
||||
pub specify: Option<syn::Ident>,
|
||||
|
||||
/// The `jar = <type>` option is used to indicate the jar; it defaults to `crate::jar`.
|
||||
///
|
||||
/// If this is `Some`, the value is the `<type>`.
|
||||
pub jar_ty: Option<syn::Type>,
|
||||
|
||||
/// The `db = <path>` option is used to indicate the db.
|
||||
///
|
||||
/// If this is `Some`, the value is the `<path>`.
|
||||
pub db_path: Option<syn::Path>,
|
||||
|
||||
/// The `recovery_fn = <path>` option is used to indicate the recovery function.
|
||||
///
|
||||
/// If this is `Some`, the value is the `<path>`.
|
||||
pub recovery_fn: Option<syn::Path>,
|
||||
|
||||
/// The `data = <ident>` option is used to define the name of the data type for an interned
|
||||
/// struct.
|
||||
///
|
||||
/// If this is `Some`, the value is the `<ident>`.
|
||||
pub data: Option<syn::Ident>,
|
||||
|
||||
/// The `lru = <usize>` option is used to set the lru capacity for a tracked function.
|
||||
///
|
||||
/// If this is `Some`, the value is the `<usize>`.
|
||||
pub lru: Option<usize>,
|
||||
|
||||
/// The `constructor = <ident>` option lets the user specify the name of
|
||||
/// the constructor of a salsa struct.
|
||||
///
|
||||
/// If this is `Some`, the value is the `<ident>`.
|
||||
pub constructor_name: Option<syn::Ident>,
|
||||
|
||||
/// Remember the `A` parameter, which plays no role after parsing.
|
||||
phantom: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A: AllowedOptions> Default for Options<A> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
return_ref: Default::default(),
|
||||
specify: Default::default(),
|
||||
no_eq: Default::default(),
|
||||
jar_ty: Default::default(),
|
||||
db_path: Default::default(),
|
||||
recovery_fn: Default::default(),
|
||||
data: Default::default(),
|
||||
constructor_name: Default::default(),
|
||||
phantom: Default::default(),
|
||||
lru: Default::default(),
|
||||
singleton: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// These flags determine which options are allowed in a given context
|
||||
pub(crate) trait AllowedOptions {
|
||||
const RETURN_REF: bool;
|
||||
const SPECIFY: bool;
|
||||
const NO_EQ: bool;
|
||||
const SINGLETON: bool;
|
||||
const JAR: bool;
|
||||
const DATA: bool;
|
||||
const DB: bool;
|
||||
const RECOVERY_FN: bool;
|
||||
const LRU: bool;
|
||||
const CONSTRUCTOR_NAME: bool;
|
||||
}
|
||||
|
||||
type Equals = syn::Token![=];
|
||||
type Comma = syn::Token![,];
|
||||
|
||||
impl<A: AllowedOptions> Options<A> {
|
||||
/// Returns the `jar type` given by the user; if none is given,
|
||||
/// returns the default `crate::Jar`.
|
||||
pub(crate) fn jar_ty(&self) -> syn::Type {
|
||||
if let Some(jar_ty) = &self.jar_ty {
|
||||
return jar_ty.clone();
|
||||
}
|
||||
|
||||
parse_quote! {crate::Jar}
|
||||
}
|
||||
|
||||
pub(crate) fn should_backdate(&self) -> bool {
|
||||
self.no_eq.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut options = Options::default();
|
||||
|
||||
while !input.is_empty() {
|
||||
let ident: syn::Ident = syn::Ident::parse_any(input)?;
|
||||
if ident == "return_ref" {
|
||||
if A::RETURN_REF {
|
||||
if let Some(old) = std::mem::replace(&mut options.return_ref, Some(ident)) {
|
||||
return Err(syn::Error::new(
|
||||
old.span(),
|
||||
"option `return_ref` provided twice",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`return_ref` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "no_eq" {
|
||||
if A::NO_EQ {
|
||||
if let Some(old) = std::mem::replace(&mut options.no_eq, Some(ident)) {
|
||||
return Err(syn::Error::new(old.span(), "option `no_eq` provided twice"));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`no_eq` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "singleton" {
|
||||
if A::SINGLETON {
|
||||
if let Some(old) = std::mem::replace(&mut options.singleton, Some(ident)) {
|
||||
return Err(syn::Error::new(
|
||||
old.span(),
|
||||
"option `singleton` provided twice",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`singleton` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "specify" {
|
||||
if A::SPECIFY {
|
||||
if let Some(old) = std::mem::replace(&mut options.specify, Some(ident)) {
|
||||
return Err(syn::Error::new(
|
||||
old.span(),
|
||||
"option `specify` provided twice",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`specify` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "jar" {
|
||||
if A::JAR {
|
||||
let _eq = Equals::parse(input)?;
|
||||
let ty = syn::Type::parse(input)?;
|
||||
if let Some(old) = std::mem::replace(&mut options.jar_ty, Some(ty)) {
|
||||
return Err(syn::Error::new(old.span(), "option `jar` provided twice"));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`jar` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "db" {
|
||||
if A::DB {
|
||||
let _eq = Equals::parse(input)?;
|
||||
let path = syn::Path::parse(input)?;
|
||||
if let Some(old) = std::mem::replace(&mut options.db_path, Some(path)) {
|
||||
return Err(syn::Error::new(old.span(), "option `db` provided twice"));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`db` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "recovery_fn" {
|
||||
if A::RECOVERY_FN {
|
||||
let _eq = Equals::parse(input)?;
|
||||
let path = syn::Path::parse(input)?;
|
||||
if let Some(old) = std::mem::replace(&mut options.recovery_fn, Some(path)) {
|
||||
return Err(syn::Error::new(
|
||||
old.span(),
|
||||
"option `recovery_fn` provided twice",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`recovery_fn` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "data" {
|
||||
if A::DATA {
|
||||
let _eq = Equals::parse(input)?;
|
||||
let ident = syn::Ident::parse(input)?;
|
||||
if let Some(old) = std::mem::replace(&mut options.data, Some(ident)) {
|
||||
return Err(syn::Error::new(old.span(), "option `data` provided twice"));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`data` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "lru" {
|
||||
if A::LRU {
|
||||
let _eq = Equals::parse(input)?;
|
||||
let lit = syn::LitInt::parse(input)?;
|
||||
let value = lit.base10_parse::<usize>()?;
|
||||
if let Some(old) = std::mem::replace(&mut options.lru, Some(value)) {
|
||||
return Err(syn::Error::new(old.span(), "option `lru` provided twice"));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`lru` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else if ident == "constructor" {
|
||||
if A::CONSTRUCTOR_NAME {
|
||||
let _eq = Equals::parse(input)?;
|
||||
let ident = syn::Ident::parse(input)?;
|
||||
if let Some(old) = std::mem::replace(&mut options.constructor_name, Some(ident))
|
||||
{
|
||||
return Err(syn::Error::new(
|
||||
old.span(),
|
||||
"option `constructor` provided twice",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
"`constructor` option not allowed here",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
ident.span(),
|
||||
format!("unrecognized option `{}`", ident),
|
||||
));
|
||||
}
|
||||
|
||||
if input.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let _comma = Comma::parse(input)?;
|
||||
}
|
||||
|
||||
Ok(options)
|
||||
}
|
||||
}
|
||||
472
third-party/vendor/salsa-2022-macros/src/salsa_struct.rs
vendored
Normal file
472
third-party/vendor/salsa-2022-macros/src/salsa_struct.rs
vendored
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
//! Common code for `#[salsa::interned]`, `#[salsa::input]`, and
|
||||
//! `#[salsa::tracked]` decorators.
|
||||
//!
|
||||
//! Example of usage:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! #[salsa::interned(jar = Jar0, data = TyData0)]
|
||||
//! #[derive(Eq, PartialEq, Hash, Debug, Clone)]
|
||||
//! struct Ty0 {
|
||||
//! field1: Type1,
|
||||
//! #[ref] field2: Type2,
|
||||
//! ...
|
||||
//! }
|
||||
//! ```
|
||||
//! For an interned or entity struct `Foo`, we generate:
|
||||
//!
|
||||
//! * the actual struct: `struct Foo(Id);`
|
||||
//! * constructor function: `impl Foo { fn new(db: &crate::Db, field1: Type1, ..., fieldN: TypeN) -> Self { ... } }
|
||||
//! * field accessors: `impl Foo { fn field1(&self) -> Type1 { self.field1.clone() } }`
|
||||
//! * if the field is `ref`, we generate `fn field1(&self) -> &Type1`
|
||||
//!
|
||||
//! Only if there are no `ref` fields:
|
||||
//!
|
||||
//! * the data type: `struct FooData { field1: Type1, ... }` or `enum FooData { ... }`
|
||||
//! * data method `impl Foo { fn data(&self, db: &dyn crate::Db) -> FooData { FooData { f: self.f(db), ... } } }`
|
||||
//! * this could be optimized, particularly for interned fields
|
||||
|
||||
use crate::{
|
||||
configuration,
|
||||
options::{AllowedOptions, Options},
|
||||
};
|
||||
use heck::ToUpperCamelCase;
|
||||
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
pub(crate) enum SalsaStructKind {
|
||||
Input,
|
||||
Tracked,
|
||||
Interned,
|
||||
}
|
||||
|
||||
pub(crate) struct SalsaStruct<A: AllowedOptions> {
|
||||
kind: SalsaStructKind,
|
||||
args: Options<A>,
|
||||
struct_item: syn::ItemStruct,
|
||||
fields: Vec<SalsaField>,
|
||||
}
|
||||
|
||||
const BANNED_FIELD_NAMES: &[&str] = &["from", "new"];
|
||||
|
||||
impl<A: AllowedOptions> SalsaStruct<A> {
|
||||
pub(crate) fn new(
|
||||
kind: SalsaStructKind,
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> syn::Result<Self> {
|
||||
let struct_item = syn::parse(input)?;
|
||||
Self::with_struct(kind, args, struct_item)
|
||||
}
|
||||
|
||||
pub(crate) fn with_struct(
|
||||
kind: SalsaStructKind,
|
||||
args: proc_macro::TokenStream,
|
||||
struct_item: syn::ItemStruct,
|
||||
) -> syn::Result<Self> {
|
||||
let args: Options<A> = syn::parse(args)?;
|
||||
let fields = Self::extract_options(&struct_item)?;
|
||||
Ok(Self {
|
||||
kind,
|
||||
args,
|
||||
struct_item,
|
||||
fields,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extract out the fields and their options:
|
||||
/// If this is a struct, it must use named fields, so we can define field accessors.
|
||||
/// If it is an enum, then this is not necessary.
|
||||
pub(crate) fn extract_options(struct_item: &syn::ItemStruct) -> syn::Result<Vec<SalsaField>> {
|
||||
match &struct_item.fields {
|
||||
syn::Fields::Named(n) => Ok(n
|
||||
.named
|
||||
.iter()
|
||||
.map(SalsaField::new)
|
||||
.collect::<syn::Result<Vec<_>>>()?),
|
||||
f => Err(syn::Error::new_spanned(
|
||||
f,
|
||||
"must have named fields for a struct",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over all named fields.
|
||||
///
|
||||
/// If this is an enum, empty iterator.
|
||||
pub(crate) fn all_fields(&self) -> impl Iterator<Item = &SalsaField> {
|
||||
self.fields.iter()
|
||||
}
|
||||
|
||||
pub(crate) fn is_identity_field(&self, field: &SalsaField) -> bool {
|
||||
match self.kind {
|
||||
SalsaStructKind::Input | SalsaStructKind::Tracked => field.has_id_attr,
|
||||
SalsaStructKind::Interned => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Names of all fields (id and value).
|
||||
///
|
||||
/// If this is an enum, empty vec.
|
||||
pub(crate) fn all_field_names(&self) -> Vec<&syn::Ident> {
|
||||
self.all_fields().map(|ef| ef.name()).collect()
|
||||
}
|
||||
|
||||
/// Visibilities of all fields
|
||||
pub(crate) fn all_field_vises(&self) -> Vec<&syn::Visibility> {
|
||||
self.all_fields().map(|ef| ef.vis()).collect()
|
||||
}
|
||||
|
||||
/// Names of getters of all fields
|
||||
pub(crate) fn all_get_field_names(&self) -> Vec<&syn::Ident> {
|
||||
self.all_fields().map(|ef| ef.get_name()).collect()
|
||||
}
|
||||
|
||||
/// Types of all fields (id and value).
|
||||
///
|
||||
/// If this is an enum, empty vec.
|
||||
pub(crate) fn all_field_tys(&self) -> Vec<&syn::Type> {
|
||||
self.all_fields().map(|ef| ef.ty()).collect()
|
||||
}
|
||||
|
||||
/// The name of the "identity" struct (this is the name the user gave, e.g., `Foo`).
|
||||
pub(crate) fn id_ident(&self) -> &syn::Ident {
|
||||
&self.struct_item.ident
|
||||
}
|
||||
|
||||
/// Type of the jar for this struct
|
||||
pub(crate) fn jar_ty(&self) -> syn::Type {
|
||||
self.args.jar_ty()
|
||||
}
|
||||
|
||||
/// checks if the "singleton" flag was set
|
||||
pub(crate) fn is_isingleton(&self) -> bool {
|
||||
self.args.singleton.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn db_dyn_ty(&self) -> syn::Type {
|
||||
let jar_ty = self.jar_ty();
|
||||
parse_quote! {
|
||||
<#jar_ty as salsa::jar::Jar<'_>>::DynDb
|
||||
}
|
||||
}
|
||||
|
||||
/// The name of the "data" struct (this comes from the `data = Foo` option or,
|
||||
/// if that is not provided, by concatenating `Data` to the name of the struct).
|
||||
pub(crate) fn data_ident(&self) -> syn::Ident {
|
||||
match &self.args.data {
|
||||
Some(d) => d.clone(),
|
||||
None => syn::Ident::new(
|
||||
&format!("__{}Data", self.id_ident()),
|
||||
self.id_ident().span(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate `struct Foo(Id)`
|
||||
pub(crate) fn id_struct(&self) -> syn::ItemStruct {
|
||||
let ident = self.id_ident();
|
||||
let visibility = &self.struct_item.vis;
|
||||
|
||||
// Extract the attributes the user gave, but screen out derive, since we are adding our own.
|
||||
let attrs: Vec<_> = self
|
||||
.struct_item
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| !attr.path.is_ident("derive"))
|
||||
.collect();
|
||||
|
||||
parse_quote! {
|
||||
#(#attrs)*
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
|
||||
#visibility struct #ident(salsa::Id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the `struct FooData` struct (or enum).
|
||||
/// This type inherits all the attributes written by the user.
|
||||
///
|
||||
/// When using named fields, we synthesize the struct and field names.
|
||||
///
|
||||
/// When no named fields are available, copy the existing type.
|
||||
pub(crate) fn data_struct(&self) -> syn::ItemStruct {
|
||||
let ident = self.data_ident();
|
||||
let visibility = self.visibility();
|
||||
let all_field_names = self.all_field_names();
|
||||
let all_field_tys = self.all_field_tys();
|
||||
parse_quote! {
|
||||
/// Internal struct used for interned item
|
||||
#[derive(Eq, PartialEq, Hash, Clone)]
|
||||
#visibility struct #ident {
|
||||
#(
|
||||
#all_field_names: #all_field_tys,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the visibility of this item
|
||||
pub(crate) fn visibility(&self) -> &syn::Visibility {
|
||||
&self.struct_item.vis
|
||||
}
|
||||
|
||||
/// Returns the `constructor_name` in `Options` if it is `Some`, else `new`
|
||||
pub(crate) fn constructor_name(&self) -> syn::Ident {
|
||||
match self.args.constructor_name.clone() {
|
||||
Some(name) => name,
|
||||
None => Ident::new("new", Span::call_site()),
|
||||
}
|
||||
}
|
||||
|
||||
/// For each of the fields passed as an argument,
|
||||
/// generate a struct named `Ident_Field` and an impl
|
||||
/// of `salsa::function::Configuration` for that struct.
|
||||
pub(crate) fn field_config_structs_and_impls<'a>(
|
||||
&self,
|
||||
fields: impl Iterator<Item = &'a SalsaField>,
|
||||
) -> (Vec<syn::ItemStruct>, Vec<syn::ItemImpl>) {
|
||||
let ident = &self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
let visibility = self.visibility();
|
||||
fields
|
||||
.map(|ef| {
|
||||
let value_field_name = ef.name();
|
||||
let value_field_ty = ef.ty();
|
||||
let value_field_backdate = ef.is_backdate_field();
|
||||
let config_name = syn::Ident::new(
|
||||
&format!(
|
||||
"__{}",
|
||||
format!("{}_{}", ident, value_field_name).to_upper_camel_case()
|
||||
),
|
||||
value_field_name.span(),
|
||||
);
|
||||
let item_struct: syn::ItemStruct = parse_quote! {
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Debug)]
|
||||
#visibility struct #config_name(std::convert::Infallible);
|
||||
};
|
||||
|
||||
let execute_string = Literal::string(&format!("`execute` method for field `{}::{}` invoked",
|
||||
ident,
|
||||
ef.name(),
|
||||
));
|
||||
|
||||
let recover_from_cycle_string = Literal::string(&format!("`execute` method for field `{}::{}` invoked",
|
||||
ident,
|
||||
ef.name(),
|
||||
));
|
||||
|
||||
let should_backdate_value_fn = configuration::should_backdate_value_fn(value_field_backdate);
|
||||
let item_impl: syn::ItemImpl = parse_quote! {
|
||||
impl salsa::function::Configuration for #config_name {
|
||||
type Jar = #jar_ty;
|
||||
type SalsaStruct = #ident;
|
||||
type Key = #ident;
|
||||
type Value = #value_field_ty;
|
||||
const CYCLE_STRATEGY: salsa::cycle::CycleRecoveryStrategy = salsa::cycle::CycleRecoveryStrategy::Panic;
|
||||
|
||||
#should_backdate_value_fn
|
||||
|
||||
fn execute(db: &salsa::function::DynDb<Self>, key: Self::Key) -> Self::Value {
|
||||
panic!(#execute_string)
|
||||
}
|
||||
|
||||
fn recover_from_cycle(db: &salsa::function::DynDb<Self>, cycle: &salsa::Cycle, key: Self::Key) -> Self::Value {
|
||||
panic!(#recover_from_cycle_string)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
(item_struct, item_impl)
|
||||
})
|
||||
.unzip()
|
||||
}
|
||||
|
||||
/// Generate `impl salsa::AsId for Foo`
|
||||
pub(crate) fn as_id_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
parse_quote! {
|
||||
impl salsa::AsId for #ident {
|
||||
fn as_id(self) -> salsa::Id {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn from_id(id: salsa::Id) -> Self {
|
||||
#ident(id)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate `impl salsa::DebugWithDb for Foo`
|
||||
pub(crate) fn as_debug_with_db_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
|
||||
let db_type = self.db_dyn_ty();
|
||||
let ident_string = ident.to_string();
|
||||
|
||||
// `::salsa::debug::helper::SalsaDebug` will use `DebugWithDb` or fallbak to `Debug`
|
||||
let fields = self
|
||||
.all_fields()
|
||||
.into_iter()
|
||||
.map(|field| -> TokenStream {
|
||||
let field_name_string = field.name().to_string();
|
||||
let field_getter = field.get_name();
|
||||
let field_ty = field.ty();
|
||||
|
||||
let field_debug = quote_spanned! { field.field.span() =>
|
||||
debug_struct = debug_struct.field(
|
||||
#field_name_string,
|
||||
&::salsa::debug::helper::SalsaDebug::<#field_ty, #db_type>::salsa_debug(
|
||||
#[allow(clippy::needless_borrow)]
|
||||
&self.#field_getter(_db),
|
||||
_db,
|
||||
_include_all_fields
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
if self.is_identity_field(field) {
|
||||
quote_spanned! { field.field.span() =>
|
||||
#field_debug
|
||||
}
|
||||
} else {
|
||||
quote_spanned! { field.field.span() =>
|
||||
if _include_all_fields {
|
||||
#field_debug
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
// `use ::salsa::debug::helper::Fallback` is needed for the fallback to `Debug` impl
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
impl ::salsa::DebugWithDb<#db_type> for #ident {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>, _db: &#db_type, _include_all_fields: bool) -> ::std::fmt::Result {
|
||||
#[allow(unused_imports)]
|
||||
use ::salsa::debug::helper::Fallback;
|
||||
let mut debug_struct = &mut f.debug_struct(#ident_string);
|
||||
debug_struct = debug_struct.field("[salsa id]", &self.0.as_u32());
|
||||
#fields
|
||||
debug_struct.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Disallow `#[id]` attributes on the fields of this struct.
|
||||
///
|
||||
/// If an `#[id]` field is found, return an error.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// * `kind`, the attribute name (e.g., `input` or `interned`)
|
||||
pub(crate) fn disallow_id_fields(&self, kind: &str) -> syn::Result<()> {
|
||||
for ef in self.all_fields() {
|
||||
if ef.has_id_attr {
|
||||
return Err(syn::Error::new(
|
||||
ef.name().span(),
|
||||
format!("`#[id]` cannot be used with `#[salsa::{kind}]`"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) const FIELD_OPTION_ATTRIBUTES: &[(&str, fn(&syn::Attribute, &mut SalsaField))] = &[
|
||||
("id", |_, ef| ef.has_id_attr = true),
|
||||
("return_ref", |_, ef| ef.has_ref_attr = true),
|
||||
("no_eq", |_, ef| ef.has_no_eq_attr = true),
|
||||
("get", |attr, ef| {
|
||||
ef.get_name = attr.parse_args().unwrap();
|
||||
}),
|
||||
("set", |attr, ef| {
|
||||
ef.set_name = attr.parse_args().unwrap();
|
||||
}),
|
||||
];
|
||||
|
||||
pub(crate) struct SalsaField {
|
||||
field: syn::Field,
|
||||
|
||||
pub(crate) has_id_attr: bool,
|
||||
pub(crate) has_ref_attr: bool,
|
||||
pub(crate) has_no_eq_attr: bool,
|
||||
get_name: syn::Ident,
|
||||
set_name: syn::Ident,
|
||||
}
|
||||
|
||||
impl SalsaField {
|
||||
pub(crate) fn new(field: &syn::Field) -> syn::Result<Self> {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let field_name_str = field_name.to_string();
|
||||
if BANNED_FIELD_NAMES.iter().any(|n| *n == field_name_str) {
|
||||
return Err(syn::Error::new(
|
||||
field_name.span(),
|
||||
&format!(
|
||||
"the field name `{}` is disallowed in salsa structs",
|
||||
field_name_str
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
let get_name = Ident::new(&field_name_str, Span::call_site());
|
||||
let set_name = Ident::new(&format!("set_{}", field_name_str), Span::call_site());
|
||||
let mut result = SalsaField {
|
||||
field: field.clone(),
|
||||
has_id_attr: false,
|
||||
has_ref_attr: false,
|
||||
has_no_eq_attr: false,
|
||||
get_name,
|
||||
set_name,
|
||||
};
|
||||
|
||||
// Scan the attributes and look for the salsa attributes:
|
||||
for attr in &field.attrs {
|
||||
for (fa, func) in FIELD_OPTION_ATTRIBUTES {
|
||||
if attr.path.is_ident(fa) {
|
||||
func(attr, &mut result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// The name of this field (all `SalsaField` instances are named).
|
||||
pub(crate) fn name(&self) -> &syn::Ident {
|
||||
self.field.ident.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// The visibility of this field.
|
||||
pub(crate) fn vis(&self) -> &syn::Visibility {
|
||||
&self.field.vis
|
||||
}
|
||||
|
||||
/// The type of this field (all `SalsaField` instances are named).
|
||||
pub(crate) fn ty(&self) -> &syn::Type {
|
||||
&self.field.ty
|
||||
}
|
||||
|
||||
/// The name of this field's get method
|
||||
pub(crate) fn get_name(&self) -> &syn::Ident {
|
||||
&self.get_name
|
||||
}
|
||||
|
||||
/// The name of this field's get method
|
||||
pub(crate) fn set_name(&self) -> &syn::Ident {
|
||||
&self.set_name
|
||||
}
|
||||
|
||||
/// Do you clone the value of this field? (True if it is not a ref field)
|
||||
pub(crate) fn is_clone_field(&self) -> bool {
|
||||
!self.has_ref_attr
|
||||
}
|
||||
|
||||
/// Do you potentially backdate the value of this field? (True if it is not a no-eq field)
|
||||
pub(crate) fn is_backdate_field(&self) -> bool {
|
||||
!self.has_no_eq_attr
|
||||
}
|
||||
}
|
||||
21
third-party/vendor/salsa-2022-macros/src/tracked.rs
vendored
Normal file
21
third-party/vendor/salsa-2022-macros/src/tracked.rs
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use syn::{spanned::Spanned, Item};
|
||||
|
||||
pub(crate) fn tracked(
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let item = syn::parse_macro_input!(input as Item);
|
||||
let res = match item {
|
||||
syn::Item::Struct(item) => crate::tracked_struct::tracked(args, item),
|
||||
syn::Item::Fn(item) => crate::tracked_fn::tracked_fn(args, item),
|
||||
syn::Item::Impl(item) => crate::tracked_fn::tracked_impl(args, item),
|
||||
_ => Err(syn::Error::new(
|
||||
item.span(),
|
||||
"tracked can only be applied to structs, functions, and impls",
|
||||
)),
|
||||
};
|
||||
match res {
|
||||
Ok(s) => s.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
893
third-party/vendor/salsa-2022-macros/src/tracked_fn.rs
vendored
Normal file
893
third-party/vendor/salsa-2022-macros/src/tracked_fn.rs
vendored
Normal file
|
|
@ -0,0 +1,893 @@
|
|||
use proc_macro2::{Literal, TokenStream};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::visit_mut::VisitMut;
|
||||
use syn::{ReturnType, Token};
|
||||
|
||||
use crate::configuration::{self, Configuration, CycleRecoveryStrategy};
|
||||
use crate::options::Options;
|
||||
|
||||
pub(crate) fn tracked_fn(
|
||||
args: proc_macro::TokenStream,
|
||||
mut item_fn: syn::ItemFn,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let args: FnArgs = syn::parse(args)?;
|
||||
if item_fn.sig.inputs.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
item_fn.sig.ident.span(),
|
||||
"tracked functions must have at least a database argument",
|
||||
));
|
||||
}
|
||||
|
||||
if let syn::FnArg::Receiver(receiver) = &item_fn.sig.inputs[0] {
|
||||
return Err(syn::Error::new(
|
||||
receiver.span(),
|
||||
"#[salsa::tracked] must also be applied to the impl block for tracked methods",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(s) = &args.specify {
|
||||
if function_type(&item_fn) == FunctionType::RequiresInterning {
|
||||
return Err(syn::Error::new(
|
||||
s.span(),
|
||||
"tracked function takes too many arguments to have its value set with `specify`",
|
||||
));
|
||||
}
|
||||
|
||||
if args.lru.is_some() {
|
||||
return Err(syn::Error::new(
|
||||
s.span(),
|
||||
"`specify` and `lru` cannot be used together",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let (config_ty, fn_struct) = fn_struct(&args, &item_fn)?;
|
||||
*item_fn.block = getter_fn(&args, &mut item_fn.sig, item_fn.block.span(), &config_ty)?;
|
||||
|
||||
Ok(quote! {
|
||||
#fn_struct
|
||||
|
||||
// we generate a `'db` lifetime that clippy
|
||||
// sometimes doesn't like
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
#item_fn
|
||||
})
|
||||
}
|
||||
|
||||
type FnArgs = Options<TrackedFn>;
|
||||
|
||||
struct TrackedFn;
|
||||
|
||||
impl crate::options::AllowedOptions for TrackedFn {
|
||||
const RETURN_REF: bool = true;
|
||||
|
||||
const SPECIFY: bool = true;
|
||||
|
||||
const NO_EQ: bool = true;
|
||||
|
||||
const SINGLETON: bool = false;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = false;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = true;
|
||||
|
||||
const LRU: bool = true;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = false;
|
||||
}
|
||||
|
||||
type ImplArgs = Options<TrackedImpl>;
|
||||
|
||||
pub(crate) fn tracked_impl(
|
||||
args: proc_macro::TokenStream,
|
||||
mut item_impl: syn::ItemImpl,
|
||||
) -> syn::Result<TokenStream> {
|
||||
let args: ImplArgs = syn::parse(args)?;
|
||||
let self_type = match &*item_impl.self_ty {
|
||||
syn::Type::Path(path) => path,
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
item_impl.self_ty.span(),
|
||||
"#[salsa::tracked] can only be applied to salsa structs",
|
||||
))
|
||||
}
|
||||
};
|
||||
let self_type_name = &self_type.path.segments.last().unwrap().ident;
|
||||
let name_prefix = match &item_impl.trait_ {
|
||||
Some((_, trait_name, _)) => format!(
|
||||
"{}_{}",
|
||||
self_type_name,
|
||||
trait_name.segments.last().unwrap().ident
|
||||
),
|
||||
None => format!("{}", self_type_name),
|
||||
};
|
||||
let extra_impls = item_impl
|
||||
.items
|
||||
.iter_mut()
|
||||
.filter_map(|item| {
|
||||
let item_method = match item {
|
||||
syn::ImplItem::Method(item_method) => item_method,
|
||||
_ => return None,
|
||||
};
|
||||
let salsa_tracked_attr = item_method.attrs.iter().position(|attr| {
|
||||
let path = &attr.path.segments;
|
||||
path.len() == 2
|
||||
&& path[0].arguments == syn::PathArguments::None
|
||||
&& path[0].ident == "salsa"
|
||||
&& path[1].arguments == syn::PathArguments::None
|
||||
&& path[1].ident == "tracked"
|
||||
})?;
|
||||
let salsa_tracked_attr = item_method.attrs.remove(salsa_tracked_attr);
|
||||
let inner_args = if !salsa_tracked_attr.tokens.is_empty() {
|
||||
salsa_tracked_attr.parse_args()
|
||||
} else {
|
||||
Ok(FnArgs::default())
|
||||
};
|
||||
let inner_args = match inner_args {
|
||||
Ok(inner_args) => inner_args,
|
||||
Err(err) => return Some(Err(err)),
|
||||
};
|
||||
let name = format!("{}_{}", name_prefix, item_method.sig.ident);
|
||||
Some(tracked_method(
|
||||
&args,
|
||||
inner_args,
|
||||
item_method,
|
||||
self_type,
|
||||
&name,
|
||||
))
|
||||
})
|
||||
// Collate all the errors so we can display them all at once
|
||||
.fold(Ok(Vec::new()), |mut acc, res| {
|
||||
match (&mut acc, res) {
|
||||
(Ok(extra_impls), Ok(impls)) => extra_impls.push(impls),
|
||||
(Ok(_), Err(err)) => acc = Err(err),
|
||||
(Err(_), Ok(_)) => {}
|
||||
(Err(errors), Err(err)) => errors.combine(err),
|
||||
}
|
||||
acc
|
||||
})?;
|
||||
|
||||
Ok(quote! {
|
||||
#item_impl
|
||||
|
||||
#(#extra_impls)*
|
||||
})
|
||||
}
|
||||
|
||||
struct TrackedImpl;
|
||||
|
||||
impl crate::options::AllowedOptions for TrackedImpl {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = false;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = false;
|
||||
|
||||
const SINGLETON: bool = false;
|
||||
}
|
||||
|
||||
fn tracked_method(
|
||||
outer_args: &ImplArgs,
|
||||
mut args: FnArgs,
|
||||
item_method: &mut syn::ImplItemMethod,
|
||||
self_type: &syn::TypePath,
|
||||
name: &str,
|
||||
) -> syn::Result<TokenStream> {
|
||||
args.jar_ty = args.jar_ty.or_else(|| outer_args.jar_ty.clone());
|
||||
|
||||
if item_method.sig.inputs.len() <= 1 {
|
||||
return Err(syn::Error::new(
|
||||
item_method.sig.ident.span(),
|
||||
"tracked methods must have at least self and a database argument",
|
||||
));
|
||||
}
|
||||
|
||||
let mut item_fn = syn::ItemFn {
|
||||
attrs: item_method.attrs.clone(),
|
||||
vis: item_method.vis.clone(),
|
||||
sig: item_method.sig.clone(),
|
||||
block: Box::new(rename_self_in_block(item_method.block.clone())?),
|
||||
};
|
||||
item_fn.sig.ident = syn::Ident::new(name, item_fn.sig.ident.span());
|
||||
// Flip the first and second arguments as the rest of the code expects the
|
||||
// database to come first and the struct to come second. We also need to
|
||||
// change the self argument to a normal typed argument called __salsa_self.
|
||||
let mut original_inputs = item_fn.sig.inputs.into_pairs();
|
||||
let self_param = match original_inputs.next().unwrap().into_value() {
|
||||
syn::FnArg::Receiver(r) if r.reference.is_none() => r,
|
||||
arg => return Err(syn::Error::new(arg.span(), "first argument must be self")),
|
||||
};
|
||||
let db_param = original_inputs.next().unwrap().into_value();
|
||||
let mut inputs = syn::punctuated::Punctuated::new();
|
||||
inputs.push(db_param);
|
||||
inputs.push(syn::FnArg::Typed(syn::PatType {
|
||||
attrs: self_param.attrs,
|
||||
pat: Box::new(syn::Pat::Ident(syn::PatIdent {
|
||||
attrs: Vec::new(),
|
||||
by_ref: None,
|
||||
mutability: self_param.mutability,
|
||||
ident: syn::Ident::new("__salsa_self", self_param.self_token.span),
|
||||
subpat: None,
|
||||
})),
|
||||
colon_token: Default::default(),
|
||||
ty: Box::new(syn::Type::Path(self_type.clone())),
|
||||
}));
|
||||
inputs.push_punct(Default::default());
|
||||
inputs.extend(original_inputs);
|
||||
item_fn.sig.inputs = inputs;
|
||||
|
||||
let (config_ty, fn_struct) = crate::tracked_fn::fn_struct(&args, &item_fn)?;
|
||||
|
||||
// we generate a `'db` lifetime that clippy
|
||||
// sometimes doesn't like
|
||||
item_method
|
||||
.attrs
|
||||
.push(syn::parse_quote! {#[allow(clippy::needless_lifetimes)]});
|
||||
|
||||
item_method.block = getter_fn(
|
||||
&args,
|
||||
&mut item_method.sig,
|
||||
item_method.block.span(),
|
||||
&config_ty,
|
||||
)?;
|
||||
|
||||
Ok(fn_struct)
|
||||
}
|
||||
|
||||
/// Rename all occurrences of `self` to `__salsa_self` in a block
|
||||
/// so that it can be used in a free function.
|
||||
fn rename_self_in_block(mut block: syn::Block) -> syn::Result<syn::Block> {
|
||||
struct RenameIdent(syn::Result<()>);
|
||||
|
||||
impl syn::visit_mut::VisitMut for RenameIdent {
|
||||
fn visit_ident_mut(&mut self, i: &mut syn::Ident) {
|
||||
if i == "__salsa_self" {
|
||||
let err = syn::Error::new(
|
||||
i.span(),
|
||||
"Existing variable name clashes with 'self' -> '__salsa_self' renaming",
|
||||
);
|
||||
match &mut self.0 {
|
||||
Ok(()) => self.0 = Err(err),
|
||||
Err(errors) => errors.combine(err),
|
||||
}
|
||||
}
|
||||
if i == "self" {
|
||||
*i = syn::Ident::new("__salsa_self", i.span());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut rename = RenameIdent(Ok(()));
|
||||
rename.visit_block_mut(&mut block);
|
||||
rename.0.map(move |()| block)
|
||||
}
|
||||
|
||||
/// Create the struct representing the function and all of its impls.
|
||||
///
|
||||
/// This returns the name of the constructed type and the code defining everything.
|
||||
fn fn_struct(args: &FnArgs, item_fn: &syn::ItemFn) -> syn::Result<(syn::Type, TokenStream)> {
|
||||
let struct_item = configuration_struct(item_fn);
|
||||
let configuration = fn_configuration(args, item_fn);
|
||||
let struct_item_ident = &struct_item.ident;
|
||||
let config_ty: syn::Type = parse_quote!(#struct_item_ident);
|
||||
let configuration_impl = configuration.to_impl(&config_ty);
|
||||
let ingredients_for_impl = ingredients_for_impl(args, item_fn, &config_ty);
|
||||
let item_impl = setter_impl(args, item_fn, &config_ty)?;
|
||||
|
||||
Ok((
|
||||
config_ty,
|
||||
quote! {
|
||||
#struct_item
|
||||
#configuration_impl
|
||||
#ingredients_for_impl
|
||||
#item_impl
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns the key type for this tracked function.
|
||||
/// This is a tuple of all the argument types (apart from the database).
|
||||
fn key_tuple_ty(item_fn: &syn::ItemFn) -> syn::Type {
|
||||
let arg_tys = item_fn.sig.inputs.iter().skip(1).map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => unreachable!(),
|
||||
syn::FnArg::Typed(pat_ty) => pat_ty.ty.clone(),
|
||||
});
|
||||
|
||||
parse_quote!(
|
||||
(#(#arg_tys,)*)
|
||||
)
|
||||
}
|
||||
|
||||
fn configuration_struct(item_fn: &syn::ItemFn) -> syn::ItemStruct {
|
||||
let fn_name = item_fn.sig.ident.clone();
|
||||
let visibility = &item_fn.vis;
|
||||
|
||||
let salsa_struct_ty = salsa_struct_ty(item_fn);
|
||||
let intern_map: syn::Type = match function_type(item_fn) {
|
||||
FunctionType::Constant => {
|
||||
parse_quote! { salsa::interned::IdentityInterner<()> }
|
||||
}
|
||||
FunctionType::SalsaStruct => {
|
||||
parse_quote! { salsa::interned::IdentityInterner<#salsa_struct_ty> }
|
||||
}
|
||||
FunctionType::RequiresInterning => {
|
||||
let key_ty = key_tuple_ty(item_fn);
|
||||
parse_quote! { salsa::interned::InternedIngredient<salsa::Id, #key_ty> }
|
||||
}
|
||||
};
|
||||
|
||||
parse_quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
#visibility struct #fn_name {
|
||||
intern_map: #intern_map,
|
||||
function: salsa::function::FunctionIngredient<Self>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
enum FunctionType {
|
||||
Constant,
|
||||
SalsaStruct,
|
||||
RequiresInterning,
|
||||
}
|
||||
|
||||
fn function_type(item_fn: &syn::ItemFn) -> FunctionType {
|
||||
match item_fn.sig.inputs.len() {
|
||||
0 => unreachable!(
|
||||
"functions have been checked to have at least a database argument by this point"
|
||||
),
|
||||
1 => FunctionType::Constant,
|
||||
2 => FunctionType::SalsaStruct,
|
||||
_ => FunctionType::RequiresInterning,
|
||||
}
|
||||
}
|
||||
|
||||
/// Every tracked fn takes a salsa struct as its second argument.
|
||||
/// This fn returns the type of that second argument.
|
||||
fn salsa_struct_ty(item_fn: &syn::ItemFn) -> syn::Type {
|
||||
if item_fn.sig.inputs.len() == 1 {
|
||||
return parse_quote! { salsa::salsa_struct::Singleton };
|
||||
}
|
||||
match &item_fn.sig.inputs[1] {
|
||||
syn::FnArg::Receiver(_) => panic!("receiver not expected"),
|
||||
syn::FnArg::Typed(pat_ty) => (*pat_ty.ty).clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_configuration(args: &FnArgs, item_fn: &syn::ItemFn) -> Configuration {
|
||||
let jar_ty = args.jar_ty();
|
||||
let salsa_struct_ty = salsa_struct_ty(item_fn);
|
||||
let key_ty = match function_type(item_fn) {
|
||||
FunctionType::Constant => parse_quote!(()),
|
||||
FunctionType::SalsaStruct => salsa_struct_ty.clone(),
|
||||
FunctionType::RequiresInterning => parse_quote!(salsa::id::Id),
|
||||
};
|
||||
let value_ty = configuration::value_ty(&item_fn.sig);
|
||||
|
||||
let fn_ty = item_fn.sig.ident.clone();
|
||||
|
||||
let indices = (0..item_fn.sig.inputs.len() - 1).map(Literal::usize_unsuffixed);
|
||||
let (cycle_strategy, recover_fn) = if let Some(recovery_fn) = &args.recovery_fn {
|
||||
// Create the `recover_from_cycle` function, which (a) maps from the interned id to the actual
|
||||
// keys and then (b) invokes the recover function itself.
|
||||
let cycle_strategy = CycleRecoveryStrategy::Fallback;
|
||||
|
||||
let cycle_fullback = parse_quote! {
|
||||
fn recover_from_cycle(__db: &salsa::function::DynDb<Self>, __cycle: &salsa::Cycle, __id: Self::Key) -> Self::Value {
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<#fn_ty>>::ingredient(__jar);
|
||||
let __key = __ingredients.intern_map.data(__runtime, __id).clone();
|
||||
#recovery_fn(__db, __cycle, #(__key.#indices),*)
|
||||
}
|
||||
};
|
||||
(cycle_strategy, cycle_fullback)
|
||||
} else {
|
||||
// When the `recovery_fn` attribute is not set, set `cycle_strategy` to `Panic`
|
||||
let cycle_strategy = CycleRecoveryStrategy::Panic;
|
||||
let cycle_panic = configuration::panic_cycle_recovery_fn();
|
||||
(cycle_strategy, cycle_panic)
|
||||
};
|
||||
|
||||
let backdate_fn = configuration::should_backdate_value_fn(args.should_backdate());
|
||||
|
||||
// The type of the configuration struct; this has the same name as the fn itself.
|
||||
|
||||
// Make a copy of the fn with a different name; we will invoke this from `execute`.
|
||||
// We need to change the name because, otherwise, if the function invoked itself
|
||||
// recursively it would not go through the query system.
|
||||
let inner_fn_name = &syn::Ident::new("__fn", item_fn.sig.ident.span());
|
||||
let mut inner_fn = item_fn.clone();
|
||||
inner_fn.sig.ident = inner_fn_name.clone();
|
||||
|
||||
// Create the `execute` function, which (a) maps from the interned id to the actual
|
||||
// keys and then (b) invokes the function itself (which we embed within).
|
||||
let indices = (0..item_fn.sig.inputs.len() - 1).map(Literal::usize_unsuffixed);
|
||||
let execute_fn = parse_quote! {
|
||||
fn execute(__db: &salsa::function::DynDb<Self>, __id: Self::Key) -> Self::Value {
|
||||
#inner_fn
|
||||
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<#fn_ty>>::ingredient(__jar);
|
||||
let __key = __ingredients.intern_map.data(__runtime, __id).clone();
|
||||
#inner_fn_name(__db, #(__key.#indices),*)
|
||||
}
|
||||
};
|
||||
|
||||
Configuration {
|
||||
jar_ty,
|
||||
salsa_struct_ty,
|
||||
key_ty,
|
||||
value_ty,
|
||||
cycle_strategy,
|
||||
backdate_fn,
|
||||
execute_fn,
|
||||
recover_fn,
|
||||
}
|
||||
}
|
||||
|
||||
fn ingredients_for_impl(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::ItemImpl {
|
||||
let jar_ty = args.jar_ty();
|
||||
let debug_name = crate::literal(&item_fn.sig.ident);
|
||||
|
||||
let intern_map: syn::Expr = match function_type(item_fn) {
|
||||
FunctionType::Constant | FunctionType::SalsaStruct => {
|
||||
parse_quote! {
|
||||
salsa::interned::IdentityInterner::new()
|
||||
}
|
||||
}
|
||||
FunctionType::RequiresInterning => {
|
||||
parse_quote! {
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
let ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient(jar);
|
||||
&ingredients.intern_map
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
let ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient_mut(jar);
|
||||
&mut ingredients.intern_map
|
||||
}
|
||||
);
|
||||
salsa::interned::InternedIngredient::new(index, #debug_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// set 0 as default to disable LRU
|
||||
let lru = args.lru.unwrap_or(0);
|
||||
|
||||
// get the name of the function as a string literal
|
||||
let debug_name = crate::literal(&item_fn.sig.ident);
|
||||
|
||||
parse_quote! {
|
||||
impl salsa::storage::IngredientsFor for #config_ty {
|
||||
type Ingredients = Self;
|
||||
type Jar = #jar_ty;
|
||||
|
||||
fn create_ingredients<DB>(routes: &mut salsa::routes::Routes<DB>) -> Self::Ingredients
|
||||
where
|
||||
DB: salsa::DbWithJar<Self::Jar> + salsa::storage::JarFromJars<Self::Jar>,
|
||||
{
|
||||
Self {
|
||||
intern_map: #intern_map,
|
||||
|
||||
function: {
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
let ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient(jar);
|
||||
&ingredients.function
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
let ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<Self::Ingredients>>::ingredient_mut(jar);
|
||||
&mut ingredients.function
|
||||
});
|
||||
let ingredient = salsa::function::FunctionIngredient::new(index, #debug_name);
|
||||
ingredient.set_capacity(#lru);
|
||||
ingredient
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn setter_impl(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ItemImpl> {
|
||||
let ref_getter_fn = ref_getter_fn(args, item_fn, config_ty)?;
|
||||
let accumulated_fn = accumulated_fn(args, item_fn, config_ty)?;
|
||||
let setter_fn = setter_fn(args, item_fn, config_ty)?;
|
||||
let specify_fn = specify_fn(args, item_fn, config_ty)?.map(|f| quote! { #f });
|
||||
let set_lru_fn = set_lru_capacity_fn(args, config_ty)?.map(|f| quote! { #f });
|
||||
|
||||
let setter_impl: syn::ItemImpl = parse_quote! {
|
||||
impl #config_ty {
|
||||
#[allow(dead_code, clippy::needless_lifetimes)]
|
||||
#ref_getter_fn
|
||||
|
||||
#[allow(dead_code, clippy::needless_lifetimes)]
|
||||
#setter_fn
|
||||
|
||||
#[allow(dead_code, clippy::needless_lifetimes)]
|
||||
#accumulated_fn
|
||||
|
||||
#set_lru_fn
|
||||
|
||||
#specify_fn
|
||||
}
|
||||
};
|
||||
|
||||
Ok(setter_impl)
|
||||
}
|
||||
|
||||
/// Creates the shim function that looks like the original function but calls
|
||||
/// into the machinery we've just generated rather than executing the code.
|
||||
fn getter_fn(
|
||||
args: &FnArgs,
|
||||
fn_sig: &mut syn::Signature,
|
||||
block_span: proc_macro2::Span,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::Block> {
|
||||
let mut is_method = false;
|
||||
let mut arg_idents: Vec<_> = fn_sig
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|arg| -> syn::Result<syn::Ident> {
|
||||
match arg {
|
||||
syn::FnArg::Receiver(receiver) => {
|
||||
is_method = true;
|
||||
Ok(syn::Ident::new("self", receiver.self_token.span()))
|
||||
}
|
||||
syn::FnArg::Typed(pat_ty) => Ok(match &*pat_ty.pat {
|
||||
syn::Pat::Ident(ident) => ident.ident.clone(),
|
||||
_ => return Err(syn::Error::new(arg.span(), "unsupported argument kind")),
|
||||
}),
|
||||
}
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
// If this is a method then the order of the database and the salsa struct are reversed
|
||||
// because the self argument must always come first.
|
||||
if is_method {
|
||||
arg_idents.swap(0, 1);
|
||||
}
|
||||
Ok(if args.return_ref.is_some() {
|
||||
make_fn_return_ref(fn_sig)?;
|
||||
parse_quote_spanned! {
|
||||
block_span => {
|
||||
#config_ty::get(#(#arg_idents,)*)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote_spanned! {
|
||||
block_span => {
|
||||
Clone::clone(#config_ty::get(#(#arg_idents,)*))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a `get` associated function that returns `&Value`
|
||||
/// (to be used when `return_ref` is specified).
|
||||
///
|
||||
/// (Helper for `getter_fn`)
|
||||
fn ref_getter_fn(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ItemFn> {
|
||||
let jar_ty = args.jar_ty();
|
||||
let mut ref_getter_fn = item_fn.clone();
|
||||
ref_getter_fn.sig.ident = syn::Ident::new("get", item_fn.sig.ident.span());
|
||||
make_fn_return_ref(&mut ref_getter_fn.sig)?;
|
||||
|
||||
let (db_var, arg_names) = fn_args(item_fn)?;
|
||||
ref_getter_fn.block = parse_quote! {
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(#db_var);
|
||||
let __ingredients = <_ as salsa::storage::HasIngredientsFor<#config_ty>>::ingredient(__jar);
|
||||
let __key = __ingredients.intern_map.intern(__runtime, (#(#arg_names),*));
|
||||
__ingredients.function.fetch(#db_var, __key)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ref_getter_fn)
|
||||
}
|
||||
|
||||
/// Creates a `set` associated function that can be used to set (given an `&mut db`)
|
||||
/// the value for this function for some inputs.
|
||||
fn setter_fn(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ImplItemMethod> {
|
||||
// The setter has *always* the same signature as the original:
|
||||
// but it takes a value arg and has no return type.
|
||||
let jar_ty = args.jar_ty();
|
||||
let (db_var, arg_names) = fn_args(item_fn)?;
|
||||
let mut setter_sig = item_fn.sig.clone();
|
||||
let value_ty = configuration::value_ty(&item_fn.sig);
|
||||
setter_sig.ident = syn::Ident::new("set", item_fn.sig.ident.span());
|
||||
match &mut setter_sig.inputs[0] {
|
||||
// change from `&dyn ...` to `&mut dyn...`
|
||||
syn::FnArg::Receiver(_) => unreachable!(), // early fns should have detected
|
||||
syn::FnArg::Typed(pat_ty) => match &mut *pat_ty.ty {
|
||||
syn::Type::Reference(ty) => {
|
||||
ty.mutability = Some(Token));
|
||||
}
|
||||
_ => unreachable!(), // early fns should have detected
|
||||
},
|
||||
}
|
||||
let value_arg = syn::Ident::new("__value", item_fn.sig.output.span());
|
||||
setter_sig.inputs.push(parse_quote!(#value_arg: #value_ty));
|
||||
setter_sig.output = ReturnType::Default;
|
||||
Ok(syn::ImplItemMethod {
|
||||
attrs: vec![],
|
||||
vis: item_fn.vis.clone(),
|
||||
defaultness: None,
|
||||
sig: setter_sig,
|
||||
block: parse_quote! {
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar_mut(#db_var);
|
||||
let __ingredients = <_ as salsa::storage::HasIngredientsFor<#config_ty>>::ingredient_mut(__jar);
|
||||
let __key = __ingredients.intern_map.intern(__runtime, (#(#arg_names),*));
|
||||
__ingredients.function.store(__runtime, __key, #value_arg, salsa::Durability::LOW)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a `set_lru_capacity` associated function that can be used to change LRU
|
||||
/// capacity at runtime.
|
||||
/// Note that this function is only generated if the tracked function has the lru option set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[salsa::tracked(lru=32)]
|
||||
/// fn my_tracked_fn(db: &dyn crate::Db, ...) { }
|
||||
///
|
||||
/// my_tracked_fn::set_lru_capacity(16)
|
||||
/// ```
|
||||
fn set_lru_capacity_fn(
|
||||
args: &FnArgs,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<Option<syn::ImplItemMethod>> {
|
||||
if args.lru.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let jar_ty = args.jar_ty();
|
||||
let lru_fn = parse_quote! {
|
||||
#[allow(dead_code, clippy::needless_lifetimes)]
|
||||
fn set_lru_capacity(__db: &salsa::function::DynDb<Self>, __value: usize) {
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients =
|
||||
<_ as salsa::storage::HasIngredientsFor<#config_ty>>::ingredient(__jar);
|
||||
__ingredients.function.set_capacity(__value);
|
||||
}
|
||||
};
|
||||
Ok(Some(lru_fn))
|
||||
}
|
||||
|
||||
fn specify_fn(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<Option<syn::ImplItemMethod>> {
|
||||
if args.specify.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// `specify` has the same signature as the original,
|
||||
// but it takes a value arg and has no return type.
|
||||
let jar_ty = args.jar_ty();
|
||||
let (db_var, arg_names) = fn_args(item_fn)?;
|
||||
let mut setter_sig = item_fn.sig.clone();
|
||||
let value_ty = configuration::value_ty(&item_fn.sig);
|
||||
setter_sig.ident = syn::Ident::new("specify", item_fn.sig.ident.span());
|
||||
let value_arg = syn::Ident::new("__value", item_fn.sig.output.span());
|
||||
setter_sig.inputs.push(parse_quote!(#value_arg: #value_ty));
|
||||
setter_sig.output = ReturnType::Default;
|
||||
Ok(Some(syn::ImplItemMethod {
|
||||
attrs: vec![],
|
||||
vis: item_fn.vis.clone(),
|
||||
defaultness: None,
|
||||
sig: setter_sig,
|
||||
block: parse_quote! {
|
||||
{
|
||||
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(#db_var);
|
||||
let __ingredients = <_ as salsa::storage::HasIngredientsFor<#config_ty>>::ingredient(__jar);
|
||||
__ingredients.function.specify_and_record(#db_var, #(#arg_names,)* #value_arg)
|
||||
}
|
||||
},
|
||||
}))
|
||||
}
|
||||
/// Given a function def tagged with `#[return_ref]`, modifies `fn_sig` so that
|
||||
/// it returns an `&Value` instead of `Value`. May introduce a name for the
|
||||
/// database lifetime if required.
|
||||
fn make_fn_return_ref(fn_sig: &mut syn::Signature) -> syn::Result<()> {
|
||||
// An input should be a `&dyn Db`.
|
||||
// We need to ensure it has a named lifetime parameter.
|
||||
let (db_lifetime, _) = db_lifetime_and_ty(fn_sig)?;
|
||||
|
||||
let (right_arrow, elem) = match fn_sig.output.clone() {
|
||||
ReturnType::Default => (syn::Token, parse_quote!(())),
|
||||
ReturnType::Type(rarrow, ty) => (rarrow, ty),
|
||||
};
|
||||
|
||||
let ref_output = syn::TypeReference {
|
||||
and_token: syn::Token),
|
||||
lifetime: Some(db_lifetime),
|
||||
mutability: None,
|
||||
elem,
|
||||
};
|
||||
|
||||
fn_sig.output = syn::ReturnType::Type(right_arrow, Box::new(ref_output.into()));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Given a function signature, identifies the name given to the `&dyn Db` reference
|
||||
/// and returns it, along with the type of the database.
|
||||
/// If the database lifetime did not have a name, then modifies the item function
|
||||
/// so that it is called `'__db` and returns that.
|
||||
fn db_lifetime_and_ty(func: &mut syn::Signature) -> syn::Result<(syn::Lifetime, &syn::Type)> {
|
||||
// If this is a method, then the database should be the second argument.
|
||||
let db_loc = if matches!(func.inputs[0], syn::FnArg::Receiver(_)) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
match &mut func.inputs[db_loc] {
|
||||
syn::FnArg::Receiver(r) => Err(syn::Error::new(r.span(), "two self arguments")),
|
||||
syn::FnArg::Typed(pat_ty) => match &mut *pat_ty.ty {
|
||||
syn::Type::Reference(ty) => match &ty.lifetime {
|
||||
Some(lt) => Ok((lt.clone(), &pat_ty.ty)),
|
||||
None => {
|
||||
let and_token_span = ty.and_token.span();
|
||||
let ident = syn::Ident::new("__db", and_token_span);
|
||||
func.generics.params.insert(
|
||||
0,
|
||||
syn::LifetimeDef {
|
||||
attrs: vec![],
|
||||
lifetime: syn::Lifetime {
|
||||
apostrophe: and_token_span,
|
||||
ident: ident.clone(),
|
||||
},
|
||||
colon_token: None,
|
||||
bounds: Default::default(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
let db_lifetime = syn::Lifetime {
|
||||
apostrophe: and_token_span,
|
||||
ident,
|
||||
};
|
||||
ty.lifetime = Some(db_lifetime.clone());
|
||||
Ok((db_lifetime, &pat_ty.ty))
|
||||
}
|
||||
},
|
||||
_ => Err(syn::Error::new(
|
||||
pat_ty.span(),
|
||||
"expected database to be a `&` type",
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the `accumulated` function, which invokes `accumulated`
|
||||
/// on the function ingredient to extract the values pushed (transitively)
|
||||
/// into an accumulator.
|
||||
fn accumulated_fn(
|
||||
args: &FnArgs,
|
||||
item_fn: &syn::ItemFn,
|
||||
config_ty: &syn::Type,
|
||||
) -> syn::Result<syn::ItemFn> {
|
||||
let jar_ty = args.jar_ty();
|
||||
|
||||
let mut accumulated_fn = item_fn.clone();
|
||||
accumulated_fn.sig.ident = syn::Ident::new("accumulated", item_fn.sig.ident.span());
|
||||
accumulated_fn.sig.generics.params.push(parse_quote! {
|
||||
__A: salsa::accumulator::Accumulator
|
||||
});
|
||||
accumulated_fn.sig.output = parse_quote! {
|
||||
-> Vec<<__A as salsa::accumulator::Accumulator>::Data>
|
||||
};
|
||||
|
||||
let (db_lifetime, _) = db_lifetime_and_ty(&mut accumulated_fn.sig)?;
|
||||
let predicate: syn::WherePredicate = parse_quote!(<#jar_ty as salsa::jar::Jar<#db_lifetime>>::DynDb: salsa::storage::HasJar<<__A as salsa::accumulator::Accumulator>::Jar>);
|
||||
|
||||
if let Some(where_clause) = &mut accumulated_fn.sig.generics.where_clause {
|
||||
where_clause.predicates.push(predicate);
|
||||
} else {
|
||||
accumulated_fn.sig.generics.where_clause = parse_quote!(where #predicate);
|
||||
}
|
||||
|
||||
let (db_var, arg_names) = fn_args(item_fn)?;
|
||||
accumulated_fn.block = parse_quote! {
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(#db_var);
|
||||
let __ingredients = <_ as salsa::storage::HasIngredientsFor<#config_ty>>::ingredient(__jar);
|
||||
let __key = __ingredients.intern_map.intern(__runtime, (#(#arg_names),*));
|
||||
__ingredients.function.accumulated::<__A>(#db_var, __key)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(accumulated_fn)
|
||||
}
|
||||
|
||||
/// Examines the function arguments and returns a tuple of:
|
||||
///
|
||||
/// * the name of the database argument
|
||||
/// * the name(s) of the key arguments
|
||||
fn fn_args(item_fn: &syn::ItemFn) -> syn::Result<(proc_macro2::Ident, Vec<proc_macro2::Ident>)> {
|
||||
// Check that we have no receiver and that all arguments have names
|
||||
if item_fn.sig.inputs.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
item_fn.sig.span(),
|
||||
"method needs a database argument",
|
||||
));
|
||||
}
|
||||
|
||||
let mut input_names = vec![];
|
||||
for input in &item_fn.sig.inputs {
|
||||
match input {
|
||||
syn::FnArg::Receiver(r) => {
|
||||
return Err(syn::Error::new(r.span(), "no self argument expected"));
|
||||
}
|
||||
syn::FnArg::Typed(pat_ty) => match &*pat_ty.pat {
|
||||
syn::Pat::Ident(ident) => {
|
||||
input_names.push(ident.ident.clone());
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
pat_ty.pat.span(),
|
||||
"all arguments must be given names",
|
||||
));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Database is the first argument
|
||||
let db_var = input_names[0].clone();
|
||||
let arg_names = input_names[1..].to_owned();
|
||||
|
||||
Ok((db_var, arg_names))
|
||||
}
|
||||
324
third-party/vendor/salsa-2022-macros/src/tracked_struct.rs
vendored
Normal file
324
third-party/vendor/salsa-2022-macros/src/tracked_struct.rs
vendored
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
use proc_macro2::{Literal, TokenStream};
|
||||
|
||||
use crate::salsa_struct::{SalsaField, SalsaStruct, SalsaStructKind};
|
||||
|
||||
/// For an tracked struct `Foo` with fields `f1: T1, ..., fN: TN`, we generate...
|
||||
///
|
||||
/// * the "id struct" `struct Foo(salsa::Id)`
|
||||
/// * the tracked ingredient, which maps the id fields to the `Id`
|
||||
/// * for each value field, a function ingredient
|
||||
pub(crate) fn tracked(
|
||||
args: proc_macro::TokenStream,
|
||||
struct_item: syn::ItemStruct,
|
||||
) -> syn::Result<TokenStream> {
|
||||
SalsaStruct::with_struct(SalsaStructKind::Tracked, args, struct_item)
|
||||
.and_then(|el| TrackedStruct(el).generate_tracked())
|
||||
}
|
||||
|
||||
struct TrackedStruct(SalsaStruct<Self>);
|
||||
|
||||
impl std::ops::Deref for TrackedStruct {
|
||||
type Target = SalsaStruct<Self>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::options::AllowedOptions for TrackedStruct {
|
||||
const RETURN_REF: bool = false;
|
||||
|
||||
const SPECIFY: bool = false;
|
||||
|
||||
const NO_EQ: bool = false;
|
||||
|
||||
const SINGLETON: bool = false;
|
||||
|
||||
const JAR: bool = true;
|
||||
|
||||
const DATA: bool = true;
|
||||
|
||||
const DB: bool = false;
|
||||
|
||||
const RECOVERY_FN: bool = false;
|
||||
|
||||
const LRU: bool = false;
|
||||
|
||||
const CONSTRUCTOR_NAME: bool = true;
|
||||
}
|
||||
|
||||
impl TrackedStruct {
|
||||
fn generate_tracked(&self) -> syn::Result<TokenStream> {
|
||||
self.validate_tracked()?;
|
||||
|
||||
let (config_structs, config_impls) =
|
||||
self.field_config_structs_and_impls(self.value_fields());
|
||||
|
||||
let id_struct = self.id_struct();
|
||||
let inherent_impl = self.tracked_inherent_impl();
|
||||
let ingredients_for_impl = self.tracked_struct_ingredients(&config_structs);
|
||||
let salsa_struct_in_db_impl = self.salsa_struct_in_db_impl();
|
||||
let tracked_struct_in_db_impl = self.tracked_struct_in_db_impl();
|
||||
let as_id_impl = self.as_id_impl();
|
||||
let as_debug_with_db_impl = self.as_debug_with_db_impl();
|
||||
Ok(quote! {
|
||||
#(#config_structs)*
|
||||
#id_struct
|
||||
#inherent_impl
|
||||
#ingredients_for_impl
|
||||
#salsa_struct_in_db_impl
|
||||
#tracked_struct_in_db_impl
|
||||
#as_id_impl
|
||||
#as_debug_with_db_impl
|
||||
#(#config_impls)*
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_tracked(&self) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate an inherent impl with methods on the tracked type.
|
||||
fn tracked_inherent_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
let db_dyn_ty = self.db_dyn_ty();
|
||||
let struct_index = self.tracked_struct_index();
|
||||
|
||||
let id_field_indices: Vec<_> = self.id_field_indices();
|
||||
let id_field_names: Vec<_> = self.id_fields().map(SalsaField::name).collect();
|
||||
let id_field_get_names: Vec<_> = self.id_fields().map(SalsaField::get_name).collect();
|
||||
let id_field_tys: Vec<_> = self.id_fields().map(SalsaField::ty).collect();
|
||||
let id_field_vises: Vec<_> = self.id_fields().map(SalsaField::vis).collect();
|
||||
let id_field_clones: Vec<_> = self.id_fields().map(SalsaField::is_clone_field).collect();
|
||||
let id_field_getters: Vec<syn::ImplItemMethod> = id_field_indices.iter().zip(&id_field_get_names).zip(&id_field_tys).zip(&id_field_vises).zip(&id_field_clones).map(|((((field_index, field_get_name), field_ty), field_vis), is_clone_field)|
|
||||
if !*is_clone_field {
|
||||
parse_quote! {
|
||||
#field_vis fn #field_get_name<'db>(self, __db: &'db #db_dyn_ty) -> &'db #field_ty
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
&__ingredients.#struct_index.tracked_struct_data(__runtime, self).#field_index
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
#field_vis fn #field_get_name<'db>(self, __db: &'db #db_dyn_ty) -> #field_ty
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#struct_index.tracked_struct_data(__runtime, self).#field_index.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.collect();
|
||||
|
||||
let value_field_indices = self.value_field_indices();
|
||||
let value_field_names: Vec<_> = self.value_fields().map(SalsaField::name).collect();
|
||||
let value_field_vises: Vec<_> = self.value_fields().map(SalsaField::vis).collect();
|
||||
let value_field_tys: Vec<_> = self.value_fields().map(SalsaField::ty).collect();
|
||||
let value_field_get_names: Vec<_> = self.value_fields().map(SalsaField::get_name).collect();
|
||||
let value_field_clones: Vec<_> = self
|
||||
.value_fields()
|
||||
.map(SalsaField::is_clone_field)
|
||||
.collect();
|
||||
let value_field_getters: Vec<syn::ImplItemMethod> = value_field_indices.iter().zip(&value_field_get_names).zip(&value_field_tys).zip(&value_field_vises).zip(&value_field_clones).map(|((((field_index, field_get_name), field_ty), field_vis), is_clone_field)|
|
||||
if !*is_clone_field {
|
||||
parse_quote! {
|
||||
#field_vis fn #field_get_name<'db>(self, __db: &'db #db_dyn_ty) -> &'db #field_ty
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#field_index.fetch(__db, self)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_quote! {
|
||||
#field_vis fn #field_get_name<'db>(self, __db: &'db #db_dyn_ty) -> #field_ty
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
__ingredients.#field_index.fetch(__db, self).clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.collect();
|
||||
|
||||
let all_field_names = self.all_field_names();
|
||||
let all_field_tys = self.all_field_tys();
|
||||
let constructor_name = self.constructor_name();
|
||||
|
||||
parse_quote! {
|
||||
impl #ident {
|
||||
pub fn #constructor_name(__db: &#db_dyn_ty, #(#all_field_names: #all_field_tys,)*) -> Self
|
||||
{
|
||||
let (__jar, __runtime) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(__db);
|
||||
let __ingredients = <#jar_ty as salsa::storage::HasIngredientsFor< #ident >>::ingredient(__jar);
|
||||
let __id = __ingredients.#struct_index.new_struct(__runtime, (#(#id_field_names,)*));
|
||||
#(
|
||||
__ingredients.#value_field_indices.specify_and_record(__db, __id, #value_field_names);
|
||||
)*
|
||||
__id
|
||||
}
|
||||
|
||||
#(#id_field_getters)*
|
||||
|
||||
#(#value_field_getters)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the `IngredientsFor` impl for this tracked struct.
|
||||
///
|
||||
/// The tracked struct's ingredients include both the main tracked struct ingredient along with a
|
||||
/// function ingredient for each of the value fields.
|
||||
fn tracked_struct_ingredients(&self, config_structs: &[syn::ItemStruct]) -> syn::ItemImpl {
|
||||
use crate::literal;
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
let id_field_tys: Vec<&syn::Type> = self.id_fields().map(SalsaField::ty).collect();
|
||||
let value_field_indices: Vec<Literal> = self.value_field_indices();
|
||||
let tracked_struct_index: Literal = self.tracked_struct_index();
|
||||
let config_struct_names = config_structs.iter().map(|s| &s.ident);
|
||||
let debug_name_struct = literal(self.id_ident());
|
||||
let debug_name_fields: Vec<_> = self.all_field_names().into_iter().map(literal).collect();
|
||||
|
||||
parse_quote! {
|
||||
impl salsa::storage::IngredientsFor for #ident {
|
||||
type Jar = #jar_ty;
|
||||
type Ingredients = (
|
||||
#(
|
||||
salsa::function::FunctionIngredient<#config_struct_names>,
|
||||
)*
|
||||
salsa::tracked_struct::TrackedStructIngredient<#ident, (#(#id_field_tys,)*)>,
|
||||
);
|
||||
|
||||
fn create_ingredients<DB>(
|
||||
routes: &mut salsa::routes::Routes<DB>,
|
||||
) -> Self::Ingredients
|
||||
where
|
||||
DB: salsa::DbWithJar<Self::Jar> + salsa::storage::JarFromJars<Self::Jar>,
|
||||
{
|
||||
(
|
||||
#(
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient(jar);
|
||||
&ingredients.#value_field_indices
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient_mut(jar);
|
||||
&mut ingredients.#value_field_indices
|
||||
},
|
||||
);
|
||||
salsa::function::FunctionIngredient::new(index, #debug_name_fields)
|
||||
},
|
||||
)*
|
||||
{
|
||||
let index = routes.push(
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient(jar);
|
||||
&ingredients.#tracked_struct_index
|
||||
},
|
||||
|jars| {
|
||||
let jar = <DB as salsa::storage::JarFromJars<Self::Jar>>::jar_from_jars_mut(jars);
|
||||
let ingredients = <_ as salsa::storage::HasIngredientsFor<Self>>::ingredient_mut(jar);
|
||||
&mut ingredients.#tracked_struct_index
|
||||
},
|
||||
);
|
||||
salsa::tracked_struct::TrackedStructIngredient::new(index, #debug_name_struct)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `SalsaStructInDb`.
|
||||
fn salsa_struct_in_db_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
let tracked_struct_index: Literal = self.tracked_struct_index();
|
||||
parse_quote! {
|
||||
impl<DB> salsa::salsa_struct::SalsaStructInDb<DB> for #ident
|
||||
where
|
||||
DB: ?Sized + salsa::DbWithJar<#jar_ty>,
|
||||
{
|
||||
fn register_dependent_fn(db: &DB, index: salsa::routes::IngredientIndex) {
|
||||
let (jar, _) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor<#ident>>::ingredient(jar);
|
||||
ingredients.#tracked_struct_index.register_dependent_fn(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `TrackedStructInDb`.
|
||||
fn tracked_struct_in_db_impl(&self) -> syn::ItemImpl {
|
||||
let ident = self.id_ident();
|
||||
let jar_ty = self.jar_ty();
|
||||
let tracked_struct_index = self.tracked_struct_index();
|
||||
parse_quote! {
|
||||
impl<DB> salsa::tracked_struct::TrackedStructInDb<DB> for #ident
|
||||
where
|
||||
DB: ?Sized + salsa::DbWithJar<#jar_ty>,
|
||||
{
|
||||
fn database_key_index(self, db: &DB) -> salsa::DatabaseKeyIndex {
|
||||
let (jar, _) = <_ as salsa::storage::HasJar<#jar_ty>>::jar(db);
|
||||
let ingredients = <#jar_ty as salsa::storage::HasIngredientsFor<#ident>>::ingredient(jar);
|
||||
ingredients.#tracked_struct_index.database_key_index(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List of id fields (fields that are part of the tracked struct's identity across revisions).
|
||||
///
|
||||
/// If this is an enum, empty iterator.
|
||||
fn id_fields(&self) -> impl Iterator<Item = &SalsaField> {
|
||||
self.all_fields().filter(|ef| ef.is_id_field())
|
||||
}
|
||||
|
||||
/// List of value fields (fields that are not part of the tracked struct's identity across revisions).
|
||||
///
|
||||
/// If this is an enum, empty iterator.
|
||||
fn value_fields(&self) -> impl Iterator<Item = &SalsaField> {
|
||||
self.all_fields().filter(|ef| !ef.is_id_field())
|
||||
}
|
||||
|
||||
/// For this struct, we create a tuple that contains the function ingredients
|
||||
/// for each "other" field and the tracked-struct ingredient. This is the index of
|
||||
/// the entity ingredient within that tuple.
|
||||
fn tracked_struct_index(&self) -> Literal {
|
||||
Literal::usize_unsuffixed(self.value_fields().count())
|
||||
}
|
||||
|
||||
/// For this struct, we create a tuple that contains the function ingredients
|
||||
/// for each "other" field and the tracked-struct ingredient. These are the indices
|
||||
/// of the function ingredients within that tuple.
|
||||
fn value_field_indices(&self) -> Vec<Literal> {
|
||||
(0..self.value_fields().count())
|
||||
.map(Literal::usize_unsuffixed)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Indices of each of the id fields
|
||||
fn id_field_indices(&self) -> Vec<Literal> {
|
||||
(0..self.id_fields().count())
|
||||
.map(Literal::usize_unsuffixed)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl SalsaField {
|
||||
/// true if this is an id field
|
||||
fn is_id_field(&self) -> bool {
|
||||
self.has_id_attr
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue