Skip to content

Commit

Permalink
feat: merge direct dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
Eh2406 committed Nov 30, 2023
1 parent 7b5d94b commit 7535159
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 29 deletions.
59 changes: 53 additions & 6 deletions src/internal/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub struct State<P: Package, VS: VersionSet, Priority: Ord + Clone> {
/// and will stay that way until the next conflict and backtrack is operated.
contradicted_incompatibilities: rustc_hash::FxHashSet<IncompId<P, VS>>,

dependencies: Map<(P, P), SmallVec<IncompId<P, VS>>>,

/// Partial solution.
/// TODO: remove pub.
pub partial_solution: PartialSolution<P, VS, Priority>,
Expand Down Expand Up @@ -61,6 +63,7 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
partial_solution: PartialSolution::empty(),
incompatibility_store,
unit_propagation_buffer: SmallVec::Empty,
dependencies: Map::default(),
}
}

Expand All @@ -78,11 +81,15 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
deps: &DependencyConstraints<P, VS>,
) -> std::ops::Range<IncompId<P, VS>> {
// Create incompatibilities and allocate them in the store.
let new_incompats_id_range = self
.incompatibility_store
.alloc_iter(deps.iter().map(|dep| {
Incompatibility::from_dependency(package.clone(), version.clone(), dep)
}));
let new_incompats_id_range =
self.incompatibility_store
.alloc_iter(deps.iter().map(|dep| {
Incompatibility::from_dependency(
package.clone(),
VS::singleton(version.clone()),
dep,
)
}));
// Merge the newly created incompatibilities with the older ones.
for id in IncompId::range_to_iter(new_incompats_id_range.clone()) {
self.merge_incompatibility(id);
Expand Down Expand Up @@ -234,7 +241,47 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
/// Here we do the simple stupid thing of just growing the Vec.
/// It may not be trivial since those incompatibilities
/// may already have derived others.
fn merge_incompatibility(&mut self, id: IncompId<P, VS>) {
fn merge_incompatibility(&mut self, mut id: IncompId<P, VS>) {
if let Some((p1, p2)) = self.incompatibility_store[id].as_dependency() {
let vs = self.incompatibility_store[id].get(p2);
let deps_lookup = self
.dependencies
.entry((p1.clone(), p2.clone()))
.or_default();
if let Some(past) = deps_lookup
.as_mut_slice()
.iter_mut()
.find(|past| self.incompatibility_store[**past].get(p2) == vs)
{
let incompat = &self.incompatibility_store[id];
let new = self
.incompatibility_store
.alloc(Incompatibility::from_dependency(
p1.clone(),
self.incompatibility_store[*past]
.get(p1)
.unwrap()
.unwrap_positive()
.union(incompat.get(p1).unwrap().unwrap_positive()), // It is safe to `simplify` here
(
&p2,
incompat
.get(p2)
.map_or(&VS::empty(), |v| v.unwrap_negative()),
),
));
for (pkg, _) in self.incompatibility_store[new].iter() {
let ids = self.incompatibilities.entry(pkg.clone()).or_default();
if let Some(slot) = ids.iter().position(|id| id == past) {
ids.remove(slot);
}
}
*past = new;
id = new;
} else {
deps_lookup.push(id);
}
}
for (pkg, term) in self.incompatibility_store[id].iter() {
if cfg!(debug_assertions) {
assert_ne!(term, &crate::term::Term::any());
Expand Down
16 changes: 11 additions & 5 deletions src/internal/incompatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,25 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
}

/// Build an incompatibility from a given dependency.
pub fn from_dependency(package: P, version: VS::V, dep: (&P, &VS)) -> Self {
let set1 = VS::singleton(version);
pub fn from_dependency(package: P, versions: VS, dep: (&P, &VS)) -> Self {
let (p2, set2) = dep;
Self {
package_terms: if set2 == &VS::empty() {
SmallMap::One([(package.clone(), Term::Positive(set1.clone()))])
SmallMap::One([(package.clone(), Term::Positive(versions.clone()))])
} else {
SmallMap::Two([
(package.clone(), Term::Positive(set1.clone())),
(package.clone(), Term::Positive(versions.clone())),
(p2.clone(), Term::Negative(set2.clone())),
])
},
kind: Kind::FromDependencyOf(package, set1, p2.clone(), set2.clone()),
kind: Kind::FromDependencyOf(package, versions, p2.clone(), set2.clone()),
}
}

pub fn as_dependency(&self) -> Option<(&P, &P)> {
match &self.kind {
Kind::FromDependencyOf(p1, _, p2, _) => Some((p1, p2)),
_ => None,
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/internal/small_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ impl<T> SmallVec<T> {
}
}

pub fn as_mut_slice(&mut self) -> &mut [T] {
match self {
Self::Empty => &mut [],
Self::One(v) => v,
Self::Two(v) => v,
Self::Flexible(v) => v,
}
}

pub fn push(&mut self, new: T) {
*self = match std::mem::take(self) {
Self::Empty => Self::One([new]),
Expand Down
9 changes: 9 additions & 0 deletions src/term.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ impl<VS: VersionSet> Term<VS> {
_ => panic!("Negative term cannot unwrap positive set"),
}
}

/// Unwrap the set contained in a negative term.
/// Will panic if used on a positive set.
pub(crate) fn unwrap_negative(&self) -> &VS {
match self {
Self::Negative(set) => set,
_ => panic!("Positive term cannot unwrap negative set"),
}
}
}

/// Set operations with terms.
Expand Down
21 changes: 3 additions & 18 deletions tests/examples.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,27 +231,12 @@ fn confusing_with_lots_of_holes() {
};
assert_eq!(
&DefaultStringReporter::report(&derivation_tree),
r#"Because there is no available version for bar and foo 1 depends on bar, foo 1 is forbidden.
And because there is no version of foo in <1 | >1, <2 | >2, <3 | >3, <4 | >4, <5 | >5, foo <2 | >2, <3 | >3, <4 | >4, <5 | >5 is forbidden. (1)
Because there is no available version for bar and foo 2 depends on bar, foo 2 is forbidden.
And because foo <2 | >2, <3 | >3, <4 | >4, <5 | >5 is forbidden (1), foo <3 | >3, <4 | >4, <5 | >5 is forbidden. (2)
Because there is no available version for bar and foo 3 depends on bar, foo 3 is forbidden.
And because foo <3 | >3, <4 | >4, <5 | >5 is forbidden (2), foo <4 | >4, <5 | >5 is forbidden. (3)
Because there is no available version for bar and foo 4 depends on bar, foo 4 is forbidden.
And because foo <4 | >4, <5 | >5 is forbidden (3), foo <5 | >5 is forbidden. (4)
Because there is no available version for bar and foo 5 depends on bar, foo 5 is forbidden.
And because foo <5 | >5 is forbidden (4), foo * is forbidden.
And because root 1 depends on foo, root 1 is forbidden."#
r#"Because there is no available version for bar and foo 1 | 2 | 3 | 4 | 5 depends on bar, foo 1 | 2 | 3 | 4 | 5 is forbidden.
And because there is no version of foo in <1 | >1, <2 | >2, <3 | >3, <4 | >4, <5 | >5 and root 1 depends on foo, root 1 is forbidden."#
);
derivation_tree.collapse_no_versions();
assert_eq!(
&DefaultStringReporter::report(&derivation_tree),
r#"Because foo <2 | >2, <3 | >3, <4 | >4, <5 | >5 depends on bar and foo 2 depends on bar, foo <3 | >3, <4 | >4, <5 | >5 is forbidden.
And because foo 3 depends on bar and foo 4 depends on bar, foo <5 | >5 is forbidden.
And because foo 5 depends on bar and root 1 depends on foo, root 1 is forbidden."#
"Because foo depends on bar and root 1 depends on foo, root 1 is forbidden."
);
}

0 comments on commit 7535159

Please sign in to comment.