@@ -13,7 +13,7 @@ use rustc_middle::traits::solve::CacheData;
1313use rustc_middle:: traits:: solve:: { CanonicalInput , Certainty , EvaluationCache , QueryResult } ;
1414use rustc_middle:: ty:: TyCtxt ;
1515use rustc_session:: Limit ;
16- use std:: { collections:: hash_map:: Entry , mem } ;
16+ use std:: collections:: hash_map:: Entry ;
1717
1818rustc_index:: newtype_index! {
1919 pub struct StackDepth { }
@@ -216,8 +216,8 @@ impl<'tcx> SearchGraph<'tcx> {
216216 cycle_participants : Default :: default ( ) ,
217217 } ;
218218 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 } ) ;
221221 v. insert ( entry_index) ;
222222 }
223223 // We have a nested goal which relies on a goal `root` deeper in the stack.
@@ -243,23 +243,31 @@ impl<'tcx> SearchGraph<'tcx> {
243243 root. cycle_participants . insert ( e. input ) ;
244244 }
245245
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
260251 } 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+ } ;
263271 }
264272 }
265273
@@ -288,15 +296,18 @@ impl<'tcx> SearchGraph<'tcx> {
288296 let provisional_entry_index =
289297 * cache. lookup_table . get ( & stack_entry. input ) . unwrap ( ) ;
290298 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...
295305 //
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
297307 // depend on a goal which is lower in the stack so it doesn't
298308 // actually depend on the current goal. This should be fairly
299309 // rare and is hopefully not relevant for performance.
310+ provisional_entry. response = Some ( response) ;
300311 #[ allow( rustc:: potential_query_instability) ]
301312 cache. lookup_table . retain ( |_key, index| * index <= provisional_entry_index) ;
302313 cache. entries . truncate ( provisional_entry_index. index ( ) + 1 ) ;
@@ -315,8 +326,8 @@ impl<'tcx> SearchGraph<'tcx> {
315326 } ) ;
316327
317328 // 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.
320331 //
321332 // It is not possible for any nested goal to depend on something deeper on the
322333 // stack, as this would have also updated the depth of the current goal.
@@ -348,6 +359,8 @@ impl<'tcx> SearchGraph<'tcx> {
348359 dep_node,
349360 result,
350361 )
362+ } else {
363+ provisional_entry. response = Some ( result) ;
351364 }
352365
353366 result
0 commit comments