#![cfg(feature = "inventory")]
mod common;
use crate::common::EventLoggerDatabase;
use salsa::{Database, Durability, Setter};
#[salsa::input(debug)]
struct Input {
value: u32,
max: u32,
}
#[salsa::interned(debug)]
struct Output<'db> {
value: u32,
}
#[salsa::tracked(cycle_initial=query_a_initial)]
fn query_c<'db>(db: &'db dyn salsa::Database, input: Input) -> u32 {
query_d(db, input)
}
#[salsa::tracked]
fn query_d<'db>(db: &'db dyn salsa::Database, input: Input) -> u32 {
let value = query_c(db, input);
if value < input.max(db) * 2 {
let result = value + input.value(db);
Output::new(db, result);
result
} else {
value
}
}
fn query_a_initial(_db: &dyn Database, _id: salsa::Id, _input: Input) -> u32 {
0
}
#[test_log::test]
fn first_iteration_input_only() {
#[salsa::tracked(cycle_initial=query_a_initial)]
fn query_a<'db>(db: &'db dyn salsa::Database, input: Input) -> u32 {
query_b(db, input)
}
#[salsa::tracked]
fn query_b<'db>(db: &'db dyn salsa::Database, input: Input) -> u32 {
let value = query_a(db, input);
if value < input.max(db) {
value + input.value(db)
} else {
value
}
}
let mut db = EventLoggerDatabase::default();
let input = Input::builder(4, 5).durability(Durability::MEDIUM).new(&db);
{
let result = query_a(&db, input);
assert_eq!(result, 8);
}
{
input.set_value(&mut db).to(3);
let result = query_a(&db, input);
assert_eq!(result, 6);
}
}
#[test_log::test]
fn nested_cycle_fewer_dependencies_in_first_iteration() {
#[salsa::interned(debug)]
struct ClassLiteral<'db> {
scope: Scope<'db>,
}
#[salsa::tracked]
impl<'db> ClassLiteral<'db> {
#[salsa::tracked]
fn context(self, db: &'db dyn salsa::Database) -> u32 {
let scope = self.scope(db);
scope.field(db)
}
}
#[salsa::tracked(debug)]
struct Scope<'db> {
field: u32,
}
#[salsa::tracked]
fn create_interned<'db>(db: &'db dyn salsa::Database, scope: Scope<'db>) -> ClassLiteral<'db> {
ClassLiteral::new(db, scope)
}
#[derive(Eq, PartialEq, Debug, salsa::Update)]
struct Index<'db> {
scope: Scope<'db>,
}
#[salsa::tracked(cycle_initial=head_initial)]
fn cycle_head<'db>(db: &'db dyn salsa::Database, input: Input) -> Option<ClassLiteral<'db>> {
let b = cycle_outer(db, input);
tracing::info!("query_b = {b:?}");
b.or_else(|| {
let index = index(db, input);
Some(create_interned(db, index.scope))
})
}
fn head_initial(_db: &dyn Database, _id: salsa::Id, _input: Input) -> Option<ClassLiteral<'_>> {
None
}
#[salsa::tracked]
fn cycle_outer<'db>(db: &'db dyn salsa::Database, input: Input) -> Option<ClassLiteral<'db>> {
cycle_participant(db, input)
}
#[salsa::tracked]
fn cycle_participant<'db>(
db: &'db dyn salsa::Database,
input: Input,
) -> Option<ClassLiteral<'db>> {
let value = cycle_head(db, input);
tracing::info!("cycle_head = {value:?}");
if let Some(value) = value {
value.context(db);
Some(value)
} else {
None
}
}
#[salsa::tracked(returns(ref))]
fn index<'db>(db: &'db dyn salsa::Database, input: Input) -> Index<'db> {
Index {
scope: Scope::new(db, input.value(db) * 2),
}
}
#[salsa::tracked]
fn entry(db: &dyn salsa::Database, input: Input) -> u32 {
let _ = input.value(db);
let head = cycle_head(db, input);
let participant = cycle_participant(db, input);
tracing::debug!("head: {head:?}, participant: {participant:?}");
head.or(participant)
.map(|class| class.scope(db).field(db))
.unwrap_or(0)
}
let mut db = EventLoggerDatabase::default();
let input = Input::builder(3, 5)
.max_durability(Durability::HIGH)
.value_durability(Durability::LOW)
.new(&db);
{
let result = entry(&db, input);
assert_eq!(result, 6);
}
db.synthetic_write(Durability::MEDIUM);
{
input.set_value(&mut db).to(4);
let result = entry(&db, input);
assert_eq!(result, 8);
}
}