salsa/
lib.rs

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