Skip to content

Commit 4a74013

Browse files
authored
feat!: no recursion and Rc for causes (pubgrub-rs#184)
* inline find_shared_ids * move clone for shorter code * topological scan not recursion * perf!: use rc not box * cargo clippy * Rc to Arc
1 parent b10fb1a commit 4a74013

File tree

4 files changed

+48
-32
lines changed

4 files changed

+48
-32
lines changed

src/internal/core.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! to write a functional PubGrub algorithm.
55
66
use std::error::Error;
7+
use std::sync::Arc;
78

89
use crate::error::PubGrubError;
910
use crate::internal::arena::Arena;
@@ -288,11 +289,6 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
288289
// Error reporting #########################################################
289290

290291
fn build_derivation_tree(&self, incompat: IncompId<P, VS>) -> DerivationTree<P, VS> {
291-
let shared_ids = self.find_shared_ids(incompat);
292-
Incompatibility::build_derivation_tree(incompat, &shared_ids, &self.incompatibility_store)
293-
}
294-
295-
fn find_shared_ids(&self, incompat: IncompId<P, VS>) -> Set<IncompId<P, VS>> {
296292
let mut all_ids = Set::default();
297293
let mut shared_ids = Set::default();
298294
let mut stack = vec![incompat];
@@ -301,12 +297,28 @@ impl<P: Package, VS: VersionSet, Priority: Ord + Clone> State<P, VS, Priority> {
301297
if all_ids.contains(&i) {
302298
shared_ids.insert(i);
303299
} else {
304-
all_ids.insert(i);
305300
stack.push(id1);
306301
stack.push(id2);
307302
}
308303
}
304+
all_ids.insert(i);
305+
}
306+
// To avoid recursion we need to generate trees in topological order.
307+
// That is to say we need to ensure that the causes are processed before the incompatibility they effect.
308+
// It happens to be that sorting by their ID maintains this property.
309+
let mut sorted_ids = all_ids.into_iter().collect::<Vec<_>>();
310+
sorted_ids.sort_unstable_by_key(|id| id.into_raw());
311+
let mut precomputed = Map::default();
312+
for id in sorted_ids {
313+
let tree = Incompatibility::build_derivation_tree(
314+
id,
315+
&shared_ids,
316+
&self.incompatibility_store,
317+
&precomputed,
318+
);
319+
precomputed.insert(id, Arc::new(tree));
309320
}
310-
shared_ids
321+
// Now the user can refer to the entire tree from its root.
322+
Arc::into_inner(precomputed.remove(&incompat).unwrap()).unwrap()
311323
}
312324
}

src/internal/incompatibility.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//! that should never be satisfied all together.
55
66
use std::fmt;
7+
use std::sync::Arc;
78

89
use crate::internal::arena::{Arena, Id};
910
use crate::internal::small_map::SmallMap;
@@ -12,7 +13,7 @@ use crate::report::{
1213
DefaultStringReportFormatter, DerivationTree, Derived, External, ReportFormatter,
1314
};
1415
use crate::term::{self, Term};
15-
use crate::type_aliases::Set;
16+
use crate::type_aliases::{Map, Set};
1617
use crate::version_set::VersionSet;
1718

