This commit is contained in:
Niko Matsakis 2024-07-15 20:29:36 -04:00
parent 4769e32d44
commit 612cec6703
20 changed files with 227 additions and 330 deletions

View file

@ -14,6 +14,7 @@
mod maybe_backdate;
mod maybe_clone;
mod setup_accumulator_impl;
mod setup_input_struct;
mod setup_interned_fn;
mod setup_interned_struct;

View file

@ -0,0 +1,43 @@
/// Macro for setting up a function that must intern its arguments.
#[macro_export]
macro_rules! setup_accumulator_impl {
(
// Name of the struct
Struct: $Struct:ident,
// Annoyingly macro-rules hygiene does not extend to items defined in the macro.
// We have the procedural macro generate names for those items that are
// not used elsewhere in the user's code.
unused_names: [
$zalsa:ident,
$zalsa_struct:ident,
$CACHE:ident,
$ingredient:ident,
]
) => {
const _: () = {
use salsa::plumbing as $zalsa;
use salsa::plumbing::accumulator as $zalsa_struct;
static $CACHE: $zalsa::IngredientCache<$zalsa_struct::IngredientImpl<$Struct>> =
$zalsa::IngredientCache::new();
fn $ingredient(db: &dyn $zalsa::Database) -> &$zalsa_struct::IngredientImpl<$Struct> {
$CACHE.get_or_create(db, || {
db.add_or_lookup_jar_by_type(&<$zalsa_struct::JarImpl<$Struct>>::default())
})
}
impl $zalsa::Accumulator for $Struct {
const DEBUG_NAME: &'static str = stringify!($Struct);
fn accumulate<Db>(self, db: &Db)
where
Db: ?Sized + $zalsa::Database,
{
$ingredient(db.as_salsa_database()).push(db.runtime(), self);
}
}
};
};
}

View file

@ -52,6 +52,11 @@ macro_rules! setup_interned_fn {
$inner:ident,
]
) => {
#[allow(non_camel_case_types)]
$vis struct $fn_name {
_priv: std::convert::Infallible,
}
$(#[$attr])*
$vis fn $fn_name<$db_lt>(
$db: &$db_lt dyn $Db,
@ -62,7 +67,7 @@ macro_rules! setup_interned_fn {
struct $Configuration;
#[derive(Copy, Clone)]
struct $InternedData<'db>(
struct $InternedData<$db_lt>(
std::ptr::NonNull<$zalsa::interned::Value<$Configuration>>,
std::marker::PhantomData<&'db $zalsa::interned::Value<$Configuration>>,
);
@ -73,6 +78,23 @@ macro_rules! setup_interned_fn {
static $INTERN_CACHE: $zalsa::IngredientCache<$zalsa::interned::IngredientImpl<$Configuration>> =
$zalsa::IngredientCache::new();
impl $Configuration {
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
$FN_CACHE.get_or_create(db.as_salsa_database(), || {
<dyn $Db as $Db>::zalsa_db(db);
db.add_or_lookup_jar_by_type(&$Configuration)
})
}
fn intern_ingredient(
db: &dyn $Db,
) -> &$zalsa::interned::IngredientImpl<$Configuration> {
$INTERN_CACHE.get_or_create(db.as_salsa_database(), || {
db.add_or_lookup_jar_by_type(&$Configuration).successor(0)
})
}
}
impl $zalsa::SalsaStructInDb for $InternedData<'_> {
fn register_dependent_fn(_db: &dyn $zalsa::Database, _index: $zalsa::IngredientIndex) {}
}
@ -112,10 +134,7 @@ macro_rules! setup_interned_fn {
}
fn id_to_input<'db>(db: &'db Self::DbView, key: salsa::Id) -> Self::Input<'db> {
let ingredient = $INTERN_CACHE.get_or_create(db.as_salsa_database(), || {
db.add_or_lookup_jar_by_type(&$Configuration).successor(0)
});
ingredient.data(key).clone()
$Configuration::intern_ingredient(db).data(key).clone()
}
}
@ -153,17 +172,21 @@ macro_rules! setup_interned_fn {
}
}
$zalsa::attach_database($db, || {
let intern_ingredient = $INTERN_CACHE.get_or_create($db.as_salsa_database(), || {
$db.add_or_lookup_jar_by_type(&$Configuration).successor(0)
});
let key = intern_ingredient.intern_id($db.runtime(), ($($input_id),*));
impl $fn_name {
pub fn accumulated<$db_lt, A: salsa::Accumulator>(
$db: &$db_lt dyn $Db,
$($input_id: $input_ty,)*
) -> Vec<A> {
use salsa::plumbing as $zalsa;
let key = $Configuration::intern_ingredient($db).intern_id($db.runtime(), ($($input_id),*));
let database_key_index = $Configuration::fn_ingredient($db).database_key_index(key);
$zalsa::accumulated_by($db.as_salsa_database(), database_key_index)
}
}
let fn_ingredient = $FN_CACHE.get_or_create($db.as_salsa_database(), || {
<dyn $Db as $Db>::zalsa_db($db);
$db.add_or_lookup_jar_by_type(&$Configuration)
});
fn_ingredient.fetch($db, key).clone()
$zalsa::attach_database($db, || {
let key = $Configuration::intern_ingredient($db).intern_id($db.runtime(), ($($input_id),*));
$Configuration::fn_ingredient($db).fetch($db, key).clone()
})
}
};

