@@ -25,7 +25,7 @@ use super::{Certainty, Goal, InferCtxtEvalExt};
2525/// It is also likely that we want to use slightly different datastructures
2626/// here as this will have to deal with far more root goals than `evaluate_all`.
2727pub struct FulfillmentCtxt < ' tcx > {
28- obligations : Vec < PredicateObligation < ' tcx > > ,
28+ obligations : ObligationStorage < ' tcx > ,
2929
3030 /// The snapshot in which this context was created. Using the context
3131 /// outside of this snapshot leads to subtle bugs if the snapshot
@@ -34,14 +34,68 @@ pub struct FulfillmentCtxt<'tcx> {
3434 usable_in_snapshot : usize ,
3535}
3636
37+ #[ derive( Default ) ]
38+ struct ObligationStorage < ' tcx > {
39+ /// Obligations which resulted in an overflow in fulfillment itself.
40+ ///
41+ /// We cannot eagerly return these as error so we instead store them here
42+ /// to avoid recomputing them each time `select_where_possible` is called.
43+ /// This also allows us to return the correct `FulfillmentError` for them.
44+ overflowed : Vec < PredicateObligation < ' tcx > > ,
45+ pending : Vec < PredicateObligation < ' tcx > > ,
46+ }
47+
48+ impl < ' tcx > ObligationStorage < ' tcx > {
49+ fn register ( & mut self , obligation : PredicateObligation < ' tcx > ) {
50+ self . pending . push ( obligation) ;
51+ }
52+
53+ fn clone_pending ( & self ) -> Vec < PredicateObligation < ' tcx > > {
54+ let mut obligations = self . pending . clone ( ) ;
55+ obligations. extend ( self . overflowed . iter ( ) . cloned ( ) ) ;
56+ obligations
57+ }
58+
59+ fn take_pending ( & mut self ) -> Vec < PredicateObligation < ' tcx > > {
60+ let mut obligations = mem:: take ( & mut self . pending ) ;
61+ obligations. extend ( self . overflowed . drain ( ..) ) ;
62+ obligations
63+ }
64+
65+ fn unstalled_for_select ( & mut self ) -> impl Iterator < Item = PredicateObligation < ' tcx > > {
66+ mem:: take ( & mut self . pending ) . into_iter ( )
67+ }
68+
69+ fn on_fulfillment_overflow ( & mut self , infcx : & InferCtxt < ' tcx > ) {
70+ infcx. probe ( |_| {
71+ // IMPORTANT: we must not use solve any inference variables in the obligations
72+ // as this is all happening inside of a probe. We use a probe to make sure
73+ // we get all obligations involved in the overflow. We pretty much check: if
74+ // we were to do another step of `select_where_possible`, which goals would
75+ // change.
76+ self . overflowed . extend ( self . pending . extract_if ( |o| {
77+ let goal = o. clone ( ) . into ( ) ;
78+ let result = infcx. evaluate_root_goal ( goal, GenerateProofTree :: Never ) . 0 ;
79+ match result {
80+ Ok ( ( has_changed, _, _) ) => has_changed,
81+ _ => false ,
82+ }
83+ } ) ) ;
84+ } )
85+ }
86+ }
87+
3788impl < ' tcx > FulfillmentCtxt < ' tcx > {
3889 pub fn new ( infcx : & InferCtxt < ' tcx > ) -> FulfillmentCtxt < ' tcx > {
3990 assert ! (
4091 infcx. next_trait_solver( ) ,
4192 "new trait solver fulfillment context created when \
4293 infcx is set up for old trait solver"
4394 ) ;
44- FulfillmentCtxt { obligations : Vec :: new ( ) , usable_in_snapshot : infcx. num_open_snapshots ( ) }
95+ FulfillmentCtxt {
96+ obligations : Default :: default ( ) ,
97+ usable_in_snapshot : infcx. num_open_snapshots ( ) ,
98+ }
4599 }
46100
47101 fn inspect_evaluated_obligation (
@@ -68,29 +122,38 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
68122 obligation : PredicateObligation < ' tcx > ,
69123 ) {
70124 assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
71- self . obligations . push ( obligation) ;
125+ self . obligations . register ( obligation) ;
72126 }
73127
74128 fn collect_remaining_errors ( & mut self , infcx : & InferCtxt < ' tcx > ) -> Vec < FulfillmentError < ' tcx > > {
75- self . obligations
129+ let mut errors: Vec < _ > = self
130+ . obligations
131+ . pending
76132 . drain ( ..)
77133 . map ( |obligation| fulfillment_error_for_stalled ( infcx, obligation) )
78- . collect ( )
134+ . collect ( ) ;
135+
136+ errors. extend ( self . obligations . overflowed . drain ( ..) . map ( |obligation| FulfillmentError {
137+ root_obligation : obligation. clone ( ) ,
138+ code : FulfillmentErrorCode :: Ambiguity { overflow : Some ( true ) } ,
139+ obligation,
140+ } ) ) ;
141+
142+ errors
79143 }
80144
81145 fn select_where_possible ( & mut self , infcx : & InferCtxt < ' tcx > ) -> Vec < FulfillmentError < ' tcx > > {
82146 assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
83147 let mut errors = Vec :: new ( ) ;
84148 for i in 0 .. {
85149 if !infcx. tcx . recursion_limit ( ) . value_within_limit ( i) {
86- // Only return true errors that we have accumulated while processing;
87- // keep ambiguities around, *including overflows*, because they shouldn't
88- // be considered true errors.
150+ self . obligations . on_fulfillment_overflow ( infcx) ;
151+ // Only return true errors that we have accumulated while processing.
89152 return errors;
90153 }
91154
92155 let mut has_changed = false ;
93- for obligation in mem :: take ( & mut self . obligations ) {
156+ for obligation in self . obligations . unstalled_for_select ( ) {
94157 let goal = obligation. clone ( ) . into ( ) ;
95158 let result = infcx. evaluate_root_goal ( goal, GenerateProofTree :: IfEnabled ) . 0 ;
96159 self . inspect_evaluated_obligation ( infcx, & obligation, & result) ;
@@ -103,18 +166,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
103166 } ;
104167 // Push any nested goals that we get from unifying our canonical response
105168 // with our obligation onto the fulfillment context.
106- self . obligations . extend ( nested_goals . into_iter ( ) . map ( | goal| {
107- Obligation :: new (
169+ for goal in nested_goals {
170+ self . obligations . register ( Obligation :: new (
108171 infcx. tcx ,
109172 obligation. cause . clone ( ) ,
110173 goal. param_env ,
111174 goal. predicate ,
112- )
113- } ) ) ;
175+ ) ) ;
176+ }
114177 has_changed |= changed;
115178 match certainty {
116179 Certainty :: Yes => { }
117- Certainty :: Maybe ( _) => self . obligations . push ( obligation) ,
180+ Certainty :: Maybe ( _) => self . obligations . register ( obligation) ,
118181 }
119182 }
120183
@@ -127,14 +190,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
127190 }
128191
129192 fn pending_obligations ( & self ) -> Vec < PredicateObligation < ' tcx > > {
130- self . obligations . clone ( )
193+ self . obligations . clone_pending ( )
131194 }
132195
133196 fn drain_unstalled_obligations (
134197 & mut self ,
135198 _: & InferCtxt < ' tcx > ,
136199 ) -> Vec < PredicateObligation < ' tcx > > {
137- std :: mem :: take ( & mut self . obligations )
200+ self . obligations . take_pending ( )
138201 }
139202}
140203
0 commit comments