@@ -788,6 +788,17 @@ fn activate_deps_loop(
788
788
// use (those with more candidates).
789
789
let mut backtrack_stack = Vec :: new ( ) ;
790
790
let mut remaining_deps = BinaryHeap :: new ( ) ;
791
+ // `past_conflicting_activations`is a cache of the reasons for each time we backtrack.
792
+ // for example after several backtracks we may have:
793
+ // past_conflicting_activations[`foo = "^1.0.2"`] = vac![map!{`foo=1.0.1`: Semver}, map!{`foo=1.0.1`: Semver}];
794
+ // This can be read as "we cannot find a candidate for dep `foo = "^1.0.2"` if either
795
+ // `foo=1.0.1` OR `foo=1.0.0` are activated"
796
+ // for another example after several backtracks we may have:
797
+ // past_conflicting_activations[`foo = ">=0.8.2, <=0.9.3"`] = vac![map!{`foo=0.8.1`: Semver, `foo=0.9.4`: Semver}];
798
+ // This can be read as "we cannot find a candidate for dep `foo = ">=0.8.2, <=0.9.3"` if both
799
+ // `foo=0.8.1` AND `foo=0.9.4` are activated" (better data structures are welcome but this works for now.)
800
+ // This is used to make sure we don't queue work we know will fail.
801
+ // See the discussion in https://github.com/rust-lang/cargo/pull/5168 for why this is so important
791
802
let mut past_conflicting_activations: HashMap < Dependency , Vec < HashMap < PackageId , ConflictReason > > > = HashMap :: new ( ) ;
792
803
for & ( ref summary, ref method) in summaries {
793
804
debug ! ( "initial activation: {}" , summary. package_id( ) ) ;
@@ -874,8 +885,15 @@ fn activate_deps_loop(
874
885
875
886
let mut remaining_candidates = RemainingCandidates :: new ( & candidates) ;
876
887
let mut successfully_activated = false ;
877
- let mut backtracked = false ;
888
+ // `conflicting_activations` stores all the reasons we were unable to activate candidates.
889
+ // One of these reasons will have to go away for backtracking to find a place to restart.
890
+ // It is also the list of things to explain in the error message if we fail to resolve.
878
891
let mut conflicting_activations = HashMap :: new ( ) ;
892
+ // When backtracking we don't fully update `conflicting_activations` especially for the
893
+ // cases that we didn't make a backtrack frame in the first place.
894
+ // This `backtracked` var stores whether we are continuing from a restored backtrack frame
895
+ // so that we can skip caching `conflicting_activations` in `past_conflicting_activations`
896
+ let mut backtracked = false ;
879
897
880
898
while !successfully_activated {
881
899
let next = remaining_candidates. next ( cx. prev_active ( & dep) , & cx. links ) ;
@@ -900,6 +918,8 @@ fn activate_deps_loop(
900
918
trace ! ( "{}[{}]>{} -- no candidates" , parent. name( ) , cur, dep. name( ) ) ;
901
919
902
920
if !just_here_for_the_error_messages && !backtracked {
921
+ // if `just_here_for_the_error_messages` then skip as it is already known to be bad.
922
+ // if `backtracked` then `conflicting_activations` may not be complete so skip.
903
923
let past = past_conflicting_activations. entry ( dep. clone ( ) ) . or_insert_with ( Vec :: new) ;
904
924
if !past. contains ( & conflicting_activations) {
905
925
trace ! ( "{}[{}]>{} adding a skip {:?}" , parent. name( ) , cur, dep. name( ) , conflicting_activations) ;
@@ -972,25 +992,31 @@ fn activate_deps_loop(
972
992
973
993
match res {
974
994
Ok ( Some ( ( mut frame, dur) ) ) => {
995
+ // at this point we have technical already activated
996
+ // but we may want to scrap it if it is not going to end well
975
997
let mut has_past_conflicting_dep = just_here_for_the_error_messages;
976
998
if !has_past_conflicting_dep {
977
999
if let Some ( conflicting) = frame. remaining_siblings . clone ( ) . filter_map ( |( _, ( deb, _, _) ) | {
978
1000
past_conflicting_activations. get ( & deb) . and_then ( |past_bad| {
1001
+ // for each dependency check all of its cashed conflicts
979
1002
past_bad. iter ( ) . find ( |conflicting| {
980
1003
conflicting
981
1004
. iter ( )
982
1005
// note: a lot of redundant work in is_active for similar debs
983
- . all ( |( con, _) | cx. is_active ( con) || * con == pid )
1006
+ . all ( |( con, _) | cx. is_active ( con) )
984
1007
} )
985
1008
} )
986
1009
} ) . next ( ) {
1010
+ // if any of them match than it will just backtrack to us
1011
+ // so let's save the effort.
987
1012
conflicting_activations. extend ( conflicting. clone ( ) ) ;
988
1013
has_past_conflicting_dep = true ;
989
1014
}
990
1015
}
991
1016
if !has_another && has_past_conflicting_dep && !backtracked {
992
1017
// we have not activated ANY candidates and
993
- // we are out of choices
1018
+ // we are out of choices so add it to the cache
1019
+ // so our parent will know that we don't work
994
1020
let past = past_conflicting_activations. entry ( dep. clone ( ) ) . or_insert_with ( Vec :: new) ;
995
1021
if !past. contains ( & conflicting_activations) {
996
1022
trace ! ( "{}[{}]>{} adding a meta-skip {:?}" , parent. name( ) , cur, dep. name( ) , conflicting_activations) ;
0 commit comments