Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 15f770b

Browse files
lcnrcompiler-errors
authored andcommittedJul 12, 2024·
enable fuzzing of SearchGraph
fully move it into `rustc_type_ir` and make it independent of `Interner`.
1 parent f56b207 commit 15f770b

File tree

13 files changed

+982
-757
lines changed

13 files changed

+982
-757
lines changed
 

‎compiler/rustc_middle/src/traits/solve.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ use crate::ty::{
88
self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
99
};
1010

11-
mod cache;
12-
13-
pub use cache::EvaluationCache;
14-
1511
pub type Goal<'tcx, P> = ir::solve::Goal<TyCtxt<'tcx>, P>;
1612
pub type QueryInput<'tcx, P> = ir::solve::QueryInput<TyCtxt<'tcx>, P>;
1713
pub type QueryResult<'tcx> = ir::solve::QueryResult<TyCtxt<'tcx>>;

‎compiler/rustc_middle/src/traits/solve/cache.rs

Lines changed: 0 additions & 121 deletions
This file was deleted.

‎compiler/rustc_middle/src/ty/context.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use rustc_hir::lang_items::LangItem;
5959
use rustc_hir::{HirId, Node, TraitCandidate};
6060
use rustc_index::IndexVec;
6161
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
62+
use rustc_query_system::cache::WithDepNode;
6263
use rustc_query_system::dep_graph::DepNodeIndex;
6364
use rustc_query_system::ich::StableHashingContext;
6465
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
@@ -75,7 +76,7 @@ use rustc_type_ir::fold::TypeFoldable;
7576
use rustc_type_ir::lang_items::TraitSolverLangItem;
7677
use rustc_type_ir::solve::SolverMode;
7778
use rustc_type_ir::TyKind::*;
78-
use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo};
79+
use rustc_type_ir::{search_graph, CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo};
7980
use tracing::{debug, instrument};
8081

8182
use std::assert_matches::assert_matches;
@@ -164,12 +165,26 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
164165
type Clause = Clause<'tcx>;
165166
type Clauses = ty::Clauses<'tcx>;
166167

167-
type EvaluationCache = &'tcx solve::EvaluationCache<'tcx>;
168+
type Tracked<T: fmt::Debug + Clone> = WithDepNode<T>;
169+
fn mk_tracked<T: fmt::Debug + Clone>(
170+
self,
171+
data: T,
172+
dep_node: DepNodeIndex,
173+
) -> Self::Tracked<T> {
174+
WithDepNode::new(dep_node, data)
175+
}
176+
fn get_tracked<T: fmt::Debug + Clone>(self, tracked: &Self::Tracked<T>) -> T {
177+
tracked.get(self)
178+
}
168179

