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::*;