@@ -260,16 +260,8 @@ fn activate_deps_loop(
260
260
. conflicting ( & resolver_ctx, & dep)
261
261
. is_some ( ) ;
262
262
263
- let mut remaining_candidates = RemainingCandidates :: new ( & candidates) ;
264
-
265
- // `conflicting_activations` stores all the reasons we were unable to
266
- // activate candidates. One of these reasons will have to go away for
267
- // backtracking to find a place to restart. It is also the list of
268
- // things to explain in the error message if we fail to resolve.
269
- //
270
- // This is a map of package ID to a reason why that packaged caused a
271
- // conflict for us.
272
- let mut conflicting_activations = ConflictMap :: new ( ) ;
263
+ let ( mut remaining_candidates, mut conflicting_activations) =
264
+ RemainingCandidates :: new ( & candidates, & resolver_ctx) ;
273
265
274
266
// When backtracking we don't fully update `conflicting_activations`
275
267
// especially for the cases that we didn't make a backtrack frame in the
@@ -279,7 +271,7 @@ fn activate_deps_loop(
279
271
let mut backtracked = false ;
280
272
281
273
loop {
282
- let next = remaining_candidates. next ( & mut conflicting_activations , & resolver_ctx ) ;
274
+ let next = remaining_candidates. next ( ) ;
283
275
284
276
let ( candidate, has_another) = next. ok_or ( ( ) ) . or_else ( |_| {
285
277
// If we get here then our `remaining_candidates` was just
@@ -712,47 +704,38 @@ struct BacktrackFrame {
712
704
/// more inputs) but in general acts like one. Each `RemainingCandidates` is
713
705
/// created with a list of candidates to choose from. When attempting to iterate
714
706
/// over the list of candidates only *valid* candidates are returned. Validity
715
- /// is defined within a `Context`.
707
+ /// is defined within a `Context`. In addition, the iterator will return
708
+ /// candidates that already have been activated first.
716
709
///
717
710
/// Candidates passed to `new` may not be returned from `next` as they could be
718
- /// filtered out, and as they are filtered the causes will be added to `conflicting_prev_active `.
711
+ /// filtered out. The filtered candidates and the causes will be returned by `new `.
719
712
#[ derive( Clone ) ]
720
713
struct RemainingCandidates {
721
- remaining : RcVecIter < Summary > ,
714
+ prioritized_candidates : RcVecIter < Summary > ,
722
715
// This is an inlined peekable generator
723
716
has_another : Option < Summary > ,
724
717
}
725
718
726
719
impl RemainingCandidates {
727
- fn new ( candidates : & Rc < Vec < Summary > > ) -> RemainingCandidates {
728
- RemainingCandidates {
729
- remaining : RcVecIter :: new ( Rc :: clone ( candidates) ) ,
730
- has_another : None ,
731
- }
732
- }
733
-
734
- /// Attempts to find another candidate to check from this list.
735
- ///
736
- /// This method will attempt to move this iterator forward, returning a
737
- /// candidate that's possible to activate. The `cx` argument is the current
738
- /// context which determines validity for candidates returned, and the `dep`
739
- /// is the dependency listing that we're activating for.
740
- ///
741
- /// If successful a `(Candidate, bool)` pair will be returned. The
742
- /// `Candidate` is the candidate to attempt to activate, and the `bool` is
743
- /// an indicator of whether there are remaining candidates to try of if
744
- /// we've reached the end of iteration.
745
- ///
746
- /// If we've reached the end of the iterator here then `Err` will be
747
- /// returned. The error will contain a map of package ID to conflict reason,
748
- /// where each package ID caused a candidate to be filtered out from the
749
- /// original list for the reason listed.
750
- fn next (
751
- & mut self ,
752
- conflicting_prev_active : & mut ConflictMap ,
720
+ /// Prefilters and sorts the list of candidates to determine which ones are
721
+ /// valid to activate, and which ones should be prioritized.
722
+ fn new (
723
+ candidates : & Rc < Vec < Summary > > ,
753
724
cx : & ResolverContext ,
754
- ) -> Option < ( Summary , bool ) > {
755
- for b in self . remaining . by_ref ( ) {
725
+ ) -> ( RemainingCandidates , ConflictMap ) {
726
+ // `conflicting_activations` stores all the reasons we were unable to
727
+ // activate candidates. One of these reasons will have to go away for
728
+ // backtracking to find a place to restart. It is also the list of
729
+ // things to explain in the error message if we fail to resolve.
730
+ //
731
+ // This is a map of package ID to a reason why that packaged caused a
732
+ // conflict for us.
733
+ let mut conflicting_activations = ConflictMap :: new ( ) ;
734
+
735
+ let mut activated_candidates: Vec < Summary > = Vec :: with_capacity ( candidates. len ( ) ) ;
736
+ let mut non_activated_candidates: Vec < Summary > = Vec :: with_capacity ( candidates. len ( ) ) ;
737
+
738
+ for b in candidates. as_ref ( ) {
756
739
let b_id = b. package_id ( ) ;
757
740
// The `links` key in the manifest dictates that there's only one
758
741
// package in a dependency graph, globally, with that particular
@@ -761,7 +744,7 @@ impl RemainingCandidates {
761
744
if let Some ( link) = b. links ( ) {
762
745
if let Some ( & a) = cx. links . get ( & link) {
763
746
if a != b_id {
764
- conflicting_prev_active
747
+ conflicting_activations
765
748
. entry ( a)
766
749
. or_insert_with ( || ConflictReason :: Links ( link) ) ;
767
750
continue ;
@@ -774,18 +757,50 @@ impl RemainingCandidates {
774
757
// semver-compatible versions of a crate. For example we can't
775
758
// simultaneously activate `foo 1.0.2` and `foo 1.2.0`. We can,
776
759
// however, activate `1.0.2` and `2.0.0`.
777
- //
778
- // Here we throw out our candidate if it's *compatible*, yet not
779
- // equal, to all previously activated versions.
780
760
if let Some ( ( a, _) ) = cx. activations . get ( & b_id. as_activations_key ( ) ) {
781
- if * a != b {
782
- conflicting_prev_active
761
+ // If this candidate is already activated, then we want to put
762
+ // it in our prioritized list to try first.
763
+ if a == b {
764
+ activated_candidates. push ( b. clone ( ) ) ;
765
+ continue ;
766
+ }
767
+ // Here we throw out our candidate if it's *compatible*, yet not
768
+ // equal, to all previously activated versions.
769
+ else {
770
+ conflicting_activations
783
771
. entry ( a. package_id ( ) )
784
772
. or_insert ( ConflictReason :: Semver ) ;
785
773
continue ;
786
774
}
775
+ } else {
776
+ non_activated_candidates. push ( b. clone ( ) ) ;
787
777
}
778
+ }
788
779
780
+ // Combine the prioritized and non-prioritized candidates into one list
781
+ // such that the prioritized candidates are tried first.
782
+ activated_candidates. append ( & mut non_activated_candidates) ;
783
+
784
+ (
785
+ RemainingCandidates {
786
+ prioritized_candidates : RcVecIter :: new ( Rc :: new ( activated_candidates) ) ,
787
+ has_another : None ,
788
+ } ,
789
+ conflicting_activations,
790
+ )
791
+ }
792
+
793
+ /// Attempts to find another candidate to check from this list.
794
+ ///
795
+ /// This method will attempt to move this iterator forward, returning a
796
+ /// candidate that's possible to activate.
797
+ ///
798
+ /// If successful a `(Candidate, bool)` pair will be returned. The
799
+ /// `Candidate` is the candidate to attempt to activate, and the `bool` is
800
+ /// an indicator of whether there are remaining candidates to try of if
801
+ /// we've reached the end of iteration.
802
+ fn next ( & mut self ) -> Option < ( Summary , bool ) > {
803
+ for b in self . prioritized_candidates . by_ref ( ) {
789
804
// Well if we made it this far then we've got a valid dependency. We
790
805
// want this iterator to be inherently "peekable" so we don't
791
806
// necessarily return the item just yet. Instead we stash it away to
@@ -961,9 +976,7 @@ fn find_candidate(
961
976
} ;
962
977
963
978
while let Some ( mut frame) = backtrack_stack. pop ( ) {
964
- let next = frame
965
- . remaining_candidates
966
- . next ( & mut frame. conflicting_activations , & frame. context ) ;
979
+ let next = frame. remaining_candidates . next ( ) ;
967
980
let Some ( ( candidate, has_another) ) = next else {
968
981
continue ;
969
982
} ;
0 commit comments