1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use std::fmt;

use crate::{
    cycle::CycleRecoveryStrategy, key::DependencyIndex, runtime::local_state::QueryOrigin,
    DatabaseKeyIndex, Id,
};

use super::Revision;

/// "Ingredients" are the bits of data that are stored within the database to make salsa work.
/// Each jar will define some number of ingredients that it requires.
/// Each use salsa macro (e.g., `#[salsa::tracked]`, `#[salsa::interned]`) adds one or more
/// ingredients to the jar struct that together are used to create the salsa concept.
/// For example, a tracked struct defines a [`crate::interned::InternedIngredient`] to store
/// its identity plus [`crate::function::FunctionIngredient`] values to store its fields.
/// The exact ingredients are determined by
/// [`IngredientsFor`](`crate::storage::IngredientsFor`) implementations generated by the
/// macro.
pub trait Ingredient<DB: ?Sized> {
    /// If this ingredient is a participant in a cycle, what is its cycle recovery strategy?
    /// (Really only relevant to [`crate::function::FunctionIngredient`],
    /// since only function ingredients push themselves onto the active query stack.)
    fn cycle_recovery_strategy(&self) -> CycleRecoveryStrategy;

    /// Has the value for `input` in this ingredient changed after `revision`?
    fn maybe_changed_after(&self, db: &DB, input: DependencyIndex, revision: Revision) -> bool;

    /// What were the inputs (if any) that were used to create the value at `key_index`.
    fn origin(&self, key_index: Id) -> Option<QueryOrigin>;

    /// Invoked when the value `output_key` should be marked as valid in the current revision.
    /// This occurs because the value for `executor`, which generated it, was marked as valid
    /// in the current revision.
    fn mark_validated_output(&self, db: &DB, executor: DatabaseKeyIndex, output_key: Option<Id>);

    /// Invoked when the value `stale_output` was output by `executor` in a previous
    /// revision, but was NOT output in the current revision.
    ///
    /// This hook is used to clear out the stale value so others cannot read it.
    fn remove_stale_output(
        &self,
        db: &DB,
        executor: DatabaseKeyIndex,
        stale_output_key: Option<Id>,
    );

    /// Informs the ingredient `self` that the salsa struct with id `id` has been deleted.
    /// This gives `self` a chance to remove any memoized data dependent on `id`.
    /// To receive this callback, `self` must register itself as a dependent function using
    /// [`SalsaStructInDb::register_dependent_fn`](`crate::salsa_struct::SalsaStructInDb::register_dependent_fn`).
    fn salsa_struct_deleted(&self, db: &DB, id: Id);

    /// Invoked when a new revision is about to start.
    /// This moment is important because it means that we have an `&mut`-reference to the
    /// database, and hence any pre-existing `&`-references must have expired.
    /// Many ingredients, given an `&'db`-reference to the database,
    /// use unsafe code to return `&'db`-references to internal values.
    /// The backing memory for those values can only be freed once an `&mut`-reference to the
    /// database is created.
    ///
    /// **Important:** to actually receive resets, the ingredient must set
    /// [`IngredientRequiresReset::RESET_ON_NEW_REVISION`] to true.
    fn reset_for_new_revision(&mut self);

    fn fmt_index(&self, index: Option<crate::Id>, fmt: &mut fmt::Formatter<'_>) -> fmt::Result;
}

/// A helper function to show human readable fmt.
pub(crate) fn fmt_index(
    debug_name: &str,
    id: Option<Id>,
    fmt: &mut fmt::Formatter<'_>,
) -> fmt::Result {
    if let Some(i) = id {
        write!(fmt, "{}({})", debug_name, u32::from(i))
    } else {
        write!(fmt, "{}()", debug_name)
    }
}

/// Defines a const indicating if an ingredient needs to be reset each round.
/// This const probably *should* be a member of `Ingredient` trait but then `Ingredient` would
/// not be dyn-safe.
pub trait IngredientRequiresReset {
    /// If this is true, then `reset_for_new_revision` will be called every new revision.
    const RESET_ON_NEW_REVISION: bool;
}