@@ -9,20 +9,26 @@ use chalk_ir::{Canonical, ConstrainedSubst, Goal, InEnvironment, UCanonical};
9
9
use chalk_ir:: { Constraints , Fallible } ;
10
10
use chalk_solve:: { coinductive_goal:: IsCoinductive , RustIrDatabase , Solution } ;
11
11
use std:: fmt;
12
+ use std:: fmt:: Debug ;
13
+ use std:: hash:: Hash ;
12
14
use tracing:: debug;
13
15
use tracing:: { info, instrument} ;
14
16
15
- struct RecursiveContext < I : Interner > {
17
+ struct RecursiveContext < K , V >
18
+ where
19
+ K : Hash + Eq + Debug + Clone ,
20
+ V : Debug + Clone ,
21
+ {
16
22
stack : Stack ,
17
23
18
24
/// The "search graph" stores "in-progress results" that are still being
19
25
/// solved.
20
- search_graph : SearchGraph < UCanonicalGoal < I > , Fallible < Solution < I > > > ,
26
+ search_graph : SearchGraph < K , V > ,
21
27
22
28
/// The "cache" stores results for goals that we have completely solved.
23
29
/// Things are added to the cache when we have completely processed their
24
30
/// result.
25
- cache : Option < Cache < UCanonicalGoal < I > , Fallible < Solution < I > > > > ,
31
+ cache : Option < Cache < K , V > > ,
26
32
27
33
/// The maximum size for goals.
28
34
max_size : usize ,
@@ -35,11 +41,11 @@ struct RecursiveContext<I: Interner> {
35
41
/// context.
36
42
struct Solver < ' me , I : Interner > {
37
43
program : & ' me dyn RustIrDatabase < I > ,
38
- context : & ' me mut RecursiveContext < I > ,
44
+ context : & ' me mut RecursiveContext < UCanonicalGoal < I > , Fallible < Solution < I > > > ,
39
45
}
40
46
41
47
pub struct RecursiveSolver < I : Interner > {
42
- ctx : Box < RecursiveContext < I > > ,
48
+ ctx : Box < RecursiveContext < UCanonicalGoal < I > , Fallible < Solution < I > > > > ,
43
49
}
44
50
45
51
impl < I : Interner > RecursiveSolver < I > {
@@ -60,32 +66,29 @@ impl<I: Interner> fmt::Debug for RecursiveSolver<I> {
60
66
}
61
67
}
62
68
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 {
69
75
RecursiveContext {
70
76
stack : Stack :: new ( overflow_depth) ,
71
77
search_graph : SearchGraph :: new ( ) ,
72
78
cache,
73
79
max_size,
74
80
}
75
81
}
82
+ }
76
83
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 > > > ,
79
87
program : & ' me dyn RustIrDatabase < I > ,
80
- ) -> Solver < ' me , I > {
81
- Solver {
82
- program,
83
- context : self ,
84
- }
88
+ ) -> Self {
89
+ Self { program, context }
85
90
}
86
- }
87
91
88
- impl < ' me , I : Interner > Solver < ' me , I > {
89
92
/// Solves a canonical goal. The substitution returned in the
90
93
/// solution will be for the fully decomposed goal. For example, given the
91
94
/// program
@@ -111,66 +114,6 @@ impl<'me, I: Interner> Solver<'me, I> {
111
114
self . solve_goal ( canonical_goal. clone ( ) , minimums)
112
115
}
113
116
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 > {
174
117
/// Attempt to solve a goal that has been fully broken down into leaf form
175
118
/// and canonicalized. This is where the action really happens, and is the
176
119
/// 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> {
264
207
}
265
208
}
266
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
+ }
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
+
267
278
fn interner ( & self ) -> & I {
268
279
& self . program . interner ( )
269
280
}
@@ -283,7 +294,9 @@ impl<I: Interner> chalk_solve::Solver<I> for RecursiveSolver<I> {
283
294
program : & dyn RustIrDatabase < I > ,
284
295
goal : & UCanonical < InEnvironment < Goal < I > > > ,
285
296
) -> 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 ( )
287
300
}
288
301
289
302
fn solve_limited (
@@ -293,7 +306,9 @@ impl<I: Interner> chalk_solve::Solver<I> for RecursiveSolver<I> {
293
306
_should_continue : & dyn std:: ops:: Fn ( ) -> bool ,
294
307
) -> Option < chalk_solve:: Solution < I > > {
295
308
// 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 ( )
297
312
}
298
313
299
314
fn solve_multiple (
0 commit comments