@@ -31,6 +31,10 @@ pub struct State<P: Package, VS: VersionSet, Priority: Ord + Clone> {
3131 /// and will stay that way until the next conflict and backtrack is operated.
3232 contradicted_incompatibilities : rustc_hash:: FxHashSet < IncompId < P , VS > > ,
3333
34+ /// All incompatibilities expressing dependencies,
35+ /// with common dependents merged.
36+ merged_dependencies : Map < ( P , P ) , SmallVec < IncompId < P , VS > > > ,
37+
3438 /// Partial solution.
3539 /// TODO: remove pub.
3640 pub partial_solution : PartialSolution < P , VS , Priority > ,
@@ -62,6 +66,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
6266 partial_solution : PartialSolution :: empty ( ) ,
6367 incompatibility_store,
6468 unit_propagation_buffer : SmallVec :: Empty ,
69+ merged_dependencies : Map :: default ( ) ,
6570 }
6671 }
6772
@@ -79,11 +84,15 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
7984 deps : & DependencyConstraints < P , VS > ,
8085 ) -> std:: ops:: Range < IncompId < P , VS > > {
8186 // Create incompatibilities and allocate them in the store.
82- let new_incompats_id_range = self
83- . incompatibility_store
84- . alloc_iter ( deps. iter ( ) . map ( |dep| {
85- Incompatibility :: from_dependency ( package. clone ( ) , version. clone ( ) , dep)
86- } ) ) ;
87+ let new_incompats_id_range =
88+ self . incompatibility_store
89+ . alloc_iter ( deps. iter ( ) . map ( |dep| {
90+ Incompatibility :: from_dependency (
91+ package. clone ( ) ,
92+ VS :: singleton ( version. clone ( ) ) ,
93+ dep,
94+ )
95+ } ) ) ;
8796 // Merge the newly created incompatibilities with the older ones.
8897 for id in IncompId :: range_to_iter ( new_incompats_id_range. clone ( ) ) {
8998 self . merge_incompatibility ( id) ;
@@ -232,11 +241,31 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
232241 /// (provided that no other version of foo exists between 1.0.0 and 2.0.0).
233242 /// We could collapse them into { foo (1.0.0 ∪ 1.1.0), not bar ^1.0.0 }
234243 /// without having to check the existence of other versions though.
235- ///
236- /// Here we do the simple stupid thing of just growing the Vec.
237- /// It may not be trivial since those incompatibilities
238- /// may already have derived others.
239- fn merge_incompatibility ( & mut self , id : IncompId < P , VS > ) {
244+ fn merge_incompatibility ( & mut self , mut id : IncompId < P , VS > ) {
245+ if let Some ( ( p1, p2) ) = self . incompatibility_store [ id] . as_dependency ( ) {
246+ // If we are a dependency, there's a good chance we can be merged with a previous dependency
247+ let deps_lookup = self
248+ . merged_dependencies
249+ . entry ( ( p1. clone ( ) , p2. clone ( ) ) )
250+ . or_default ( ) ;
251+ if let Some ( ( past, merged) ) = deps_lookup. as_mut_slice ( ) . iter_mut ( ) . find_map ( |past| {
252+ self . incompatibility_store [ id]
253+ . merge_dependents ( & self . incompatibility_store [ * past] )
254+ . map ( |m| ( past, m) )
255+ } ) {
256+ let new = self . incompatibility_store . alloc ( merged) ;
257+ for ( pkg, _) in self . incompatibility_store [ new] . iter ( ) {
258+ self . incompatibilities
259+ . entry ( pkg. clone ( ) )
260+ . or_default ( )
261+ . retain ( |id| id != past) ;
262+ }
263+ * past = new;
264+ id = new;
265+ } else {
266+ deps_lookup. push ( id) ;
267+ }
268+ }
240269 for ( pkg, term) in self . incompatibility_store [ id] . iter ( ) {
241270 if cfg ! ( debug_assertions) {
242271 assert_ne ! ( term, & crate :: term:: Term :: any( ) ) ;
0 commit comments