@@ -24,7 +24,7 @@ use super::{Certainty, InferCtxtEvalExt};
2424/// It is also likely that we want to use slightly different datastructures
2525/// here as this will have to deal with far more root goals than `evaluate_all`.
2626pub struct FulfillmentCtxt < ' tcx > {
27- obligations : Vec < PredicateObligation < ' tcx > > ,
27+ obligations : ObligationStorage < ' tcx > ,
2828
2929 /// The snapshot in which this context was created. Using the context
3030 /// outside of this snapshot leads to subtle bugs if the snapshot
@@ -33,14 +33,68 @@ pub struct FulfillmentCtxt<'tcx> {
3333 usable_in_snapshot : usize ,
3434}
3535
36+ #[ derive( Default ) ]
37+ struct ObligationStorage < ' tcx > {
38+ /// Obligations which resulted in an overflow in fulfillment itself.
39+ ///
40+ /// We cannot eagerly return these as error so we instead store them here
41+ /// to avoid recomputing them each time `select_where_possible` is called.
42+ /// This also allows us to return the correct `FulfillmentError` for them.
43+ overflowed : Vec < PredicateObligation < ' tcx > > ,
44+ pending : Vec < PredicateObligation < ' tcx > > ,
45+ }
46+
47+ impl < ' tcx > ObligationStorage < ' tcx > {
48+ fn register ( & mut self , obligation : PredicateObligation < ' tcx > ) {
49+ self . pending . push ( obligation) ;
50+ }
51+
52+ fn clone_pending ( & self ) -> Vec < PredicateObligation < ' tcx > > {
53+ let mut obligations = self . pending . clone ( ) ;
54+ obligations. extend ( self . overflowed . iter ( ) . cloned ( ) ) ;
55+ obligations
56+ }
57+
58+ fn take_pending ( & mut self ) -> Vec < PredicateObligation < ' tcx > > {
59+ let mut obligations = mem:: take ( & mut self . pending ) ;
60+ obligations. extend ( self . overflowed . drain ( ..) ) ;
61+ obligations
62+ }
63+
64+ fn unstalled_for_select ( & mut self ) -> impl Iterator < Item = PredicateObligation < ' tcx > > {
65+ mem:: take ( & mut self . pending ) . into_iter ( )
66+ }
67+
68+ fn on_fulfillment_overflow ( & mut self , infcx : & InferCtxt < ' tcx > ) {
69+ infcx. probe ( |_| {
70+ // IMPORTANT: we must not use solve any inference variables in the obligations
71+ // as this is all happening inside of a probe. We use a probe to make sure
72+ // we get all obligations involved in the overflow. We pretty much check: if
73+ // we were to do another step of `select_where_possible`, which goals would
74+ // change.
75+ self . overflowed . extend ( self . pending . extract_if ( |o| {
76+ let goal = o. clone ( ) . into ( ) ;
77+ let result = infcx. evaluate_root_goal ( goal, GenerateProofTree :: Never ) . 0 ;
78+ match result {
79+ Ok ( ( has_changed, _) ) => has_changed,
80+ _ => false ,
81+ }
82+ } ) ) ;
83+ } )
84+ }
85+ }
86+
3687impl < ' tcx > FulfillmentCtxt < ' tcx > {
3788 pub fn new ( infcx : & InferCtxt < ' tcx > ) -> FulfillmentCtxt < ' tcx > {
3889 assert ! (
3990 infcx. next_trait_solver( ) ,
4091 "new trait solver fulfillment context created when \
4192 infcx is set up for old trait solver"
4293 ) ;
43- FulfillmentCtxt { obligations : Vec :: new ( ) , usable_in_snapshot : infcx. num_open_snapshots ( ) }
94+ FulfillmentCtxt {
95+ obligations : Default :: default ( ) ,
96+ usable_in_snapshot : infcx. num_open_snapshots ( ) ,
97+ }
4498 }
4599
46100 fn inspect_evaluated_obligation (
@@ -67,29 +121,38 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
67121 obligation : PredicateObligation < ' tcx > ,
68122 ) {
69123 assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
70- self . obligations . push ( obligation) ;
124+ self . obligations . register ( obligation) ;
71125 }
72126
73127 fn collect_remaining_errors ( & mut self , infcx : & InferCtxt < ' tcx > ) -> Vec < FulfillmentError < ' tcx > > {
74- self . obligations
128+ let mut errors: Vec < _ > = self
129+ . obligations
130+ . pending
75131 . drain ( ..)
76132 . map ( |obligation| fulfillment_error_for_stalled ( infcx, obligation) )
77- . collect ( )
133+ . collect ( ) ;
134+
135+ errors. extend ( self . obligations . overflowed . drain ( ..) . map ( |obligation| FulfillmentError {
136+ root_obligation : obligation. clone ( ) ,
137+ code : FulfillmentErrorCode :: Ambiguity { overflow : Some ( true ) } ,
138+ obligation,
139+ } ) ) ;
140+
141+ errors
78142 }
79143
80144 fn select_where_possible ( & mut self , infcx : & InferCtxt < ' tcx > ) -> Vec < FulfillmentError < ' tcx > > {
81145 assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
82146 let mut errors = Vec :: new ( ) ;
83147 for i in 0 .. {
84148 if !infcx. tcx . recursion_limit ( ) . value_within_limit ( i) {
85- // Only return true errors that we have accumulated while processing;
86- // keep ambiguities around, *including overflows*, because they shouldn't
87- // be considered true errors.
149+ self . obligations . on_fulfillment_overflow ( infcx) ;
150+ // Only return true errors that we have accumulated while processing.
88151 return errors;
89152 }
90153
91154 let mut has_changed = false ;
92- for obligation in mem :: take ( & mut self . obligations ) {
155+ for obligation in self . obligations . unstalled_for_select ( ) {
93156 let goal = obligation. clone ( ) . into ( ) ;
94157 let result = infcx. evaluate_root_goal ( goal, GenerateProofTree :: IfEnabled ) . 0 ;
95158 self . inspect_evaluated_obligation ( infcx, & obligation, & result) ;
@@ -103,7 +166,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
103166 has_changed |= changed;
104167 match certainty {
105168 Certainty :: Yes => { }
106- Certainty :: Maybe ( _) => self . obligations . push ( obligation) ,
169+ Certainty :: Maybe ( _) => self . obligations . register ( obligation) ,
107170 }
108171 }
109172
@@ -116,14 +179,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
116179 }
117180
118181 fn pending_obligations ( & self ) -> Vec < PredicateObligation < ' tcx > > {
119- self . obligations . clone ( )
182+ self . obligations . clone_pending ( )
120183 }
121184
122185 fn drain_unstalled_obligations (
123186 & mut self ,
124187 _: & InferCtxt < ' tcx > ,
125188 ) -> Vec < PredicateObligation < ' tcx > > {
126- std :: mem :: take ( & mut self . obligations )
189+ self . obligations . take_pending ( )
127190 }
128191}
129192
0 commit comments