@@ -24,7 +24,7 @@ use super::{Certainty, InferCtxtEvalExt};
24
24
/// It is also likely that we want to use slightly different datastructures
25
25
/// here as this will have to deal with far more root goals than `evaluate_all`.
26
26
pub struct FulfillmentCtxt < ' tcx > {
27
- obligations : Vec < PredicateObligation < ' tcx > > ,
27
+ obligations : ObligationStorage < ' tcx > ,
28
28
29
29
/// The snapshot in which this context was created. Using the context
30
30
/// outside of this snapshot leads to subtle bugs if the snapshot
@@ -33,14 +33,68 @@ pub struct FulfillmentCtxt<'tcx> {
33
33
usable_in_snapshot : usize ,
34
34
}
35
35
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
+
36
87
impl < ' tcx > FulfillmentCtxt < ' tcx > {
37
88
pub fn new ( infcx : & InferCtxt < ' tcx > ) -> FulfillmentCtxt < ' tcx > {
38
89
assert ! (
39
90
infcx. next_trait_solver( ) ,
40
91
"new trait solver fulfillment context created when \
41
92
infcx is set up for old trait solver"
42
93
) ;
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
+ }
44
98
}
45
99
46
100
fn inspect_evaluated_obligation (
@@ -67,29 +121,38 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
67
121
obligation : PredicateObligation < ' tcx > ,
68
122
) {
69
123
assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
70
- self . obligations . push ( obligation) ;
124
+ self . obligations . register ( obligation) ;
71
125
}
72
126
73
127
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
75
131
. drain ( ..)
76
132
. 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
78
142
}
79
143
80
144
fn select_where_possible ( & mut self , infcx : & InferCtxt < ' tcx > ) -> Vec < FulfillmentError < ' tcx > > {
81
145
assert_eq ! ( self . usable_in_snapshot, infcx. num_open_snapshots( ) ) ;
82
146
let mut errors = Vec :: new ( ) ;
83
147
for i in 0 .. {
84
148
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.
88
151
return errors;
89
152
}
90
153
91
154
let mut has_changed = false ;
92
- for obligation in mem :: take ( & mut self . obligations ) {
155
+ for obligation in self . obligations . unstalled_for_select ( ) {
93
156
let goal = obligation. clone ( ) . into ( ) ;
94
157
let result = infcx. evaluate_root_goal ( goal, GenerateProofTree :: IfEnabled ) . 0 ;
95
158
self . inspect_evaluated_obligation ( infcx, & obligation, & result) ;
@@ -103,7 +166,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
103
166
has_changed |= changed;
104
167
match certainty {
105
168
Certainty :: Yes => { }
106
- Certainty :: Maybe ( _) => self . obligations . push ( obligation) ,
169
+ Certainty :: Maybe ( _) => self . obligations . register ( obligation) ,
107
170
}
108
171
}
109
172
@@ -116,14 +179,14 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
116
179
}
117
180
118
181
fn pending_obligations ( & self ) -> Vec < PredicateObligation < ' tcx > > {
119
- self . obligations . clone ( )
182
+ self . obligations . clone_pending ( )
120
183
}
121
184
122
185
fn drain_unstalled_obligations (
123
186
& mut self ,
124
187
_: & InferCtxt < ' tcx > ,
125
188
) -> Vec < PredicateObligation < ' tcx > > {
126
- std :: mem :: take ( & mut self . obligations )
189
+ self . obligations . take_pending ( )
127
190
}
128
191
}
129
192
0 commit comments