@@ -83,27 +83,12 @@ where
83
83
/// Attempt to solve a goal that has been fully broken down into leaf form
84
84
/// and canonicalized. This is where the action really happens, and is the
85
85
/// place where we would perform caching in rustc (and may eventually do in Chalk).
86
- #[ instrument(
87
- level = "info" ,
88
- skip(
89
- self ,
90
- minimums,
91
- is_coinductive_goal,
92
- initial_value,
93
- solve_iteration,
94
- reached_fixed_point,
95
- error_value
96
- )
97
- ) ]
86
+ #[ instrument( level = "info" , skip( self , minimums, solver_stuff, ) ) ]
98
87
fn solve_goal (
99
88
& mut self ,
100
89
goal : & K ,
101
90
minimums : & mut Minimums ,
102
- is_coinductive_goal : impl Fn ( & K ) -> bool ,
103
- initial_value : impl Fn ( & K , bool ) -> V ,
104
- solve_iteration : impl FnMut ( & mut Self , & K , & mut Minimums ) -> V ,
105
- reached_fixed_point : impl Fn ( & V , & V ) -> bool ,
106
- error_value : impl Fn ( ) -> V ,
91
+ solver_stuff : impl SolverStuff < K , V > ,
107
92
) -> V {
108
93
// First check the cache.
109
94
if let Some ( cache) = & self . cache {
@@ -122,7 +107,7 @@ where
122
107
// see the corresponding section in the coinduction chapter:
123
108
// https://rust-lang.github.io/chalk/book/recursive/coinduction.html#mixed-co-inductive-and-inductive-cycles
124
109
if self . stack . mixed_inductive_coinductive_cycle_from ( depth) {
125
- return error_value ( ) ;
110
+ return solver_stuff . error_value ( ) ;
126
111
}
127
112
}
128
113
@@ -138,13 +123,12 @@ where
138
123
} else {
139
124
// Otherwise, push the goal onto the stack and create a table.
140
125
// The initial result for this table depends on whether the goal is coinductive.
141
- let coinductive_goal = is_coinductive_goal ( goal) ;
142
- let initial_solution = initial_value ( goal, coinductive_goal) ;
126
+ let coinductive_goal = solver_stuff . is_coinductive_goal ( goal) ;
127
+ let initial_solution = solver_stuff . initial_value ( goal, coinductive_goal) ;
143
128
let depth = self . stack . push ( coinductive_goal) ;
144
129
let dfn = self . search_graph . insert ( & goal, depth, initial_solution) ;
145
130
146
- let subgoal_minimums =
147
- self . solve_new_subgoal ( & goal, depth, dfn, solve_iteration, reached_fixed_point) ;
131
+ let subgoal_minimums = self . solve_new_subgoal ( & goal, depth, dfn, solver_stuff) ;
148
132
149
133
self . search_graph [ dfn] . links = subgoal_minimums;
150
134
self . search_graph [ dfn] . stack_depth = None ;
@@ -175,14 +159,13 @@ where
175
159
}
176
160
}
177
161
178
- #[ instrument( level = "debug" , skip( self , solve_iteration , reached_fixed_point ) ) ]
162
+ #[ instrument( level = "debug" , skip( self , solver_stuff ) ) ]
179
163
fn solve_new_subgoal (
180
164
& mut self ,
181
165
canonical_goal : & K ,
182
166
depth : StackDepth ,
183
167
dfn : DepthFirstNumber ,
184
- mut solve_iteration : impl FnMut ( & mut Self , & K , & mut Minimums ) -> V ,
185
- reached_fixed_point : impl Fn ( & V , & V ) -> bool ,
168
+ solver_stuff : impl SolverStuff < K , V > ,
186
169
) -> Minimums {
187
170
// We start with `answer = None` and try to solve the goal. At the end of the iteration,
188
171
// `answer` will be updated with the result of the solving process. If we detect a cycle
@@ -195,7 +178,7 @@ where
195
178
// so this function will eventually be constant and the loop terminates.
196
179
loop {
197
180
let minimums = & mut Minimums :: new ( ) ;
198
- let current_answer = solve_iteration ( self , & canonical_goal, minimums) ;
181
+ let current_answer = solver_stuff . solve_iteration ( self , & canonical_goal, minimums) ;
199
182
200
183
debug ! (
201
184
"solve_new_subgoal: loop iteration result = {:?} with minimums {:?}" ,
@@ -212,7 +195,7 @@ where
212
195
let old_answer =
213
196
std:: mem:: replace ( & mut self . search_graph [ dfn] . solution , current_answer) ;
214
197
215
- if reached_fixed_point ( & old_answer, & self . search_graph [ dfn] . solution ) {
198
+ if solver_stuff . reached_fixed_point ( & old_answer, & self . search_graph [ dfn] . solution ) {
216
199
return * minimums;
217
200
}
218
201
@@ -256,47 +239,85 @@ impl<'me, I: Interner> Solver<'me, I> {
256
239
}
257
240
}
258
241
242
+ trait SolverStuff < K , V > : Copy
243
+ where
244
+ K : Hash + Eq + Debug + Clone ,
245
+ V : Debug + Clone ,
246
+ {
247
+ fn is_coinductive_goal ( self , goal : & K ) -> bool ;
248
+ fn initial_value ( self , goal : & K , coinductive_goal : bool ) -> V ;
249
+ fn solve_iteration (
250
+ self ,
251
+ context : & mut RecursiveContext < K , V > ,
252
+ goal : & K ,
253
+ minimums : & mut Minimums ,
254
+ ) -> V ;
255
+ fn reached_fixed_point ( self , old_value : & V , new_value : & V ) -> bool ;
256
+ fn error_value ( self ) -> V ;
257
+ }
258
+
259
+ impl < I : Interner > SolverStuff < UCanonicalGoal < I > , Fallible < Solution < I > > > for & dyn RustIrDatabase < I > {
260
+ fn is_coinductive_goal ( self , goal : & UCanonicalGoal < I > ) -> bool {
261
+ goal. is_coinductive ( self )
262
+ }
263
+
264
+ fn initial_value (
265
+ self ,
266
+ goal : & UCanonicalGoal < I > ,
267
+ coinductive_goal : bool ,
268
+ ) -> Fallible < Solution < I > > {
269
+ if coinductive_goal {
270
+ Ok ( Solution :: Unique ( Canonical {
271
+ value : ConstrainedSubst {
272
+ subst : goal. trivial_substitution ( self . interner ( ) ) ,
273
+ constraints : Constraints :: empty ( self . interner ( ) ) ,
274
+ } ,
275
+ binders : goal. canonical . binders . clone ( ) ,
276
+ } ) )
277
+ } else {
278
+ Err ( NoSolution )
279
+ }
280
+ }
281
+
282
+ fn solve_iteration (
283
+ self ,
284
+ context : & mut RecursiveContext < UCanonicalGoal < I > , Fallible < Solution < I > > > ,
285
+ goal : & UCanonicalGoal < I > ,
286
+ minimums : & mut Minimums ,
287
+ ) -> Fallible < Solution < I > > {
288
+ Solver :: new ( context, self ) . solve_iteration ( goal, minimums)
289
+ }
290
+
291
+ fn reached_fixed_point (
292
+ self ,
293
+ old_answer : & Fallible < Solution < I > > ,
294
+ current_answer : & Fallible < Solution < I > > ,
295
+ ) -> bool {
296
+ // Some of our subgoals depended on us. We need to re-run
297
+ // with the current answer.
298
+ old_answer == current_answer || {
299
+ // Subtle: if our current answer is ambiguous, we can just stop, and
300
+ // in fact we *must* -- otherwise, we sometimes fail to reach a
301
+ // fixed point. See `multiple_ambiguous_cycles` for more.
302
+ match & current_answer {
303
+ Ok ( s) => s. is_ambig ( ) ,
304
+ Err ( _) => false ,
305
+ }
306
+ }
307
+ }
308
+
309
+ fn error_value ( self ) -> Fallible < Solution < I > > {
310
+ Err ( NoSolution )
311
+ }
312
+ }
313
+
259
314
impl < ' me , I : Interner > SolveDatabase < I > for Solver < ' me , I > {
260
315
fn solve_goal (
261
316
& mut self ,
262
317
goal : UCanonicalGoal < I > ,
263
318
minimums : & mut Minimums ,
264
319
) -> Fallible < Solution < I > > {
265
- let program = self . program ;
266
- let interner = program. interner ( ) ;
267
- self . context . solve_goal (
268
- & goal,
269
- minimums,
270
- |goal| goal. is_coinductive ( program) ,
271
- |goal, coinductive_goal| {
272
- if coinductive_goal {
273
- Ok ( Solution :: Unique ( Canonical {
274
- value : ConstrainedSubst {
275
- subst : goal. trivial_substitution ( interner) ,
276
- constraints : Constraints :: empty ( interner) ,
277
- } ,
278
- binders : goal. canonical . binders . clone ( ) ,
279
- } ) )
280
- } else {
281
- Err ( NoSolution )
282
- }
283
- } ,
284
- |context, goal, minimums| Solver :: new ( context, program) . solve_iteration ( goal, minimums) ,
285
- |old_answer, current_answer| {
286
- // Some of our subgoals depended on us. We need to re-run
287
- // with the current answer.
288
- old_answer == current_answer || {
289
- // Subtle: if our current answer is ambiguous, we can just stop, and
290
- // in fact we *must* -- otherwise, we sometimes fail to reach a
291
- // fixed point. See `multiple_ambiguous_cycles` for more.
292
- match & current_answer {
293
- Ok ( s) => s. is_ambig ( ) ,
294
- Err ( _) => false ,
295
- }
296
- }
297
- } ,
298
- || Err ( NoSolution ) ,
299
- )
320
+ self . context . solve_goal ( & goal, minimums, self . program )
300
321
}
301
322
302
323
fn interner ( & self ) -> & I {
0 commit comments