@@ -154,12 +154,12 @@ struct TraitObligationStack<'prev, 'tcx: 'prev> {
154154 /// selection-context's freshener. Used to check for recursion.
155155 fresh_trait_ref : ty:: PolyTraitRef < ' tcx > ,
156156
157- /// Starts out as false -- if, during evaluation, we encounter a
158- /// cycle, then we will set this flag to true for all participants
159- /// in the cycle (apart from the "head" node) . These participants
160- /// will then forego caching their results. This is not the most
161- /// efficient solution, but it addresses #60010. The problem we
162- /// are trying to prevent:
157+ /// Starts out equal to `depth` -- if, during evaluation, we
158+ /// encounter a cycle, then we will set this flag to the minimum
159+ /// depth of that cycle for all participants in the cycle . These
160+ /// participants will then forego caching their results. This is
161+ /// not the most efficient solution, but it addresses #60010. The
162+ /// problem we are trying to prevent:
163163 ///
164164 /// - If you have `A: AutoTrait` requires `B: AutoTrait` and `C: NonAutoTrait`
165165 /// - `B: AutoTrait` requires `A: AutoTrait` (coinductive cycle, ok)
@@ -182,7 +182,7 @@ struct TraitObligationStack<'prev, 'tcx: 'prev> {
182182 /// evaluate each member of a cycle up to N times, where N is the
183183 /// length of the cycle. This means the performance impact is
184184 /// bounded and we shouldn't have any terrible worst-cases.
185- in_cycle : Cell < bool > ,
185+ reached_depth : Cell < usize > ,
186186
187187 previous : TraitObligationStackList < ' prev , ' tcx > ,
188188
@@ -877,14 +877,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
877877 let ( result, dep_node) = self . in_task ( |this| this. evaluate_stack ( & stack) ) ;
878878 let result = result?;
879879
880- if !stack. in_cycle . get ( ) {
880+ let reached_depth = stack. reached_depth . get ( ) ;
881+ if reached_depth >= stack. depth {
881882 debug ! ( "CACHE MISS: EVAL({:?})={:?}" , fresh_trait_ref, result) ;
882883 self . insert_evaluation_cache ( obligation. param_env , fresh_trait_ref, dep_node, result) ;
883884 } else {
884885 debug ! (
885886 "evaluate_trait_predicate_recursively: skipping cache because {:?} \
886- is a cycle participant",
887+ is a cycle participant (at depth {}, reached depth {}) ",
887888 fresh_trait_ref,
889+ stack. depth,
890+ reached_depth,
888891 ) ;
889892 }
890893
@@ -986,10 +989,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
986989 // affect the inferencer state and (b) that if we see two
987990 // fresh regions with the same index, they refer to the same
988991 // unbound type variable.
989- if let Some ( rec_index) = stack. iter ( )
990- . skip ( 1 ) // skip top-most frame
991- . position ( |prev| stack. obligation . param_env == prev. obligation . param_env &&
992- stack. fresh_trait_ref == prev. fresh_trait_ref )
992+ if let Some ( cycle_depth) = stack. iter ( )
993+ . skip ( 1 ) // skip top-most frame
994+ . find ( |prev| stack. obligation . param_env == prev. obligation . param_env &&
995+ stack. fresh_trait_ref == prev. fresh_trait_ref )
996+ . map ( |stack| stack. depth )
993997 {
994998 debug ! ( "evaluate_stack({:?}) --> recursive" , stack. fresh_trait_ref) ;
995999
@@ -999,17 +1003,17 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
9991003 // from the cycle head. We mark them as participating in a
10001004 // cycle. This suppresses caching for those nodes. See
10011005 // `in_cycle` field for more details.
1002- for item in stack. iter ( ) . take ( rec_index + 1 ) {
1006+ for item in stack. iter ( ) . take_while ( |s| s . depth > cycle_depth ) {
10031007 debug ! ( "evaluate_stack: marking {:?} as cycle participant" , item. fresh_trait_ref) ;
1004- item. in_cycle . set ( true ) ;
1008+ item. update_reached_depth ( cycle_depth ) ;
10051009 }
10061010
10071011 // Subtle: when checking for a coinductive cycle, we do
10081012 // not compare using the "freshened trait refs" (which
10091013 // have erased regions) but rather the fully explicit
10101014 // trait refs. This is important because it's only a cycle
10111015 // if the regions match exactly.
1012- let cycle = stack. iter ( ) . skip ( 1 ) . take ( rec_index + 1 ) ;
1016+ let cycle = stack. iter ( ) . skip ( 1 ) . take_while ( |s| s . depth >= cycle_depth ) ;
10131017 let cycle = cycle. map ( |stack| ty:: Predicate :: Trait ( stack. obligation . predicate ) ) ;
10141018 if self . coinductive_match ( cycle) {
10151019 debug ! (
@@ -1228,6 +1232,11 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
12281232 }
12291233
12301234 // If no match, compute result and insert into cache.
1235+ //
1236+ // FIXME(nikomatsakis) -- this cache is not taking into
1237+ // account cycles that may have occurred in forming the
1238+ // candidate. I don't know of any specific problems that
1239+ // result but it seems awfully suspicious.
12311240 let ( candidate, dep_node) =
12321241 self . in_task ( |this| this. candidate_from_obligation_no_cache ( stack) ) ;
12331242
@@ -3743,12 +3752,13 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
37433752 . to_poly_trait_ref ( )
37443753 . fold_with ( & mut self . freshener ) ;
37453754
3755+ let depth = previous_stack. depth ( ) + 1 ;
37463756 TraitObligationStack {
37473757 obligation,
37483758 fresh_trait_ref,
3749- in_cycle : Cell :: new ( false ) ,
3759+ reached_depth : Cell :: new ( depth ) ,
37503760 previous : previous_stack,
3751- depth : previous_stack . depth ( ) + 1 ,
3761+ depth,
37523762 }
37533763 }
37543764
@@ -3944,6 +3954,21 @@ impl<'o, 'tcx> TraitObligationStack<'o, 'tcx> {
39443954 fn iter ( & ' o self ) -> TraitObligationStackList < ' o , ' tcx > {
39453955 self . list ( )
39463956 }
3957+
3958+ /// Indicates that attempting to evaluate this stack entry
3959+ /// required accessing something from the stack at depth `reached_depth`.
3960+ fn update_reached_depth ( & self , reached_depth : usize ) {
3961+ assert ! (
3962+ self . depth > reached_depth,
3963+ "invoked `update_reached_depth` with something under this stack: \
3964+ self.depth={} reached_depth={}",
3965+ self . depth,
3966+ reached_depth,
3967+ ) ;
3968+ debug ! ( "update_reached_depth(reached_depth={})" , reached_depth) ;
3969+ let v = self . reached_depth . get ( ) ;
3970+ self . reached_depth . set ( v. min ( reached_depth) ) ;
3971+ }
39473972}
39483973
39493974#[ derive( Copy , Clone ) ]
0 commit comments