pub struct RecursionGuard<K: Hash + Eq + Copy> { /* private fields */ }Expand description
Tracks recursion state for cycle detection, depth limiting, and iteration bounding.
§Usage
use crate::recursion::{RecursionGuard, RecursionProfile, RecursionResult};
let mut guard = RecursionGuard::with_profile(RecursionProfile::TypeEvaluation);
match guard.enter(key) {
RecursionResult::Entered => {
let result = do_work();
guard.leave(key);
result
}
RecursionResult::Cycle => handle_cycle(),
RecursionResult::DepthExceeded
| RecursionResult::IterationExceeded => handle_exceeded(),
}§Debug-mode safety
In debug builds (#[cfg(debug_assertions)]):
- Dropping a guard with entries still in the visiting set panics.
- Calling
leave(key)with a key not in the visiting set panics.
Implementations§
Source§impl<K: Hash + Eq + Copy> RecursionGuard<K>
impl<K: Hash + Eq + Copy> RecursionGuard<K>
Sourcepub fn new(max_depth: u32, max_iterations: u32) -> Self
pub fn new(max_depth: u32, max_iterations: u32) -> Self
Create a guard with explicit limits.
Prefer with_profile for standard use cases.
Sourcepub fn with_profile(profile: RecursionProfile) -> Self
pub fn with_profile(profile: RecursionProfile) -> Self
Create a guard from a named RecursionProfile.
Sourcepub const fn with_max_visiting(self, max_visiting: u32) -> Self
pub const fn with_max_visiting(self, max_visiting: u32) -> Self
Builder: set a custom max visiting-set size.
Sourcepub fn enter(&mut self, key: K) -> RecursionResult
pub fn enter(&mut self, key: K) -> RecursionResult
Try to enter a recursive computation for key.
Returns RecursionResult::Entered if the computation may proceed.
On success the caller must call leave with the
same key when done.
The other variants indicate why entry was denied:
Cycle:keyis already being visited.DepthExceeded: nesting is too deep.IterationExceeded: total work budget exhausted.
Sourcepub fn scope<T>(
&mut self,
key: K,
f: impl FnOnce() -> T,
) -> Result<T, RecursionResult>
pub fn scope<T>( &mut self, key: K, f: impl FnOnce() -> T, ) -> Result<T, RecursionResult>
Execute f inside a guarded scope.
Calls enter(key), runs f if entered, then calls leave(key).
Returns Ok(value) on success or Err(reason) if entry was denied.
This is the safest API when the guard is standalone (not a field of a
struct that f also needs to mutate).
§Panic safety
If f panics, leave() is not called — the entry leaks. This is
safe because the guard’s Drop impl (debug builds) checks
std::thread::panicking() and suppresses the leak-detection panic
during unwinding.
Sourcepub fn is_visiting(&self, key: &K) -> bool
pub fn is_visiting(&self, key: &K) -> bool
Check if key is currently being visited (without entering).
Sourcepub fn is_visiting_any(&self, predicate: impl Fn(&K) -> bool) -> bool
pub fn is_visiting_any(&self, predicate: impl Fn(&K) -> bool) -> bool
Check if any currently-visiting key satisfies the predicate.
Used for symbol-level cycle detection: the same interface may appear
with different DefIds in different checker contexts, so we need to
check all visiting entries for symbol-level matches.
Sourcepub const fn depth(&self) -> u32
pub const fn depth(&self) -> u32
Current recursion depth (number of active entries on the stack).
Sourcepub const fn iterations(&self) -> u32
pub const fn iterations(&self) -> u32
Total enter attempts so far (successful or not).
Sourcepub fn visiting_count(&self) -> usize
pub fn visiting_count(&self) -> usize
Number of keys currently in the visiting set.
Sourcepub const fn max_iterations(&self) -> u32
pub const fn max_iterations(&self) -> u32
The configured maximum iterations.
Sourcepub const fn is_exceeded(&self) -> bool
pub const fn is_exceeded(&self) -> bool
Returns true if any limit was previously exceeded.
Once set, this flag stays true until reset() is called.
This is sticky: even if depth later decreases below the limit, the flag
remains set. This is intentional — callers use it to bail out early on
subsequent calls (e.g. TS2589 “excessively deep” diagnostics).
Sourcepub const fn mark_exceeded(&mut self)
pub const fn mark_exceeded(&mut self)
Manually mark the guard as exceeded.
Useful when an external condition (e.g. distribution size limit) means further recursion should be blocked.