1313/// behavior as long as the resulting behavior is still correct.
1414use std:: cmp:: Ordering ;
1515use std:: collections:: BTreeMap ;
16+ use std:: collections:: hash_map:: Entry ;
1617use std:: fmt:: Debug ;
1718use std:: hash:: Hash ;
1819use std:: marker:: PhantomData ;
1920
2021use derive_where:: derive_where;
2122use rustc_index:: { Idx , IndexVec } ;
23+ #[ cfg( feature = "nightly" ) ]
24+ use rustc_macros:: { HashStable_NoContext , TyDecodable , TyEncodable } ;
2225use tracing:: debug;
2326
2427use crate :: data_structures:: HashMap ;
@@ -109,20 +112,29 @@ pub trait Delegate {
109112/// In the initial iteration of a cycle, we do not yet have a provisional
110113/// result. In the case we return an initial provisional result depending
111114/// on the kind of cycle.
112- #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
115+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
116+ #[ cfg_attr( feature = "nightly" , derive( TyDecodable , TyEncodable , HashStable_NoContext ) ) ]
113117pub enum PathKind {
114- Coinductive ,
118+ /// A path consisting of only inductive/unproductive steps.
115119 Inductive ,
120+ /// A path which is not be coinductive right now but we may want
121+ /// to change of them to be so in the future. We return an ambiguous
122+ /// result in this case to prevent people from relying on this.
123+ Unknown ,
124+ /// A path with at least one coinductive step. Such cycles hold.
125+ Coinductive ,
116126}
127+
117128impl PathKind {
118129 /// Returns the path kind when merging `self` with `rest`.
119130 ///
120131 /// Given an inductive path `self` and a coinductive path `rest`,
121132 /// the path `self -> rest` would be coinductive.
122133 fn extend ( self , rest : PathKind ) -> PathKind {
123- match self {
124- PathKind :: Coinductive => PathKind :: Coinductive ,
125- PathKind :: Inductive => rest,
134+ match ( self , rest) {
135+ ( PathKind :: Coinductive , _) | ( _, PathKind :: Coinductive ) => PathKind :: Coinductive ,
136+ ( PathKind :: Unknown , _) | ( _, PathKind :: Unknown ) => PathKind :: Unknown ,
137+ ( PathKind :: Inductive , PathKind :: Inductive ) => PathKind :: Inductive ,
126138 }
127139 }
128140}
@@ -156,9 +168,6 @@ impl UsageKind {
156168 }
157169 }
158170 }
159- fn and_merge ( & mut self , other : impl Into < Self > ) {
160- * self = self . merge ( other) ;
161- }
162171}
163172
164173/// For each goal we track whether the paths from this goal
@@ -294,14 +303,68 @@ impl CycleHeads {
294303
295304 let path_from_entry = match step_kind {
296305 PathKind :: Coinductive => AllPathsToHeadCoinductive :: Yes ,
297- PathKind :: Inductive => path_from_entry,
306+ PathKind :: Unknown | PathKind :: Inductive => path_from_entry,
298307 } ;
299308
300309 self . insert ( head, path_from_entry) ;
301310 }
302311 }
303312}
304313
314+ bitflags:: bitflags! {
315+ /// Tracks how nested goals have been accessed. This is necessary to disable
316+ /// global cache entries if computing them would otherwise result in a cycle or
317+ /// access a provisional cache entry.
318+ #[ derive( Debug , Clone , Copy ) ]
319+ pub struct PathsToNested : u8 {
320+ /// The initial value when adding a goal to its own nested goals.
321+ const EMPTY = 1 << 0 ;
322+ const INDUCTIVE = 1 << 1 ;
323+ const UNKNOWN = 1 << 2 ;
324+ const COINDUCTIVE = 1 << 3 ;
325+ }
326+ }
327+ impl From < PathKind > for PathsToNested {
328+ fn from ( path : PathKind ) -> PathsToNested {
329+ match path {
330+ PathKind :: Inductive => PathsToNested :: INDUCTIVE ,
331+ PathKind :: Unknown => PathsToNested :: UNKNOWN ,
332+ PathKind :: Coinductive => PathsToNested :: COINDUCTIVE ,
333+ }
334+ }
335+ }
336+ impl PathsToNested {
337+ #[ must_use]
338+ fn extend_with ( mut self , path : PathKind ) -> Self {
339+ match path {
340+ PathKind :: Inductive => {
341+ if self . intersects ( PathsToNested :: EMPTY ) {
342+ self . remove ( PathsToNested :: EMPTY ) ;
343+ self . insert ( PathsToNested :: INDUCTIVE ) ;
344+ }
345+ }
346+ PathKind :: Unknown => {
347+ if self . intersects ( PathsToNested :: EMPTY | PathsToNested :: INDUCTIVE ) {
348+ self . remove ( PathsToNested :: EMPTY | PathsToNested :: INDUCTIVE ) ;
349+ self . insert ( PathsToNested :: UNKNOWN ) ;
350+ }
351+ }
352+ PathKind :: Coinductive => {
353+ if self . intersects (
354+ PathsToNested :: EMPTY | PathsToNested :: INDUCTIVE | PathsToNested :: UNKNOWN ,
355+ ) {
356+ self . remove (
357+ PathsToNested :: EMPTY | PathsToNested :: INDUCTIVE | PathsToNested :: UNKNOWN ,
358+ ) ;
359+ self . insert ( PathsToNested :: COINDUCTIVE ) ;
360+ }
361+ }
362+ }
363+
364+ self
365+ }
366+ }
367+
305368/// The nested goals of each stack entry and the path from the
306369/// stack entry to that nested goal.
307370///
@@ -319,15 +382,18 @@ impl CycleHeads {
319382/// results from a the cycle BAB depending on the cycle root.
320383#[ derive_where( Debug , Default , Clone ; X : Cx ) ]
321384struct NestedGoals < X : Cx > {
322- nested_goals : HashMap < X :: Input , UsageKind > ,
385+ nested_goals : HashMap < X :: Input , PathsToNested > ,
323386}
324387impl < X : Cx > NestedGoals < X > {
325388 fn is_empty ( & self ) -> bool {
326389 self . nested_goals . is_empty ( )
327390 }
328391
329- fn insert ( & mut self , input : X :: Input , path_from_entry : UsageKind ) {
330- self . nested_goals . entry ( input) . or_insert ( path_from_entry) . and_merge ( path_from_entry) ;
392+ fn insert ( & mut self , input : X :: Input , paths_to_nested : PathsToNested ) {
393+ match self . nested_goals . entry ( input) {
394+ Entry :: Occupied ( mut entry) => * entry. get_mut ( ) |= paths_to_nested,
395+ Entry :: Vacant ( entry) => drop ( entry. insert ( paths_to_nested) ) ,
396+ }
331397 }
332398
333399 /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
@@ -338,18 +404,15 @@ impl<X: Cx> NestedGoals<X> {
338404 /// the same as for the child.
339405 fn extend_from_child ( & mut self , step_kind : PathKind , nested_goals : & NestedGoals < X > ) {
340406 #[ allow( rustc:: potential_query_instability) ]
341- for ( input, path_from_entry) in nested_goals. iter ( ) {
342- let path_from_entry = match step_kind {
343- PathKind :: Coinductive => UsageKind :: Single ( PathKind :: Coinductive ) ,
344- PathKind :: Inductive => path_from_entry,
345- } ;
346- self . insert ( input, path_from_entry) ;
407+ for ( input, paths_to_nested) in nested_goals. iter ( ) {
408+ let paths_to_nested = paths_to_nested. extend_with ( step_kind) ;
409+ self . insert ( input, paths_to_nested) ;
347410 }
348411 }
349412
350413 #[ cfg_attr( feature = "nightly" , rustc_lint_query_instability) ]
351414 #[ allow( rustc:: potential_query_instability) ]
352- fn iter ( & self ) -> impl Iterator < Item = ( X :: Input , UsageKind ) > + ' _ {
415+ fn iter ( & self ) -> impl Iterator < Item = ( X :: Input , PathsToNested ) > + ' _ {
353416 self . nested_goals . iter ( ) . map ( |( i, p) | ( * i, * p) )
354417 }
355418
@@ -487,7 +550,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
487550 // goals as this change may cause them to now depend on additional
488551 // goals, resulting in new cycles. See the dev-guide for examples.
489552 if parent_depends_on_cycle {
490- parent. nested_goals . insert ( parent. input , UsageKind :: Single ( PathKind :: Inductive ) )
553+ parent. nested_goals . insert ( parent. input , PathsToNested :: EMPTY ) ;
491554 }
492555 }
493556 }
@@ -663,7 +726,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
663726 //
664727 // We must therefore not use the global cache entry for `B` in that case.
665728 // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
666- last. nested_goals . insert ( last. input , UsageKind :: Single ( PathKind :: Inductive ) ) ;
729+ last. nested_goals . insert ( last. input , PathsToNested :: EMPTY ) ;
667730 }
668731
669732 debug ! ( "encountered stack overflow" ) ;
@@ -745,16 +808,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
745808
746809 // We now care about the path from the next highest cycle head to the
747810 // provisional cache entry.
748- match path_from_head {
749- PathKind :: Coinductive => { }
750- PathKind :: Inductive => {
751- * path_from_head = Self :: cycle_path_kind (
752- & self . stack ,
753- stack_entry. step_kind_from_parent ,
754- head,
755- )
756- }
757- }
811+ * path_from_head = path_from_head. extend ( Self :: cycle_path_kind (
812+ & self . stack ,
813+ stack_entry. step_kind_from_parent ,
814+ head,
815+ ) ) ;
758816 // Mutate the result of the provisional cache entry in case we did
759817 // not reach a fixpoint.
760818 * result = mutate_result ( input, * result) ;
@@ -854,7 +912,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
854912 for & ProvisionalCacheEntry {
855913 encountered_overflow,
856914 ref heads,
857- path_from_head,
915+ path_from_head : head_to_provisional ,
858916 result : _,
859917 } in entries. iter ( )
860918 {
@@ -866,24 +924,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
866924
867925 // A provisional cache entry only applies if the path from its highest head
868926 // matches the path when encountering the goal.
927+ //
928+ // We check if any of the paths taken while computing the global goal
929+ // would end up with an applicable provisional cache entry.
869930 let head = heads. highest_cycle_head ( ) ;
870- let full_path = match Self :: cycle_path_kind ( stack, step_kind_from_parent, head) {
871- PathKind :: Coinductive => UsageKind :: Single ( PathKind :: Coinductive ) ,
872- PathKind :: Inductive => path_from_global_entry,
873- } ;
874-
875- match ( full_path, path_from_head) {
876- ( UsageKind :: Mixed , _)
877- | ( UsageKind :: Single ( PathKind :: Coinductive ) , PathKind :: Coinductive )
878- | ( UsageKind :: Single ( PathKind :: Inductive ) , PathKind :: Inductive ) => {
879- debug ! (
880- ?full_path,
881- ?path_from_head,
882- "cache entry not applicable due to matching paths"
883- ) ;
884- return false ;
885- }
886- _ => debug ! ( ?full_path, ?path_from_head, "paths don't match" ) ,
931+ let head_to_curr = Self :: cycle_path_kind ( stack, step_kind_from_parent, head) ;
932+ let full_paths = path_from_global_entry. extend_with ( head_to_curr) ;
933+ if full_paths. contains ( head_to_provisional. into ( ) ) {
934+ debug ! (
935+ ?full_paths,
936+ ?head_to_provisional,
937+ "cache entry not applicable due to matching paths"
938+ ) ;
939+ return false ;
887940 }
888941 }
889942 }
@@ -982,8 +1035,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
9821035 let last = & mut self . stack [ last_index] ;
9831036 last. reached_depth = last. reached_depth . max ( next_index) ;
9841037
985- last. nested_goals . insert ( input, UsageKind :: Single ( step_kind_from_parent) ) ;
986- last. nested_goals . insert ( last. input , UsageKind :: Single ( PathKind :: Inductive ) ) ;
1038+ last. nested_goals . insert ( input, step_kind_from_parent. into ( ) ) ;
1039+ last. nested_goals . insert ( last. input , PathsToNested :: EMPTY ) ;
9871040 if last_index != head {
9881041 last. heads . insert ( head, step_kind_from_parent) ;
9891042 }
0 commit comments