@@ -541,23 +541,33 @@ struct BacktrackFrame<'a> {
541
541
#[ derive( Clone ) ]
542
542
struct RemainingCandidates {
543
543
remaining : RcVecIter < Candidate > ,
544
+ // note: change to RcList or something if clone is to expensive
545
+ conflicting_prev_active : HashSet < PackageId > ,
544
546
}
545
547
546
548
impl RemainingCandidates {
547
- fn next ( & mut self , prev_active : & [ Summary ] ) -> Option < Candidate > {
549
+ fn next ( & mut self , prev_active : & [ Summary ] ) -> Result < Candidate , HashSet < PackageId > > {
548
550
// Filter the set of candidates based on the previously activated
549
551
// versions for this dependency. We can actually use a version if it
550
552
// precisely matches an activated version or if it is otherwise
551
553
// incompatible with all other activated versions. Note that we
552
554
// define "compatible" here in terms of the semver sense where if
553
555
// the left-most nonzero digit is the same they're considered
554
556
// compatible.
555
- self . remaining . by_ref ( ) . map ( |p| p. 1 ) . find ( |b| {
556
- prev_active. iter ( ) . any ( |a| * a == b. summary ) ||
557
- prev_active. iter ( ) . all ( |a| {
558
- !compatible ( a. version ( ) , b. summary . version ( ) )
559
- } )
560
- } )
557
+ //
558
+ // When we are done we return the set of previously activated
559
+ // that conflicted with the ones we tried. If any of these change
560
+ // then we would have considered different candidates.
561
+ for ( _, b) in self . remaining . by_ref ( ) {
562
+ if let Some ( a) = prev_active. iter ( ) . find ( |a| compatible ( a. version ( ) , b. summary . version ( ) ) ) {
563
+ if * a != b. summary {
564
+ self . conflicting_prev_active . insert ( a. package_id ( ) . clone ( ) ) ;
565
+ continue
566
+ }
567
+ }
568
+ return Ok ( b) ;
569
+ }
570
+ Err ( self . conflicting_prev_active . clone ( ) )
561
571
}
562
572
}
563
573
@@ -650,9 +660,10 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
650
660
dep. name( ) , prev_active. len( ) ) ;
651
661
let mut candidates = RemainingCandidates {
652
662
remaining : RcVecIter :: new ( Rc :: clone ( & candidates) ) ,
663
+ conflicting_prev_active : HashSet :: new ( ) ,
653
664
} ;
654
665
( candidates. next ( prev_active) ,
655
- candidates. clone ( ) . next ( prev_active) . is_some ( ) ,
666
+ candidates. clone ( ) . next ( prev_active) . is_ok ( ) ,
656
667
candidates)
657
668
} ;
658
669
@@ -669,7 +680,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
669
680
// turn. We could possibly fail to activate each candidate, so we try
670
681
// each one in turn.
671
682
let candidate = match next {
672
- Some ( candidate) => {
683
+ Ok ( candidate) => {
673
684
// We have a candidate. Add an entry to the `backtrack_stack` so
674
685
// we can try the next one if this one fails.
675
686
if has_another {
@@ -685,7 +696,7 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
685
696
}
686
697
candidate
687
698
}
688
- None => {
699
+ Err ( mut conflicting ) => {
689
700
// This dependency has no valid candidate. Backtrack until we
690
701
// find a dependency that does have a candidate to try, and try
691
702
// to activate that one. This resets the `remaining_deps` to
@@ -698,10 +709,11 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
698
709
& mut parent,
699
710
& mut cur,
700
711
& mut dep,
701
- & mut features) {
712
+ & mut features,
713
+ & mut conflicting) {
702
714
None => return Err ( activation_error ( & cx, registry, & parent,
703
715
& dep,
704
- cx . prev_active ( & dep ) ,
716
+ conflicting ,
705
717
& candidates, config) ) ,
706
718
Some ( candidate) => candidate,
707
719
}
@@ -725,39 +737,44 @@ fn activate_deps_loop<'a>(mut cx: Context<'a>,
725
737
Ok ( cx)
726
738
}
727
739
728
- // Looks through the states in `backtrack_stack` for dependencies with
729
- // remaining candidates. For each one, also checks if rolling back
730
- // could change the outcome of the failed resolution that caused backtracking
731
- // in the first place - namely, if we've backtracked past the parent of the
732
- // failed dep, or the previous number of activations of the failed dep has
733
- // changed (possibly relaxing version constraints). If the outcome could differ,
734
- // resets `cx` and `remaining_deps` to that level and returns the
735
- // next candidate. If all candidates have been exhausted, returns None.
736
- // Read https://github.com/rust-lang/cargo/pull/4834#issuecomment-362871537 for
737
- // a more detailed explanation of the logic here.
738
- fn find_candidate < ' a > ( backtrack_stack : & mut Vec < BacktrackFrame < ' a > > ,
739
- cx : & mut Context < ' a > ,
740
- remaining_deps : & mut BinaryHeap < DepsFrame > ,
741
- parent : & mut Summary ,
742
- cur : & mut usize ,
743
- dep : & mut Dependency ,
744
- features : & mut Rc < Vec < String > > ) -> Option < Candidate > {
745
- let num_dep_prev_active = cx. prev_active ( dep) . len ( ) ;
740
+ /// Looks through the states in `backtrack_stack` for dependencies with
741
+ /// remaining candidates. For each one, also checks if rolling back
742
+ /// could change the outcome of the failed resolution that caused backtracking
743
+ /// in the first place. Namely, if we've backtracked past the parent of the
744
+ /// failed dep, or any of the packages flagged as giving us trouble in conflicting_activations.
745
+ /// Read https://github.com/rust-lang/cargo/pull/4834
746
+ /// For several more detailed explanations of the logic here.
747
+ ///
748
+ /// If the outcome could differ, resets `cx` and `remaining_deps` to that
749
+ /// level and returns the next candidate.
750
+ /// If all candidates have been exhausted, returns None.
751
+ fn find_candidate < ' a > (
752
+ backtrack_stack : & mut Vec < BacktrackFrame < ' a > > ,
753
+ cx : & mut Context < ' a > ,
754
+ remaining_deps : & mut BinaryHeap < DepsFrame > ,
755
+ parent : & mut Summary ,
756
+ cur : & mut usize ,
757
+ dep : & mut Dependency ,
758
+ features : & mut Rc < Vec < String > > ,
759
+ conflicting_activations : & mut HashSet < PackageId > ,
760
+ ) -> Option < Candidate > {
746
761
while let Some ( mut frame) = backtrack_stack. pop ( ) {
747
762
let ( next, has_another) = {
748
763
let prev_active = frame. context_backup . prev_active ( & frame. dep ) ;
749
- ( frame. remaining_candidates . next ( prev_active) ,
750
- frame. remaining_candidates . clone ( ) . next ( prev_active) . is_some ( ) )
764
+ (
765
+ frame. remaining_candidates . next ( prev_active) ,
766
+ frame. remaining_candidates . clone ( ) . next ( prev_active) . is_ok ( ) ,
767
+ )
751
768
} ;
752
- let cur_num_dep_prev_active = frame. context_backup . prev_active ( dep ) . len ( ) ;
753
- // Activations should monotonically decrease during backtracking
754
- assert ! ( cur_num_dep_prev_active <= num_dep_prev_active ) ;
755
- let maychange = !frame . context_backup . is_active ( parent ) ||
756
- cur_num_dep_prev_active != num_dep_prev_active ;
757
- if !maychange {
758
- continue
769
+ if frame. context_backup . is_active ( parent . package_id ( ) )
770
+ && conflicting_activations
771
+ . iter ( )
772
+ // note: a lot of redundant work in is_active for similar debs
773
+ . all ( |con| frame . context_backup . is_active ( con ) )
774
+ {
775
+ continue ;
759
776
}
760
- if let Some ( candidate) = next {
777
+ if let Ok ( candidate) = next {
761
778
* cur = frame. cur ;
762
779
if has_another {
763
780
* cx = frame. context_backup . clone ( ) ;
@@ -773,7 +790,7 @@ fn find_candidate<'a>(backtrack_stack: &mut Vec<BacktrackFrame<'a>>,
773
790
* dep = frame. dep ;
774
791
* features = frame. features ;
775
792
}
776
- return Some ( candidate)
793
+ return Some ( candidate) ;
777
794
}
778
795
}
779
796
None
@@ -783,7 +800,7 @@ fn activation_error(cx: &Context,
783
800
registry : & mut Registry ,
784
801
parent : & Summary ,
785
802
dep : & Dependency ,
786
- prev_active : & [ Summary ] ,
803
+ conflicting_activations : HashSet < PackageId > ,
787
804
candidates : & [ Candidate ] ,
788
805
config : Option < & Config > ) -> CargoError {
789
806
let graph = cx. graph ( ) ;
@@ -799,29 +816,31 @@ fn activation_error(cx: &Context,
799
816
dep_path_desc
800
817
} ;
801
818
if !candidates. is_empty ( ) {
802
- let mut msg = format ! ( "failed to select a version for `{0}` \n \
819
+ let mut msg = format ! ( "failed to select a version for `{}`. \n \
803
820
all possible versions conflict with \
804
- previously selected versions of `{0}` \n ",
821
+ previously selected packages. \n ",
805
822
dep. name( ) ) ;
806
823
msg. push_str ( "required by " ) ;
807
824
msg. push_str ( & describe_path ( parent. package_id ( ) ) ) ;
808
- for v in prev_active. iter ( ) {
825
+ let mut conflicting_activations: Vec < _ > = conflicting_activations. iter ( ) . collect ( ) ;
826
+ conflicting_activations. sort_unstable ( ) ;
827
+ for v in conflicting_activations. iter ( ) . rev ( ) {
809
828
msg. push_str ( "\n previously selected " ) ;
810
- msg. push_str ( & describe_path ( v. package_id ( ) ) ) ;
829
+ msg. push_str ( & describe_path ( v) ) ;
811
830
}
812
831
813
- msg. push_str ( & format ! ( "\n possible versions to select: {}" ,
814
- candidates. iter( )
815
- . map( |v| v. summary. version( ) )
816
- . map( |v| v. to_string( ) )
817
- . collect:: <Vec <_>>( )
818
- . join( ", " ) ) ) ;
832
+ msg. push_str ( "\n possible versions to select: " ) ;
833
+ msg . push_str ( & candidates. iter ( )
834
+ . map ( |v| v. summary . version ( ) )
835
+ . map ( |v| v. to_string ( ) )
836
+ . collect :: < Vec < _ > > ( )
837
+ . join ( ", " ) ) ;
819
838
820
839
return format_err ! ( "{}" , msg)
821
840
}
822
841
823
842
// Once we're all the way down here, we're definitely lost in the
824
- // weeds! We didn't actually use any candidates above , so we need to
843
+ // weeds! We didn't actually find any candidates, so we need to
825
844
// give an error message that nothing was found.
826
845
//
827
846
// Note that we re-query the registry with a new dependency that
@@ -834,7 +853,7 @@ fn activation_error(cx: &Context,
834
853
Ok ( candidates) => candidates,
835
854
Err ( e) => return e,
836
855
} ;
837
- candidates. sort_by ( |a, b| {
856
+ candidates. sort_unstable_by ( |a, b| {
838
857
b. version ( ) . cmp ( a. version ( ) )
839
858
} ) ;
840
859
@@ -1172,11 +1191,10 @@ impl<'a> Context<'a> {
1172
1191
. unwrap_or ( & [ ] )
1173
1192
}
1174
1193
1175
- fn is_active ( & mut self , summary : & Summary ) -> bool {
1176
- let id = summary. package_id ( ) ;
1194
+ fn is_active ( & mut self , id : & PackageId ) -> bool {
1177
1195
self . activations . get ( id. name ( ) )
1178
1196
. and_then ( |v| v. get ( id. source_id ( ) ) )
1179
- . map ( |v| v. iter ( ) . any ( |s| s == summary ) )
1197
+ . map ( |v| v. iter ( ) . any ( |s| s. package_id ( ) == id ) )
1180
1198
. unwrap_or ( false )
1181
1199
}
1182
1200
0 commit comments