Skip to content

Commit 759b616

Browse files
committed
Auto merge of #6910 - Eh2406:small-things, r=alexcrichton
Small things This has two small changes that are worth saving from my most recent attempt to speedup #6258 (comment): 1. This removes the `ConflictCache::contains` added in #6776. Replacing it with a more general and easier to explain system based on the existing `ConflictCache::find_conflicting`. 2. This adds code to print the used part of the input for failing resolver tests. This is very helpful when 1. The proptest shrinking algorithm is interrupted, at least you have the smallest one found so far. 2. Hand minimizing, remove a dep and it will tell you all the packages that are no longer needed for the test to fail.
2 parents 573f9bb + d11e42c commit 759b616

File tree

3 files changed

+95
-65
lines changed

3 files changed

+95
-65
lines changed

src/cargo/core/resolver/conflict_cache.rs

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,17 @@ enum ConflictStoreTrie {
1818

1919
impl ConflictStoreTrie {
2020
/// Finds any known set of conflicts, if any,
21-
/// which are activated in `cx` and contain `PackageId` specified.
21+
/// where all elements return some from `is_active` and contain `PackageId` specified.
2222
/// If more then one are activated, then it will return
2323
/// one that will allow for the most jump-back.
24-
fn find_conflicting(
24+
fn find(
2525
&self,
26-
cx: &Context,
26+
is_active: &impl Fn(PackageId) -> Option<usize>,
2727
must_contain: Option<PackageId>,
2828
) -> Option<(&ConflictMap, usize)> {
2929
match self {
3030
ConflictStoreTrie::Leaf(c) => {
3131
if must_contain.is_none() {
32-
// `is_conflicting` checks that all the elements are active,
33-
// but we have checked each one by the recursion of this function.
34-
debug_assert!(cx.is_conflicting(None, c).is_some());
3532
Some((c, 0))
3633
} else {
3734
// We did not find `must_contain`, so we need to keep looking.
@@ -45,9 +42,9 @@ impl ConflictStoreTrie {
4542
.unwrap_or_else(|| m.range(..))
4643
{
4744
// If the key is active, then we need to check all of the corresponding subtrie.
48-
if let Some(age_this) = cx.is_active(pid) {
45+
if let Some(age_this) = is_active(pid) {
4946
if let Some((o, age_o)) =
50-
store.find_conflicting(cx, must_contain.filter(|&f| f != pid))
47+
store.find(is_active, must_contain.filter(|&f| f != pid))
5148
{
5249
let age = if must_contain == Some(pid) {
5350
// all the results will include `must_contain`
@@ -102,28 +99,6 @@ impl ConflictStoreTrie {
10299
*self = ConflictStoreTrie::Leaf(con)
103100
}
104101
}
105-
106-
fn contains(&self, mut iter: impl Iterator<Item = PackageId>, con: &ConflictMap) -> bool {
107-
match (self, iter.next()) {
108-
(ConflictStoreTrie::Leaf(c), None) => {
109-
if cfg!(debug_assertions) {
110-
let a: Vec<_> = con.keys().collect();
111-
let b: Vec<_> = c.keys().collect();
112-
assert_eq!(a, b);
113-
}
114-
true
115-
}
116-
(ConflictStoreTrie::Leaf(_), Some(_)) => false,
117-
(ConflictStoreTrie::Node(_), None) => false,
118-
(ConflictStoreTrie::Node(m), Some(n)) => {
119-
if let Some(next) = m.get(&n) {
120-
next.contains(iter, con)
121-
} else {
122-
false
123-
}
124-
}
125-
}
126-
}
127102
}
128103

129104
pub(super) struct ConflictCache {
@@ -172,6 +147,17 @@ impl ConflictCache {
172147
dep_from_pid: HashMap::new(),
173148
}
174149
}
150+
pub fn find(
151+
&self,
152+
dep: &Dependency,
153+
is_active: &impl Fn(PackageId) -> Option<usize>,
154+
must_contain: Option<PackageId>,
155+
) -> Option<&ConflictMap> {
156+
self.con_from_dep
157+
.get(dep)?
158+
.find(is_active, must_contain)
159+
.map(|(c, _)| c)
160+
}
175161
/// Finds any known set of conflicts, if any,
176162
/// which are activated in `cx` and contain `PackageId` specified.
177163
/// If more then one are activated, then it will return
@@ -182,14 +168,11 @@ impl ConflictCache {
182168
dep: &Dependency,
183169
must_contain: Option<PackageId>,
184170
) -> Option<&ConflictMap> {
185-
let out = self
186-
.con_from_dep
187-
.get(dep)?
188-
.find_conflicting(cx, must_contain)
189-
.map(|(c, _)| c);
171+
let out = self.find(dep, &|id| cx.is_active(id), must_contain);
190172
if cfg!(debug_assertions) {
191-
if let Some(f) = must_contain {
192-
if let Some(c) = &out {
173+
if let Some(c) = &out {
174+
assert!(cx.is_conflicting(None, c).is_some());
175+
if let Some(f) = must_contain {
193176
assert!(c.contains_key(&f));
194177
}
195178
}
@@ -229,17 +212,6 @@ impl ConflictCache {
229212
}
230213
}
231214

232-
/// Check if a conflict was previously added of the form:
233-
/// `dep` is known to be unresolvable if
234-
/// all the `PackageId` entries are activated.
235-
pub fn contains(&self, dep: &Dependency, con: &ConflictMap) -> bool {
236-
if let Some(cst) = self.con_from_dep.get(dep) {
237-
cst.contains(con.keys().cloned(), con)
238-
} else {
239-
false
240-
}
241-
}
242-
243215
pub fn dependencies_conflicting_with(&self, pid: PackageId) -> Option<&HashSet<Dependency>> {
244216
self.dep_from_pid.get(&pid)
245217
}

src/cargo/core/resolver/mod.rs

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -895,24 +895,61 @@ fn generalize_conflicting(
895895
// A dep is equivalent to one of the things it can resolve to.
896896
// Thus, if all the things it can resolve to have already ben determined
897897
// to be conflicting, then we can just say that we conflict with the parent.
898-
if registry
898+
if let Some(others) = registry
899899
.query(critical_parents_dep)
900900
.expect("an already used dep now error!?")
901901
.iter()
902902
.rev() // the last one to be tried is the least likely to be in the cache, so start with that.
903-
.all(|other| {
904-
let mut con = conflicting_activations.clone();
905-
con.remove(&backtrack_critical_id);
906-
con.insert(
907-
other.summary.package_id(),
908-
backtrack_critical_reason.clone(),
909-
);
910-
past_conflicting_activations.contains(dep, &con)
903+
.map(|other| {
904+
past_conflicting_activations
905+
.find(
906+
dep,
907+
&|id| {
908+
if id == other.summary.package_id() {
909+
// we are imagining that we used other instead
910+
Some(backtrack_critical_age)
911+
} else {
912+
cx.is_active(id).filter(|&age|
913+
// we only care about things that are older then critical_age
914+
age < backtrack_critical_age)
915+
}
916+
},
917+
Some(other.summary.package_id()),
918+
)
919+
.map(|con| (other.summary.package_id(), con))
911920
})
921+
.collect::<Option<Vec<(PackageId, &ConflictMap)>>>()
912922
{
913923
let mut con = conflicting_activations.clone();
914-
con.remove(&backtrack_critical_id);
924+
// It is always valid to combine previously inserted conflicts.
925+
// A, B are both known bad states each that can never be activated.
926+
// A + B is redundant but cant be activated, as if
927+
// A + B is active then A is active and we know that is not ok.
928+
for (_, other) in &others {
929+
con.extend(other.iter().map(|(&id, re)| (id, re.clone())));
930+
}
931+
// Now that we have this combined conflict, we can do a substitution:
932+
// A dep is equivalent to one of the things it can resolve to.
933+
// So we can remove all the things that it resolves to and replace with the parent.
934+
for (other_id, _) in &others {
935+
con.remove(other_id);
936+
}
915937
con.insert(*critical_parent, backtrack_critical_reason);
938+
939+
if cfg!(debug_assertions) {
940+
// the entire point is to find an older conflict, so let's make sure we did
941+
let new_age = con
942+
.keys()
943+
.map(|&c| cx.is_active(c).expect("not currently active!?"))
944+
.max()
945+
.unwrap();
946+
assert!(
947+
new_age < backtrack_critical_age,
948+
"new_age {} < backtrack_critical_age {}",
949+
new_age,
950+
backtrack_critical_age
951+
);
952+
}
916953
past_conflicting_activations.insert(dep, &con);
917954
return Some(con);
918955
}

tests/testsuite/support/resolver.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,20 @@ pub fn resolve_with_config_raw(
7373
registry: &[Summary],
7474
config: Option<&Config>,
7575
) -> CargoResult<Resolve> {
76-
struct MyRegistry<'a>(&'a [Summary]);
76+
struct MyRegistry<'a> {
77+
list: &'a [Summary],
78+
used: HashSet<PackageId>,
79+
};
7780
impl<'a> Registry for MyRegistry<'a> {
7881
fn query(
7982
&mut self,
8083
dep: &Dependency,
8184
f: &mut dyn FnMut(Summary),
8285
fuzzy: bool,
8386
) -> CargoResult<()> {
84-
for summary in self.0.iter() {
87+
for summary in self.list.iter() {
8588
if fuzzy || dep.matches(summary) {
89+
self.used.insert(summary.package_id());
8690
f(summary.clone());
8791
}
8892
}
@@ -97,7 +101,28 @@ pub fn resolve_with_config_raw(
97101
false
98102
}
99103
}
100-
let mut registry = MyRegistry(registry);
104+
impl<'a> Drop for MyRegistry<'a> {
105+
fn drop(&mut self) {
106+
if std::thread::panicking() && self.list.len() != self.used.len() {
107+
// we found a case that causes a panic and did not use all of the input.
108+
// lets print the part of the input that was used for minimization.
109+
println!(
110+
"{:?}",
111+
PrettyPrintRegistry(
112+
self.list
113+
.iter()
114+
.filter(|s| { self.used.contains(&s.package_id()) })
115+
.cloned()
116+
.collect()
117+
)
118+
);
119+
}
120+
}
121+
}
122+
let mut registry = MyRegistry {
123+
list: registry,
124+
used: HashSet::new(),
125+
};
101126
let summary = Summary::new(
102127
pkg,
103128
deps,
@@ -348,8 +373,6 @@ fn meta_test_deep_pretty_print_registry() {
348373
pkg!(("baz", "1.0.1")),
349374
pkg!(("cat", "1.0.2") => [dep_req_kind("other", "2", Kind::Build, false)]),
350375
pkg!(("cat", "1.0.3") => [dep_req_kind("other", "2", Kind::Development, false)]),
351-
pkg!(("cat", "1.0.4") => [dep_req_kind("other", "2", Kind::Build, false)]),
352-
pkg!(("cat", "1.0.5") => [dep_req_kind("other", "2", Kind::Development, false)]),
353376
pkg!(("dep_req", "1.0.0")),
354377
pkg!(("dep_req", "2.0.0")),
355378
])
@@ -363,8 +386,6 @@ fn meta_test_deep_pretty_print_registry() {
363386
pkg!((\"baz\", \"1.0.1\")),\
364387
pkg!((\"cat\", \"1.0.2\") => [dep_req_kind(\"other\", \"^2\", Kind::Build, false),]),\
365388
pkg!((\"cat\", \"1.0.3\") => [dep_req_kind(\"other\", \"^2\", Kind::Development, false),]),\
366-
pkg!((\"cat\", \"1.0.4\") => [dep_req_kind(\"other\", \"^2\", Kind::Build, false),]),\
367-
pkg!((\"cat\", \"1.0.5\") => [dep_req_kind(\"other\", \"^2\", Kind::Development, false),]),\
368389
pkg!((\"dep_req\", \"1.0.0\")),\
369390
pkg!((\"dep_req\", \"2.0.0\")),]"
370391
)

0 commit comments

Comments
 (0)