ra_salsa/
lib.rs

1#![allow(clippy::type_complexity)]
2#![allow(clippy::question_mark)]
3#![allow(missing_docs)]
4#![warn(rust_2018_idioms)]
5
6//! The salsa crate is a crate for incremental recomputation.  It
7//! permits you to define a "database" of queries with both inputs and
8//! values derived from those inputs; as you set the inputs, you can
9//! re-execute the derived queries and it will try to re-use results
10//! from previous invocations as appropriate.
11
12mod derived;
13mod derived_lru;
14mod durability;
15mod hash;
16mod input;
17mod intern_id;
18mod interned;
19mod lru;
20mod revision;
21mod runtime;
22mod storage;
23
24pub mod debug;
25/// Items in this module are public for implementation reasons,
26/// and are exempt from the SemVer guarantees.
27#[doc(hidden)]
28pub mod plumbing;
29
30use crate::plumbing::CycleRecoveryStrategy;
31use crate::plumbing::DerivedQueryStorageOps;
32use crate::plumbing::InputQueryStorageOps;
33use crate::plumbing::LruQueryStorageOps;
34use crate::plumbing::QueryStorageMassOps;
35use crate::plumbing::QueryStorageOps;
36pub use crate::revision::Revision;
37use std::fmt::{self, Debug};
38use std::hash::Hash;
39use std::panic::AssertUnwindSafe;
40use std::panic::{self, UnwindSafe};
41
42pub use crate::durability::Durability;
43pub use crate::intern_id::InternId;
44pub use crate::interned::{InternKey, InternValue, InternValueTrivial};
45pub use crate::runtime::Runtime;
46pub use crate::runtime::RuntimeId;
47pub use crate::storage::Storage;
48
49/// The base trait which your "query context" must implement. Gives
50/// access to the salsa runtime, which you must embed into your query
51/// context (along with whatever other state you may require).
52pub trait Database: plumbing::DatabaseOps {
53    /// This function is invoked at key points in the salsa
54    /// runtime. It permits the database to be customized and to
55    /// inject logging or other custom behavior.
56    fn salsa_event(&self, event_fn: Event) {
57        _ = event_fn;
58    }
59
60    /// Starts unwinding the stack if the current revision is cancelled.
61    ///
62    /// This method can be called by query implementations that perform
63    /// potentially expensive computations, in order to speed up propagation of
64    /// cancellation.
65    ///
66    /// Cancellation will automatically be triggered by salsa on any query
67    /// invocation.
68    ///
69    /// This method should not be overridden by `Database` implementors. A
70    /// `salsa_event` is emitted when this method is called, so that should be
71    /// used instead.
72    #[inline]
73    fn unwind_if_cancelled(&self) {
74        let runtime = self.salsa_runtime();
75        self.salsa_event(Event {
76            runtime_id: runtime.id(),
77            kind: EventKind::WillCheckCancellation,
78        });
79
80        let current_revision = runtime.current_revision();
81        let pending_revision = runtime.pending_revision();
82        tracing::trace!(
83            "unwind_if_cancelled: current_revision={:?}, pending_revision={:?}",
84            current_revision,
85            pending_revision
86        );
87        if pending_revision > current_revision {
88            runtime.unwind_cancelled();
89        }
90    }
91
92    /// Gives access to the underlying salsa runtime.
93    ///
94    /// This method should not be overridden by `Database` implementors.
95    fn salsa_runtime(&self) -> &Runtime {
96        self.ops_salsa_runtime()
97    }
98
99    /// A "synthetic write" causes the system to act *as though* some
100    /// input of durability `durability` has changed. This is mostly
101    /// useful for profiling scenarios.
102    ///
103    /// **WARNING:** Just like an ordinary write, this method triggers
104    /// cancellation. If you invoke it while a snapshot exists, it
105    /// will block until that snapshot is dropped -- if that snapshot
106    /// is owned by the current thread, this could trigger deadlock.
107    fn synthetic_write(&mut self, durability: Durability) {
108        plumbing::DatabaseOps::synthetic_write(self, durability)
109    }
110}
111
112/// The `Event` struct identifies various notable things that can
113/// occur during salsa execution. Instances of this struct are given
114/// to `salsa_event`.
115pub struct Event {
116    /// The id of the snapshot that triggered the event.  Usually
117    /// 1-to-1 with a thread, as well.
118    pub runtime_id: RuntimeId,
119
120    /// What sort of event was it.
121    pub kind: EventKind,
122}
123
124impl Event {
125    /// Returns a type that gives a user-readable debug output.
126    /// Use like `println!("{:?}", index.debug(db))`.
127    pub fn debug<'me, D>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me
128    where
129        D: ?Sized + plumbing::DatabaseOps,
130    {
131        EventDebug { event: self, db }
132    }
133}
134
135impl fmt::Debug for Event {
136    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
137        fmt.debug_struct("Event")
138            .field("runtime_id", &self.runtime_id)
139            .field("kind", &self.kind)
140            .finish()
141    }
142}
143
144struct EventDebug<'me, D: ?Sized>
145where
146    D: plumbing::DatabaseOps,
147{
148    event: &'me Event,
149    db: &'me D,
150}
151
152impl<D: ?Sized> fmt::Debug for EventDebug<'_, D>
153where
154    D: plumbing::DatabaseOps,
155{
156    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
157        fmt.debug_struct("Event")
158            .field("runtime_id", &self.event.runtime_id)
159            .field("kind", &self.event.kind.debug(self.db))
160            .finish()
161    }
162}
163
164/// An enum identifying the various kinds of events that can occur.
165pub enum EventKind {
166    /// Occurs when we found that all inputs to a memoized value are
167    /// up-to-date and hence the value can be re-used without
168    /// executing the closure.
169    ///
170    /// Executes before the "re-used" value is returned.
171    DidValidateMemoizedValue {
172        /// The database-key for the affected value. Implements `Debug`.
173        database_key: DatabaseKeyIndex,
174    },
175
176    /// Indicates that another thread (with id `other_runtime_id`) is processing the
177    /// given query (`database_key`), so we will block until they
178    /// finish.
179    ///
180    /// Executes after we have registered with the other thread but
181    /// before they have answered us.
182    ///
183    /// (NB: you can find the `id` of the current thread via the
184    /// `salsa_runtime`)
185    WillBlockOn {
186        /// The id of the runtime we will block on.
187        other_runtime_id: RuntimeId,
188
189        /// The database-key for the affected value. Implements `Debug`.
190        database_key: DatabaseKeyIndex,
191    },
192
193    /// Indicates that the function for this query will be executed.
194    /// This is either because it has never executed before or because
195    /// its inputs may be out of date.
196    WillExecute {
197        /// The database-key for the affected value. Implements `Debug`.
198        database_key: DatabaseKeyIndex,
199    },
200
201    /// Indicates that `unwind_if_cancelled` was called and salsa will check if
202    /// the current revision has been cancelled.
203    WillCheckCancellation,
204}
205
206impl EventKind {
207    /// Returns a type that gives a user-readable debug output.
208    /// Use like `println!("{:?}", index.debug(db))`.
209    pub fn debug<'me, D>(&'me self, db: &'me D) -> impl std::fmt::Debug + 'me
210    where
211        D: ?Sized + plumbing::DatabaseOps,
212    {
213        EventKindDebug { kind: self, db }
214    }
215}
216
217impl fmt::Debug for EventKind {
218    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
219        match self {
220            EventKind::DidValidateMemoizedValue { database_key } => fmt
221                .debug_struct("DidValidateMemoizedValue")
222                .field("database_key", database_key)
223                .finish(),
224            EventKind::WillBlockOn { other_runtime_id, database_key } => fmt
225                .debug_struct("WillBlockOn")
226                .field("other_runtime_id", other_runtime_id)
227                .field("database_key", database_key)
228                .finish(),
229            EventKind::WillExecute { database_key } => {
230                fmt.debug_struct("WillExecute").field("database_key", database_key).finish()
231            }
232            EventKind::WillCheckCancellation => fmt.debug_struct("WillCheckCancellation").finish(),
233        }
234    }
235}
236
237struct EventKindDebug<'me, D: ?Sized>
238where
239    D: plumbing::DatabaseOps,
240{
241    kind: &'me EventKind,
242    db: &'me D,
243}
244
245impl<D: ?Sized> fmt::Debug for EventKindDebug<'_, D>
246where
247    D: plumbing::DatabaseOps,
248{
249    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
250        match self.kind {
251            EventKind::DidValidateMemoizedValue { database_key } => fmt
252                .debug_struct("DidValidateMemoizedValue")
253                .field("database_key", &database_key.debug(self.db))
254                .finish(),
255            EventKind::WillBlockOn { other_runtime_id, database_key } => fmt
256                .debug_struct("WillBlockOn")
257                .field("other_runtime_id", &other_runtime_id)
258                .field("database_key", &database_key.debug(self.db))
259                .finish(),
260            EventKind::WillExecute { database_key } => fmt
261                .debug_struct("WillExecute")
262                .field("database_key", &database_key.debug(self.db))
263                .finish(),
264            EventKind::WillCheckCancellation => fmt.debug_struct("WillCheckCancellation").finish(),
265        }
266    }
267}
268
269/// Indicates a database that also supports parallel query
270/// evaluation. All of Salsa's base query support is capable of
271/// parallel execution, but for it to work, your query key/value types
272/// must also be `Send`, as must any additional data in your database.
273pub trait ParallelDatabase: Database + Send {
274    /// Creates a second handle to the database that holds the
275    /// database fixed at a particular revision. So long as this
276    /// "frozen" handle exists, any attempt to [`set`] an input will
277    /// block.
278    ///
279    /// [`set`]: struct.QueryTable.html#method.set
280    ///
281    /// This is the method you are meant to use most of the time in a
282    /// parallel setting where modifications may arise asynchronously
283    /// (e.g., a language server). In this context, it is common to
284    /// wish to "fork off" a snapshot of the database performing some
285    /// series of queries in parallel and arranging the results. Using
286    /// this method for that purpose ensures that those queries will
287    /// see a consistent view of the database (it is also advisable
288    /// for those queries to use the [`Database::unwind_if_cancelled`]
289    /// method to check for cancellation).
290    ///
291    /// # Panics
292    ///
293    /// It is not permitted to create a snapshot from inside of a
294    /// query. Attempting to do so will panic.
295    ///
296    /// # Deadlock warning
297    ///
298    /// The intended pattern for snapshots is that, once created, they
299    /// are sent to another thread and used from there. As such, the
300    /// `snapshot` acquires a "read lock" on the database --
301    /// therefore, so long as the `snapshot` is not dropped, any
302    /// attempt to `set` a value in the database will block. If the
303    /// `snapshot` is owned by the same thread that is attempting to
304    /// `set`, this will cause a problem.
305    ///
306    /// # How to implement this
307    ///
308    /// Typically, this method will create a second copy of your
309    /// database type (`MyDatabaseType`, in the example below),
310    /// cloning over each of the fields from `self` into this new
311    /// copy. For the field that stores the salsa runtime, you should
312    /// use [the `Runtime::snapshot` method][rfm] to create a snapshot of the
313    /// runtime. Finally, package up the result using `Snapshot::new`,
314    /// which is a simple wrapper type that only gives `&self` access
315    /// to the database within (thus preventing the use of methods
316    /// that may mutate the inputs):
317    ///
318    /// [rfm]: struct.Runtime.html#method.snapshot
319    ///
320    /// ```rust,ignore
321    /// impl ParallelDatabase for MyDatabaseType {
322    ///     fn snapshot(&self) -> Snapshot<Self> {
323    ///         Snapshot::new(
324    ///             MyDatabaseType {
325    ///                 runtime: self.runtime.snapshot(self),
326    ///                 other_field: self.other_field.clone(),
327    ///             }
328    ///         )
329    ///     }
330    /// }
331    /// ```
332    fn snapshot(&self) -> Snapshot<Self>;
333}
334
335/// Simple wrapper struct that takes ownership of a database `DB` and
336/// only gives `&self` access to it. See [the `snapshot` method][fm]
337/// for more details.
338///
339/// [fm]: trait.ParallelDatabase.html#method.snapshot
340#[derive(Debug)]
341pub struct Snapshot<DB: ?Sized>
342where
343    DB: ParallelDatabase,
344{
345    db: DB,
346}
347
348impl<DB> Snapshot<DB>
349where
350    DB: ParallelDatabase,
351{
352    /// Creates a `Snapshot` that wraps the given database handle
353    /// `db`. From this point forward, only shared references to `db`
354    /// will be possible.
355    pub fn new(db: DB) -> Self {
356        Snapshot { db }
357    }
358}
359
360impl<DB> std::ops::Deref for Snapshot<DB>
361where
362    DB: ParallelDatabase,
363{
364    type Target = DB;
365
366    fn deref(&self) -> &DB {
367        &self.db
368    }
369}
370
371/// An integer that uniquely identifies a particular query instance within the
372/// database. Used to track dependencies between queries. Fully ordered and
373/// equatable but those orderings are arbitrary, and meant to be used only for
374/// inserting into maps and the like.
375#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
376pub struct DatabaseKeyIndex {
377    group_index: u16,
378    query_index: u16,
379    key_index: u32,
380}
381
382impl DatabaseKeyIndex {
383    /// Returns the index of the query group containing this key.
384    #[inline]
385    pub fn group_index(self) -> u16 {
386        self.group_index
387    }
388
389    /// Returns the index of the query within its query group.
390    #[inline]
391    pub fn query_index(self) -> u16 {
392        self.query_index
393    }
394
395    /// Returns the index of this particular query key within the query.
396    #[inline]
397    pub fn key_index(self) -> u32 {
398        self.key_index
399    }
400
401    /// Returns a type that gives a user-readable debug output.
402    /// Use like `println!("{:?}", index.debug(db))`.
403    pub fn debug<D>(self, db: &D) -> impl std::fmt::Debug + '_
404    where
405        D: ?Sized + plumbing::DatabaseOps,
406    {
407        DatabaseKeyIndexDebug { index: self, db }
408    }
409}
410
411/// Helper type for `DatabaseKeyIndex::debug`
412struct DatabaseKeyIndexDebug<'me, D: ?Sized>
413where
414    D: plumbing::DatabaseOps,
415{
416    index: DatabaseKeyIndex,
417    db: &'me D,
418}
419
420impl<D: ?Sized> std::fmt::Debug for DatabaseKeyIndexDebug<'_, D>
421where
422    D: plumbing::DatabaseOps,
423{
424    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
425        self.db.fmt_index(self.index, fmt)
426    }
427}
428
429/// Trait implements by all of the "special types" associated with
430/// each of your queries.
431///
432/// Base trait of `Query` that has a lifetime parameter to allow the `DynDb` to be non-'static.
433pub trait QueryDb<'d>: Sized {
434    /// Dyn version of the associated trait for this query group.
435    type DynDb: ?Sized + Database + HasQueryGroup<Self::Group> + 'd;
436
437    /// Associate query group struct.
438    type Group: plumbing::QueryGroup<GroupStorage = Self::GroupStorage>;
439
440    /// Generated struct that contains storage for all queries in a group.
441    type GroupStorage;
442}
443
444/// Trait implements by all of the "special types" associated with
445/// each of your queries.
446pub trait Query: Debug + Default + Sized + for<'d> QueryDb<'d> {
447    /// Type that you give as a parameter -- for queries with zero
448    /// or more than one input, this will be a tuple.
449    type Key: Clone + Debug + Hash + Eq;
450
451    /// What value does the query return?
452    type Value: Clone + Debug;
453
454    /// Internal struct storing the values for the query.
455    // type Storage: plumbing::QueryStorageOps<Self>;
456    type Storage;
457
458    /// A unique index identifying this query within the group.
459    const QUERY_INDEX: u16;
460
461    /// Name of the query method (e.g., `foo`)
462    const QUERY_NAME: &'static str;
463
464    /// Extract storage for this query from the storage for its group.
465    fn query_storage<'a>(
466        group_storage: &'a <Self as QueryDb<'_>>::GroupStorage,
467    ) -> &'a std::sync::Arc<Self::Storage>;
468
469    /// Extract storage for this query from the storage for its group.
470    fn query_storage_mut<'a>(
471        group_storage: &'a <Self as QueryDb<'_>>::GroupStorage,
472    ) -> &'a std::sync::Arc<Self::Storage>;
473}
474
475/// Return value from [the `query` method] on `Database`.
476/// Gives access to various less common operations on queries.
477///
478/// [the `query` method]: trait.Database.html#method.query
479pub struct QueryTable<'me, Q>
480where
481    Q: Query,
482{
483    db: &'me <Q as QueryDb<'me>>::DynDb,
484    storage: &'me Q::Storage,
485}
486
487impl<'me, Q> QueryTable<'me, Q>
488where
489    Q: Query,
490    Q::Storage: QueryStorageOps<Q>,
491{
492    /// Constructs a new `QueryTable`.
493    pub fn new(db: &'me <Q as QueryDb<'me>>::DynDb, storage: &'me Q::Storage) -> Self {
494        Self { db, storage }
495    }
496
497    /// Execute the query on a given input. Usually it's easier to
498    /// invoke the trait method directly. Note that for variadic
499    /// queries (those with no inputs, or those with more than one
500    /// input) the key will be a tuple.
501    pub fn get(&self, key: Q::Key) -> Q::Value {
502        self.storage.fetch(self.db, &key)
503    }
504
505    /// Completely clears the storage for this query.
506    ///
507    /// This method breaks internal invariants of salsa, so any further queries
508    /// might return nonsense results. It is useful only in very specific
509    /// circumstances -- for example, when one wants to observe which values
510    /// dropped together with the table
511    pub fn purge(&self)
512    where
513        Q::Storage: plumbing::QueryStorageMassOps,
514    {
515        self.storage.purge();
516    }
517
518    pub fn storage(&self) -> &<Q as Query>::Storage {
519        self.storage
520    }
521}
522
523/// Return value from [the `query_mut` method] on `Database`.
524/// Gives access to the `set` method, notably, that is used to
525/// set the value of an input query.
526///
527/// [the `query_mut` method]: trait.Database.html#method.query_mut
528pub struct QueryTableMut<'me, Q>
529where
530    Q: Query + 'me,
531{
532    runtime: &'me mut Runtime,
533    storage: &'me Q::Storage,
534}
535
536impl<'me, Q> QueryTableMut<'me, Q>
537where
538    Q: Query,
539{
540    /// Constructs a new `QueryTableMut`.
541    pub fn new(runtime: &'me mut Runtime, storage: &'me Q::Storage) -> Self {
542        Self { runtime, storage }
543    }
544
545    /// Assign a value to an "input query". Must be used outside of
546    /// an active query computation.
547    ///
548    /// If you are using `snapshot`, see the notes on blocking
549    /// and cancellation on [the `query_mut` method].
550    ///
551    /// [the `query_mut` method]: trait.Database.html#method.query_mut
552    pub fn set(&mut self, key: Q::Key, value: Q::Value)
553    where
554        Q::Storage: plumbing::InputQueryStorageOps<Q>,
555    {
556        self.set_with_durability(key, value, Durability::LOW);
557    }
558
559    /// Assign a value to an "input query", with the additional
560    /// promise that this value will **never change**. Must be used
561    /// outside of an active query computation.
562    ///
563    /// If you are using `snapshot`, see the notes on blocking
564    /// and cancellation on [the `query_mut` method].
565    ///
566    /// [the `query_mut` method]: trait.Database.html#method.query_mut
567    pub fn set_with_durability(&mut self, key: Q::Key, value: Q::Value, durability: Durability)
568    where
569        Q::Storage: plumbing::InputQueryStorageOps<Q>,
570    {
571        self.storage.set(self.runtime, &key, value, durability);
572    }
573
574    /// Sets the size of LRU cache of values for this query table.
575    ///
576    /// That is, at most `cap` values will be preset in the table at the same
577    /// time. This helps with keeping maximum memory usage under control, at the
578    /// cost of potential extra recalculations of evicted values.
579    ///
580    /// If `cap` is zero, all values are preserved, this is the default.
581    pub fn set_lru_capacity(&self, cap: u16)
582    where
583        Q::Storage: plumbing::LruQueryStorageOps,
584    {
585        self.storage.set_lru_capacity(cap);
586    }
587
588    /// Marks the computed value as outdated.
589    ///
590    /// This causes salsa to re-execute the query function on the next access to
591    /// the query, even if all dependencies are up to date.
592    ///
593    /// This is most commonly used as part of the [on-demand input
594    /// pattern](https://salsa-rs.github.io/salsa/common_patterns/on_demand_inputs.html).
595    pub fn invalidate(&mut self, key: &Q::Key)
596    where
597        Q::Storage: plumbing::DerivedQueryStorageOps<Q>,
598    {
599        self.storage.invalidate(self.runtime, key)
600    }
601}
602
603/// A panic payload indicating that execution of a salsa query was cancelled.
604///
605/// This can occur for a few reasons:
606/// *
607/// *
608/// *
609#[derive(Debug)]
610#[non_exhaustive]
611pub enum Cancelled {
612    /// The query was operating on revision R, but there is a pending write to move to revision R+1.
613    #[non_exhaustive]
614    PendingWrite,
615
616    /// The query was blocked on another thread, and that thread panicked.
617    #[non_exhaustive]
618    PropagatedPanic,
619}
620
621impl Cancelled {
622    fn throw(self) -> ! {
623        // We use resume and not panic here to avoid running the panic
624        // hook (that is, to avoid collecting and printing backtrace).
625        std::panic::resume_unwind(Box::new(self));
626    }
627
628    /// Runs `f`, and catches any salsa cancellation.
629    pub fn catch<F, T>(f: F) -> Result<T, Cancelled>
630    where
631        F: FnOnce() -> T + UnwindSafe,
632    {
633        match panic::catch_unwind(f) {
634            Ok(t) => Ok(t),
635            Err(payload) => match payload.downcast() {
636                Ok(cancelled) => Err(*cancelled),
637                Err(payload) => panic::resume_unwind(payload),
638            },
639        }
640    }
641}
642
643impl std::fmt::Display for Cancelled {
644    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
645        let why = match self {
646            Cancelled::PendingWrite => "pending write",
647            Cancelled::PropagatedPanic => "propagated panic",
648        };
649        f.write_str("cancelled because of ")?;
650        f.write_str(why)
651    }
652}
653
654impl std::error::Error for Cancelled {}
655
656/// Captures the participants of a cycle that occurred when executing a query.
657///
658/// This type is meant to be used to help give meaningful error messages to the
659/// user or to help salsa developers figure out why their program is resulting
660/// in a computation cycle.
661///
662/// It is used in a few ways:
663///
664/// * During [cycle recovery](https://https://salsa-rs.github.io/salsa/cycles/fallback.html),
665///   where it is given to the fallback function.
666/// * As the panic value when an unexpected cycle (i.e., a cycle where one or more participants
667///   lacks cycle recovery information) occurs.
668///
669/// You can read more about cycle handling in
670/// the [salsa book](https://https://salsa-rs.github.io/salsa/cycles.html).
671#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
672pub struct Cycle {
673    participants: plumbing::CycleParticipants,
674}
675
676impl Cycle {
677    pub(crate) fn new(participants: plumbing::CycleParticipants) -> Self {
678        Self { participants }
679    }
680
681    /// True if two `Cycle` values represent the same cycle.
682    pub(crate) fn is(&self, cycle: &Cycle) -> bool {
683        triomphe::Arc::ptr_eq(&self.participants, &cycle.participants)
684    }
685
686    pub(crate) fn throw(self) -> ! {
687        tracing::trace!("throwing cycle {:?}", self);
688        std::panic::resume_unwind(Box::new(self))
689    }
690
691    pub(crate) fn catch<T>(execute: impl FnOnce() -> T) -> Result<T, Cycle> {
692        match std::panic::catch_unwind(AssertUnwindSafe(execute)) {
693            Ok(v) => Ok(v),
694            Err(err) => match err.downcast::<Cycle>() {
695                Ok(cycle) => Err(*cycle),
696                Err(other) => std::panic::resume_unwind(other),
697            },
698        }
699    }
700
701    /// Iterate over the [`DatabaseKeyIndex`] for each query participating
702    /// in the cycle. The start point of this iteration within the cycle
703    /// is arbitrary but deterministic, but the ordering is otherwise determined
704    /// by the execution.
705    pub fn participant_keys(&self) -> impl Iterator<Item = DatabaseKeyIndex> + '_ {
706        self.participants.iter().copied()
707    }
708
709    /// Returns a vector with the debug information for
710    /// all the participants in the cycle.
711    pub fn all_participants<DB: ?Sized + Database>(&self, db: &DB) -> Vec<String> {
712        self.participant_keys().map(|d| format!("{:?}", d.debug(db))).collect()
713    }
714
715    /// Returns a vector with the debug information for
716    /// those participants in the cycle that lacked recovery
717    /// information.
718    pub fn unexpected_participants<DB: ?Sized + Database>(&self, db: &DB) -> Vec<String> {
719        self.participant_keys()
720            .filter(|&d| db.cycle_recovery_strategy(d) == CycleRecoveryStrategy::Panic)
721            .map(|d| format!("{:?}", d.debug(db)))
722            .collect()
723    }
724
725    /// Returns a "debug" view onto this strict that can be used to print out information.
726    pub fn debug<'me, DB: ?Sized + Database>(&'me self, db: &'me DB) -> impl std::fmt::Debug + 'me {
727        struct UnexpectedCycleDebug<'me> {
728            c: &'me Cycle,
729            db: &'me dyn Database,
730        }
731
732        impl std::fmt::Debug for UnexpectedCycleDebug<'_> {
733            fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
734                fmt.debug_struct("UnexpectedCycle")
735                    .field("all_participants", &self.c.all_participants(self.db))
736                    .field("unexpected_participants", &self.c.unexpected_participants(self.db))
737                    .finish()
738            }
739        }
740
741        UnexpectedCycleDebug { c: self, db: db.ops_database() }
742    }
743}
744
745// Re-export the procedural macros.
746#[allow(unused_imports)]
747#[macro_use]
748extern crate ra_salsa_macros;
749use plumbing::HasQueryGroup;
750pub use ra_salsa_macros::*;