1
1
use std:: marker:: PhantomData ;
2
2
use std:: mem;
3
+ use std:: ops:: ControlFlow ;
3
4
4
5
use rustc_data_structures:: thinvec:: ExtractIf ;
6
+ use rustc_hir:: def_id:: LocalDefId ;
5
7
use rustc_infer:: infer:: InferCtxt ;
6
8
use rustc_infer:: traits:: query:: NoSolution ;
7
9
use rustc_infer:: traits:: {
8
10
FromSolverError , PredicateObligation , PredicateObligations , TraitEngine ,
9
11
} ;
12
+ use rustc_middle:: ty:: {
13
+ self , Ty , TyCtxt , TypeSuperVisitable , TypeVisitable , TypeVisitor , TypingMode ,
14
+ } ;
10
15
use rustc_next_trait_solver:: solve:: { GenerateProofTree , HasChanged , SolverDelegateEvalExt as _} ;
16
+ use rustc_span:: Span ;
11
17
use tracing:: instrument;
12
18
13
19
use self :: derive_errors:: * ;
14
20
use super :: Certainty ;
15
21
use super :: delegate:: SolverDelegate ;
22
+ use super :: inspect:: { self , ProofTreeInferCtxtExt } ;
16
23
use crate :: traits:: { FulfillmentError , ScrubbedTraitError } ;
17
24
18
25
mod derive_errors;
@@ -39,7 +46,7 @@ pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
39
46
_errors : PhantomData < E > ,
40
47
}
41
48
42
- #[ derive( Default ) ]
49
+ #[ derive( Default , Debug ) ]
43
50
struct ObligationStorage < ' tcx > {
44
51
/// Obligations which resulted in an overflow in fulfillment itself.
45
52
///
@@ -55,20 +62,23 @@ impl<'tcx> ObligationStorage<'tcx> {
55
62
self . pending . push ( obligation) ;
56
63
}
57
64
65
+ fn has_pending_obligations ( & self ) -> bool {
66
+ !self . pending . is_empty ( ) || !self . overflowed . is_empty ( )
67
+ }
68
+
58
69
fn clone_pending ( & self ) -> PredicateObligations < ' tcx > {
59
70
let mut obligations = self . pending . clone ( ) ;
60
71
obligations. extend ( self . overflowed . iter ( ) . cloned ( ) ) ;
61
72
obligations
62
73
}
63
74
64
- fn take_pending ( & mut self ) -> PredicateObligations < ' tcx > {
65
- let mut obligations = mem:: take ( & mut self . pending ) ;
66
- obligations. append ( & mut self . overflowed ) ;
67
- obligations
68
- }
69
-
70
- fn unstalled_for_select ( & mut self ) -> impl Iterator < Item = PredicateObligation < ' tcx > > + ' tcx {
71
- mem:: take ( & mut self . pending ) . into_iter ( )
75
+ fn drain_pending (
76
+ & mut self ,
77
+ cond : impl Fn ( & PredicateObligation < ' tcx > ) -> bool ,
78
+ ) -> PredicateObligations < ' tcx > {
79
+ let ( unstalled, pending) = mem:: take ( & mut self . pending ) . into_iter ( ) . partition ( cond) ;
80
+ self . pending = pending;
81
+ unstalled
72
82
}
73
83
74
84
fn on_fulfillment_overflow ( & mut self , infcx : & InferCtxt < ' tcx > ) {
@@ -160,7 +170,7 @@ where
160
170
}
161
171
162
172
let mut has_changed = false ;
163
- for obligation in self . obligations . unstalled_for_select ( ) {
173
+ for obligation in self . obligations . drain_pending ( |_| true ) {
164
174
let goal = obligation. as_goal ( ) ;
165
175
let result = <& SolverDelegate < ' tcx > >:: from ( infcx)
166
176
. evaluate_root_goal ( goal, GenerateProofTree :: No , obligation. cause . span )
@@ -196,15 +206,78 @@ where
196
206
}
197
207
198
208
fn has_pending_obligations ( & self ) -> bool {
199
- ! self . obligations . pending . is_empty ( ) || ! self . obligations . overflowed . is_empty ( )
209
+ self . obligations . has_pending_obligations ( )
200
210
}
201
211
202
212
fn pending_obligations ( & self ) -> PredicateObligations < ' tcx > {
203
213
self . obligations . clone_pending ( )
204
214
}
205
215
206
- fn drain_unstalled_obligations ( & mut self , _: & InferCtxt < ' tcx > ) -> PredicateObligations < ' tcx > {
207
- self . obligations . take_pending ( )
216
+ fn drain_stalled_obligations_for_coroutines (
217
+ & mut self ,
218
+ infcx : & InferCtxt < ' tcx > ,
219
+ ) -> PredicateObligations < ' tcx > {
220
+ self . obligations . drain_pending ( |obl| {
221
+ let stalled_generators = match infcx. typing_mode ( ) {
222
+ TypingMode :: Analysis { defining_opaque_types : _, stalled_generators } => {
223
+ stalled_generators
224
+ }
225
+ TypingMode :: Coherence
226
+ | TypingMode :: PostBorrowckAnalysis { defined_opaque_types : _ }
227
+ | TypingMode :: PostAnalysis => return false ,
228
+ } ;
229
+
230
+ if stalled_generators. is_empty ( ) {
231
+ return false ;
232
+ }
233
+
234
+ infcx. probe ( |_| {
235
+ infcx
236
+ . visit_proof_tree (
237
+ obl. as_goal ( ) ,
238
+ & mut StalledOnCoroutines { stalled_generators, span : obl. cause . span } ,
239
+ )
240
+ . is_break ( )
241
+ } )
242
+ } )
243
+ }
244
+ }
245
+
246
+ struct StalledOnCoroutines < ' tcx > {
247
+ stalled_generators : & ' tcx ty:: List < LocalDefId > ,
248
+ span : Span ,
249
+ // TODO: Cache
250
+ }
251
+
252
+ impl < ' tcx > inspect:: ProofTreeVisitor < ' tcx > for StalledOnCoroutines < ' tcx > {
253
+ type Result = ControlFlow < ( ) > ;
254
+
255
+ fn span ( & self ) -> rustc_span:: Span {
256
+ self . span
257
+ }
258
+
259
+ fn visit_goal ( & mut self , inspect_goal : & super :: inspect:: InspectGoal < ' _ , ' tcx > ) -> Self :: Result {
260
+ inspect_goal. goal ( ) . predicate . visit_with ( self ) ?;
261
+
262
+ if let Some ( candidate) = inspect_goal. unique_applicable_candidate ( ) {
263
+ candidate. visit_nested_no_probe ( self )
264
+ } else {
265
+ ControlFlow :: Continue ( ( ) )
266
+ }
267
+ }
268
+ }
269
+
270
+ impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for StalledOnCoroutines < ' tcx > {
271
+ type Result = ControlFlow < ( ) > ;
272
+
273
+ fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> Self :: Result {
274
+ if let ty:: CoroutineWitness ( def_id, _) = * ty. kind ( )
275
+ && def_id. as_local ( ) . is_some_and ( |def_id| self . stalled_generators . contains ( & def_id) )
276
+ {
277
+ return ControlFlow :: Break ( ( ) ) ;
278
+ }
279
+
280
+ ty. super_visit_with ( self )
208
281
}
209
282
}
210
283
0 commit comments