Skip to content

Commit a95bae5

Browse files
authored
panic with string message again for cycle panics (#898)
1 parent 38a3c9e commit a95bae5

File tree

6 files changed

+28
-63
lines changed

6 files changed

+28
-63
lines changed

src/cycle.rs

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@
4949
//! cycle head may then iterate, which may result in a new set of iterations on the inner cycle,
5050
//! for each iteration of the outer cycle.
5151
52-
use core::fmt;
53-
use std::panic;
54-
5552
use thin_vec::{thin_vec, ThinVec};
5653

5754
use crate::key::DatabaseKeyIndex;
@@ -62,48 +59,6 @@ use crate::sync::OnceLock;
6259
/// Should only be relevant in case of a badly configured cycle recovery.
6360
pub const MAX_ITERATIONS: IterationCount = IterationCount(200);
6461

65-
pub struct UnexpectedCycle {
66-
backtrace: std::backtrace::Backtrace,
67-
query_trace: Option<crate::Backtrace>,
68-
}
69-
70-
impl fmt::Display for UnexpectedCycle {
71-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72-
f.write_str("cycle detected but no cycle handler found\n")?;
73-
self.backtrace.fmt(f)?;
74-
if let Some(query_trace) = &self.query_trace {
75-
f.write_str("\n")?;
76-
query_trace.fmt(f)?;
77-
}
78-
Ok(())
79-
}
80-
}
81-
82-
impl UnexpectedCycle {
83-
pub(crate) fn throw() -> ! {
84-
// We use resume and not panic here to avoid running the panic
85-
// hook (that is to avoid printing the backtrace).
86-
panic::resume_unwind(Box::new(Self {
87-
backtrace: std::backtrace::Backtrace::capture(),
88-
query_trace: crate::Backtrace::capture(),
89-
}));
90-
}
91-
92-
/// Runs `f`, and catches any salsa cycle.
93-
pub fn catch<F, T>(f: F) -> Result<T, UnexpectedCycle>
94-
where
95-
F: FnOnce() -> T + panic::UnwindSafe,
96-
{
97-
match panic::catch_unwind(f) {
98-
Ok(t) => Ok(t),
99-
Err(payload) => match payload.downcast() {
100-
Ok(cycle) => Err(*cycle),
101-
Err(payload) => panic::resume_unwind(payload),
102-
},
103-
}
104-
}
105-
}
106-
10762
/// Return value from a cycle recovery function.
10863
#[derive(Debug)]
10964
pub enum CycleRecoveryAction<T> {

src/function/fetch.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::cycle::{CycleHeads, CycleRecoveryStrategy, IterationCount, UnexpectedCycle};
1+
use crate::cycle::{CycleHeads, CycleRecoveryStrategy, IterationCount};
22
use crate::function::memo::Memo;
33
use crate::function::sync::ClaimResult;
44
use crate::function::{Configuration, IngredientImpl, VerifyResult};
@@ -161,7 +161,13 @@ where
161161
}
162162
// no provisional value; create/insert/return initial provisional value
163163
return match C::CYCLE_STRATEGY {
164-
CycleRecoveryStrategy::Panic => UnexpectedCycle::throw(),
164+
CycleRecoveryStrategy::Panic => zalsa_local.with_query_stack(|stack| {
165+
panic!(
166+
"dependency graph cycle when querying {database_key_index:#?}, \
167+
set cycle_fn/cycle_initial to fixpoint iterate.\n\
168+
Query stack:\n{stack:#?}",
169+
);
170+
}),
165171
CycleRecoveryStrategy::Fixpoint => {
166172
tracing::debug!(
167173
"hit cycle at {database_key_index:#?}, \

src/function/maybe_changed_after.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use crate::accumulator::accumulated_map::InputAccumulatedValues;
2-
use crate::cycle::{
3-
CycleHeads, CycleRecoveryStrategy, IterationCount, ProvisionalStatus, UnexpectedCycle,
4-
};
2+
use crate::cycle::{CycleHeads, CycleRecoveryStrategy, IterationCount, ProvisionalStatus};
53
use crate::function::memo::Memo;
64
use crate::function::sync::ClaimResult;
75
use crate::function::{Configuration, IngredientImpl};
@@ -108,7 +106,13 @@ where
108106
return None;
109107
}
110108
ClaimResult::Cycle { .. } => match C::CYCLE_STRATEGY {
111-
CycleRecoveryStrategy::Panic => UnexpectedCycle::throw(),
109+
CycleRecoveryStrategy::Panic => db.zalsa_local().with_query_stack(|stack| {
110+
panic!(
111+
"dependency graph cycle when validating {database_key_index:#?}, \
112+
set cycle_fn/cycle_initial to fixpoint iterate.\n\
113+
Query stack:\n{stack:#?}",
114+
);
115+
}),
112116
CycleRecoveryStrategy::FallbackImmediate => {
113117
return Some(VerifyResult::unchanged());
114118
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub use salsa_macros::{accumulator, db, input, interned, tracked, Supertype, Upd
4242
pub use self::accumulator::Accumulator;
4343
pub use self::active_query::Backtrace;
4444
pub use self::cancelled::Cancelled;
45-
pub use self::cycle::{CycleRecoveryAction, UnexpectedCycle};
45+
pub use self::cycle::CycleRecoveryAction;
4646
pub use self::database::{AsDynDatabase, Database};
4747
pub use self::database_impl::DatabaseImpl;
4848
pub use self::durability::Durability;

tests/cycle.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
mod common;
66
use common::{ExecuteValidateLoggerDatabase, LogDatabase};
77
use expect_test::expect;
8-
use salsa::{
9-
CycleRecoveryAction, Database as Db, DatabaseImpl as DbImpl, Durability, Setter,
10-
UnexpectedCycle,
11-
};
8+
use salsa::{CycleRecoveryAction, Database as Db, DatabaseImpl as DbImpl, Durability, Setter};
129
#[cfg(not(miri))]
1310
use test_log::test;
1411

@@ -225,12 +222,13 @@ fn value(num: u8) -> Input {
225222
///
226223
/// Simple self-cycle, no iteration, should panic.
227224
#[test]
225+
#[should_panic(expected = "dependency graph cycle")]
228226
fn self_panic() {
229227
let mut db = DbImpl::new();
230228
let a_in = Inputs::new(&db, vec![]);
231229
let a = Input::MinPanic(a_in);
232230
a_in.set_inputs(&mut db).to(vec![a.clone()]);
233-
UnexpectedCycle::catch(|| a.eval(&db)).unwrap_err();
231+
a.eval(&db);
234232
}
235233

236234
/// a:Np(u10, a) -+
@@ -239,13 +237,14 @@ fn self_panic() {
239237
///
240238
/// Simple self-cycle with untracked read, no iteration, should panic.
241239
#[test]
240+
#[should_panic(expected = "dependency graph cycle")]
242241
fn self_untracked_panic() {
243242
let mut db = DbImpl::new();
244243
let a_in = Inputs::new(&db, vec![]);
245244
let a = Input::MinPanic(a_in);
246245
a_in.set_inputs(&mut db).to(vec![untracked(10), a.clone()]);
247246

248-
UnexpectedCycle::catch(|| a.eval(&db)).unwrap_err();
247+
a.eval(&db);
249248
}
250249

251250
/// a:Ni(a) -+
@@ -289,6 +288,7 @@ fn two_mixed_converge_initial_value() {
289288
/// Two-query cycle, one with iteration and one without.
290289
/// If we enter from the one with no iteration, we panic.
291290
#[test]
291+
#[should_panic(expected = "dependency graph cycle")]
292292
fn two_mixed_panic() {
293293
let mut db = DbImpl::new();
294294
let a_in = Inputs::new(&db, vec![]);
@@ -298,7 +298,7 @@ fn two_mixed_panic() {
298298
a_in.set_inputs(&mut db).to(vec![b]);
299299
b_in.set_inputs(&mut db).to(vec![a.clone()]);
300300

301-
UnexpectedCycle::catch(|| a.eval(&db)).unwrap_err();
301+
a.eval(&db);
302302
}
303303

304304
/// a:Ni(b) --> b:Xi(a)
@@ -369,6 +369,7 @@ fn two_indirect_iterate_converge_initial_value() {
369369
///
370370
/// Two-query cycle, enter indirectly at node without iteration, panic.
371371
#[test]
372+
#[should_panic(expected = "dependency graph cycle")]
372373
fn two_indirect_panic() {
373374
let mut db = DbImpl::new();
374375
let a_in = Inputs::new(&db, vec![]);
@@ -381,7 +382,7 @@ fn two_indirect_panic() {
381382
b_in.set_inputs(&mut db).to(vec![c]);
382383
c_in.set_inputs(&mut db).to(vec![b]);
383384

384-
UnexpectedCycle::catch(|| a.eval(&db)).unwrap_err();
385+
a.eval(&db);
385386
}
386387

387388
/// a:Np(b) -> b:Ni(v200,c) -> c:Xp(b)

tests/cycle_initial_call_back_into_cycle.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
//! Calling back into the same cycle from your cycle initial function will trigger another cycle.
22
3-
use salsa::UnexpectedCycle;
4-
53
#[salsa::tracked]
64
fn initial_value(db: &dyn salsa::Database) -> u32 {
75
query(db)
@@ -30,8 +28,9 @@ fn cycle_fn(
3028
}
3129

3230
#[test_log::test]
31+
#[should_panic(expected = "dependency graph cycle")]
3332
fn the_test() {
3433
let db = salsa::DatabaseImpl::default();
3534

36-
UnexpectedCycle::catch(|| query(&db)).unwrap_err();
35+
query(&db);
3736
}

0 commit comments

Comments
 (0)