@@ -477,8 +477,12 @@ impl<'a> Display for DisplaySymbolStackKey<'a> {
477477/// [`find_all_complete_paths`]: #method.find_all_complete_paths
478478pub struct PathStitcher {
479479 candidate_paths : Vec < Handle < PartialPath > > ,
480- queue : VecDeque < Path > ,
481- next_iteration : VecDeque < Path > ,
480+ queue : VecDeque < ( Path , AppendingCycleDetector < OwnedOrDatabasePath > ) > ,
481+ next_iteration : (
482+ VecDeque < Path > ,
483+ VecDeque < AppendingCycleDetector < OwnedOrDatabasePath > > ,
484+ ) ,
485+ appended_paths : Appendages < OwnedOrDatabasePath > ,
482486 cycle_detector : SimilarPathDetector < Path > ,
483487 max_work_per_phase : usize ,
484488 #[ cfg( feature = "copious-debugging" ) ]
@@ -513,17 +517,24 @@ impl PathStitcher {
513517 copious_debugging ! ( " Initial node {}" , node. display( graph) ) ;
514518 db. find_candidate_partial_paths_from_node ( graph, partials, node, & mut candidate_paths) ;
515519 }
520+ let mut appended_paths = Appendages :: new ( ) ;
516521 let next_iteration = candidate_paths
517522 . iter ( )
518523 . filter_map ( |partial_path| {
519- Path :: from_partial_path ( graph, paths, partials, & db[ * partial_path] )
524+ Path :: from_partial_path ( graph, paths, partials, & db[ * partial_path] ) . map ( |p| {
525+ (
526+ p,
527+ AppendingCycleDetector :: from ( & mut appended_paths, partial_path. into ( ) ) ,
528+ )
529+ } )
520530 } )
521- . collect ( ) ;
531+ . unzip ( ) ;
522532 copious_debugging ! ( "==> End phase 0" ) ;
523533 PathStitcher {
524534 candidate_paths,
525535 queue : VecDeque :: new ( ) ,
526536 next_iteration,
537+ appended_paths,
527538 cycle_detector : SimilarPathDetector :: new ( ) ,
528539 // By default, there's no artificial bound on the amount of work done per phase
529540 max_work_per_phase : usize:: MAX ,
@@ -535,21 +546,21 @@ impl PathStitcher {
535546 /// Returns an iterator of all of the (possibly incomplete) paths that were encountered during
536547 /// the most recent phase of the path-stitching algorithm.
537548 pub fn previous_phase_paths ( & self ) -> impl Iterator < Item = & Path > + ' _ {
538- self . next_iteration . iter ( )
549+ self . next_iteration . 0 . iter ( )
539550 }
540551
541552 /// Returns a slice of all of the (possibly incomplete) paths that were encountered during the
542553 /// most recent phase of the path-stitching algorithm.
543554 pub fn previous_phase_paths_slice ( & mut self ) -> & [ Path ] {
544- self . next_iteration . make_contiguous ( ) ;
545- self . next_iteration . as_slices ( ) . 0
555+ self . next_iteration . 0 . make_contiguous ( ) ;
556+ self . next_iteration . 0 . as_slices ( ) . 0
546557 }
547558
548559 /// Returns a mutable slice of all of the (possibly incomplete) paths that were encountered
549560 /// during the most recent phase of the path-stitching algorithm.
550561 pub fn previous_phase_paths_slice_mut ( & mut self ) -> & mut [ Path ] {
551- self . next_iteration . make_contiguous ( ) ;
552- self . next_iteration . as_mut_slices ( ) . 0
562+ self . next_iteration . 0 . make_contiguous ( ) ;
563+ self . next_iteration . 0 . as_mut_slices ( ) . 0
553564 }
554565
555566 /// Sets the maximum amount of work that can be performed during each phase of the algorithm.
@@ -571,6 +582,7 @@ impl PathStitcher {
571582 partials : & mut PartialPaths ,
572583 db : & mut Database ,
573584 path : & Path ,
585+ cycle_detector : AppendingCycleDetector < OwnedOrDatabasePath > ,
574586 ) -> usize {
575587 copious_debugging ! ( "--> Candidate path {}" , path. display( graph, paths) ) ;
576588 self . candidate_paths . clear ( ) ;
@@ -592,29 +604,42 @@ impl PathStitcher {
592604 }
593605
594606 let extension_count = self . candidate_paths . len ( ) ;
595- self . next_iteration . reserve ( extension_count) ;
607+ self . next_iteration . 0 . reserve ( extension_count) ;
608+ self . next_iteration . 1 . reserve ( extension_count) ;
596609 for extension in & self . candidate_paths {
597- let extension = & db[ * extension] ;
610+ let extension_path = & db[ * extension] ;
598611 copious_debugging ! ( " Extend {}" , path. display( graph, paths) , ) ;
599- copious_debugging ! ( " with {}" , extension . display( graph, partials) ) ;
612+ copious_debugging ! ( " with {}" , extension_path . display( graph, partials) ) ;
600613 let mut new_path = path. clone ( ) ;
614+ let mut new_cycle_detector = cycle_detector. clone ( ) ;
601615 // If there are errors adding this partial path to the path, or resolving the resulting
602616 // path, just skip the partial path — it's not a fatal error.
603617 #[ cfg_attr( not( feature = "copious-debugging" ) , allow( unused_variables) ) ]
604- if let Err ( err) = new_path. append_partial_path ( graph, paths, partials, extension ) {
618+ if let Err ( err) = new_path. append_partial_path ( graph, paths, partials, extension_path ) {
605619 copious_debugging ! ( " is invalid: {:?}" , err) ;
606620 continue ;
607621 }
622+ new_cycle_detector. append ( & mut self . appended_paths , extension. into ( ) ) ;
623+ let cycles =
624+ new_cycle_detector. is_cyclic ( graph, partials, db, & mut self . appended_paths ) ;
625+ if !cycles
626+ . into_iter ( )
627+ . all ( |c| c == Cyclicity :: StrengthensPrecondition )
628+ {
629+ copious_debugging ! ( " is invalid: cyclic" ) ;
630+ continue ;
631+ }
608632 copious_debugging ! ( " is {}" , new_path. display( graph, paths) ) ;
609- self . next_iteration . push_back ( new_path) ;
633+ self . next_iteration . 0 . push_back ( new_path) ;
634+ self . next_iteration . 1 . push_back ( new_cycle_detector) ;
610635 }
611636
612637 extension_count
613638 }
614639
615640 /// Returns whether the path-stitching algorithm has completed.
616641 pub fn is_complete ( & self ) -> bool {
617- self . queue . is_empty ( ) && self . next_iteration . is_empty ( )
642+ self . queue . is_empty ( ) && self . next_iteration . 0 . is_empty ( )
618643 }
619644
620645 /// Runs the next phase of the path-stitching algorithm. We will have built up a set of
@@ -634,16 +659,21 @@ impl PathStitcher {
634659 db : & mut Database ,
635660 ) {
636661 copious_debugging ! ( "==> Start phase {}" , self . phase_number) ;
637- self . queue . extend ( self . next_iteration . drain ( ..) ) ;
662+ self . queue . extend (
663+ self . next_iteration
664+ . 0
665+ . drain ( ..)
666+ . zip ( self . next_iteration . 1 . drain ( ..) ) ,
667+ ) ;
638668 let mut work_performed = 0 ;
639- while let Some ( path) = self . queue . pop_front ( ) {
669+ while let Some ( ( path, cycle_detector ) ) = self . queue . pop_front ( ) {
640670 if !self
641671 . cycle_detector
642672 . should_process_path ( & path, |probe| probe. cmp ( graph, paths, & path) )
643673 {
644674 continue ;
645675 }
646- work_performed += self . stitch_path ( graph, paths, partials, db, & path) ;
676+ work_performed += self . stitch_path ( graph, paths, partials, db, & path, cycle_detector ) ;
647677 if work_performed >= self . max_work_per_phase {
648678 break ;
649679 }
@@ -795,8 +825,8 @@ impl ForwardPartialPathStitcher {
795825 candidate_partial_paths,
796826 queue : VecDeque :: new ( ) ,
797827 next_iteration,
798- similar_path_detector : SimilarPathDetector :: new ( ) ,
799828 appended_paths,
829+ similar_path_detector : SimilarPathDetector :: new ( ) ,
800830 // By default, there's no artificial bound on the amount of work done per phase
801831 max_work_per_phase : usize:: MAX ,
802832 #[ cfg( feature = "copious-debugging" ) ]
0 commit comments