@@ -15,7 +15,7 @@ use rustc_middle::traits::solve::{
15
15
CanonicalInput , CanonicalResponse , Certainty , IsNormalizesToHack , PredefinedOpaques ,
16
16
PredefinedOpaquesData , QueryResult ,
17
17
} ;
18
- use rustc_middle:: traits:: DefiningAnchor ;
18
+ use rustc_middle:: traits:: { specialization_graph , DefiningAnchor } ;
19
19
use rustc_middle:: ty:: {
20
20
self , OpaqueTypeKey , Ty , TyCtxt , TypeFoldable , TypeSuperVisitable , TypeVisitable ,
21
21
TypeVisitableExt , TypeVisitor ,
@@ -25,11 +25,10 @@ use rustc_span::DUMMY_SP;
25
25
use std:: io:: Write ;
26
26
use std:: ops:: ControlFlow ;
27
27
28
- use crate :: traits:: specialization_graph;
29
28
use crate :: traits:: vtable:: { count_own_vtable_entries, prepare_vtable_segments, VtblSegment } ;
30
29
31
30
use super :: inspect:: ProofTreeBuilder ;
32
- use super :: search_graph:: { self , OverflowHandler } ;
31
+ use super :: search_graph;
33
32
use super :: SolverMode ;
34
33
use super :: { search_graph:: SearchGraph , Goal } ;
35
34
pub use select:: InferCtxtSelectExt ;
@@ -175,6 +174,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
175
174
self . search_graph . solver_mode ( )
176
175
}
177
176
177
+ pub ( super ) fn local_overflow_limit ( & self ) -> usize {
178
+ self . search_graph . local_overflow_limit ( )
179
+ }
180
+
178
181
/// Creates a root evaluation context and search graph. This should only be
179
182
/// used from outside of any evaluation, and other methods should be preferred
180
183
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
@@ -479,101 +482,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
479
482
let inspect = self . inspect . new_evaluate_added_goals ( ) ;
480
483
let inspect = core:: mem:: replace ( & mut self . inspect , inspect) ;
481
484
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 ;
568
493
}
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 ;
574
498
}
575
- } ,
576
- ) ;
499
+ }
500
+ }
577
501
578
502
self . inspect . eval_added_goals_result ( response) ;
579
503
@@ -584,9 +508,84 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
584
508
let goal_evaluations = std:: mem:: replace ( & mut self . inspect , inspect) ;
585
509
self . inspect . added_goals_evaluation ( goal_evaluations) ;
586
510
587
- self . nested_goals = goals;
588
511
response
589
512
}
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
+ }
590
589
}
591
590
592
591
impl < ' tcx > EvalCtxt < ' _ , ' tcx > {
0 commit comments