@@ -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;
@@ -276,6 +276,12 @@ where
276276 }
277277}
278278
279+ #[ derive( Debug , Clone , Copy ) ]
280+ enum RerunStalled {
281+ WontMakeProgress ( Certainty ) ,
282+ MayMakeProgress ,
283+ }
284+
279285impl < ' a , D , I > EvalCtxt < ' a , D >
280286where
281287 D : SolverDelegate < Interner = I > ,
@@ -474,6 +480,81 @@ where
474480 Ok ( goal_evaluation)
475481 }
476482
483+ /// This is a fast path optimization:
484+ /// If we have run this goal before, and it was stalled, check that any of the goal's
485+ /// args have changed. This is a cheap way to determine that if we were to rerun this goal now,
486+ /// it will remain stalled since it'll canonicalize the same way and evaluation is pure.
487+ /// Therefore, we can skip this rerun
488+ fn rerunning_stalled_goal_may_make_progress (
489+ & self ,
490+ stalled_on : Option < & GoalStalledOn < I > > ,
491+ ) -> RerunStalled {
492+ use RerunStalled :: * ;
493+
494+ // If fast paths are turned off, then we assume all goals can always make progress
495+ if self . delegate . disable_trait_solver_fast_paths ( ) {
496+ return MayMakeProgress ;
497+ }
498+
499+ // If the goal isn't stalled, we should definitely run it.
500+ let Some ( & GoalStalledOn {
501+ num_opaques,
502+ ref stalled_vars,
503+ ref sub_roots,
504+ stalled_certainty,
505+ ref previously_succeeded_in_erased,
506+ } ) = stalled_on
507+ else {
508+ return MayMakeProgress ;
509+ } ;
510+
511+ // If any of the stalled goal's generic arguments changed,
512+ // rerunning might make progress so we should rerun.
513+ if stalled_vars. iter ( ) . any ( |value| self . delegate . is_changed_arg ( * value) ) {
514+ return MayMakeProgress ;
515+ }
516+
517+ // If some inference took place in any of the sub roots,
518+ // rerunning might make progress so we should rerun.
519+ if sub_roots. iter ( ) . any ( |& vid| self . delegate . sub_unification_table_root_var ( vid) != vid) {
520+ return MayMakeProgress ;
521+ }
522+
523+ // If any opaques changed in the opaque type storage,
524+ // rerunning might make progress so we should rerun.
525+ if self . delegate . opaque_types_storage_num_entries ( ) . needs_reevaluation ( num_opaques) {
526+ // Unless this goal previously succeeded in erased mode.
527+ // If the stalled goal successfully evaluated while erasing opaque types,
528+ // and the current state of the opaque type storage is not different in a way that is
529+ // relevant, this stalled goal cannot make any progress and we set this variable to true.
530+ let mut previous_erased_run_is_still_valid = false ;
531+
532+ if let & SucceededInErased :: Yes { accessed_opaques } = previously_succeeded_in_erased {
533+ match self . should_rerun_after_erased_canonicalization (
534+ accessed_opaques,
535+ self . typing_mode ( ) ,
536+ & self . delegate . clone_opaque_types_lookup_table ( ) ,
537+ ) {
538+ RerunDecision :: Yes => { }
539+ RerunDecision :: EagerlyPropagateToParent => {
540+ unreachable ! ( "we never retry stalled queries if the parent was erased" )
541+ }
542+ RerunDecision :: No => {
543+ previous_erased_run_is_still_valid = true ;
544+ }
545+ }
546+ }
547+
548+ if !previous_erased_run_is_still_valid {
549+ return MayMakeProgress ;
550+ }
551+ }
552+
553+ // Otherwise, we can be sure that this stalled goal cannot make any progress
554+ // and we can exit early.
555+ WontMakeProgress ( stalled_certainty)
556+ }
557+
477558 /// Recursively evaluates `goal`, returning the nested goals in case
478559 /// the nested goal is a `NormalizesTo` goal.
479560 ///
@@ -487,21 +568,8 @@ where
487568 goal : Goal < I , I :: Predicate > ,
488569 stalled_on : Option < GoalStalledOn < I > > ,
489570 ) -> Result < ( NestedNormalizationGoals < I > , GoalEvaluation < I > ) , NoSolutionOrRerunNonErased > {
490- // If we have run this goal before, and it was stalled, check that any of the goal's
491- // args have changed. Otherwise, we don't need to re-run the goal because it'll remain
492- // stalled, since it'll canonicalize the same way and evaluation is pure.
493- if let Some ( GoalStalledOn {
494- num_opaques,
495- ref stalled_vars,
496- ref sub_roots,
497- stalled_certainty,
498- } ) = stalled_on
499- && !self . delegate . disable_trait_solver_fast_paths ( )
500- && !stalled_vars. iter ( ) . any ( |value| self . delegate . is_changed_arg ( * value) )
501- && !sub_roots
502- . iter ( )
503- . any ( |& vid| self . delegate . sub_unification_table_root_var ( vid) != vid)
504- && !self . delegate . opaque_types_storage_num_entries ( ) . needs_reevaluation ( num_opaques)
571+ if let RerunStalled :: WontMakeProgress ( stalled_certainty) =
572+ self . rerunning_stalled_goal_may_make_progress ( stalled_on. as_ref ( ) )
505573 {
506574 return Ok ( (
507575 NestedNormalizationGoals :: empty ( ) ,
@@ -531,7 +599,7 @@ where
531599 )
532600 . entered ( ) ;
533601
534- let ( result, orig_values, canonical_goal) = ' retry_canonicalize: {
602+ let ( result, orig_values, canonical_goal, succeeded_in_erased ) = ' retry_canonicalize: {
535603 let skip_erased_attempt = if typing_mode. is_coherence ( ) {
536604 true
537605 } else {
@@ -587,11 +655,23 @@ where
587655 match should_rerun {
588656 RerunDecision :: Yes => debug ! ( "rerunning in original typing mode" ) ,
589657 RerunDecision :: No => {
590- break ' retry_canonicalize ( canonical_result, orig_values, canonical_goal) ;
658+ break ' retry_canonicalize (
659+ canonical_result,
660+ orig_values,
661+ canonical_goal,
662+ SucceededInErased :: Yes { accessed_opaques } ,
663+ ) ;
591664 }
592665 RerunDecision :: EagerlyPropagateToParent => {
593666 self . opaque_accesses . update ( accessed_opaques) ?;
594- break ' retry_canonicalize ( canonical_result, orig_values, canonical_goal) ;
667+ break ' retry_canonicalize (
668+ canonical_result,
669+ orig_values,
670+ canonical_goal,
671+ // If we're propagating up, we should never retry the goal.
672+ // That means `No` is fine to return, it doesn't really matter.
673+ SucceededInErased :: No ,
674+ ) ;
595675 }
596676 }
597677 }
@@ -610,7 +690,7 @@ where
610690 "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:?}"
611691 ) ;
612692
613- ( canonical_result, orig_values, canonical_goal)
693+ ( canonical_result, orig_values, canonical_goal, SucceededInErased :: No )
614694 } ;
615695
616696 debug ! ( ?result) ;
@@ -718,6 +798,7 @@ where
718798 stalled_vars,
719799 sub_roots,
720800 stalled_certainty : certainty,
801+ previously_succeeded_in_erased : succeeded_in_erased,
721802 } )
722803 }
723804 } ,
0 commit comments