View file

@ -50,6 +50,11 @@ macro_rules! setup_struct_fn {
$inner:ident,
]
) => {
#[allow(non_camel_case_types)]
$vis struct $fn_name {
_priv: std::convert::Infallible,
}
$(#[$attr])*
$vis fn $fn_name<$db_lt>(
$db: &$db_lt dyn $Db,
@ -62,6 +67,15 @@ macro_rules! setup_struct_fn {
static $FN_CACHE: $zalsa::IngredientCache<$zalsa::function::IngredientImpl<$Configuration>> =
$zalsa::IngredientCache::new();
impl $Configuration {
fn fn_ingredient(db: &dyn $Db) -> &$zalsa::function::IngredientImpl<$Configuration> {
$FN_CACHE.get_or_create(db.as_salsa_database(), || {
<dyn $Db as $Db>::zalsa_db(db);
db.add_or_lookup_jar_by_type(&$Configuration)
})
}
}
impl $zalsa::function::Configuration for $Configuration {
const DEBUG_NAME: &'static str = stringify!($fn_name);
@ -114,13 +128,20 @@ macro_rules! setup_struct_fn {
}
}
$zalsa::attach_database($db, || {
let fn_ingredient = $FN_CACHE.get_or_create($db.as_salsa_database(), || {
<dyn $Db as $Db>::zalsa_db($db);
$db.add_or_lookup_jar_by_type(&$Configuration)
});
impl $fn_name {
pub fn accumulated<$db_lt, A: salsa::Accumulator>(
$db: &$db_lt dyn $Db,
$input_id: $input_ty,
) -> Vec<A> {
use salsa::plumbing as $zalsa;
let key = $zalsa::AsId::as_id(&$input_id);
let database_key_index = $Configuration::fn_ingredient($db).database_key_index(key);
$zalsa::accumulated_by($db.as_salsa_database(), database_key_index)
}
}
fn_ingredient.fetch($db, $zalsa::AsId::as_id(&$input_id)).clone()
$zalsa::attach_database($db, || {
$Configuration::fn_ingredient($db).fetch($db, $zalsa::AsId::as_id(&$input_id)).clone()
})
}
};

View file

@ -1,4 +1,7 @@
use syn::{spanned::Spanned, ItemStruct};
use proc_macro2::TokenStream;
use syn::{parse::Nothing, spanned::Spanned};
use crate::hygiene::Hygiene;
// #[salsa::accumulator(jar = Jar0)]
// struct Accumulator(DataType);
@ -7,159 +10,49 @@ 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",
))
let hygiene = Hygiene::from1(&input);
let _ = syn::parse_macro_input!(args as Nothing);
let struct_item = syn::parse_macro_input!(input as syn::ItemStruct);
let ident = struct_item.ident.clone();
let m = StructMacro {
hygiene,
struct_item,
};
match m.try_expand() {
Ok(v) => crate::debug::dump_tokens(&ident, v).into(),
Err(e) => e.to_compile_error().into(),
}
}
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_spanned! { data_ty.span() =>
(std::marker::PhantomData<#data_ty>)
});
struct_item_out
struct StructMacro {
hygiene: Hygiene,
struct_item: syn::ItemStruct,
}
fn inherent_impl(args: &Args, struct_ty: &syn::Type, data_ty: &syn::Type) -> syn::ItemImpl {
let jar_ty = args.jar_ty();
parse_quote_spanned! { struct_ty.span() =>
#[allow(dead_code, clippy::pedantic, clippy::complexity, clippy::style)]
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)
#[allow(non_snake_case)]
impl StructMacro {
fn try_expand(self) -> syn::Result<TokenStream> {
let ident = self.struct_item.ident.clone();
let zalsa = self.hygiene.ident("zalsa");
let zalsa_struct = self.hygiene.ident("zalsa_struct");
let CACHE = self.hygiene.ident("CACHE");
let ingredient = self.hygiene.ident("ingredient");
let struct_item = self.struct_item;
Ok(quote! {
#struct_item
salsa::plumbing::setup_accumulator_impl! {
Struct: #ident,
unused_names: [
#zalsa,
#zalsa_struct,
#CACHE,
#ingredient,
]
}
}
}
}
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_spanned! { struct_name.span() =>
#[allow(dead_code, clippy::pedantic, clippy::complexity, clippy::style)]
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_spanned! { struct_ty.span() =>
#[allow(dead_code, clippy::pedantic, clippy::complexity, clippy::style)]
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
}
}
})
}
}

