80
80
}
81
81
}
82
82
83
+ /// Attempt to solve a goal that has been fully broken down into leaf form
84
+ /// and canonicalized. This is where the action really happens, and is the
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
+ initial_value,
92
+ solve_iteration,
93
+ reached_fixed_point,
94
+ error_value
95
+ )
96
+ ) ]
97
+ fn solve_goal (
98
+ & mut self ,
99
+ goal : & K ,
100
+ minimums : & mut Minimums ,
101
+ initial_value : impl Fn ( & K ) -> ( bool , V ) ,
102
+ solve_iteration : impl FnMut ( & mut Self , & K , & mut Minimums ) -> V ,
103
+ reached_fixed_point : impl Fn ( & V , & V ) -> bool ,
104
+ error_value : impl Fn ( ) -> V ,
105
+ ) -> V {
106
+ // First check the cache.
107
+ if let Some ( cache) = & self . cache {
108
+ if let Some ( value) = cache. get ( & goal) {
109
+ debug ! ( "solve_reduced_goal: cache hit, value={:?}" , value) ;
110
+ return value. clone ( ) ;
111
+ }
112
+ }
113
+
114
+ // Next, check if the goal is in the search tree already.
115
+ if let Some ( dfn) = self . search_graph . lookup ( & goal) {
116
+ // Check if this table is still on the stack.
117
+ if let Some ( depth) = self . search_graph [ dfn] . stack_depth {
118
+ self . stack [ depth] . flag_cycle ( ) ;
119
+ // Mixed cycles are not allowed. For more information about this
120
+ // see the corresponding section in the coinduction chapter:
121
+ // https://rust-lang.github.io/chalk/book/recursive/coinduction.html#mixed-co-inductive-and-inductive-cycles
122
+ if self . stack . mixed_inductive_coinductive_cycle_from ( depth) {
123
+ return error_value ( ) ;
124
+ }
125
+ }
126
+
127
+ minimums. update_from ( self . search_graph [ dfn] . links ) ;
128
+
129
+ // Return the solution from the table.
130
+ let previous_solution = self . search_graph [ dfn] . solution . clone ( ) ;
131
+ info ! (
132
+ "solve_goal: cycle detected, previous solution {:?}" ,
133
+ previous_solution,
134
+ ) ;
135
+ previous_solution
136
+ } else {
137
+ // Otherwise, push the goal onto the stack and create a table.
138
+ // The initial result for this table depends on whether the goal is coinductive.
139
+ let ( coinductive_goal, initial_solution) = initial_value ( goal) ;
140
+ let depth = self . stack . push ( coinductive_goal) ;
141
+ let dfn = self . search_graph . insert ( & goal, depth, initial_solution) ;
142
+
143
+ let subgoal_minimums =
144
+ self . solve_new_subgoal ( & goal, depth, dfn, solve_iteration, reached_fixed_point) ;
145
+
146
+ self . search_graph [ dfn] . links = subgoal_minimums;
147
+ self . search_graph [ dfn] . stack_depth = None ;
148
+ self . stack . pop ( depth) ;
149
+ minimums. update_from ( subgoal_minimums) ;
150
+
151
+ // Read final result from table.
152
+ let result = self . search_graph [ dfn] . solution . clone ( ) ;
153
+
154
+ // If processing this subgoal did not involve anything
155
+ // outside of its subtree, then we can promote it to the
156
+ // cache now. This is a sort of hack to alleviate the
157
+ // worst of the repeated work that we do during tabling.
158
+ if subgoal_minimums. positive >= dfn {
159
+ if let Some ( cache) = & mut self . cache {
160
+ self . search_graph . move_to_cache ( dfn, cache) ;
161
+ debug ! ( "solve_reduced_goal: SCC head encountered, moving to cache" ) ;
162
+ } else {
163
+ debug ! (
164
+ "solve_reduced_goal: SCC head encountered, rolling back as caching disabled"
165
+ ) ;
166
+ self . search_graph . rollback_to ( dfn) ;
167
+ }
168
+ }
169
+
170
+ info ! ( "solve_goal: solution = {:?}" , result) ;
171
+ result
172
+ }
173
+ }
174
+
83
175
#[ instrument( level = "debug" , skip( self , solve_iteration, reached_fixed_point) ) ]
84
176
fn solve_new_subgoal (
85
177
& mut self ,
@@ -159,122 +251,6 @@ impl<'me, I: Interner> Solver<'me, I> {
159
251
let minimums = & mut Minimums :: new ( ) ;
160
252
self . solve_goal ( canonical_goal. clone ( ) , minimums)
161
253
}
162
-
163
- /// Attempt to solve a goal that has been fully broken down into leaf form
164
- /// and canonicalized. This is where the action really happens, and is the
165
- /// place where we would perform caching in rustc (and may eventually do in Chalk).
166
- #[ instrument( level = "info" , skip( self , minimums) ) ]
167
- fn solve_goal (
168
- & mut self ,
169
- goal : UCanonicalGoal < I > ,
170
- minimums : & mut Minimums ,
171
- ) -> Fallible < Solution < I > > {
172
- // First check the cache.
173
- if let Some ( cache) = & self . context . cache {
174
- if let Some ( value) = cache. get ( & goal) {
175
- debug ! ( "solve_reduced_goal: cache hit, value={:?}" , value) ;
176
- return value. clone ( ) ;
177
- }
178
- }
179
-
180
- // Next, check if the goal is in the search tree already.
181
- if let Some ( dfn) = self . context . search_graph . lookup ( & goal) {
182
- // Check if this table is still on the stack.
183
- if let Some ( depth) = self . context . search_graph [ dfn] . stack_depth {
184
- self . context . stack [ depth] . flag_cycle ( ) ;
185
- // Mixed cycles are not allowed. For more information about this
186
- // see the corresponding section in the coinduction chapter:
187
- // https://rust-lang.github.io/chalk/book/recursive/coinduction.html#mixed-co-inductive-and-inductive-cycles
188
- if self
189
- . context
190
- . stack
191
- . mixed_inductive_coinductive_cycle_from ( depth)
192
- {
193
- return Err ( NoSolution ) ;
194
- }
195
- }
196
-
197
- minimums. update_from ( self . context . search_graph [ dfn] . links ) ;
198
-
199
- // Return the solution from the table.
200
- let previous_solution = self . context . search_graph [ dfn] . solution . clone ( ) ;
201
- info ! (
202
- "solve_goal: cycle detected, previous solution {:?}" ,
203
- previous_solution,
204
- ) ;
205
- previous_solution
206
- } else {
207
- // Otherwise, push the goal onto the stack and create a table.
208
- // The initial result for this table depends on whether the goal is coinductive.
209
- let coinductive_goal = goal. is_coinductive ( self . program ) ;
210
- let depth = self . context . stack . push ( coinductive_goal) ;
211
- let initial_solution = if coinductive_goal {
212
- Ok ( Solution :: Unique ( Canonical {
213
- value : ConstrainedSubst {
214
- subst : goal. trivial_substitution ( self . interner ( ) ) ,
215
- constraints : Constraints :: empty ( self . interner ( ) ) ,
216
- } ,
217
- binders : goal. canonical . binders . clone ( ) ,
218
- } ) )
219
- } else {
220
- Err ( NoSolution )
221
- } ;
222
- let dfn = self
223
- . context
224
- . search_graph
225
- . insert ( & goal, depth, initial_solution) ;
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
-
250
- self . context . search_graph [ dfn] . links = subgoal_minimums;
251
- self . context . search_graph [ dfn] . stack_depth = None ;
252
- self . context . stack . pop ( depth) ;
253
- minimums. update_from ( subgoal_minimums) ;
254
-
255
- // Read final result from table.
256
- let result = self . context . search_graph [ dfn] . solution . clone ( ) ;
257
-
258
- // If processing this subgoal did not involve anything
259
- // outside of its subtree, then we can promote it to the
260
- // cache now. This is a sort of hack to alleviate the
261
- // worst of the repeated work that we do during tabling.
262
- if subgoal_minimums. positive >= dfn {
263
- if let Some ( cache) = & mut self . context . cache {
264
- self . context . search_graph . move_to_cache ( dfn, cache) ;
265
- debug ! ( "solve_reduced_goal: SCC head encountered, moving to cache" ) ;
266
- } else {
267
- debug ! (
268
- "solve_reduced_goal: SCC head encountered, rolling back as caching disabled"
269
- ) ;
270
- self . context . search_graph . rollback_to ( dfn) ;
271
- }
272
- }
273
-
274
- info ! ( "solve_goal: solution = {:?}" , result) ;
275
- result
276
- }
277
- }
278
254
}
279
255
280
256
impl < ' me , I : Interner > SolveDatabase < I > for Solver < ' me , I > {
@@ -283,7 +259,42 @@ impl<'me, I: Interner> SolveDatabase<I> for Solver<'me, I> {
283
259
goal : UCanonicalGoal < I > ,
284
260
minimums : & mut Minimums ,
285
261
) -> Fallible < Solution < I > > {
286
- self . solve_goal ( goal, minimums)
262
+ let program = self . program ;
263
+ let interner = program. interner ( ) ;
264
+ self . context . solve_goal (
265
+ & goal,
266
+ minimums,
267
+ |goal| {
268
+ let coinductive_goal = goal. is_coinductive ( program) ;
269
+ let initial_solution = if coinductive_goal {
270
+ Ok ( Solution :: Unique ( Canonical {
271
+ value : ConstrainedSubst {
272
+ subst : goal. trivial_substitution ( interner) ,
273
+ constraints : Constraints :: empty ( interner) ,
274
+ } ,
275
+ binders : goal. canonical . binders . clone ( ) ,
276
+ } ) )
277
+ } else {
278
+ Err ( NoSolution )
279
+ } ;
280
+ ( coinductive_goal, initial_solution)
281
+ } ,
282
+ |context, goal, minimums| Solver :: new ( context, program) . solve_iteration ( goal, minimums) ,
283
+ |old_answer, current_answer| {
284
+ // Some of our subgoals depended on us. We need to re-run
285
+ // with the current answer.
286
+ old_answer == current_answer || {
287
+ // Subtle: if our current answer is ambiguous, we can just stop, and
288
+ // in fact we *must* -- otherwise, we sometimes fail to reach a
289
+ // fixed point. See `multiple_ambiguous_cycles` for more.
290
+ match & current_answer {
291
+ Ok ( s) => s. is_ambig ( ) ,
292
+ Err ( _) => false ,
293
+ }
294
+ }
295
+ } ,
296
+ || Err ( NoSolution ) ,
297
+ )
287
298
}
288
299
289
300
fn interner ( & self ) -> & I {
0 commit comments