1819
/// An incompatibility is a set of terms for different packages
@@ -227,36 +228,36 @@ impl<P: Package, VS: VersionSet> Incompatibility<P, VS> {
227228
self_id: Id<Self>,
228229
shared_ids: &Set<Id<Self>>,
229230
store: &Arena<Self>,
231+
precomputed: &Map<Id<Self>, Arc<DerivationTree<P, VS>>>,
230232
) -> DerivationTree<P, VS> {
231-
match &store[self_id].kind {
233+
match store[self_id].kind.clone() {
232234
Kind::DerivedFrom(id1, id2) => {
233-
let cause1 = Self::build_derivation_tree(*id1, shared_ids, store);
234-
let cause2 = Self::build_derivation_tree(*id2, shared_ids, store);
235235
let derived = Derived {
236236
terms: store[self_id].package_terms.as_map(),
237237
shared_id: shared_ids.get(&self_id).map(|id| id.into_raw()),
238-
cause1: Box::new(cause1),
239-
cause2: Box::new(cause2),
238+
cause1: precomputed
239+
.get(&id1)
240+
.expect("Non-topological calls building tree")
241+
.clone(),
242+
cause2: precomputed
243+
.get(&id2)
244+
.expect("Non-topological calls building tree")
245+
.clone(),
240246
};
241247
DerivationTree::Derived(derived)
242248
}
243249
Kind::NotRoot(package, version) => {
244-
DerivationTree::External(External::NotRoot(package.clone(), version.clone()))
250+
DerivationTree::External(External::NotRoot(package, version))
245251
}
246252
Kind::NoVersions(package, set) => {
247-
DerivationTree::External(External::NoVersions(package.clone(), set.clone()))
253+
DerivationTree::External(External::NoVersions(package, set))
248254
}
249-
Kind::UnavailableDependencies(package, set) => DerivationTree::External(
250-
External::UnavailableDependencies(package.clone(), set.clone()),
251-
),
252-
Kind::FromDependencyOf(package, set, dep_package, dep_set) => {
253-
DerivationTree::External(External::FromDependencyOf(
254-
package.clone(),
255-
set.clone(),
256-
dep_package.clone(),
257-
dep_set.clone(),
258-
))
255+
Kind::UnavailableDependencies(package, set) => {
256+
DerivationTree::External(External::UnavailableDependencies(package, set))
259257
}
258+
Kind::FromDependencyOf(package, set, dep_package, dep_set) => DerivationTree::External(
259+
External::FromDependencyOf(package, set, dep_package, dep_set),
260+
),
260261
}
261262
}
262263
}

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,6 @@
220220
//! with a cache, you may want to know that some versions
221221
//! do not exist in your cache.
222222
223-
#![allow(clippy::rc_buffer)]
224223
#![warn(missing_docs)]
225224

226225
pub mod error;

src/report.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
//! dependency solving failed.
55
66
use std::fmt;
7-
use std::ops::{Deref, DerefMut};
7+
use std::ops::Deref;
8+
use std::sync::Arc;
89

910
use crate::package::Package;
1011
use crate::term::Term;
@@ -64,9 +65,9 @@ pub struct Derived<P: Package, VS: VersionSet> {
6465
/// and refer to the explanation for the other times.
6566
pub shared_id: Option<usize>,
6667
/// First cause.
67-
pub cause1: Box<DerivationTree<P, VS>>,
68+
pub cause1: Arc<DerivationTree<P, VS>>,
6869
/// Second cause.
69-
pub cause2: Box<DerivationTree<P, VS>>,
70+
pub cause2: Arc<DerivationTree<P, VS>>,
7071
}
7172

7273
impl<P: Package, VS: VersionSet> DerivationTree<P, VS> {
@@ -82,7 +83,10 @@ impl<P: Package, VS: VersionSet> DerivationTree<P, VS> {
8283
match self {
8384
DerivationTree::External(_) => {}
8485
DerivationTree::Derived(derived) => {
85-
match (derived.cause1.deref_mut(), derived.cause2.deref_mut()) {
86+
match (
87+
Arc::make_mut(&mut derived.cause1),
88+
Arc::make_mut(&mut derived.cause2),
89+
) {
8690
(DerivationTree::External(External::NoVersions(p, r)), ref mut cause2) => {
8791
cause2.collapse_no_versions();
8892
*self = cause2
@@ -98,8 +102,8 @@ impl<P: Package, VS: VersionSet> DerivationTree<P, VS> {
98102
.unwrap_or_else(|| self.to_owned());
99103
}
100104
_ => {
101-
derived.cause1.collapse_no_versions();
102-
derived.cause2.collapse_no_versions();
105+
Arc::make_mut(&mut derived.cause1).collapse_no_versions();
106+
Arc::make_mut(&mut derived.cause2).collapse_no_versions();
103107
}
104108
}
105109
}

0 commit comments

Comments
 (0)