@@ -15,7 +15,7 @@ use rustc_middle::traits::solve::{
1515 CanonicalInput , CanonicalResponse , Certainty , IsNormalizesToHack , PredefinedOpaques ,
1616 PredefinedOpaquesData , QueryResult ,
1717} ;
18- use rustc_middle:: traits:: DefiningAnchor ;
18+ use rustc_middle:: traits:: { specialization_graph , DefiningAnchor } ;
1919use rustc_middle:: ty:: {
2020 self , OpaqueTypeKey , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
2121 TypeVisitableExt , TypeVisitor ,
@@ -25,11 +25,10 @@ use rustc_span::DUMMY_SP;
2525use std:: io:: Write ;
2626use std:: ops:: ControlFlow ;
2727
28- use crate :: traits:: specialization_graph;
2928use crate :: traits:: vtable:: { count_own_vtable_entries, prepare_vtable_segments, VtblSegment } ;
3029
3130use super :: inspect:: ProofTreeBuilder ;
32- use super :: search_graph:: { self , OverflowHandler } ;
31+ use super :: search_graph;
3332use super :: SolverMode ;
3433use super :: { search_graph:: SearchGraph , Goal } ;
3534pub use select:: InferCtxtSelectExt ;
@@ -175,6 +174,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
175174 self . search_graph . solver_mode ( )
176175 }
177176
177+ pub ( super ) fn local_overflow_limit ( & self ) -> usize {
178+ self . search_graph . local_overflow_limit ( )
179+ }
180+
178181 /// Creates a root evaluation context and search graph. This should only be
179182 /// used from outside of any evaluation, and other methods should be preferred
180183 /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
@@ -479,101 +482,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
479482 let inspect = self . inspect . new_evaluate_added_goals ( ) ;
480483 let inspect = core:: mem:: replace ( & mut self . inspect , inspect) ;
481484
482- let mut goals = core:: mem:: replace ( & mut self . nested_goals , NestedGoals :: new ( ) ) ;
483- let mut new_goals = NestedGoals :: new ( ) ;
484-
485- let response = self . repeat_while_none (
486- |_| Ok ( Certainty :: OVERFLOW ) ,
487- |this| {
488- this. inspect . evaluate_added_goals_loop_start ( ) ;
489-
490- let mut has_changed = Err ( Certainty :: Yes ) ;
491-
492- if let Some ( goal) = goals. normalizes_to_hack_goal . take ( ) {
493- // Replace the goal with an unconstrained infer var, so the
494- // RHS does not affect projection candidate assembly.
495- let unconstrained_rhs = this. next_term_infer_of_kind ( goal. predicate . term ) ;
496- let unconstrained_goal = goal. with (
497- this. tcx ( ) ,
498- ty:: ProjectionPredicate {
499- projection_ty : goal. predicate . projection_ty ,
500- term : unconstrained_rhs,
501- } ,
502- ) ;
503-
504- let ( _, certainty, instantiate_goals) =
505- match this. evaluate_goal ( IsNormalizesToHack :: Yes , unconstrained_goal) {
506- Ok ( r) => r,
507- Err ( NoSolution ) => return Some ( Err ( NoSolution ) ) ,
508- } ;
509- new_goals. goals . extend ( instantiate_goals) ;
510-
511- // Finally, equate the goal's RHS with the unconstrained var.
512- // We put the nested goals from this into goals instead of
513- // next_goals to avoid needing to process the loop one extra
514- // time if this goal returns something -- I don't think this
515- // matters in practice, though.
516- match this. eq_and_get_goals (
517- goal. param_env ,
518- goal. predicate . term ,
519- unconstrained_rhs,
520- ) {
521- Ok ( eq_goals) => {
522- goals. goals . extend ( eq_goals) ;
523- }
524- Err ( NoSolution ) => return Some ( Err ( NoSolution ) ) ,
525- } ;
526-
527- // We only look at the `projection_ty` part here rather than
528- // looking at the "has changed" return from evaluate_goal,
529- // because we expect the `unconstrained_rhs` part of the predicate
530- // to have changed -- that means we actually normalized successfully!
531- if goal. predicate . projection_ty
532- != this. resolve_vars_if_possible ( goal. predicate . projection_ty )
533- {
534- has_changed = Ok ( ( ) )
535- }
536-
537- match certainty {
538- Certainty :: Yes => { }
539- Certainty :: Maybe ( _) => {
540- // We need to resolve vars here so that we correctly
541- // deal with `has_changed` in the next iteration.
542- new_goals. normalizes_to_hack_goal =
543- Some ( this. resolve_vars_if_possible ( goal) ) ;
544- has_changed = has_changed. map_err ( |c| c. unify_with ( certainty) ) ;
545- }
546- }
547- }
548-
549- for goal in goals. goals . drain ( ..) {
550- let ( changed, certainty, instantiate_goals) =
551- match this. evaluate_goal ( IsNormalizesToHack :: No , goal) {
552- Ok ( result) => result,
553- Err ( NoSolution ) => return Some ( Err ( NoSolution ) ) ,
554- } ;
555- new_goals. goals . extend ( instantiate_goals) ;
556-
557- if changed {
558- has_changed = Ok ( ( ) ) ;
559- }
560-
561- match certainty {
562- Certainty :: Yes => { }
563- Certainty :: Maybe ( _) => {
564- new_goals. goals . push ( goal) ;
565- has_changed = has_changed. map_err ( |c| c. unify_with ( certainty) ) ;
566- }
567- }
485+ let mut response = Ok ( Certainty :: OVERFLOW ) ;
486+ for _ in 0 ..self . local_overflow_limit ( ) {
487+ // FIXME: This match is a bit ugly, it might be nice to change the inspect
488+ // stuff to use a closure instead. which should hopefully simplify this a bit.
489+ match self . evaluate_added_goals_step ( ) {
490+ Ok ( Some ( cert) ) => {
491+ response = Ok ( cert) ;
492+ break ;
568493 }
569-
570- core:: mem:: swap ( & mut new_goals, & mut goals) ;
571- match has_changed {
572- Ok ( ( ) ) => None ,
573- Err ( certainty) => Some ( Ok ( certainty) ) ,
494+ Ok ( None ) => { }
495+ Err ( NoSolution ) => {
496+ response = Err ( NoSolution ) ;
497+ break ;
574498 }
575- } ,
576- ) ;
499+ }
500+ }
577501
578502 self . inspect . eval_added_goals_result ( response) ;
579503
@@ -584,9 +508,84 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
584508 let goal_evaluations = std:: mem:: replace ( & mut self . inspect , inspect) ;
585509 self . inspect . added_goals_evaluation ( goal_evaluations) ;
586510
587- self . nested_goals = goals;
588511 response
589512 }
513+
514+ /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning.
515+ ///
516+ /// Goals for the next step get directly added the the nested goals of the `EvalCtxt`.
517+ fn evaluate_added_goals_step ( & mut self ) -> Result < Option < Certainty > , NoSolution > {
518+ let tcx = self . tcx ( ) ;
519+ let mut goals = core:: mem:: replace ( & mut self . nested_goals , NestedGoals :: new ( ) ) ;
520+
521+ self . inspect . evaluate_added_goals_loop_start ( ) ;
522+ // If this loop did not result in any progress, what's our final certainty.
523+ let mut unchanged_certainty = Some ( Certainty :: Yes ) ;
524+ if let Some ( goal) = goals. normalizes_to_hack_goal . take ( ) {
525+ // Replace the goal with an unconstrained infer var, so the
526+ // RHS does not affect projection candidate assembly.
527+ let unconstrained_rhs = self . next_term_infer_of_kind ( goal. predicate . term ) ;
528+ let unconstrained_goal = goal. with (
529+ tcx,
530+ ty:: ProjectionPredicate {
531+ projection_ty : goal. predicate . projection_ty ,
532+ term : unconstrained_rhs,
533+ } ,
534+ ) ;
535+
536+ let ( _, certainty, instantiate_goals) =
537+ self . evaluate_goal ( IsNormalizesToHack :: Yes , unconstrained_goal) ?;
538+ self . add_goals ( instantiate_goals) ;
539+
540+ // Finally, equate the goal's RHS with the unconstrained var.
541+ // We put the nested goals from this into goals instead of
542+ // next_goals to avoid needing to process the loop one extra
543+ // time if this goal returns something -- I don't think this
544+ // matters in practice, though.
545+ let eq_goals =
546+ self . eq_and_get_goals ( goal. param_env , goal. predicate . term , unconstrained_rhs) ?;
547+ goals. goals . extend ( eq_goals) ;
548+
549+ // We only look at the `projection_ty` part here rather than
550+ // looking at the "has changed" return from evaluate_goal,
551+ // because we expect the `unconstrained_rhs` part of the predicate
552+ // to have changed -- that means we actually normalized successfully!
553+ if goal. predicate . projection_ty
554+ != self . resolve_vars_if_possible ( goal. predicate . projection_ty )
555+ {
556+ unchanged_certainty = None ;
557+ }
558+
559+ match certainty {
560+ Certainty :: Yes => { }
561+ Certainty :: Maybe ( _) => {
562+ // We need to resolve vars here so that we correctly
563+ // deal with `has_changed` in the next iteration.
564+ self . set_normalizes_to_hack_goal ( self . resolve_vars_if_possible ( goal) ) ;
565+ unchanged_certainty = unchanged_certainty. map ( |c| c. unify_with ( certainty) ) ;
566+ }
567+ }
568+ }
569+
570+ for goal in goals. goals . drain ( ..) {
571+ let ( has_changed, certainty, instantiate_goals) =
572+ self . evaluate_goal ( IsNormalizesToHack :: No , goal) ?;
573+ self . add_goals ( instantiate_goals) ;
574+ if has_changed {
575+ unchanged_certainty = None ;
576+ }
577+
578+ match certainty {
579+ Certainty :: Yes => { }
580+ Certainty :: Maybe ( _) => {
581+ self . add_goal ( goal) ;
582+ unchanged_certainty = unchanged_certainty. map ( |c| c. unify_with ( certainty) ) ;
583+ }
584+ }
585+ }
586+
587+ Ok ( unchanged_certainty)
588+ }
590589}
591590
592591impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
0 commit comments