@@ -12,6 +12,7 @@ use rustc_middle::ty;
12
12
use rustc_middle:: ty:: TyCtxt ;
13
13
use rustc_session:: Limit ;
14
14
use std:: collections:: hash_map:: Entry ;
15
+ use std:: mem;
15
16
16
17
rustc_index:: newtype_index! {
17
18
#[ orderable]
@@ -25,23 +26,17 @@ struct StackEntry<'tcx> {
25
26
/// The maximum depth reached by this stack entry, only up-to date
26
27
/// for the top of the stack and lazily updated for the rest.
27
28
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 ,
30
34
31
35
encountered_overflow : bool ,
32
36
has_been_used : bool ,
33
37
/// Starts out as `None` and gets set when rerunning this
34
38
/// goal in case we encounter a cycle.
35
39
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 > > ,
45
40
}
46
41
47
42
pub ( super ) struct SearchGraph < ' tcx > {
@@ -52,6 +47,14 @@ pub(super) struct SearchGraph<'tcx> {
52
47
/// An element is *deeper* in the stack if its index is *lower*.
53
48
stack : IndexVec < StackDepth , StackEntry < ' tcx > > ,
54
49
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 > > ,
55
58
}
56
59
57
60
impl < ' tcx > SearchGraph < ' tcx > {
@@ -61,6 +64,7 @@ impl<'tcx> SearchGraph<'tcx> {
61
64
local_overflow_limit : tcx. recursion_limit ( ) . 0 . checked_ilog2 ( ) . unwrap_or ( 0 ) as usize ,
62
65
stack : Default :: default ( ) ,
63
66
stack_entries : Default :: default ( ) ,
67
+ cycle_participants : Default :: default ( ) ,
64
68
}
65
69
}
66
70
@@ -109,7 +113,13 @@ impl<'tcx> SearchGraph<'tcx> {
109
113
}
110
114
111
115
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
+ }
113
123
}
114
124
115
125
pub ( super ) fn current_goal_is_normalizes_to ( & self ) -> bool {
@@ -209,11 +219,10 @@ impl<'tcx> SearchGraph<'tcx> {
209
219
input,
210
220
available_depth,
211
221
reached_depth : depth,
212
- cycle_root_depth : depth ,
222
+ non_root_cycle_participant : false ,
213
223
encountered_overflow : false ,
214
224
has_been_used : false ,
215
225
provisional_result : None ,
216
- cycle_participants : Default :: default ( ) ,
217
226
} ;
218
227
assert_eq ! ( self . stack. push( entry) , depth) ;
219
228
v. insert ( depth) ;
@@ -232,26 +241,11 @@ impl<'tcx> SearchGraph<'tcx> {
232
241
233
242
let stack_depth = * entry. get ( ) ;
234
243
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 ) ;
240
246
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 ) ;
255
249
}
256
250
257
251
// If we're in a cycle, we have to retry proving the cycle head
@@ -325,23 +319,24 @@ impl<'tcx> SearchGraph<'tcx> {
325
319
//
326
320
// It is not possible for any nested goal to depend on something deeper on the
327
321
// 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 {
329
323
// When encountering a cycle, both inductive and coinductive, we only
330
324
// move the root into the global cache. We also store all other cycle
331
325
// participants involved.
332
326
//
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
334
328
// 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
336
330
// more details.
337
331
let reached_depth = final_entry. reached_depth . as_usize ( ) - self . stack . len ( ) ;
332
+ let cycle_participants = mem:: take ( & mut self . cycle_participants ) ;
338
333
self . global_cache ( tcx) . insert (
339
334
tcx,
340
335
input,
341
336
proof_tree,
342
337
reached_depth,
343
338
final_entry. encountered_overflow ,
344
- final_entry . cycle_participants ,
339
+ cycle_participants,
345
340
dep_node,
346
341
result,
347
342
)
0 commit comments