169-
fn evaluation_cache(self, mode: SolverMode) -> &'tcx solve::EvaluationCache<'tcx> {
180+
fn with_global_cache<R>(
181+
self,
182+
mode: SolverMode,
183+
f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R,
184+
) -> R {
170185
match mode {
171-
SolverMode::Normal => &self.new_solver_evaluation_cache,
172-
SolverMode::Coherence => &self.new_solver_coherence_evaluation_cache,
186+
SolverMode::Normal => f(&mut *self.new_solver_evaluation_cache.lock()),
187+
SolverMode::Coherence => f(&mut *self.new_solver_coherence_evaluation_cache.lock()),
173188
}
174189
}
175190

@@ -1283,8 +1298,8 @@ pub struct GlobalCtxt<'tcx> {
12831298
pub evaluation_cache: traits::EvaluationCache<'tcx>,
12841299

12851300
/// Caches the results of goal evaluation in the new solver.
1286-
pub new_solver_evaluation_cache: solve::EvaluationCache<'tcx>,
1287-
pub new_solver_coherence_evaluation_cache: solve::EvaluationCache<'tcx>,
1301+
pub new_solver_evaluation_cache: Lock<search_graph::GlobalCache<TyCtxt<'tcx>>>,
1302+
pub new_solver_coherence_evaluation_cache: Lock<search_graph::GlobalCache<TyCtxt<'tcx>>>,
12881303

12891304
pub canonical_param_env_cache: CanonicalParamEnvCache<'tcx>,
12901305

‎compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ use crate::delegate::SolverDelegate;
1616
use crate::solve::inspect::{self, ProofTreeBuilder};
1717
use crate::solve::search_graph::SearchGraph;
1818
use crate::solve::{
19-
search_graph, CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind,
20-
GoalSource, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData,
21-
QueryResult, SolverMode, FIXPOINT_STEP_LIMIT,
19+
CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind, GoalSource, MaybeCause,
20+
NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryResult, SolverMode,
21+
FIXPOINT_STEP_LIMIT,
2222
};
2323

2424
pub(super) mod canonical;
@@ -72,7 +72,7 @@ where
7272
/// new placeholders to the caller.
7373
pub(super) max_input_universe: ty::UniverseIndex,
7474

75-
pub(super) search_graph: &'a mut SearchGraph<I>,
75+
pub(super) search_graph: &'a mut SearchGraph<D>,
7676

7777
nested_goals: NestedGoals<I>,
7878

@@ -200,7 +200,7 @@ where
200200
generate_proof_tree: GenerateProofTree,
201201
f: impl FnOnce(&mut EvalCtxt<'_, D>) -> R,
202202
) -> (R, Option<inspect::GoalEvaluation<I>>) {
203-
let mut search_graph = search_graph::SearchGraph::new(delegate.solver_mode());
203+
let mut search_graph = SearchGraph::new(delegate.solver_mode());
204204

205205
let mut ecx = EvalCtxt {
206206
delegate,
@@ -241,7 +241,7 @@ where
241241
/// and registering opaques from the canonicalized input.
242242
fn enter_canonical<R>(
243243
cx: I,
244-
search_graph: &'a mut search_graph::SearchGraph<I>,
244+
search_graph: &'a mut SearchGraph<D>,
245245
canonical_input: CanonicalInput<I>,
246246
canonical_goal_evaluation: &mut ProofTreeBuilder<D>,
247247
f: impl FnOnce(&mut EvalCtxt<'_, D>, Goal<I, I::Predicate>) -> R,
@@ -296,7 +296,7 @@ where
296296
#[instrument(level = "debug", skip(cx, search_graph, goal_evaluation), ret)]
297297
fn evaluate_canonical_goal(
298298
cx: I,
299-
search_graph: &'a mut search_graph::SearchGraph<I>,
299+
search_graph: &'a mut SearchGraph<D>,
300300
canonical_input: CanonicalInput<I>,
301301
goal_evaluation: &mut ProofTreeBuilder<D>,
302302
) -> QueryResult<I> {

‎compiler/rustc_next_trait_solver/src/solve/inspect/build.rs

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::marker::PhantomData;
88
use std::mem;
99

1010
use rustc_type_ir::inherent::*;
11-
use rustc_type_ir::{self as ty, Interner};
11+
use rustc_type_ir::{self as ty, search_graph, Interner};
1212

1313
use crate::delegate::SolverDelegate;
1414
use crate::solve::eval_ctxt::canonical;
@@ -38,7 +38,7 @@ use crate::solve::{
3838
/// trees. At the end of trait solving `ProofTreeBuilder::finalize`
3939
/// is called to recursively convert the whole structure to a
4040
/// finished proof tree.
41-
pub(in crate::solve) struct ProofTreeBuilder<D, I = <D as SolverDelegate>::Interner>
41+
pub(crate) struct ProofTreeBuilder<D, I = <D as SolverDelegate>::Interner>
4242
where
4343
D: SolverDelegate<Interner = I>,
4444
I: Interner,
@@ -321,23 +321,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
321321
})
322322
}
323323

324-
pub fn finalize_canonical_goal_evaluation(
325-
&mut self,
326-
cx: I,
327-
) -> Option<I::CanonicalGoalEvaluationStepRef> {
328-
self.as_mut().map(|this| match this {
329-
DebugSolver::CanonicalGoalEvaluation(evaluation) => {
330-
let final_revision = mem::take(&mut evaluation.final_revision).unwrap();
331-
let final_revision =
332-
cx.intern_canonical_goal_evaluation_step(final_revision.finalize());
333-
let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
334-
assert_eq!(evaluation.kind.replace(kind), None);
335-
final_revision
336-
}
337-
_ => unreachable!(),
338-
})
339-
}
340-
341324
pub fn canonical_goal_evaluation(&mut self, canonical_goal_evaluation: ProofTreeBuilder<D>) {
342325
if let Some(this) = self.as_mut() {
343326
match (this, *canonical_goal_evaluation.state.unwrap()) {
@@ -571,3 +554,51 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
571554
}
572555
}
573556
}
557+
558+
impl<D, I> search_graph::ProofTreeBuilder<I> for ProofTreeBuilder<D>
559+
where
560+
D: SolverDelegate<Interner = I>,
561+
I: Interner,
562+
{
563+
fn try_apply_proof_tree(
564+
&mut self,
565+
proof_tree: Option<I::CanonicalGoalEvaluationStepRef>,
566+
) -> bool {
567+
if !self.is_noop() {
568+
if let Some(final_revision) = proof_tree {
569+
let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
570+
self.canonical_goal_evaluation_kind(kind);
571+
true
572+
} else {
573+
false
574+
}
575+
} else {
576+
true
577+
}
578+
}
579+
580+
fn on_provisional_cache_hit(&mut self) {
581+
self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::ProvisionalCacheHit);
582+
}
583+
584+
fn on_cycle_in_stack(&mut self) {
585+
self.canonical_goal_evaluation_kind(WipCanonicalGoalEvaluationKind::CycleInStack);
586+
}
587+
588+
fn finalize_canonical_goal_evaluation(
589+
&mut self,
590+
tcx: I,
591+
) -> Option<I::CanonicalGoalEvaluationStepRef> {
592+
self.as_mut().map(|this| match this {
593+
DebugSolver::CanonicalGoalEvaluation(evaluation) => {
594+
let final_revision = mem::take(&mut evaluation.final_revision).unwrap();
595+
let final_revision =
596+
tcx.intern_canonical_goal_evaluation_step(final_revision.finalize());
597+
let kind = WipCanonicalGoalEvaluationKind::Interned { final_revision };
598+
assert_eq!(evaluation.kind.replace(kind), None);
599+
final_revision
600+
}
601+
_ => unreachable!(),
602+
})
603+
}
604+
}

‎compiler/rustc_next_trait_solver/src/solve/search_graph.rs

Lines changed: 57 additions & 566 deletions
Large diffs are not rendered by default.

‎compiler/rustc_query_system/src/cache.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl<Key: Eq + Hash, Value: Clone> Cache<Key, Value> {
4040
}
4141
}
4242

43-
#[derive(Clone, Eq, PartialEq)]
43+
#[derive(Debug, Clone, Eq, PartialEq)]
4444
pub struct WithDepNode<T> {
4545
dep_node: DepNodeIndex,
4646
cached_value: T,

‎compiler/rustc_type_ir/src/inherent.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ use std::hash::Hash;
88

99
use rustc_ast_ir::Mutability;
1010

11-
use crate::data_structures::HashSet;
1211
use crate::elaborate::Elaboratable;
1312
use crate::fold::{TypeFoldable, TypeSuperFoldable};
1413
use crate::relate::Relate;
15-
use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal};
14+
use crate::solve::Reveal;
1615
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
1716
use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
1817

@@ -539,33 +538,6 @@ pub trait Features<I: Interner>: Copy {
539538
fn associated_const_equality(self) -> bool;
540539
}
541540

542-
pub trait EvaluationCache<I: Interner> {
543-
/// Insert a final result into the global cache.
544-
fn insert(
545-
&self,
546-
tcx: I,
547-
key: CanonicalInput<I>,
548-
proof_tree: Option<I::CanonicalGoalEvaluationStepRef>,
549-
additional_depth: usize,
550-
encountered_overflow: bool,
551-
cycle_participants: HashSet<CanonicalInput<I>>,
552-
dep_node: I::DepNodeIndex,
553-
result: QueryResult<I>,
554-
);
555-
556-
/// Try to fetch a cached result, checking the recursion limit
557-
/// and handling root goals of coinductive cycles.
558-
///
559-
/// If this returns `Some` the cache result can be used.
560-
fn get(
561-
&self,
562-
tcx: I,
563-
key: CanonicalInput<I>,
564-
stack_entries: impl IntoIterator<Item = CanonicalInput<I>>,
565-
available_depth: usize,
566-
) -> Option<CacheData<I>>;
567-
}
568-
569541
pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
570542
fn is_local(self) -> bool;
571543

‎compiler/rustc_type_ir/src/interner.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ use crate::inherent::*;
1010
use crate::ir_print::IrPrint;
1111
use crate::lang_items::TraitSolverLangItem;
1212
use crate::relate::Relate;
13+
use crate::search_graph;
1314
use crate::solve::inspect::CanonicalGoalEvaluationStep;
14-
use crate::solve::{ExternalConstraintsData, PredefinedOpaquesData, SolverMode};
15+
use crate::solve::{
16+
CanonicalInput, ExternalConstraintsData, PredefinedOpaquesData, QueryResult, SolverMode,
17+
};
1518
use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
1619
use crate::{self as ty};
1720

@@ -86,6 +89,13 @@ pub trait Interner:
8689
) -> Self::ExternalConstraints;
8790

8891
type DepNodeIndex;
92+
type Tracked<T: Debug + Clone>: Debug;
93+
fn mk_tracked<T: Debug + Clone>(
94+
self,
95+
data: T,
96+
dep_node: Self::DepNodeIndex,
97+
) -> Self::Tracked<T>;
98+
fn get_tracked<T: Debug + Clone>(self, tracked: &Self::Tracked<T>) -> T;
8999
fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex);
90100

