42
42
//! now considered to be in error.
43
43
//!
44
44
//! When the call to `process_obligations` completes, you get back an `Outcome`,
45
- //! which includes three bits of information:
45
+ //! which includes two bits of information:
46
46
//!
47
47
//! - `completed`: a list of obligations where processing was fully
48
48
//! completed without error (meaning that all transitive subobligations
53
53
//! all the obligations in `C` have been found completed.
54
54
//! - `errors`: a list of errors that occurred and associated backtraces
55
55
//! at the time of error, which can be used to give context to the user.
56
- //! - `stalled`: if true, then none of the existing obligations were
57
- //! *shallowly successful* (that is, no callback returned `Changed(_)`).
58
- //! This implies that all obligations were either errors or returned an
59
- //! ambiguous result, which means that any further calls to
60
- //! `process_obligations` would simply yield back further ambiguous
61
- //! results. This is used by the `FulfillmentContext` to decide when it
62
- //! has reached a steady state.
56
+ //!
57
+ //! Upon completion, none of the existing obligations were *shallowly
58
+ //! successful* (that is, no callback returned `Changed(_)`). This implies that
59
+ //! all obligations were either errors or returned an ambiguous result.
63
60
//!
64
61
//! ### Implementation details
65
62
//!
@@ -260,8 +257,6 @@ pub trait OutcomeTrait {
260
257
type Obligation ;
261
258
262
259
fn new ( ) -> Self ;
263
- fn mark_not_stalled ( & mut self ) ;
264
- fn is_stalled ( & self ) -> bool ;
265
260
fn record_completed ( & mut self , outcome : & Self :: Obligation ) ;
266
261
fn record_error ( & mut self , error : Self :: Error ) ;
267
262
}
@@ -270,30 +265,14 @@ pub trait OutcomeTrait {
270
265
pub struct Outcome < O , E > {
271
266
/// Backtrace of obligations that were found to be in error.
272
267
pub errors : Vec < Error < O , E > > ,
273
-
274
- /// If true, then we saw no successful obligations, which means
275
- /// there is no point in further iteration. This is based on the
276
- /// assumption that when trait matching returns `Error` or
277
- /// `Unchanged`, those results do not affect environmental
278
- /// inference state. (Note that if we invoke `process_obligations`
279
- /// with no pending obligations, stalled will be true.)
280
- pub stalled : bool ,
281
268
}
282
269
283
270
impl < O , E > OutcomeTrait for Outcome < O , E > {
284
271
type Error = Error < O , E > ;
285
272
type Obligation = O ;
286
273
287
274
fn new ( ) -> Self {
288
- Self { stalled : true , errors : vec ! [ ] }
289
- }
290
-
291
- fn mark_not_stalled ( & mut self ) {
292
- self . stalled = false ;
293
- }
294
-
295
- fn is_stalled ( & self ) -> bool {
296
- self . stalled
275
+ Self { errors : vec ! [ ] }
297
276
}
298
277
299
278
fn record_completed ( & mut self , _outcome : & Self :: Obligation ) {
@@ -415,10 +394,7 @@ impl<O: ForestObligation> ObligationForest<O> {
415
394
. insert ( node. obligation . as_cache_key ( ) ) ;
416
395
}
417
396
418
- /// Performs a pass through the obligation list. This must
419
- /// be called in a loop until `outcome.stalled` is false.
420
- ///
421
- /// This _cannot_ be unrolled (presently, at least).
397
+ /// Performs a fixpoint computation over the obligation list.
422
398
#[ inline( never) ]
423
399
pub fn process_obligations < P , OUT > ( & mut self , processor : & mut P ) -> OUT
424
400
where
@@ -427,55 +403,66 @@ impl<O: ForestObligation> ObligationForest<O> {
427
403
{
428
404
let mut outcome = OUT :: new ( ) ;
429
405
430
- // Note that the loop body can append new nodes, and those new nodes
431
- // will then be processed by subsequent iterations of the loop.
432
- //
433
- // We can't use an iterator for the loop because `self.nodes` is
434
- // appended to and the borrow checker would complain. We also can't use
435
- // `for index in 0..self.nodes.len() { ... }` because the range would
436
- // be computed with the initial length, and we would miss the appended
437
- // nodes. Therefore we use a `while` loop.
438
- let mut index = 0 ;
439
- while let Some ( node) = self . nodes . get_mut ( index) {
440
- // `processor.process_obligation` can modify the predicate within
441
- // `node.obligation`, and that predicate is the key used for
442
- // `self.active_cache`. This means that `self.active_cache` can get
443
- // out of sync with `nodes`. It's not very common, but it does
444
- // happen, and code in `compress` has to allow for it.
445
- if node. state . get ( ) != NodeState :: Pending {
446
- index += 1 ;
447
- continue ;
448
- }
449
-
450
- match processor. process_obligation ( & mut node. obligation ) {
451
- ProcessResult :: Unchanged => {
452
- // No change in state.
406
+ // Fixpoint computation: we repeat until the inner loop stalls.
407
+ loop {
408
+ let mut has_changed = false ;
409
+
410
+ // Note that the loop body can append new nodes, and those new nodes
411
+ // will then be processed by subsequent iterations of the loop.
412
+ //
413
+ // We can't use an iterator for the loop because `self.nodes` is
414
+ // appended to and the borrow checker would complain. We also can't use
415
+ // `for index in 0..self.nodes.len() { ... }` because the range would
416
+ // be computed with the initial length, and we would miss the appended
417
+ // nodes. Therefore we use a `while` loop.
418
+ let mut index = 0 ;
419
+ while let Some ( node) = self . nodes . get_mut ( index) {
420
+ // `processor.process_obligation` can modify the predicate within
421
+ // `node.obligation`, and that predicate is the key used for
422
+ // `self.active_cache`. This means that `self.active_cache` can get
423
+ // out of sync with `nodes`. It's not very common, but it does
424
+ // happen, and code in `compress` has to allow for it.
425
+ if node. state . get ( ) != NodeState :: Pending {
426
+ index += 1 ;
427
+ continue ;
453
428
}
454
- ProcessResult :: Changed ( children) => {
455
- // We are not (yet) stalled.
456
- outcome. mark_not_stalled ( ) ;
457
- node. state . set ( NodeState :: Success ) ;
458
-
459
- for child in children {
460
- let st = self . register_obligation_at ( child, Some ( index) ) ;
461
- if let Err ( ( ) ) = st {
462
- // Error already reported - propagate it
463
- // to our node.
464
- self . error_at ( index) ;
429
+
430
+ match processor. process_obligation ( & mut node. obligation ) {
431
+ ProcessResult :: Unchanged => {
432
+ // No change in state.
433
+ }
434
+ ProcessResult :: Changed ( children) => {
435
+ // We are not (yet) stalled.
436
+ has_changed = true ;
437
+ node. state . set ( NodeState :: Success ) ;
438
+
439
+ for child in children {
440
+ let st = self . register_obligation_at ( child, Some ( index) ) ;
441
+ if let Err ( ( ) ) = st {
442
+ // Error already reported - propagate it
443
+ // to our node.
444
+ self . error_at ( index) ;
445
+ }
465
446
}
466
447
}
448
+ ProcessResult :: Error ( err) => {
449
+ has_changed = true ;
450
+ outcome. record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
451
+ }
467
452
}
468
- ProcessResult :: Error ( err) => {
469
- outcome. mark_not_stalled ( ) ;
470
- outcome. record_error ( Error { error : err, backtrace : self . error_at ( index) } ) ;
471
- }
453
+ index += 1 ;
454
+ }
455
+
456
+ // If unchanged, then we saw no successful obligations, which means
457
+ // there is no point in further iteration. This is based on the
458
+ // assumption that when trait matching returns `Error` or
459
+ // `Unchanged`, those results do not affect environmental inference
460
+ // state. (Note that this will occur if we invoke
461
+ // `process_obligations` with no pending obligations.)
462
+ if !has_changed {
463
+ break ;
472
464
}
473
- index += 1 ;
474
- }
475
465
476
- // There's no need to perform marking, cycle processing and compression when nothing
477
- // changed.
478
- if !outcome. is_stalled ( ) {
479
466
self . mark_successes ( ) ;
480
467
self . process_cycles ( processor) ;
481
468
self . compress ( |obl| outcome. record_completed ( obl) ) ;
0 commit comments