Skip to content

Commit c0be9ce

Browse files
committed
fast path one more problem
1 parent ec0ad71 commit c0be9ce

File tree

2 files changed

+123
-51
lines changed

2 files changed

+123
-51
lines changed

src/cargo/core/resolver/mod.rs

Lines changed: 115 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -909,67 +909,137 @@ fn generalize_conflicting(
909909
return None;
910910
}
911911

912+
let our_candidates: HashSet<PackageId> =
913+
our_candidates.iter().map(|our| our.package_id()).collect();
914+
912915
// What parents does that critical activation have
913916
for (critical_parent, critical_parents_deps) in
914917
cx.parents.edges(&backtrack_critical_id).filter(|(p, _)| {
915918
// it will only help backjump further if it is older then the critical_age
916919
cx.is_active(*p).expect("parent not currently active!?") < backtrack_critical_age
917920
})
918921
{
919-
for critical_parents_dep in critical_parents_deps.iter() {
920-
let critical_parent_candidates = registry
922+
'dep: for critical_parents_dep in critical_parents_deps.iter() {
923+
let mut con = conflicting_activations.clone();
924+
con.remove(&backtrack_critical_id);
925+
con.insert(*critical_parent, backtrack_critical_reason.clone());
926+
927+
for other in registry
921928
.query(critical_parents_dep)
922-
.expect("an already used dep now error!?");
929+
.expect("an already used dep now error!?")
930+
.iter()
931+
{
932+
if (our_activation_key
933+
.map_or(false, |our| other.package_id().as_activations_key() == our)
934+
|| our_link.map_or(false, |_| other.links() == our_link))
935+
&& !our_candidates.contains(&other.package_id())
936+
{
937+
continue;
938+
}
923939

924-
if (
925-
our_activation_key.is_some()
926-
&& critical_parent_candidates.iter().all(|other| {
927-
other.package_id().as_activations_key() == our_activation_key.unwrap()
928-
&& our_candidates
929-
.iter()
930-
.all(|our| our.package_id() != other.package_id())
931-
})
932-
// all of `critical_parent_candidates` are semver compatible with all of `our_candidates`
933-
// and we have none in common. Thus `dep` can not be resolved when `critical_parents_dep` has bean resolved.
934-
) || (
935-
our_link.is_some()
936-
&& critical_parent_candidates.iter().all(|other| {
937-
other.links() == our_link
938-
&& our_candidates
939-
.iter()
940-
.all(|our| our.package_id() != other.package_id())
941-
})
942-
// all of `critical_parent_candidates` have the same `links` as all of `our_candidates`
943-
// and we have none in common. Thus `dep` can not be resolved when `critical_parents_dep` has bean resolved.
944-
) {
945-
// but that is not how the cache is set up. So we add the less general but much faster,
946-
// "our dep will not succeed if critical_parent is activated".
947-
let mut con = conflicting_activations.clone();
948-
con.remove(&backtrack_critical_id);
949-
con.insert(*critical_parent, backtrack_critical_reason);
950-
951-
if cfg!(debug_assertions) {
952-
// the entire point is to find an older conflict, so let's make sure we did
953-
let new_age = con
954-
.keys()
955-
.map(|&c| cx.is_active(c).expect("not currently active!?"))
956-
.max()
957-
.unwrap();
958-
assert!(
959-
new_age < backtrack_critical_age,
960-
"new_age {} < backtrack_critical_age {}",
961-
new_age,
962-
backtrack_critical_age
963-
);
940+
// ok so we dont have a semver nor a links problem with `critical_parents_dep`
941+
// getting set to `other`, maybe it still wont work do to one of its dependencies.
942+
if let Some(conflicting) = generalize_children_conflicting(
943+
cx,
944+
backtrack_critical_age,
945+
registry,
946+
past_conflicting_activations,
947+
dep,
948+
&other,
949+
*critical_parent,
950+
) {
951+
con.extend(conflicting.into_iter());
952+
continue;
953+
} else {
954+
continue 'dep;
964955
}
965-
past_conflicting_activations.insert(dep, &con);
966-
return Some(con);
967956
}
957+
958+
if cfg!(debug_assertions) {
959+
// the entire point is to find an older conflict, so let's make sure we did
960+
let new_age = con
961+
.keys()
962+
.map(|&c| cx.is_active(c).expect("not currently active!?"))
963+
.max()
964+
.unwrap();
965+
assert!(
966+
new_age < backtrack_critical_age,
967+
"new_age {} < backtrack_critical_age {}",
968+
new_age,
969+
backtrack_critical_age
970+
);
971+
}
972+
past_conflicting_activations.insert(dep, &con);
973+
return Some(con);
968974
}
969975
}
970976
None
971977
}
972978

979+
/// Sees if the candidate is not usable because one of its children will have a conflict
980+
///
981+
/// Panics if the input conflict is not all active in `cx`.
982+
fn generalize_children_conflicting(
983+
cx: &Context,
984+
critical_age: usize,
985+
registry: &mut RegistryQueryer<'_>,
986+
past_conflicting_activations: &mut conflict_cache::ConflictCache,
987+
dep: &Dependency,
988+
candidate: &Summary,
989+
parent: PackageId,
990+
) -> Option<ConflictMap> {
991+
let method = Method::Required {
992+
dev_deps: false,
993+
features: Rc::new(dep.features().iter().cloned().collect()),
994+
all_features: false,
995+
uses_default_features: dep.uses_default_features(),
996+
};
997+
let mut out = ConflictMap::new();
998+
match registry.build_deps(Some(parent), candidate, &method) {
999+
Err(ActivateError::Fatal(_)) => return None,
1000+
Err(ActivateError::Conflict(pid, reason)) => {
1001+
out.insert(pid, reason);
1002+
}
1003+
Ok(deps) => {
1004+
let con = deps
1005+
.1
1006+
.iter()
1007+
.filter_map(|(ref new_dep, _, _)| {
1008+
past_conflicting_activations.find(
1009+
new_dep,
1010+
&|id| {
1011+
if id == candidate.package_id() {
1012+
// we are imagining that we used other instead
1013+
Some(critical_age)
1014+
} else {
1015+
cx.is_active(id).filter(|&age|
1016+
// we only care about things that are older then critical_age
1017+
age < critical_age)
1018+
}
1019+
},
1020+
None,
1021+
)
1022+
})
1023+
.next()?;
1024+
out.extend(
1025+
con.iter()
1026+
.filter(|(pid, _)| **pid != candidate.package_id())
1027+
.map(|(&pid, reason)| (pid, reason.clone())),
1028+
);
1029+
}
1030+
};
1031+
// If one of candidate deps is known unresolvable
1032+
// then candidate will not succeed.
1033+
// How ever if candidate is part of the reason that
1034+
// one of its deps conflicts then
1035+
// we can make a stronger statement
1036+
// because candidate will definitely be activated when
1037+
// we try its dep.
1038+
out.remove(&candidate.package_id());
1039+
1040+
Some(out)
1041+
}
1042+
9731043
/// Looks through the states in `backtrack_stack` for dependencies with
9741044
/// remaining candidates. For each one, also checks if rolling back
9751045
/// could change the outcome of the failed resolution that caused backtracking

tests/testsuite/resolve.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,22 +1352,24 @@ fn large_conflict_cache() {
13521352
// a large number of conflicts can easily be generated by a sys crate.
13531353
let sys_name = format!("{}-sys", (b'a' + name) as char);
13541354
let in_len = input.len();
1355-
input.push(pkg!(("last", format!("{}.0.0", in_len)) => [dep_req(&sys_name, "=0.0.0")]));
1355+
input.push(pkg!(("last", format!("{}.0.0", in_len)) => [dep_req(&sys_name, "<=0.0.1")]));
13561356
root_deps.push(dep_req(&sys_name, ">= 0.0.1"));
13571357

13581358
// a large number of conflicts can also easily be generated by a major release version.
13591359
let plane_name = format!("{}", (b'a' + name) as char);
13601360
let in_len = input.len();
1361-
input.push(pkg!(("last", format!("{}.0.0", in_len)) => [dep_req(&plane_name, "=1.0.0")]));
1361+
input.push(pkg!(("last", format!("{}.0.0", in_len)) => [dep_req(&plane_name, "<=1.0.1")]));
13621362
root_deps.push(dep_req(&plane_name, ">= 1.0.1"));
13631363

1364-
for i in 0..NUM_VERSIONS {
1364+
input.push(pkg!((&sys_name, "0.0.0")));
1365+
input.push(pkg!((&plane_name, "1.0.0")));
1366+
// one version that can't be activated for some other reason
1367+
input.push(pkg!((&sys_name, "0.0.1") => [dep("bad")]));
1368+
input.push(pkg!((&plane_name, "1.0.1") => [dep("bad")]));
1369+
for i in 2..=NUM_VERSIONS {
13651370
input.push(pkg!((&sys_name, format!("{}.0.0", i))));
13661371
input.push(pkg!((&plane_name, format!("1.0.{}", i))));
13671372
}
1368-
// and one version that can't be activated for some other reason
1369-
input.push(pkg!((&sys_name, format!("{}.0.0", NUM_VERSIONS)) => [dep("bad")]));
1370-
input.push(pkg!((&plane_name, format!("1.0.{}", NUM_VERSIONS)) => [dep("bad")]));
13711373
}
13721374
let reg = registry(input);
13731375
let _ = resolve(pkg_id("root"), root_deps, &reg);

0 commit comments

Comments
 (0)