1- use std:: cmp:: Reverse ;
2-
31use pubgrub:: Range ;
42use rustc_hash:: FxHashMap ;
3+ use std:: cmp:: Reverse ;
4+ use std:: collections:: hash_map:: OccupiedEntry ;
55
66use crate :: fork_urls:: ForkUrls ;
77use uv_normalize:: PackageName ;
@@ -40,19 +40,22 @@ impl PubGrubPriorities {
4040 match self . 0 . entry ( name. clone ( ) ) {
4141 std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
4242 // Preserve the original index.
43- let index = match entry. get ( ) {
44- PubGrubPriority :: Unspecified ( Reverse ( index) ) => * index,
45- PubGrubPriority :: Singleton ( Reverse ( index) ) => * index,
46- PubGrubPriority :: DirectUrl ( Reverse ( index) ) => * index,
47- PubGrubPriority :: Root => next,
48- } ;
43+ let index = Self :: get_index ( next, & mut entry) ;
4944
5045 // Compute the priority.
5146 let priority = if urls. get ( name) . is_some ( ) {
5247 PubGrubPriority :: DirectUrl ( Reverse ( index) )
5348 } else if version. as_singleton ( ) . is_some ( ) {
5449 PubGrubPriority :: Singleton ( Reverse ( index) )
5550 } else {
51+ // Keep the conflict-causing packages to avoid loops where we seesaw between
52+ // `Unspecified` and `Conflict*`.
53+ if matches ! (
54+ entry. get( ) ,
55+ PubGrubPriority :: ConflictEarly ( _) | PubGrubPriority :: ConflictLate ( _)
56+ ) {
57+ return ;
58+ }
5659 PubGrubPriority :: Unspecified ( Reverse ( index) )
5760 } ;
5861
@@ -77,6 +80,17 @@ impl PubGrubPriorities {
7780 }
7881 }
7982
83+ fn get_index ( next : usize , entry : & mut OccupiedEntry < PackageName , PubGrubPriority > ) -> usize {
84+ match entry. get ( ) {
85+ PubGrubPriority :: ConflictLate ( Reverse ( index) ) => * index,
86+ PubGrubPriority :: Unspecified ( Reverse ( index) ) => * index,
87+ PubGrubPriority :: ConflictEarly ( Reverse ( index) ) => * index,
88+ PubGrubPriority :: Singleton ( Reverse ( index) ) => * index,
89+ PubGrubPriority :: DirectUrl ( Reverse ( index) ) => * index,
90+ PubGrubPriority :: Root => next,
91+ }
92+ }
93+
8094 /// Return the [`PubGrubPriority`] of the given package, if it exists.
8195 pub ( crate ) fn get ( & self , package : & PubGrubPackage ) -> Option < PubGrubPriority > {
8296 match & * * package {
@@ -88,6 +102,66 @@ impl PubGrubPriorities {
88102 PubGrubPackageInner :: Package { name, .. } => self . 0 . get ( name) . copied ( ) ,
89103 }
90104 }
105+
106+ /// Returns whether the priority was changed, i.e., it's the first time we hit this condition
107+ /// for the package.
108+ pub ( crate ) fn make_conflict_early ( & mut self , package : & PubGrubPackage ) -> bool {
109+ let next = self . 0 . len ( ) ;
110+ let Some ( name) = package. name_no_root ( ) else {
111+ // Not a correctness bug
112+ assert ! (
113+ !cfg!( debug_assertions) ,
114+ "URL packages must not be involved in conflict handling"
115+ ) ;
116+ return false ;
117+ } ;
118+ match self . 0 . entry ( name. clone ( ) ) {
119+ std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
120+ if matches ! ( entry. get( ) , PubGrubPriority :: ConflictEarly ( _) ) {
121+ // Already in the right category
122+ return false ;
123+ } ;
124+ let index = Self :: get_index ( next, & mut entry) ;
125+ entry. insert ( PubGrubPriority :: ConflictEarly ( Reverse ( index) ) ) ;
126+ true
127+ }
128+ std:: collections:: hash_map:: Entry :: Vacant ( entry) => {
129+ entry. insert ( PubGrubPriority :: ConflictEarly ( Reverse ( next) ) ) ;
130+ true
131+ }
132+ }
133+ }
134+
135+ pub ( crate ) fn make_conflict_late ( & mut self , package : & PubGrubPackage ) -> bool {
136+ let next = self . 0 . len ( ) ;
137+ let Some ( name) = package. name_no_root ( ) else {
138+ // Not a correctness bug
139+ assert ! (
140+ !cfg!( debug_assertions) ,
141+ "URL packages must not be involved in conflict handling"
142+ ) ;
143+ return false ;
144+ } ;
145+ match self . 0 . entry ( name. clone ( ) ) {
146+ std:: collections:: hash_map:: Entry :: Occupied ( mut entry) => {
147+ // The ConflictEarly` match avoids infinite loops.
148+ if matches ! (
149+ entry. get( ) ,
150+ PubGrubPriority :: ConflictLate ( _) | PubGrubPriority :: ConflictEarly ( _)
151+ ) {
152+ // Already in the right category
153+ return false ;
154+ } ;
155+ let index = Self :: get_index ( next, & mut entry) ;
156+ entry. insert ( PubGrubPriority :: ConflictLate ( Reverse ( index) ) ) ;
157+ true
158+ }
159+ std:: collections:: hash_map:: Entry :: Vacant ( entry) => {
160+ entry. insert ( PubGrubPriority :: ConflictLate ( Reverse ( next) ) ) ;
161+ true
162+ }
163+ }
164+ }
91165}
92166
93167#[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
@@ -101,6 +175,15 @@ pub(crate) enum PubGrubPriority {
101175 /// in the dependency graph.
102176 Unspecified ( Reverse < usize > ) ,
103177
178+ /// Selected version of this package were often the culprit of rejecting another package, so
179+ /// it's deprioritized behind `ConflictEarly`. It's still the higher than `Unspecified` to
180+ /// conflict before selecting unrelated packages.
181+ ConflictLate ( Reverse < usize > ) ,
182+
183+ /// Selected version of this package were often rejected, so it's prioritized over
184+ /// `ConflictLate`.
185+ ConflictEarly ( Reverse < usize > ) ,
186+
104187 /// The version range is constrained to a single version (e.g., with the `==` operator).
105188 Singleton ( Reverse < usize > ) ,
106189
0 commit comments