Skip to content

Commit ff2c007

Browse files
committed
make RecursiveContext type def'n generic over K, V
1 parent 2fb3433 commit ff2c007

File tree

1 file changed

+97
-82
lines changed

1 file changed

+97
-82
lines changed

chalk-recursive/src/recursive.rs

Lines changed: 97 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,26 @@ use chalk_ir::{Canonical, ConstrainedSubst, Goal, InEnvironment, UCanonical};
99
use chalk_ir::{Constraints, Fallible};
1010
use chalk_solve::{coinductive_goal::IsCoinductive, RustIrDatabase, Solution};
1111
use std::fmt;
12+
use std::fmt::Debug;
13+
use std::hash::Hash;
1214
use tracing::debug;
1315
use tracing::{info, instrument};
1416

15-
struct RecursiveContext<I: Interner> {
17+
struct RecursiveContext<K, V>
18+
where
19+
K: Hash + Eq + Debug + Clone,
20+
V: Debug + Clone,
21+
{
1622
stack: Stack,
1723

1824
/// The "search graph" stores "in-progress results" that are still being
1925
/// solved.
20-
search_graph: SearchGraph<UCanonicalGoal<I>, Fallible<Solution<I>>>,
26+
search_graph: SearchGraph<K, V>,
2127

2228
/// The "cache" stores results for goals that we have completely solved.
2329
/// Things are added to the cache when we have completely processed their
2430
/// result.
25-
cache: Option<Cache<UCanonicalGoal<I>, Fallible<Solution<I>>>>,
31+
cache: Option<Cache<K, V>>,
2632

2733
/// The maximum size for goals.
2834
max_size: usize,
@@ -35,11 +41,11 @@ struct RecursiveContext<I: Interner> {
3541
/// context.
3642
struct Solver<'me, I: Interner> {
3743
program: &'me dyn RustIrDatabase<I>,
38-
context: &'me mut RecursiveContext<I>,
44+
context: &'me mut RecursiveContext<UCanonicalGoal<I>, Fallible<Solution<I>>>,
3945
}
4046

4147
pub struct RecursiveSolver<I: Interner> {
42-
ctx: Box<RecursiveContext<I>>,
48+
ctx: Box<RecursiveContext<UCanonicalGoal<I>, Fallible<Solution<I>>>>,
4349
}
4450

4551
impl<I: Interner> RecursiveSolver<I> {
@@ -60,32 +66,29 @@ impl<I: Interner> fmt::Debug for RecursiveSolver<I> {
6066
}
6167
}
6268

63-
impl<I: Interner> RecursiveContext<I> {
64-
pub fn new(
65-
overflow_depth: usize,
66-
max_size: usize,
67-
cache: Option<Cache<UCanonicalGoal<I>, Fallible<Solution<I>>>>,
68-
) -> Self {
69+
impl<K, V> RecursiveContext<K, V>
70+
where
71+
K: Hash + Eq + Debug + Clone,
72+
V: Debug + Clone,
73+
{
74+
pub fn new(overflow_depth: usize, max_size: usize, cache: Option<Cache<K, V>>) -> Self {
6975
RecursiveContext {
7076
stack: Stack::new(overflow_depth),
7177
search_graph: SearchGraph::new(),
7278
cache,
7379
max_size,
7480
}
7581
}
82+
}
7683

77-
pub(crate) fn solver<'me>(
78-
&'me mut self,
84+
impl<'me, I: Interner> Solver<'me, I> {
85+
pub(crate) fn new(
86+
context: &'me mut RecursiveContext<UCanonicalGoal<I>, Fallible<Solution<I>>>,
7987
program: &'me dyn RustIrDatabase<I>,
80-
) -> Solver<'me, I> {
81-
Solver {
82-
program,
83-
context: self,
84-
}
88+
) -> Self {
89+
Self { program, context }
8590
}
86-
}
8791

88-
impl<'me, I: Interner> Solver<'me, I> {
8992
/// Solves a canonical goal. The substitution returned in the
9093
/// solution will be for the fully decomposed goal. For example, given the
9194
/// program
@@ -111,66 +114,6 @@ impl<'me, I: Interner> Solver<'me, I> {
111114
self.solve_goal(canonical_goal.clone(), minimums)
112115
}
113116

114-
#[instrument(level = "debug", skip(self))]
115-
fn solve_new_subgoal(
116-
&mut self,
117-
canonical_goal: UCanonicalGoal<I>,
118-
depth: StackDepth,
119-
dfn: DepthFirstNumber,
120-
) -> Minimums {
121-
// We start with `answer = None` and try to solve the goal. At the end of the iteration,
122-
// `answer` will be updated with the result of the solving process. If we detect a cycle
123-
// during the solving process, we cache `answer` and try to solve the goal again. We repeat
124-
// until we reach a fixed point for `answer`.
125-
// Considering the partial order:
126-
// - None < Some(Unique) < Some(Ambiguous)
127-
// - None < Some(CannotProve)
128-
// the function which maps the loop iteration to `answer` is a nondecreasing function
129-
// so this function will eventually be constant and the loop terminates.
130-
loop {
131-
let minimums = &mut Minimums::new();
132-
let current_answer = self.solve_iteration(&canonical_goal, minimums);
133-
134-
debug!(
135-
"solve_new_subgoal: loop iteration result = {:?} with minimums {:?}",
136-
current_answer, minimums
137-
);
138-
139-
if !self.context.stack[depth].read_and_reset_cycle_flag() {
140-
// None of our subgoals depended on us directly.
141-
// We can return.
142-
self.context.search_graph[dfn].solution = current_answer;
143-
return *minimums;
144-
}
145-
146-
// Some of our subgoals depended on us. We need to re-run
147-
// with the current answer.
148-
if self.context.search_graph[dfn].solution == current_answer {
149-
// Reached a fixed point.
150-
return *minimums;
151-
}
152-
153-
let current_answer_is_ambig = match &current_answer {
154-
Ok(s) => s.is_ambig(),
155-
Err(_) => false,
156-
};
157-
158-
self.context.search_graph[dfn].solution = current_answer;
159-
160-
// Subtle: if our current answer is ambiguous, we can just stop, and
161-
// in fact we *must* -- otherwise, we sometimes fail to reach a
162-
// fixed point. See `multiple_ambiguous_cycles` for more.
163-
if current_answer_is_ambig {
164-
return *minimums;
165-
}
166-
167-
// Otherwise: rollback the search tree and try again.
168-
self.context.search_graph.rollback_to(dfn + 1);
169-
}
170-
}
171-
}
172-
173-
impl<'me, I: Interner> SolveDatabase<I> for Solver<'me, I> {
174117
/// Attempt to solve a goal that has been fully broken down into leaf form
175118
/// and canonicalized. This is where the action really happens, and is the
176119
/// place where we would perform caching in rustc (and may eventually do in Chalk).
@@ -264,6 +207,74 @@ impl<'me, I: Interner> SolveDatabase<I> for Solver<'me, I> {
264207
}
265208
}
266209

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+
}
267+
}
268+
269+
impl<'me, I: Interner> SolveDatabase<I> for Solver<'me, I> {
270+
fn solve_goal(
271+
&mut self,
272+
goal: UCanonicalGoal<I>,
273+
minimums: &mut Minimums,
274+
) -> Fallible<Solution<I>> {
275+
self.solve_goal(goal, minimums)
276+
}
277+
267278
fn interner(&self) -> &I {
268279
&self.program.interner()
269280
}
@@ -283,7 +294,9 @@ impl<I: Interner> chalk_solve::Solver<I> for RecursiveSolver<I> {
283294
program: &dyn RustIrDatabase<I>,
284295
goal: &UCanonical<InEnvironment<Goal<I>>>,
285296
) -> Option<chalk_solve::Solution<I>> {
286-
self.ctx.solver(program).solve_root_goal(goal).ok()
297+
Solver::new(&mut self.ctx, program)
298+
.solve_root_goal(goal)
299+
.ok()
287300
}
288301

289302
fn solve_limited(
@@ -293,7 +306,9 @@ impl<I: Interner> chalk_solve::Solver<I> for RecursiveSolver<I> {
293306
_should_continue: &dyn std::ops::Fn() -> bool,
294307
) -> Option<chalk_solve::Solution<I>> {
295308
// TODO support should_continue in recursive solver
296-
self.ctx.solver(program).solve_root_goal(goal).ok()
309+
Solver::new(&mut self.ctx, program)
310+
.solve_root_goal(goal)
311+
.ok()
297312
}
298313

299314
fn solve_multiple(

0 commit comments

Comments
 (0)