@@ -12,6 +12,7 @@ use rustc_middle::ty;
1212use rustc_middle:: ty:: TyCtxt ;
1313use rustc_session:: Limit ;
1414use std:: collections:: hash_map:: Entry ;
15+ use std:: mem;
1516
1617rustc_index:: newtype_index! {
1718 #[ orderable]
@@ -25,23 +26,17 @@ struct StackEntry<'tcx> {
2526 /// The maximum depth reached by this stack entry, only up-to date
2627 /// for the top of the stack and lazily updated for the rest.
2728 reached_depth : StackDepth ,
28- /// In case of a cycle, the depth of the root.
29- cycle_root_depth : StackDepth ,
29+ /// Whether this entry is a cycle participant which is not a root.
30+ ///
31+ /// If so, it must not be moved to the global cache. See
32+ /// [SearchGraph::cycle_participants] for more details.
33+ non_root_cycle_participant : bool ,
3034
3135 encountered_overflow : bool ,
3236 has_been_used : bool ,
3337 /// Starts out as `None` and gets set when rerunning this
3438 /// goal in case we encounter a cycle.
3539 provisional_result : Option < QueryResult < ' tcx > > ,
36-
37- /// We put only the root goal of a coinductive cycle into the global cache.
38- ///
39- /// If we were to use that result when later trying to prove another cycle
40- /// participant, we can end up with unstable query results.
41- ///
42- /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
43- /// an example of where this is needed.
44- cycle_participants : FxHashSet < CanonicalInput < ' tcx > > ,
4540}
4641
4742pub ( super ) struct SearchGraph < ' tcx > {
@@ -52,6 +47,14 @@ pub(super) struct SearchGraph<'tcx> {
5247 /// An element is *deeper* in the stack if its index is *lower*.
5348 stack : IndexVec < StackDepth , StackEntry < ' tcx > > ,
5449 stack_entries : FxHashMap < CanonicalInput < ' tcx > , StackDepth > ,
50+ /// We put only the root goal of a coinductive cycle into the global cache.
51+ ///
52+ /// If we were to use that result when later trying to prove another cycle
53+ /// participant, we can end up with unstable query results.
54+ ///
55+ /// See tests/ui/next-solver/coinduction/incompleteness-unstable-result.rs for
56+ /// an example of where this is needed.
57+ cycle_participants : FxHashSet < CanonicalInput < ' tcx > > ,
5558}
5659
5760impl < ' tcx > SearchGraph < ' tcx > {
@@ -61,6 +64,7 @@ impl<'tcx> SearchGraph<'tcx> {
6164 local_overflow_limit : tcx. recursion_limit ( ) . 0 . checked_ilog2 ( ) . unwrap_or ( 0 ) as usize ,
6265 stack : Default :: default ( ) ,
6366 stack_entries : Default :: default ( ) ,
67+ cycle_participants : Default :: default ( ) ,
6468 }
6569 }
6670
@@ -109,7 +113,13 @@ impl<'tcx> SearchGraph<'tcx> {
109113 }
110114
111115 pub ( super ) fn is_empty ( & self ) -> bool {
112- self . stack . is_empty ( )
116+ if self . stack . is_empty ( ) {
117+ debug_assert ! ( self . stack_entries. is_empty( ) ) ;
118+ debug_assert ! ( self . cycle_participants. is_empty( ) ) ;
119+ true
120+ } else {
121+ false
122+ }
113123 }
114124
115125 pub ( super ) fn current_goal_is_normalizes_to ( & self ) -> bool {
@@ -209,11 +219,10 @@ impl<'tcx> SearchGraph<'tcx> {
209219 input,
210220 available_depth,
211221 reached_depth : depth,
212- cycle_root_depth : depth ,
222+ non_root_cycle_participant : false ,
213223 encountered_overflow : false ,
214224 has_been_used : false ,
215225 provisional_result : None ,
216- cycle_participants : Default :: default ( ) ,
217226 } ;
218227 assert_eq ! ( self . stack. push( entry) , depth) ;
219228 v. insert ( depth) ;
@@ -232,26 +241,11 @@ impl<'tcx> SearchGraph<'tcx> {
232241
233242 let stack_depth = * entry. get ( ) ;
234243 debug ! ( "encountered cycle with depth {stack_depth:?}" ) ;
235- // We start by updating the root depth of all cycle participants, and
236- // add all cycle participants to the root.
237- let root_depth = self . stack [ stack_depth] . cycle_root_depth ;
238- let ( prev, participants) = self . stack . raw . split_at_mut ( stack_depth. as_usize ( ) + 1 ) ;
239- let root = & mut prev[ root_depth. as_usize ( ) ] ;
244+ // We start by tagging all non-root cycle participants.
245+ let participants = self . stack . raw . iter_mut ( ) . skip ( stack_depth. as_usize ( ) + 1 ) ;
240246 for entry in participants {
241- debug_assert ! ( entry. cycle_root_depth >= root_depth) ;
242- entry. cycle_root_depth = root_depth;
243- root. cycle_participants . insert ( entry. input ) ;
244- // FIXME(@lcnr): I believe that this line is needed as we could
245- // otherwise access a cache entry for the root of a cycle while
246- // computing the result for a cycle participant. This can result
247- // in unstable results due to incompleteness.
248- //
249- // However, a test for this would be an even more complex version of
250- // tests/ui/traits/next-solver/coinduction/incompleteness-unstable-result.rs.
251- // I did not bother to write such a test and we have no regression test
252- // for this. It would be good to have such a test :)
253- #[ allow( rustc:: potential_query_instability) ]
254- root. cycle_participants . extend ( entry. cycle_participants . drain ( ) ) ;
247+ entry. non_root_cycle_participant = true ;
248+ self . cycle_participants . insert ( entry. input ) ;
255249 }
256250
257251 // If we're in a cycle, we have to retry proving the cycle head
@@ -325,23 +319,24 @@ impl<'tcx> SearchGraph<'tcx> {
325319 //
326320 // It is not possible for any nested goal to depend on something deeper on the
327321 // stack, as this would have also updated the depth of the current goal.
328- if final_entry. cycle_root_depth == self . stack . next_index ( ) {
322+ if ! final_entry. non_root_cycle_participant {
329323 // When encountering a cycle, both inductive and coinductive, we only
330324 // move the root into the global cache. We also store all other cycle
331325 // participants involved.
332326 //
333- // We disable the global cache entry of the root goal if a cycle
327+ // We must not use the global cache entry of a root goal if a cycle
334328 // participant is on the stack. This is necessary to prevent unstable
335- // results. See the comment of `StackEntry ::cycle_participants` for
329+ // results. See the comment of `SearchGraph ::cycle_participants` for
336330 // more details.
337331 let reached_depth = final_entry. reached_depth . as_usize ( ) - self . stack . len ( ) ;
332+ let cycle_participants = mem:: take ( & mut self . cycle_participants ) ;
338333 self . global_cache ( tcx) . insert (
339334 tcx,
340335 input,
341336 proof_tree,
342337 reached_depth,
343338 final_entry. encountered_overflow ,
344- final_entry . cycle_participants ,
339+ cycle_participants,
345340 dep_node,
346341 result,
347342 )
0 commit comments