91101
// Kinds of tys
@@ -125,8 +135,11 @@ pub trait Interner:
125135
type Clause: Clause<Self>;
126136
type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
127137

128-
type EvaluationCache: EvaluationCache<Self>;
129-
fn evaluation_cache(self, mode: SolverMode) -> Self::EvaluationCache;
138+
fn with_global_cache<R>(
139+
self,
140+
mode: SolverMode,
141+
f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R,
142+
) -> R;
130143

131144
fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
132145

@@ -373,3 +386,32 @@ impl<T, R, E> CollectAndApply<T, R> for Result<T, E> {
373386
})
374387
}
375388
}
389+
390+
impl<I: Interner> search_graph::Cx for I {
391+
type ProofTree = Option<I::CanonicalGoalEvaluationStepRef>;
392+
type Input = CanonicalInput<I>;
393+
type Result = QueryResult<I>;
394+
395+
type DepNodeIndex = I::DepNodeIndex;
396+
type Tracked<T: Debug + Clone> = I::Tracked<T>;
397+
fn mk_tracked<T: Debug + Clone>(
398+
self,
399+
data: T,
400+
dep_node_index: I::DepNodeIndex,
401+
) -> I::Tracked<T> {
402+
I::mk_tracked(self, data, dep_node_index)
403+
}
404+
fn get_tracked<T: Debug + Clone>(self, tracked: &I::Tracked<T>) -> T {
405+
I::get_tracked(self, tracked)
406+
}
407+
fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, I::DepNodeIndex) {
408+
I::with_cached_task(self, task)
409+
}
410+
fn with_global_cache<R>(
411+
self,
412+
mode: SolverMode,
413+
f: impl FnOnce(&mut search_graph::GlobalCache<Self>) -> R,
414+
) -> R {
415+
I::with_global_cache(self, mode, f)
416+
}
417+
}

