@@ -25,7 +25,7 @@ use super::{Certainty, Goal, InferCtxtEvalExt};
25
25
/// It is also likely that we want to use slightly different datastructures
26
26
/// here as this will have to deal with far more root goals than `evaluate_all`.
27
27
pub struct FulfillmentCtxt < ' tcx > {
28
- obligations : Vec < PredicateObligation < ' tcx > > ,
28
+ obligations : ObligationStorage < ' tcx > ,
29
29
30
30
/// The snapshot in which this context was created. Using the context
31
31
/// outside of this snapshot leads to subtle bugs if the snapshot
@@ -34,14 +34,68 @@ pub struct FulfillmentCtxt<'tcx> {
34
34
usable_in_snapshot : usize ,
35
35
}
36
36
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
+
37
88
impl < ' tcx > FulfillmentCtxt < ' tcx > {
38
89
pub fn new ( infcx : & InferCtxt < ' tcx > ) -> FulfillmentCtxt < ' tcx > {
39
90
assert ! (
40
91
infcx. next_trait_solver( ) ,
41
92
"new trait solver fulfillment context created when \
42
93
infcx is set up for old trait solver"
43
94
) ;
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
+ }
45
99
}
46
100
47
101
fn inspect_evaluated_obligation (
@@ -68,29 +122,38 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
68
122
obligation : PredicateObligation < ' tcx > ,
69
123
) {
70
124
assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
71
- self . obligations . push ( obligation) ;
125
+ self . obligations . register ( obligation) ;
72
126
}
73
127
74
128
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
76
132
. drain ( ..)
77
133
. 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
79
143
}
80
144
81
145
fn select_where_possible ( & mut self , infcx : & InferCtxt < ' tcx > ) -> Vec < FulfillmentError < ' tcx > > {
82
146
assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
83
147
let mut errors = Vec :: new ( ) ;
84
148
for i in 0 .. {
85
149
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.
89
152
return errors;
90
153
}
91
154
92
155
let mut has_changed = false ;
93
- for obligation in mem :: take ( & mut self . obligations ) {
156
+ for obligation in self . obligations . unstalled_for_select ( ) {
94
157
let goal = obligation. clone ( ) . into ( ) ;
95
158
let result = infcx. evaluate_root_goal ( goal, GenerateProofTree :: IfEnabled ) . 0 ;
96
159
self . inspect_evaluated_obligation ( infcx, & obligation, & result) ;
@@ -103,18 +166,18 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
103
166
} ;
104
167
// Push any nested goals that we get from unifying our canonical response
105
168
// 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 (
108
171
infcx. tcx ,
109
172
obligation. cause . clone ( ) ,
110
173
goal. param_env ,
111
174
goal. predicate ,
112
- )
113
- } ) ) ;
175
+ ) ) ;
176
+ }
114
177
has_changed |= changed;
115
178
match certainty {
116
179
Certainty :: Yes => { }
117
- Certainty :: Maybe ( _) => self . obligations . push ( obligation) ,
180
+ Certainty :: Maybe ( _) => self . obligations . register ( obligation) ,
118
181
}
119
182
}
120
183
@@ -127,14 +190,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
127
190
}
128
191
129
192
fn pending_obligations ( & self ) -> Vec < PredicateObligation < ' tcx > > {
130
- self . obligations . clone ( )
193
+ self . obligations . clone_pending ( )
131
194
}
132
195
133
196
fn drain_unstalled_obligations (
134
197
& mut self ,
135
198
_: & InferCtxt < ' tcx > ,
136
199
) -> Vec < PredicateObligation < ' tcx > > {
137
- std :: mem :: take ( & mut self . obligations )
200
+ self . obligations . take_pending ( )
138
201
}
139
202
}
140
203
0 commit comments