Skip to content

Commit 18994ec

Browse files
committed
extract the logic for solve_new_subgoal
1 parent ff2c007 commit 18994ec

File tree

1 file changed

+70
-59
lines changed

1 file changed

+70
-59
lines changed

chalk-recursive/src/recursive.rs

Lines changed: 70 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,52 @@ where
7979
max_size,
8080
}
8181
}
82+
83+
#[instrument(level = "debug", skip(self, solve_iteration, reached_fixed_point))]
84+
fn solve_new_subgoal(
85+
&mut self,
86+
canonical_goal: &K,
87+
depth: StackDepth,
88+
dfn: DepthFirstNumber,
89+
mut solve_iteration: impl FnMut(&mut Self, &K, &mut Minimums) -> V,
90+
reached_fixed_point: impl Fn(&V, &V) -> bool,
91+
) -> Minimums {
92+
// We start with `answer = None` and try to solve the goal. At the end of the iteration,
93+
// `answer` will be updated with the result of the solving process. If we detect a cycle
94+
// during the solving process, we cache `answer` and try to solve the goal again. We repeat
95+
// until we reach a fixed point for `answer`.
96+
// Considering the partial order:
97+
// - None < Some(Unique) < Some(Ambiguous)
98+
// - None < Some(CannotProve)
99+
// the function which maps the loop iteration to `answer` is a nondecreasing function
100+
// so this function will eventually be constant and the loop terminates.
101+
loop {
102+
let minimums = &mut Minimums::new();
103+
let current_answer = solve_iteration(self, &canonical_goal, minimums);
104+
105+
debug!(
106+
"solve_new_subgoal: loop iteration result = {:?} with minimums {:?}",
107+
current_answer, minimums
108+
);
109+
110+
if !self.stack[depth].read_and_reset_cycle_flag() {
111+
// None of our subgoals depended on us directly.
112+
// We can return.
113+
self.search_graph[dfn].solution = current_answer;
114+
return *minimums;
115+
}
116+
117+
let old_answer =
118+
std::mem::replace(&mut self.search_graph[dfn].solution, current_answer);
119+
120+
if reached_fixed_point(&old_answer, &self.search_graph[dfn].solution) {
121+
return *minimums;
122+
}
123+
124+
// Otherwise: rollback the search tree and try again.
125+
self.search_graph.rollback_to(dfn + 1);
126+
}
127+
}
82128
}
83129

84130
impl<'me, I: Interner> Solver<'me, I> {
@@ -177,7 +223,30 @@ impl<'me, I: Interner> Solver<'me, I> {
177223
.context
178224
.search_graph
179225
.insert(&goal, depth, initial_solution);
180-
let subgoal_minimums = self.solve_new_subgoal(goal, depth, dfn);
226+
227+
let program = self.program;
228+
let subgoal_minimums = self.context.solve_new_subgoal(
229+
&goal,
230+
depth,
231+
dfn,
232+
|context, goal, minimums| {
233+
Solver::new(context, program).solve_iteration(goal, minimums)
234+
},
235+
|old_answer, current_answer| {
236+
// Some of our subgoals depended on us. We need to re-run
237+
// with the current answer.
238+
old_answer == current_answer || {
239+
// Subtle: if our current answer is ambiguous, we can just stop, and
240+
// in fact we *must* -- otherwise, we sometimes fail to reach a
241+
// fixed point. See `multiple_ambiguous_cycles` for more.
242+
match &current_answer {
243+
Ok(s) => s.is_ambig(),
244+
Err(_) => false,
245+
}
246+
}
247+
},
248+
);
249+
181250
self.context.search_graph[dfn].links = subgoal_minimums;
182251
self.context.search_graph[dfn].stack_depth = None;
183252
self.context.stack.pop(depth);
@@ -206,64 +275,6 @@ impl<'me, I: Interner> Solver<'me, I> {
206275
result
207276
}
208277
}
209-
210-
#[instrument(level = "debug", skip(self))]
211-
fn solve_new_subgoal(
212-
&mut self,
213-
canonical_goal: UCanonicalGoal<I>,
214-
depth: StackDepth,
215-
dfn: DepthFirstNumber,
216-
) -> Minimums {
217-
// We start with `answer = None` and try to solve the goal. At the end of the iteration,
218-
// `answer` will be updated with the result of the solving process. If we detect a cycle
219-
// during the solving process, we cache `answer` and try to solve the goal again. We repeat
220-
// until we reach a fixed point for `answer`.
221-
// Considering the partial order:
222-
// - None < Some(Unique) < Some(Ambiguous)
223-
// - None < Some(CannotProve)
224-
// the function which maps the loop iteration to `answer` is a nondecreasing function
225-
// so this function will eventually be constant and the loop terminates.
226-
loop {
227-
let minimums = &mut Minimums::new();
228-
let current_answer = self.solve_iteration(&canonical_goal, minimums);
229-
230-
debug!(
231-
"solve_new_subgoal: loop iteration result = {:?} with minimums {:?}",
232-
current_answer, minimums
233-
);
234-
235-
if !self.context.stack[depth].read_and_reset_cycle_flag() {
236-
// None of our subgoals depended on us directly.
237-
// We can return.
238-
self.context.search_graph[dfn].solution = current_answer;
239-
return *minimums;
240-
}
241-
242-
// Some of our subgoals depended on us. We need to re-run
243-
// with the current answer.
244-
if self.context.search_graph[dfn].solution == current_answer {
245-
// Reached a fixed point.
246-
return *minimums;
247-
}
248-
249-
let current_answer_is_ambig = match &current_answer {
250-
Ok(s) => s.is_ambig(),
251-
Err(_) => false,
252-
};
253-
254-
self.context.search_graph[dfn].solution = current_answer;
255-
256-
// Subtle: if our current answer is ambiguous, we can just stop, and
257-
// in fact we *must* -- otherwise, we sometimes fail to reach a
258-
// fixed point. See `multiple_ambiguous_cycles` for more.
259-
if current_answer_is_ambig {
260-
return *minimums;
261-
}
262-
263-
// Otherwise: rollback the search tree and try again.
264-
self.context.search_graph.rollback_to(dfn + 1);
265-
}
266-
}
267278
}
268279

269280
impl<'me, I: Interner> SolveDatabase<I> for Solver<'me, I> {

0 commit comments

Comments
 (0)