View file

@ -40,8 +40,6 @@ impl crate::options::AllowedOptions for InputStruct {
const NO_EQ: bool = false;
const SINGLETON: bool = true;
const JAR: bool = true;
const DATA: bool = true;
const DB: bool = false;

View file

@ -42,8 +42,6 @@ impl crate::options::AllowedOptions for InternedStruct {
const SINGLETON: bool = true;
const JAR: bool = true;
const DATA: bool = true;
const DB: bool = false;

View file

@ -89,7 +89,6 @@ pub(crate) trait AllowedOptions {
const SPECIFY: bool;
const NO_EQ: bool;
const SINGLETON: bool;
const JAR: bool;
const DATA: bool;
const DB: bool;
const RECOVERY_FN: bool;
@ -100,18 +99,6 @@ pub(crate) trait AllowedOptions {
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}
}
}
impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut options = Options::default();
@ -171,19 +158,6 @@ impl<A: AllowedOptions> syn::parse::Parse for Options<A> {
"`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)?;

View file

@ -31,8 +31,6 @@ impl crate::options::AllowedOptions for TrackedFn {
const SINGLETON: bool = false;
const JAR: bool = false;
const DATA: bool = false;
const DB: bool = false;

View file

@ -37,8 +37,6 @@ impl crate::options::AllowedOptions for TrackedStruct {
const SINGLETON: bool = true;
const JAR: bool = true;
const DATA: bool = true;
const DB: bool = false;

View file

@ -8,6 +8,7 @@ use notify_debouncer_mini::{
notify::{RecommendedWatcher, RecursiveMode},
DebounceEventResult, Debouncer,
};
use salsa::{Accumulator, Setter};
// ANCHOR: main
fn main() -> Result<()> {
@ -31,7 +32,7 @@ fn main() -> Result<()> {
println!("Sum is: {}", sum);
} else {
for diagnostic in diagnostics {
println!("{}", diagnostic);
println!("{}", diagnostic.0);
}
}
@ -132,21 +133,20 @@ impl salsa::Database for Database {
}
#[salsa::accumulator]
#[derive(Clone, Debug)]
struct Diagnostic(String);
impl Diagnostic {
fn push_error(db: &dyn Db, file: File, error: Report) {
Diagnostic::push(
db,
format!(
"Error in file {}: {:?}\n",
file.path(db)
.file_name()
.unwrap_or_else(|| "<unknown>".as_ref())
.to_string_lossy(),
error,
),
)
Diagnostic(format!(
"Error in file {}: {:?}\n",
file.path(db)
.file_name()
.unwrap_or_else(|| "<unknown>".as_ref())
.to_string_lossy(),
error,
))
.accumulate(db);
}
}

View file

@ -15,17 +15,20 @@ use crate::{
Database, DatabaseKeyIndex, Event, EventKind, Id, Revision, Runtime,
};
pub trait Accumulator: Jar {
pub trait Accumulator: Clone + Debug + Send + Sync + 'static + Sized {
const DEBUG_NAME: &'static str;
type Data: Clone + Debug + Send + Sync;
/// Accumulate an instance of this in the database for later retrieval.
fn accumulate<Db>(self, db: &Db)
where
Db: ?Sized + Database;
}
pub struct AccumulatorJar<A: Accumulator> {
pub struct JarImpl<A: Accumulator> {
phantom: PhantomData<A>,
}
impl<A: Accumulator> Default for AccumulatorJar<A> {
impl<A: Accumulator> Default for JarImpl<A> {
fn default() -> Self {
Self {
phantom: Default::default(),
@ -33,29 +36,29 @@ impl<A: Accumulator> Default for AccumulatorJar<A> {
}
}
impl<A: Accumulator> Jar for AccumulatorJar<A> {
impl<A: Accumulator> Jar for JarImpl<A> {
fn create_ingredients(&self, first_index: IngredientIndex) -> Vec<Box<dyn Ingredient>> {
vec![Box::new(<AccumulatorIngredient<A>>::new(first_index))]
vec![Box::new(<IngredientImpl<A>>::new(first_index))]
}
}
pub struct AccumulatorIngredient<A: Accumulator> {
pub struct IngredientImpl<A: Accumulator> {
index: IngredientIndex,
map: FxDashMap<DatabaseKeyIndex, AccumulatedValues<A::Data>>,
map: FxDashMap<DatabaseKeyIndex, AccumulatedValues<A>>,
}
struct AccumulatedValues<Data> {
struct AccumulatedValues<A> {
produced_at: Revision,
values: Vec<Data>,
values: Vec<A>,
}
impl<A: Accumulator> AccumulatorIngredient<A> {
impl<A: Accumulator> IngredientImpl<A> {
/// Find the accumulator ingrediate for `A` in the database, if any.
pub fn from_db<Db>(db: &Db) -> Option<&Self>
where
Db: ?Sized + Database,
{
let jar: AccumulatorJar<A> = Default::default();
let jar: JarImpl<A> = Default::default();
let index = db.add_or_lookup_jar_by_type(&jar);
let ingredient = db.lookup_ingredient(index).assert_type::<Self>();
Some(ingredient)
@ -75,7 +78,7 @@ impl<A: Accumulator> AccumulatorIngredient<A> {
}
}
pub fn push(&self, runtime: &Runtime, value: A::Data) {
pub fn push(&self, runtime: &Runtime, value: A) {
let current_revision = runtime.current_revision();
let (active_query, _) = match runtime.active_query() {
Some(pair) => pair,
@ -105,7 +108,7 @@ impl<A: Accumulator> AccumulatorIngredient<A> {
&self,
runtime: &Runtime,
query: DatabaseKeyIndex,
output: &mut Vec<A::Data>,
output: &mut Vec<A>,
) {
let current_revision = runtime.current_revision();
if let Some(v) = self.map.get(&query) {
@ -126,7 +129,7 @@ impl<A: Accumulator> AccumulatorIngredient<A> {
}
}
impl<A: Accumulator> Ingredient for AccumulatorIngredient<A> {
impl<A: Accumulator> Ingredient for IngredientImpl<A> {
fn ingredient_index(&self) -> IngredientIndex {
self.index
}
@ -193,11 +196,11 @@ impl<A: Accumulator> Ingredient for AccumulatorIngredient<A> {
}
}
impl<A: Accumulator> IngredientRequiresReset for AccumulatorIngredient<A> {
impl<A: Accumulator> IngredientRequiresReset for IngredientImpl<A> {
const RESET_ON_NEW_REVISION: bool = false;
}
impl<A> std::fmt::Debug for AccumulatorIngredient<A>
impl<A> std::fmt::Debug for IngredientImpl<A>
where
A: Accumulator,
{
@ -207,3 +210,16 @@ where
.finish()
}
}
pub fn accumulated_by<A>(db: &dyn Database, database_key_index: DatabaseKeyIndex) -> Vec<A>
where
A: Accumulator,
{
let Some(accumulator) = <IngredientImpl<A>>::from_db(db) else {
return vec![];
};
let runtime = db.runtime();
let mut output = vec![];
accumulator.produced_by(runtime, database_key_index, &mut output);
output
}

View file

@ -16,7 +16,6 @@ use self::delete::DeletedEntries;
use super::ingredient::Ingredient;
mod accumulated;
mod backdate;
mod delete;
mod diff_outputs;
@ -146,7 +145,7 @@ where
}
}
fn database_key_index(&self, k: Id) -> DatabaseKeyIndex {
pub fn database_key_index(&self, k: Id) -> DatabaseKeyIndex {
DatabaseKeyIndex {
ingredient_index: self.index,
key_index: k,

View file

@ -1,79 +0,0 @@
use crate::{
accumulator::AccumulatorIngredient, hash::FxHashSet, runtime::local_state::QueryOrigin,
storage::DatabaseGen, DatabaseKeyIndex, Id,
};
use super::{Configuration, IngredientImpl};
use crate::accumulator::Accumulator;
impl<C> IngredientImpl<C>
where
C: Configuration,
{
/// Returns all the values accumulated into `accumulator` by this query and its
/// transitive inputs.
pub fn accumulated<'db, A>(&'db self, db: &'db C::DbView, key: Id) -> Vec<A::Data>
where
A: Accumulator,
{
// To start, ensure that the value is up to date:
self.fetch(db, key);
let Some(accumulator_ingredient) = <AccumulatorIngredient<A>>::from_db(db) else {
return vec![];
};
// Now walk over all the things that the value depended on
// and find the values they accumulated into the given
// accumulator:
let runtime = db.runtime();
let mut result = vec![];
let mut stack = Stack::new(self.database_key_index(key));
while let Some(input) = stack.pop() {
accumulator_ingredient.produced_by(runtime, input, &mut result);
stack.extend(input.origin(db.as_salsa_database()));
}
result
}
}
/// The stack is used to execute a DFS across all the queries
/// that were transitively executed by some given start query.
/// When we visit a query Q0, we look at its dependencies Q1...Qn,
/// and if they have not already been visited, we push them on the stack.
struct Stack {
/// Stack of queries left to visit.
v: Vec<DatabaseKeyIndex>,
/// Set of all queries we've seen.
s: FxHashSet<DatabaseKeyIndex>,
}
impl Stack {
fn new(start: DatabaseKeyIndex) -> Self {
Self {
v: vec![start],
s: FxHashSet::default(),
}
}
fn pop(&mut self) -> Option<DatabaseKeyIndex> {
self.v.pop()
}
/// Extend the stack of queries with the dependencies from `origin`.
fn extend(&mut self, origin: Option<QueryOrigin>) {
match origin {
None | Some(QueryOrigin::Assigned(_)) | Some(QueryOrigin::BaseInput) => {}
Some(QueryOrigin::Derived(edges)) | Some(QueryOrigin::DerivedUntracked(edges)) => {
for dependency_index in edges.inputs() {
if let Ok(i) = DatabaseKeyIndex::try_from(dependency_index) {
if self.s.insert(i) {
self.v.push(i)
}
}
}
}
}
}
}

View file

@ -1,5 +1,4 @@
use crate::cycle::CycleRecoveryStrategy;
use crate::id::AsId;
use crate::ingredient::{fmt_index, Ingredient, IngredientRequiresReset};
use crate::input::Configuration;
use crate::runtime::local_state::QueryOrigin;

View file

@ -24,6 +24,7 @@ mod tracked_struct;
mod update;
mod views;
pub use self::accumulator::Accumulator;
pub use self::cancelled::Cancelled;
pub use self::cycle::Cycle;
pub use self::database::Database;
@ -44,12 +45,19 @@ pub use salsa_macros::interned;
pub use salsa_macros::tracked;
pub use salsa_macros::Update;
pub mod prelude {
pub use crate::Accumulator;
pub use crate::Setter;
}
/// Internal names used by salsa macros.
///
/// # WARNING
///
/// The contents of this module are NOT subject to semver.
pub mod plumbing {
pub use crate::accumulator::accumulated_by;
pub use crate::accumulator::Accumulator;
pub use crate::array::Array;
pub use crate::cycle::Cycle;
pub use crate::cycle::CycleRecoveryStrategy;
@ -84,6 +92,7 @@ pub mod plumbing {
pub use salsa_macro_rules::maybe_backdate;
pub use salsa_macro_rules::maybe_clone;
pub use salsa_macro_rules::maybe_cloned_ty;
pub use salsa_macro_rules::setup_accumulator_impl;
pub use salsa_macro_rules::setup_input_struct;
pub use salsa_macro_rules::setup_interned_fn;
pub use salsa_macro_rules::setup_interned_struct;
@ -91,6 +100,11 @@ pub mod plumbing {
pub use salsa_macro_rules::setup_tracked_struct;
pub use salsa_macro_rules::unexpected_cycle_recovery;
pub mod accumulator {
pub use crate::accumulator::IngredientImpl;
pub use crate::accumulator::JarImpl;
}
pub mod input {
pub use crate::input::input_field::FieldIngredientImpl;
pub use crate::input::setter::SetterImpl;

View file

@ -50,7 +50,7 @@ pub struct RuntimeId {
counter: usize,
}
#[derive(Clone, Debug)]
#[derive(Copy, Clone, Debug)]
pub struct StampedValue<V> {
pub value: V,
pub durability: Durability,

View file

@ -1,8 +1,7 @@
use std::any::{Any, TypeId};
use std::sync::Arc;
use orx_concurrent_vec::ConcurrentVec;
use parking_lot::{Condvar, Mutex};
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use crate::cycle::CycleRecoveryStrategy;

View file

@ -8,10 +8,8 @@ use common::{HasLogger, Logger};
use expect_test::expect;
use test_log::test;
#[salsa::jar(db = Db)]
struct Jar(List, Integers, compute);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct List {
@ -43,17 +41,19 @@ fn compute(db: &dyn Db, input: List) {
eprintln!("pushed result {:?}", result);
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, _event: salsa::Event) {}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {

View file

@ -7,12 +7,11 @@ mod common;
use common::{HasLogger, Logger};
use expect_test::expect;
use salsa::prelude::*;
use test_log::test;
#[salsa::jar(db = Db)]
struct Jar(List, Integers, compute);
trait Db: salsa::DbWithJar<Jar> + HasLogger {}
#[salsa::db]
trait Db: salsa::Database + HasLogger {}
#[salsa::input]
struct List {
@ -21,6 +20,7 @@ struct List {
}
#[salsa::accumulator]
#[derive(Clone, Debug)]
struct Integers(u32);
#[salsa::tracked]
@ -28,11 +28,11 @@ fn compute(db: &dyn Db, input: List) -> u32 {
db.push_log(format!("compute({:?})", input,));
// always pushes 0
Integers::push(db, 0);
Integers(0).accumulate(db);
let result = if let Some(next) = input.next(db) {
let next_integers = compute::accumulated::<Integers>(db, next);
let v = input.value(db) + next_integers.iter().sum::<u32>();
let v = input.value(db) + next_integers.iter().map(|i| i.0).sum::<u32>();
v
} else {
input.value(db)
@ -42,17 +42,19 @@ fn compute(db: &dyn Db, input: List) -> u32 {
result
}
#[salsa::db(Jar)]
#[salsa::db]
#[derive(Default)]
struct Database {
storage: salsa::Storage<Self>,
logger: Logger,
}
#[salsa::db]
impl salsa::Database for Database {
fn salsa_event(&self, _event: salsa::Event) {}
}
#[salsa::db]
impl Db for Database {}
impl HasLogger for Database {