Skip to content

Commit bf28b0f

Browse files
committed
add comments to generalize_conflicting and use more general logic
1 parent afd240e commit bf28b0f

File tree

2 files changed

+68
-58
lines changed

2 files changed

+68
-58
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: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -895,24 +895,62 @@ 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+
#[cfg(debug_assertions)]
940+
{
941+
// the entire point is to find an older conflict, so let's make sure we did
942+
let new_age = con
943+
.keys()
944+
.map(|&c| cx.is_active(c).expect("not currently active!?"))
945+
.max()
946+
.unwrap();
947+
assert!(
948+
new_age < backtrack_critical_age,
949+
"new_age {} < backtrack_critical_age {}",
950+
new_age,
951+
backtrack_critical_age
952+
);
953+
}
916954
past_conflicting_activations.insert(dep, &con);
917955
return Some(con);
918956
}

0 commit comments

Comments
 (0)