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 } ;
2223#[ cfg( feature = "nightly" ) ]
23- use rustc_macros:: HashStable_NoContext ;
24+ use rustc_macros:: { HashStable_NoContext , TyDecodable , TyEncodable } ;
2425use tracing:: debug;
2526
2627use crate :: data_structures:: HashMap ;
@@ -111,21 +112,29 @@ pub trait Delegate {
111112/// In the initial iteration of a cycle, we do not yet have a provisional
112113/// result. In the case we return an initial provisional result depending
113114/// on the kind of cycle.
114- #[ derive( Copy , Clone , Debug , PartialEq , Eq , Hash ) ]
115- #[ cfg_attr( feature = "nightly" , derive( HashStable_NoContext ) ) ]
115+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Hash ) ]
116+ #[ cfg_attr( feature = "nightly" , derive( TyDecodable , TyEncodable , HashStable_NoContext ) ) ]
116117pub enum PathKind {
117- Coinductive ,
118+ /// A path consisting of only inductive/unproductive steps.
118119 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 ,
119126}
127+
120128impl PathKind {
121129 /// Returns the path kind when merging `self` with `rest`.
122130 ///
123131 /// Given an inductive path `self` and a coinductive path `rest`,
124132 /// the path `self -> rest` would be coinductive.
125133 fn extend ( self , rest : PathKind ) -> PathKind {
126- match self {
127- PathKind :: Coinductive => PathKind :: Coinductive ,
128- 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 ,
129138 }
130139 }
131140}
@@ -159,9 +168,6 @@ impl UsageKind {
159168 }
160169 }
161170 }
162- fn and_merge ( & mut self , other : impl Into < Self > ) {
163- * self = self . merge ( other) ;
164- }
165171}
166172
167173/// For each goal we track whether the paths from this goal
@@ -297,14 +303,68 @@ impl CycleHeads {
297303
298304 let path_from_entry = match step_kind {
299305 PathKind :: Coinductive => AllPathsToHeadCoinductive :: Yes ,
300- PathKind :: Inductive => path_from_entry,
306+ PathKind :: Unknown | PathKind :: Inductive => path_from_entry,
301307 } ;
302308
303309 self . insert ( head, path_from_entry) ;
304310 }
305311 }
306312}
307313
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+
308368/// The nested goals of each stack entry and the path from the
309369/// stack entry to that nested goal.
310370///
@@ -322,15 +382,18 @@ impl CycleHeads {
322382/// results from a the cycle BAB depending on the cycle root.
323383#[ derive_where( Debug , Default , Clone ; X : Cx ) ]
324384struct NestedGoals < X : Cx > {
325- nested_goals : HashMap < X :: Input , UsageKind > ,
385+ nested_goals : HashMap < X :: Input , PathsToNested > ,
326386}
327387impl < X : Cx > NestedGoals < X > {
328388 fn is_empty ( & self ) -> bool {
329389 self . nested_goals . is_empty ( )
330390 }
331391
332- fn insert ( & mut self , input : X :: Input , path_from_entry : UsageKind ) {
333- 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+ }
334397 }
335398
336399 /// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
@@ -341,18 +404,15 @@ impl<X: Cx> NestedGoals<X> {
341404 /// the same as for the child.
342405 fn extend_from_child ( & mut self , step_kind : PathKind , nested_goals : & NestedGoals < X > ) {
343406 #[ allow( rustc:: potential_query_instability) ]
344- for ( input, path_from_entry) in nested_goals. iter ( ) {
345- let path_from_entry = match step_kind {
346- PathKind :: Coinductive => UsageKind :: Single ( PathKind :: Coinductive ) ,
347- PathKind :: Inductive => path_from_entry,
348- } ;
349- 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) ;
350410 }
351411 }
352412
353413 #[ cfg_attr( feature = "nightly" , rustc_lint_query_instability) ]
354414 #[ allow( rustc:: potential_query_instability) ]
355- fn iter ( & self ) -> impl Iterator < Item = ( X :: Input , UsageKind ) > {
415+ fn iter ( & self ) -> impl Iterator < Item = ( X :: Input , PathsToNested ) > + ' _ {
356416 self . nested_goals . iter ( ) . map ( |( i, p) | ( * i, * p) )
357417 }
358418
@@ -490,7 +550,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
490550 // goals as this change may cause them to now depend on additional
491551 // goals, resulting in new cycles. See the dev-guide for examples.
492552 if parent_depends_on_cycle {
493- parent. nested_goals . insert ( parent. input , UsageKind :: Single ( PathKind :: Inductive ) )
553+ parent. nested_goals . insert ( parent. input , PathsToNested :: EMPTY ) ;
494554 }
495555 }
496556 }
@@ -666,7 +726,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
666726 //
667727 // We must therefore not use the global cache entry for `B` in that case.
668728 // See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
669- last. nested_goals . insert ( last. input , UsageKind :: Single ( PathKind :: Inductive ) ) ;
729+ last. nested_goals . insert ( last. input , PathsToNested :: EMPTY ) ;
670730 }
671731
672732 debug ! ( "encountered stack overflow" ) ;
@@ -749,16 +809,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
749809
750810 // We now care about the path from the next highest cycle head to the
751811 // provisional cache entry.
752- match path_from_head {
753- PathKind :: Coinductive => { }
754- PathKind :: Inductive => {
755- * path_from_head = Self :: cycle_path_kind (
756- & self . stack ,
757- stack_entry. step_kind_from_parent ,
758- head,
759- )
760- }
761- }
812+ * path_from_head = path_from_head. extend ( Self :: cycle_path_kind (
813+ & self . stack ,
814+ stack_entry. step_kind_from_parent ,
815+ head,
816+ ) ) ;
762817 // Mutate the result of the provisional cache entry in case we did
763818 // not reach a fixpoint.
764819 * result = mutate_result ( input, * result) ;
@@ -858,7 +913,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
858913 for & ProvisionalCacheEntry {
859914 encountered_overflow,
860915 ref heads,
861- path_from_head,
916+ path_from_head : head_to_provisional ,
862917 result : _,
863918 } in entries. iter ( )
864919 {
@@ -870,24 +925,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
870925
871926 // A provisional cache entry only applies if the path from its highest head
872927 // matches the path when encountering the goal.
928+ //
929+ // We check if any of the paths taken while computing the global goal
930+ // would end up with an applicable provisional cache entry.
873931 let head = heads. highest_cycle_head ( ) ;
874- let full_path = match Self :: cycle_path_kind ( stack, step_kind_from_parent, head) {
875- PathKind :: Coinductive => UsageKind :: Single ( PathKind :: Coinductive ) ,
876- PathKind :: Inductive => path_from_global_entry,
877- } ;
878-
879- match ( full_path, path_from_head) {
880- ( UsageKind :: Mixed , _)
881- | ( UsageKind :: Single ( PathKind :: Coinductive ) , PathKind :: Coinductive )
882- | ( UsageKind :: Single ( PathKind :: Inductive ) , PathKind :: Inductive ) => {
883- debug ! (
884- ?full_path,
885- ?path_from_head,
886- "cache entry not applicable due to matching paths"
887- ) ;
888- return false ;
889- }
890- _ => debug ! ( ?full_path, ?path_from_head, "paths don't match" ) ,
932+ let head_to_curr = Self :: cycle_path_kind ( stack, step_kind_from_parent, head) ;
933+ let full_paths = path_from_global_entry. extend_with ( head_to_curr) ;
934+ if full_paths. contains ( head_to_provisional. into ( ) ) {
935+ debug ! (
936+ ?full_paths,
937+ ?head_to_provisional,
938+ "cache entry not applicable due to matching paths"
939+ ) ;
940+ return false ;
891941 }
892942 }
893943 }
@@ -986,8 +1036,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
9861036 let last = & mut self . stack [ last_index] ;
9871037 last. reached_depth = last. reached_depth . max ( next_index) ;
9881038
989- last. nested_goals . insert ( input, UsageKind :: Single ( step_kind_from_parent) ) ;
990- last. nested_goals . insert ( last. input , UsageKind :: Single ( PathKind :: Inductive ) ) ;
1039+ last. nested_goals . insert ( input, step_kind_from_parent. into ( ) ) ;
1040+ last. nested_goals . insert ( last. input , PathsToNested :: EMPTY ) ;
9911041 if last_index != head {
9921042 last. heads . insert ( head, step_kind_from_parent) ;
9931043 }
0 commit comments