Use ExtractIf in fulfillment loop · rust-lang/rust@eb04ad7 (original) (raw)
1
1
`use std:📑:PhantomData;
`
2
``
`-
use std::mem;
`
3
2
`use std::ops::ControlFlow;
`
4
3
``
5
4
`use rustc_data_structures::thinvec::ExtractIf;
`
`@@ -75,6 +74,13 @@ impl<'tcx> ObligationStorage<'tcx> {
`
75
74
`self.pending.push((obligation, stalled_on));
`
76
75
`}
`
77
76
``
``
77
`+
fn register_overflowed(
`
``
78
`+
&mut self,
`
``
79
`+
overflowed: impl IntoIterator<Item = PredicateObligation<'tcx>>,
`
``
80
`+
) {
`
``
81
`+
self.overflowed.extend(overflowed);
`
``
82
`+
}
`
``
83
+
78
84
`fn has_pending_obligations(&self) -> bool {
`
79
85
` !self.pending.is_empty() || !self.overflowed.is_empty()
`
80
86
`}
`
`@@ -88,35 +94,10 @@ impl<'tcx> ObligationStorage<'tcx> {
`
88
94
``
89
95
`fn drain_pending(
`
90
96
`&mut self,
`
91
``
`-
cond: impl Fn(&PredicateObligation<'tcx>) -> bool,
`
92
``
`-
) -> PendingObligations<'tcx> {
`
93
``
`-
let (unstalled, pending) =
`
94
``
`-
mem::take(&mut self.pending).into_iter().partition(|(o, _)| cond(o));
`
95
``
`-
self.pending = pending;
`
96
``
`-
unstalled
`
97
``
`-
}
`
98
``
-
99
``
`-
fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
`
100
``
`-
infcx.probe(|_| {
`
101
``
`-
// IMPORTANT: we must not use solve any inference variables in the obligations
`
102
``
`-
// as this is all happening inside of a probe. We use a probe to make sure
`
103
``
`-
// we get all obligations involved in the overflow. We pretty much check: if
`
104
``
`` -
// we were to do another step of select_where_possible, which goals would
``
105
``
`-
// change.
`
106
``
`-
// FIXME: https://github.com/Gankra/thin-vec/pull/66 is merged, this can be removed.
`
107
``
`-
self.overflowed.extend(
`
108
``
`-
ExtractIf::new(&mut self.pending, |(o, stalled_on)| {
`
109
``
`-
let goal = o.as_goal();
`
110
``
`-
let result = <&SolverDelegate<'tcx>>::from(infcx).evaluate_root_goal(
`
111
``
`-
goal,
`
112
``
`-
o.cause.span,
`
113
``
`-
stalled_on.take(),
`
114
``
`-
);
`
115
``
`-
matches!(result, Ok(GoalEvaluation { has_changed: HasChanged::Yes, .. }))
`
116
``
`-
})
`
117
``
`-
.map(|(o, _)| o),
`
118
``
`-
);
`
119
``
`-
})
`
``
97
`+
cond: impl Fn(&PredicateObligation<'tcx>, Option<&GoalStalledOn<TyCtxt<'tcx>>>) -> bool,
`
``
98
`+
) -> impl Iterator<Item = (PredicateObligation<'tcx>, Option<GoalStalledOn<TyCtxt<'tcx>>>)>
`
``
99
`+
{
`
``
100
`+
ExtractIf::new(&mut self.pending, move |(o, stalled)| cond(o, stalled.as_ref()))
`
120
101
`}
`
121
102
`}
`
122
103
``
`@@ -133,21 +114,6 @@ impl<'tcx, E: 'tcx> FulfillmentCtxt<'tcx, E> {
`
133
114
`_errors: PhantomData,
`
134
115
`}
`
135
116
`}
`
136
``
-
137
``
`-
fn inspect_evaluated_obligation(
`
138
``
`-
&self,
`
139
``
`-
infcx: &InferCtxt<'tcx>,
`
140
``
`-
obligation: &PredicateObligation<'tcx>,
`
141
``
`-
result: &Result<GoalEvaluation<TyCtxt<'tcx>>, NoSolution>,
`
142
``
`-
) {
`
143
``
`-
if let Some(inspector) = infcx.obligation_inspector.get() {
`
144
``
`-
let result = match result {
`
145
``
`-
Ok(GoalEvaluation { certainty, .. }) => Ok(*certainty),
`
146
``
`-
Err(NoSolution) => Err(NoSolution),
`
147
``
`-
};
`
148
``
`-
(inspector)(infcx, &obligation, result);
`
149
``
`-
}
`
150
``
`-
}
`
151
117
`}
`
152
118
``
153
119
`impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
`
`@@ -181,32 +147,42 @@ where
`
181
147
``
182
148
`fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec {
`
183
149
`assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
`
``
150
`+
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
`
184
151
`let mut errors = Vec::new();
`
185
152
`loop {
`
186
153
`let mut any_changed = false;
`
187
``
`-
for (mut obligation, stalled_on) in self.obligations.drain_pending(|_| true) {
`
``
154
`+
let mut overflowed = vec![];
`
``
155
`+
let mut pending = vec![];
`
``
156
+
``
157
`+
for (mut obligation, stalled_on) in self.obligations.drain_pending(|_, stalled_on| {
`
``
158
`+
stalled_on.is_none_or(|s| !delegate.is_still_stalled(s))
`
``
159
`+
}) {
`
188
160
`if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
`
189
``
`-
self.obligations.on_fulfillment_overflow(infcx);
`
190
``
`-
// Only return true errors that we have accumulated while processing.
`
191
``
`-
return errors;
`
``
161
`+
overflowed.push(obligation);
`
``
162
`+
continue;
`
192
163
`}
`
193
164
``
194
165
`let goal = obligation.as_goal();
`
195
``
`-
let delegate = <&SolverDelegate<'tcx>>::from(infcx);
`
196
166
`if let Some(certainty) =
`
197
167
` delegate.compute_goal_fast_path(goal, obligation.cause.span)
`
198
168
`{
`
199
169
`match certainty {
`
200
170
`Certainty::Yes => {}
`
201
``
`-
Certainty::Maybe(_) => {
`
202
``
`-
self.obligations.register(obligation, None);
`
203
``
`-
}
`
``
171
`+
Certainty::Maybe(_) => pending.push((obligation, None)),
`
204
172
`}
`
205
173
`continue;
`
206
174
`}
`
207
175
``
208
176
`let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on);
`
209
``
`-
self.inspect_evaluated_obligation(infcx, &obligation, &result);
`
``
177
+
``
178
`+
if let Some(inspector) = infcx.obligation_inspector.get() {
`
``
179
`+
let result = match result {
`
``
180
`+
Ok(GoalEvaluation { certainty, .. }) => Ok(certainty),
`
``
181
`+
Err(NoSolution) => Err(NoSolution),
`
``
182
`+
};
`
``
183
`+
(inspector)(infcx, &obligation, result);
`
``
184
`+
}
`
``
185
+
210
186
`let GoalEvaluation { certainty, has_changed, stalled_on } = match result {
`
211
187
`Ok(result) => result,
`
212
188
`Err(NoSolution) => {
`
`@@ -231,10 +207,19 @@ where
`
231
207
``
232
208
`match certainty {
`
233
209
`Certainty::Yes => {}
`
234
``
`-
Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
`
``
210
`+
Certainty::Maybe(_) => pending.push((obligation, stalled_on)),
`
235
211
`}
`
236
212
`}
`
237
213
``
``
214
`+
if !overflowed.is_empty() {
`
``
215
`+
self.obligations.register_overflowed(overflowed);
`
``
216
`+
return errors;
`
``
217
`+
}
`
``
218
+
``
219
`+
for (obligation, stalled_on) in pending {
`
``
220
`+
self.obligations.register(obligation, stalled_on);
`
``
221
`+
}
`
``
222
+
238
223
`if !any_changed {
`
239
224
`break;
`
240
225
`}
`
`@@ -270,7 +255,7 @@ where
`
270
255
`}
`
271
256
``
272
257
`self.obligations
`
273
``
`-
.drain_pending(|obl| {
`
``
258
`+
.drain_pending(|obl, _| {
`
274
259
` infcx.probe(|_| {
`
275
260
` infcx
`
276
261
`.visit_proof_tree(
`