‎compiler/rustc_type_ir/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub mod lang_items;
3030
pub mod lift;
3131
pub mod outlives;
3232
pub mod relate;
33+
pub mod search_graph;
3334
pub mod solve;
3435

3536
// These modules are not `pub` since they are glob-imported.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use rustc_index::IndexVec;
2+
3+
use super::{AvailableDepth, Cx, StackDepth, StackEntry};
4+
use crate::data_structures::{HashMap, HashSet};
5+
6+
#[derive(derivative::Derivative)]
7+
#[derivative(Debug(bound = ""), Clone(bound = ""), Copy(bound = ""))]
8+
struct QueryData<X: Cx> {
9+
result: X::Result,
10+
proof_tree: X::ProofTree,
11+
}
12+
13+
struct Success<X: Cx> {
14+
data: X::Tracked<QueryData<X>>,
15+
additional_depth: usize,
16+
}
17+
18+
/// The cache entry for a given input.
19+
///
20+
/// This contains results whose computation never hit the
21+
/// recursion limit in `success`, and all results which hit
22+
/// the recursion limit in `with_overflow`.
23+
#[derive(derivative::Derivative)]
24+
#[derivative(Default(bound = ""))]
25+
struct CacheEntry<X: Cx> {
26+
success: Option<Success<X>>,
27+
/// We have to be careful when caching roots of cycles.
28+
///
29+
/// See the doc comment of `StackEntry::cycle_participants` for more
30+
/// details.
31+
nested_goals: HashSet<X::Input>,
32+
with_overflow: HashMap<usize, X::Tracked<QueryData<X>>>,
33+
}
34+
35+
#[derive(derivative::Derivative)]
36+
#[derivative(Debug(bound = ""))]
37+
pub(super) struct CacheData<'a, X: Cx> {
38+
pub(super) result: X::Result,
39+
pub(super) proof_tree: X::ProofTree,
40+
pub(super) additional_depth: usize,
41+
pub(super) encountered_overflow: bool,
42+
// FIXME: This is currently unused, but impacts the design
43+
// by requiring a closure for `Cx::with_global_cache`.
44+
pub(super) nested_goals: &'a HashSet<X::Input>,
45+
}
46+
47+
#[derive(derivative::Derivative)]
48+
#[derivative(Default(bound = ""))]
49+
pub struct GlobalCache<X: Cx> {
50+
map: HashMap<X::Input, CacheEntry<X>>,
51+
}
52+
53+
impl<X: Cx> GlobalCache<X> {
54+
/// Insert a final result into the global cache.
55+
pub(super) fn insert(
56+
&mut self,
57+
cx: X,
58+
input: X::Input,
59+
60+
result: X::Result,
61+
proof_tree: X::ProofTree,
62+
dep_node: X::DepNodeIndex,
63+
64+
additional_depth: usize,
65+
encountered_overflow: bool,
66+
nested_goals: &HashSet<X::Input>,
67+
) {
68+
let data = cx.mk_tracked(QueryData { result, proof_tree }, dep_node);
69+
let entry = self.map.entry(input).or_default();
70+
entry.nested_goals.extend(nested_goals);
71+
if encountered_overflow {
72+
entry.with_overflow.insert(additional_depth, data);
73+
} else {
74+
entry.success = Some(Success { data, additional_depth });
75+
}
76+
}
77+
78+
/// Try to fetch a cached result, checking the recursion limit
79+
/// and handling root goals of coinductive cycles.
80+
///
81+
/// If this returns `Some` the cache result can be used.
82+
pub(super) fn get<'a>(
83+
&'a self,
84+
cx: X,
85+
input: X::Input,
86+
stack: &IndexVec<StackDepth, StackEntry<X>>,
87+
available_depth: AvailableDepth,
88+
) -> Option<CacheData<'a, X>> {
89+
let entry = self.map.get(&input)?;
90+
if stack.iter().any(|e| entry.nested_goals.contains(&e.input)) {
91+
return None;
92+
}
93+
94+
if let Some(ref success) = entry.success {
95+
if available_depth.cache_entry_is_applicable(success.additional_depth) {
96+
let QueryData { result, proof_tree } = cx.get_tracked(&success.data);
97+
return Some(CacheData {
98+
result,
99+
proof_tree,
100+
additional_depth: success.additional_depth,
101+
encountered_overflow: false,
102+
nested_goals: &entry.nested_goals,
103+
});
104+
}
105+
}
106+
107+
entry.with_overflow.get(&available_depth.0).map(|e| {
108+
let QueryData { result, proof_tree } = cx.get_tracked(e);
109+
CacheData {
110+
result,
111+
proof_tree,
112+
additional_depth: available_depth.0,
113+
encountered_overflow: true,
114+
nested_goals: &entry.nested_goals,
115+
}
116+
})
117+
}
118+
}

