@@ -14,6 +14,7 @@ use crate::internal::partial_solution::{DecisionLevel, PartialSolution};
14
14
use crate :: package:: Package ;
15
15
use crate :: report:: DerivationTree ;
16
16
use crate :: solver:: DependencyConstraints ;
17
+ use crate :: type_aliases:: Map ;
17
18
use crate :: version:: Version ;
18
19
19
20
/// Current state of the PubGrub algorithm.
@@ -22,8 +23,7 @@ pub struct State<P: Package, V: Version> {
22
23
root_package : P ,
23
24
root_version : V ,
24
25
25
- /// TODO: remove pub.
26
- pub incompatibilities : Vec < IncompId < P , V > > ,
26
+ incompatibilities : Map < P , Vec < IncompId < P , V > > > ,
27
27
28
28
/// Partial solution.
29
29
/// TODO: remove pub.
@@ -46,10 +46,12 @@ impl<P: Package, V: Version> State<P, V> {
46
46
root_package. clone ( ) ,
47
47
root_version. clone ( ) ,
48
48
) ) ;
49
+ let mut incompatibilities = Map :: default ( ) ;
50
+ incompatibilities. insert ( root_package. clone ( ) , vec ! [ not_root_id] ) ;
49
51
Self {
50
52
root_package,
51
53
root_version,
52
- incompatibilities : vec ! [ not_root_id ] ,
54
+ incompatibilities,
53
55
partial_solution : PartialSolution :: empty ( ) ,
54
56
incompatibility_store,
55
57
unit_propagation_buffer : vec ! [ ] ,
@@ -58,10 +60,8 @@ impl<P: Package, V: Version> State<P, V> {
58
60
59
61
/// Add an incompatibility to the state.
60
62
pub fn add_incompatibility ( & mut self , incompat : Incompatibility < P , V > ) {
61
- Incompatibility :: merge_into (
62
- self . incompatibility_store . alloc ( incompat) ,
63
- & mut self . incompatibilities ,
64
- ) ;
63
+ let id = self . incompatibility_store . alloc ( incompat) ;
64
+ self . merge_incompatibility ( id) ;
65
65
}
66
66
67
67
/// Add an incompatibility to the state.
@@ -79,7 +79,7 @@ impl<P: Package, V: Version> State<P, V> {
79
79
} ) ) ;
80
80
// Merge the newly created incompatibilities with the older ones.
81
81
for id in IncompId :: range_to_iter ( new_incompats_id_range. clone ( ) ) {
82
- Incompatibility :: merge_into ( id , & mut self . incompatibilities ) ;
82
+ self . merge_incompatibility ( id ) ;
83
83
}
84
84
new_incompats_id_range
85
85
}
@@ -98,12 +98,9 @@ impl<P: Package, V: Version> State<P, V> {
98
98
// Iterate over incompatibilities in reverse order
99
99
// to evaluate first the newest incompatibilities.
100
100
let mut conflict_id = None ;
101
- for & incompat_id in self . incompatibilities . iter ( ) . rev ( ) {
101
+ // We only care about incompatibilities if it contains the current package.
102
+ for & incompat_id in self . incompatibilities [ & current_package] . iter ( ) . rev ( ) {
102
103
let current_incompat = & self . incompatibility_store [ incompat_id] ;
103
- // We only care about that incompatibility if it contains the current package.
104
- if current_incompat. get ( & current_package) . is_none ( ) {
105
- continue ;
106
- }
107
104
match self . partial_solution . relation ( current_incompat) {
108
105
// If the partial solution satisfies the incompatibility
109
106
// we must perform conflict resolution.
@@ -204,7 +201,35 @@ impl<P: Package, V: Version> State<P, V> {
204
201
self . partial_solution
205
202
. backtrack ( decision_level, & self . incompatibility_store ) ;
206
203
if incompat_changed {
207
- Incompatibility :: merge_into ( incompat, & mut self . incompatibilities ) ;
204
+ self . merge_incompatibility ( incompat) ;
205
+ }
206
+ }
207
+
208
+ /// Add this incompatibility into the set of all incompatibilities.
209
+ ///
210
+ /// Pub collapses identical dependencies from adjacent package versions
211
+ /// into individual incompatibilities.
212
+ /// This substantially reduces the total number of incompatibilities
213
+ /// and makes it much easier for Pub to reason about multiple versions of packages at once.
214
+ ///
215
+ /// For example, rather than representing
216
+ /// foo 1.0.0 depends on bar ^1.0.0 and
217
+ /// foo 1.1.0 depends on bar ^1.0.0
218
+ /// as two separate incompatibilities,
219
+ /// they are collapsed together into the single incompatibility {foo ^1.0.0, not bar ^1.0.0}
220
+ /// (provided that no other version of foo exists between 1.0.0 and 2.0.0).
221
+ /// We could collapse them into { foo (1.0.0 ∪ 1.1.0), not bar ^1.0.0 }
222
+ /// without having to check the existence of other versions though.
223
+ ///
224
+ /// Here we do the simple stupid thing of just growing the Vec.
225
+ /// It may not be trivial since those incompatibilities
226
+ /// may already have derived others.
227
+ fn merge_incompatibility ( & mut self , id : IncompId < P , V > ) {
228
+ for ( pkg, _term) in self . incompatibility_store [ id] . iter ( ) {
229
+ self . incompatibilities
230
+ . entry ( pkg. clone ( ) )
231
+ . or_default ( )
232
+ . push ( id) ;
208
233
}
209
234
}
210
235
0 commit comments