@@ -13,7 +13,7 @@ use rustc_middle::traits::solve::CacheData;
13
13
use rustc_middle:: traits:: solve:: { CanonicalInput , Certainty , EvaluationCache , QueryResult } ;
14
14
use rustc_middle:: ty:: TyCtxt ;
15
15
use rustc_session:: Limit ;
16
- use std:: { collections:: hash_map:: Entry , mem } ;
16
+ use std:: collections:: hash_map:: Entry ;
17
17
18
18
rustc_index:: newtype_index! {
19
19
pub struct StackDepth { }
@@ -216,8 +216,8 @@ impl<'tcx> SearchGraph<'tcx> {
216
216
cycle_participants : Default :: default ( ) ,
217
217
} ;
218
218
assert_eq ! ( self . stack. push( entry) , depth) ;
219
- let response = Self :: response_no_constraints ( tcx , input , Certainty :: Yes ) ;
220
- let entry_index = cache. entries . push ( ProvisionalEntry { response, depth, input } ) ;
219
+ let entry_index =
220
+ cache. entries . push ( ProvisionalEntry { response : None , depth, input } ) ;
221
221
v. insert ( entry_index) ;
222
222
}
223
223
// We have a nested goal which relies on a goal `root` deeper in the stack.
@@ -243,23 +243,31 @@ impl<'tcx> SearchGraph<'tcx> {
243
243
root. cycle_participants . insert ( e. input ) ;
244
244
}
245
245
246
- // NOTE: The goals on the stack aren't the only goals involved in this cycle.
247
- // We can also depend on goals which aren't part of the stack but coinductively
248
- // depend on the stack themselves. We already checked whether all the goals
249
- // between these goals and their root on the stack. This means that as long as
250
- // each goal in a cycle is checked for coinductivity by itself, simply checking
251
- // the stack is enough.
252
- if self . stack . raw [ stack_depth. index ( ) ..]
253
- . iter ( )
254
- . all ( |g| g. input . value . goal . predicate . is_coinductive ( tcx) )
255
- {
256
- // If we're in a coinductive cycle, we have to retry proving the current goal
257
- // until we reach a fixpoint.
258
- self . stack [ stack_depth] . has_been_used = true ;
259
- return cache. provisional_result ( entry_index) ;
246
+ // If we're in a cycle, we have to retry proving the current goal
247
+ // until we reach a fixpoint.
248
+ self . stack [ stack_depth] . has_been_used = true ;
249
+ return if let Some ( result) = cache. provisional_result ( entry_index) {
250
+ result
260
251
} else {
261
- return Self :: response_no_constraints ( tcx, input, Certainty :: OVERFLOW ) ;
262
- }
252
+ // If we don't have a provisional result yet, the goal has to
253
+ // still be on the stack.
254
+ let mut goal_on_stack = false ;
255
+ let mut is_coinductive = true ;
256
+ for entry in self . stack . raw [ stack_depth. index ( ) ..]
257
+ . iter ( )
258
+ . skip_while ( |entry| entry. input != input)
259
+ {
260
+ goal_on_stack = true ;
261
+ is_coinductive &= entry. input . value . goal . predicate . is_coinductive ( tcx) ;
262
+ }
263
+ debug_assert ! ( goal_on_stack) ;
264
+
265
+ if is_coinductive {
266
+ Self :: response_no_constraints ( tcx, input, Certainty :: Yes )
267
+ } else {
268
+ Self :: response_no_constraints ( tcx, input, Certainty :: OVERFLOW )
269
+ }
270
+ } ;
263
271
}
264
272
}
265
273
@@ -288,15 +296,18 @@ impl<'tcx> SearchGraph<'tcx> {
288
296
let provisional_entry_index =
289
297
* cache. lookup_table . get ( & stack_entry. input ) . unwrap ( ) ;
290
298
let provisional_entry = & mut cache. entries [ provisional_entry_index] ;
291
- let prev_response = mem:: replace ( & mut provisional_entry. response , response) ;
292
- if stack_entry. has_been_used && prev_response != response {
293
- // If so, remove all entries whose result depends on this goal
294
- // from the provisional cache...
299
+ if stack_entry. has_been_used
300
+ && provisional_entry. response . map_or ( true , |r| r != response)
301
+ {
302
+ // If so, update the provisional result for this goal and remove
303
+ // all entries whose result depends on this goal from the provisional
304
+ // cache...
295
305
//
296
- // That's not completely correct, as a nested goal can also
306
+ // That's not completely correct, as a nested goal can also only
297
307
// depend on a goal which is lower in the stack so it doesn't
298
308
// actually depend on the current goal. This should be fairly
299
309
// rare and is hopefully not relevant for performance.
310
+ provisional_entry. response = Some ( response) ;
300
311
#[ allow( rustc:: potential_query_instability) ]
301
312
cache. lookup_table . retain ( |_key, index| * index <= provisional_entry_index) ;
302
313
cache. entries . truncate ( provisional_entry_index. index ( ) + 1 ) ;
@@ -315,8 +326,8 @@ impl<'tcx> SearchGraph<'tcx> {
315
326
} ) ;
316
327
317
328
// We're now done with this goal. In case this goal is involved in a larger cycle
318
- // do not remove it from the provisional cache and do not add it to the global
319
- // cache.
329
+ // do not remove it from the provisional cache and update its provisional result.
330
+ // We only add the root of cycles to the global cache.
320
331
//
321
332
// It is not possible for any nested goal to depend on something deeper on the
322
333
// stack, as this would have also updated the depth of the current goal.
@@ -348,6 +359,8 @@ impl<'tcx> SearchGraph<'tcx> {
348
359
dep_node,
349
360
result,
350
361
)
362
+ } else {
363
+ provisional_entry. response = Some ( result) ;
351
364
}
352
365
353
366
result
0 commit comments