@@ -35,8 +35,8 @@ use crate::solve::ty::may_use_unstable_feature;
3535use crate :: solve:: {
3636 CanonicalInput , CanonicalResponse , Certainty , ExternalConstraintsData , FIXPOINT_STEP_LIMIT ,
3737 Goal , GoalEvaluation , GoalSource , GoalStalledOn , HasChanged , MaybeCause ,
38- NestedNormalizationGoals , NoSolution , QueryInput , QueryResult , Response , VisibleForLeakCheck ,
39- inspect,
38+ NestedNormalizationGoals , NoSolution , QueryInput , QueryResult , Response , SucceededInErased ,
39+ VisibleForLeakCheck , inspect,
4040} ;
4141
4242mod probe;
@@ -284,6 +284,12 @@ where
284284 }
285285}
286286
287+ #[ derive( Debug , Clone , Copy ) ]
288+ enum RerunStalled {
289+ WontMakeProgress ( Certainty ) ,
290+ MayMakeProgress ,
291+ }
292+
287293impl < ' a , D , I > EvalCtxt < ' a , D >
288294where
289295 D : SolverDelegate < Interner = I > ,
@@ -482,6 +488,81 @@ where
482488 Ok ( goal_evaluation)
483489 }
484490
491+ /// This is a fast path optimization:
492+ /// If we have run this goal before, and it was stalled, check that any of the goal's
493+ /// args have changed. This is a cheap way to determine that if we were to rerun this goal now,
494+ /// it will remain stalled since it'll canonicalize the same way and evaluation is pure.
495+ /// Therefore, we can skip this rerun
496+ fn rerunning_stalled_goal_may_make_progress (
497+ & self ,
498+ stalled_on : Option < & GoalStalledOn < I > > ,
499+ ) -> RerunStalled {
500+ use RerunStalled :: * ;
501+
502+ // If fast paths are turned off, then we assume all goals can always make progress
503+ if self . delegate . disable_trait_solver_fast_paths ( ) {
504+ return MayMakeProgress ;
505+ }
506+
507+ // If the goal isn't stalled, we should definitely run it.
508+ let Some ( & GoalStalledOn {
509+ num_opaques,
510+ ref stalled_vars,
511+ ref sub_roots,
512+ stalled_certainty,
513+ ref previously_succeeded_in_erased,
514+ } ) = stalled_on
515+ else {
516+ return MayMakeProgress ;
517+ } ;
518+
519+ // If any of the stalled goal's generic arguments changed,
520+ // rerunning might make progress so we should rerun.
521+ if stalled_vars. iter ( ) . any ( |value| self . delegate . is_changed_arg ( * value) ) {
522+ return MayMakeProgress ;
523+ }
524+
525+ // If some inference took place in any of the sub roots,
526+ // rerunning might make progress so we should rerun.
527+ if sub_roots. iter ( ) . any ( |& vid| self . delegate . sub_unification_table_root_var ( vid) != vid) {
528+ return MayMakeProgress ;
529+ }
530+
531+ // If any opaques changed in the opaque type storage,
532+ // rerunning might make progress so we should rerun.
533+ if self . delegate . opaque_types_storage_num_entries ( ) . needs_reevaluation ( num_opaques) {
534+ // Unless this goal previously succeeded in erased mode.
535+ // If the stalled goal successfully evaluated while erasing opaque types,
536+ // and the current state of the opaque type storage is not different in a way that is
537+ // relevant, this stalled goal cannot make any progress and we set this variable to true.
538+ let mut previous_erased_run_is_still_valid = false ;
539+
540+ if let & SucceededInErased :: Yes { accessed_opaques } = previously_succeeded_in_erased {
541+ match self . should_rerun_after_erased_canonicalization (
542+ accessed_opaques,
543+ self . typing_mode ( ) ,
544+ & self . delegate . clone_opaque_types_lookup_table ( ) ,
545+ ) {
546+ RerunDecision :: Yes => { }
547+ RerunDecision :: EagerlyPropagateToParent => {
548+ unreachable ! ( "we never retry stalled queries if the parent was erased" )
549+ }
550+ RerunDecision :: No => {
551+ previous_erased_run_is_still_valid = true ;
552+ }
553+ }
554+ }
555+
556+ if !previous_erased_run_is_still_valid {
557+ return MayMakeProgress ;
558+ }
559+ }
560+
561+ // Otherwise, we can be sure that this stalled goal cannot make any progress
562+ // and we can exit early.
563+ WontMakeProgress ( stalled_certainty)
564+ }
565+
485566 /// Recursively evaluates `goal`, returning the nested goals in case
486567 /// the nested goal is a `NormalizesTo` goal.
487568 ///
@@ -495,21 +576,8 @@ where
495576 goal : Goal < I , I :: Predicate > ,
496577 stalled_on : Option < GoalStalledOn < I > > ,
497578 ) -> Result < ( NestedNormalizationGoals < I > , GoalEvaluation < I > ) , NoSolutionOrRerunNonErased > {
498- // If we have run this goal before, and it was stalled, check that any of the goal's
499- // args have changed. Otherwise, we don't need to re-run the goal because it'll remain
500- // stalled, since it'll canonicalize the same way and evaluation is pure.
501- if let Some ( GoalStalledOn {
502- num_opaques,
503- ref stalled_vars,
504- ref sub_roots,
505- stalled_certainty,
506- } ) = stalled_on
507- && !self . delegate . disable_trait_solver_fast_paths ( )
508- && !stalled_vars. iter ( ) . any ( |value| self . delegate . is_changed_arg ( * value) )
509- && !sub_roots
510- . iter ( )
511- . any ( |& vid| self . delegate . sub_unification_table_root_var ( vid) != vid)
512- && !self . delegate . opaque_types_storage_num_entries ( ) . needs_reevaluation ( num_opaques)
579+ if let RerunStalled :: WontMakeProgress ( stalled_certainty) =
580+ self . rerunning_stalled_goal_may_make_progress ( stalled_on. as_ref ( ) )
513581 {
514582 return Ok ( (
515583 NestedNormalizationGoals :: empty ( ) ,
@@ -539,7 +607,7 @@ where
539607 )
540608 . entered ( ) ;
541609
542- let ( result, orig_values, canonical_goal) = ' retry_canonicalize: {
610+ let ( result, orig_values, canonical_goal, succeeded_in_erased ) = ' retry_canonicalize: {
543611 let skip_erased_attempt = if typing_mode. is_coherence ( ) {
544612 true
545613 } else {
@@ -595,11 +663,23 @@ where
595663 match should_rerun {
596664 RerunDecision :: Yes => debug ! ( "rerunning in original typing mode" ) ,
597665 RerunDecision :: No => {
598- break ' retry_canonicalize ( canonical_result, orig_values, canonical_goal) ;
666+ break ' retry_canonicalize (
667+ canonical_result,
668+ orig_values,
669+ canonical_goal,
670+ SucceededInErased :: Yes { accessed_opaques } ,
671+ ) ;
599672 }
600673 RerunDecision :: EagerlyPropagateToParent => {
601674 self . opaque_accesses . update ( accessed_opaques) ?;
602- break ' retry_canonicalize ( canonical_result, orig_values, canonical_goal) ;
675+ break ' retry_canonicalize (
676+ canonical_result,
677+ orig_values,
678+ canonical_goal,
679+ // If we're propagating up, we should never retry the goal.
680+ // That means `No` is fine to return, it doesn't really matter.
681+ SucceededInErased :: No ,
682+ ) ;
603683 }
604684 }
605685 }
@@ -618,7 +698,7 @@ where
618698 "we run without TypingMode::ErasedNotCoherence, so opaques are available, and we don't retry if the outer typing mode is ErasedNotCoherence: {accessed_opaques:?} after {goal:?}"
619699 ) ;
620700
621- ( canonical_result, orig_values, canonical_goal)
701+ ( canonical_result, orig_values, canonical_goal, SucceededInErased :: No )
622702 } ;
623703
624704 debug ! ( ?result) ;
@@ -714,6 +794,7 @@ where
714794 stalled_vars,
715795 sub_roots,
716796 stalled_certainty : certainty,
797+ previously_succeeded_in_erased : succeeded_in_erased,
717798 } )
718799 }
719800 } ,
0 commit comments