‎compiler/rustc_type_ir/src/search_graph/mod.rs

Lines changed: 605 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use super::*;
2+
3+
impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
4+
#[allow(rustc::potential_query_instability)]
5+
pub(super) fn check_invariants(&self) {
6+
if !cfg!(debug_assertions) {
7+
return;
8+
}
9+
10+
let SearchGraph { mode: _, stack, provisional_cache, _marker } = self;
11+
if stack.is_empty() {
12+
assert!(provisional_cache.is_empty());
13+
}
14+
15+
for (depth, entry) in stack.iter_enumerated() {
16+
let StackEntry {
17+
input,
18+
available_depth: _,
19+
reached_depth: _,
20+
non_root_cycle_participant,
21+
encountered_overflow: _,
22+
has_been_used,
23+
ref nested_goals,
24+
provisional_result,
25+
} = *entry;
26+
let cache_entry = provisional_cache.get(&entry.input).unwrap();
27+
assert_eq!(cache_entry.stack_depth, Some(depth));
28+
if let Some(head) = non_root_cycle_participant {
29+
assert!(head < depth);
30+
assert!(nested_goals.is_empty());
31+
assert_ne!(stack[head].has_been_used, None);
32+
33+
let mut current_root = head;
34+
while let Some(parent) = stack[current_root].non_root_cycle_participant {
35+
current_root = parent;
36+
}
37+
assert!(stack[current_root].nested_goals.contains(&input));
38+
}
39+
40+
if !nested_goals.is_empty() {
41+
assert!(provisional_result.is_some() || has_been_used.is_some());
42+
for entry in stack.iter().take(depth.as_usize()) {
43+
assert_eq!(nested_goals.get(&entry.input), None);
44+
}
45+
}
46+
}
47+
48+
for (&input, entry) in &self.provisional_cache {
49+
let ProvisionalCacheEntry { stack_depth, with_coinductive_stack, with_inductive_stack } =
50+
entry;
51+
assert!(
52+
stack_depth.is_some()
53+
|| with_coinductive_stack.is_some()
54+
|| with_inductive_stack.is_some()
55+
);
56+
57+
if let &Some(stack_depth) = stack_depth {
58+
assert_eq!(stack[stack_depth].input, input);
59+
}
60+
61+
let check_detached = |detached_entry: &DetachedEntry<X>| {
62+
let DetachedEntry { head, result: _ } = *detached_entry;
63+
assert_ne!(stack[head].has_been_used, None);
64+
};
65+
66+
if let Some(with_coinductive_stack) = with_coinductive_stack {
67+
check_detached(with_coinductive_stack);
68+
}
69+
70+
if let Some(with_inductive_stack) = with_inductive_stack {
71+
check_detached(with_inductive_stack);
72+
}
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)
Please sign in to comment.