Skip to content

Commit a507ae4

Browse files
committed
Auto merge of #6860 - Eh2406:no-RcList, r=alexcrichton
Make required dependency as future an error, remove RcList This makes it an error to set a feature on a dependency if it is the name of a required dependency not a feature. This has been a warning for a long time. The resolver will backtrack to find a version that works, if one is available. So I think it is safe to make the change. If you need to make a optional dependency into a required dependency without a semver breaking change this can be done with rename dependencies. The result is that we can remove 3 data structures that get cloned once per tick of the resolver.
2 parents 6be1265 + 35ff555 commit a507ae4

File tree

9 files changed

+124
-151
lines changed

9 files changed

+124
-151
lines changed

src/cargo/core/resolver/context.rs

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ use crate::util::CargoResult;
1313
use crate::util::Graph;
1414

1515
use super::errors::ActivateResult;
16-
use super::types::{
17-
ConflictMap, ConflictReason, DepInfo, GraphNode, Method, RcList, RegistryQueryer,
18-
};
16+
use super::types::{ConflictMap, ConflictReason, DepInfo, Method, RegistryQueryer};
1917

2018
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
2119
pub use super::encode::{Metadata, WorkspaceResolve};
@@ -37,20 +35,9 @@ pub struct Context {
3735
pub public_dependency:
3836
Option<im_rc::HashMap<PackageId, im_rc::HashMap<InternedString, (PackageId, bool)>>>,
3937

40-
// This is somewhat redundant with the `resolve_graph` that stores the same data,
41-
// but for querying in the opposite order.
4238
/// a way to look up for a package in activations what packages required it
4339
/// and all of the exact deps that it fulfilled.
4440
pub parents: Graph<PackageId, Rc<Vec<Dependency>>>,
45-
46-
// These are two cheaply-cloneable lists (O(1) clone) which are effectively
47-
// hash maps but are built up as "construction lists". We'll iterate these
48-
// at the very end and actually construct the map that we're making.
49-
pub resolve_graph: RcList<GraphNode>,
50-
pub resolve_replacements: RcList<(PackageId, PackageId)>,
51-
52-
// These warnings are printed after resolution.
53-
pub warnings: RcList<String>,
5441
}
5542

5643
/// When backtracking it can be useful to know how far back to go.
@@ -98,7 +85,6 @@ impl PackageId {
9885
impl Context {
9986
pub fn new(check_public_visible_dependencies: bool) -> Context {
10087
Context {
101-
resolve_graph: RcList::new(),
10288
resolve_features: im_rc::HashMap::new(),
10389
links: im_rc::HashMap::new(),
10490
public_dependency: if check_public_visible_dependencies {
@@ -107,9 +93,7 @@ impl Context {
10793
None
10894
},
10995
parents: Graph::new(),
110-
resolve_replacements: RcList::new(),
11196
activations: im_rc::HashMap::new(),
112-
warnings: RcList::new(),
11397
}
11498
}
11599

@@ -128,7 +112,6 @@ impl Context {
128112
);
129113
}
130114
im_rc::hashmap::Entry::Vacant(v) => {
131-
self.resolve_graph.push(GraphNode::Add(id));
132115
if let Some(link) = summary.links() {
133116
ensure!(
134117
self.links.insert(link, id).is_none(),
@@ -229,7 +212,7 @@ impl Context {
229212
}
230213

231214
/// Returns all dependencies and the features we want from them.
232-
fn resolve_features<'b>(
215+
pub fn resolve_features<'b>(
233216
&mut self,
234217
parent: Option<&Summary>,
235218
s: &'b Summary,
@@ -267,14 +250,20 @@ impl Context {
267250
.iter()
268251
.any(|d| d.is_optional() && d.name_in_toml() == dep.name_in_toml());
269252
if always_required && base.0 {
270-
self.warnings.push(format!(
271-
"Package `{}` does not have feature `{}`. It has a required dependency \
272-
with that name, but only optional dependencies can be used as features. \
273-
This is currently a warning to ease the transition, but it will become an \
274-
error in the future.",
275-
s.package_id(),
276-
dep.name_in_toml()
277-
));
253+
return Err(match parent {
254+
None => failure::format_err!(
255+
"Package `{}` does not have feature `{}`. It has a required dependency \
256+
with that name, but only optional dependencies can be used as features.",
257+
s.package_id(),
258+
dep.name_in_toml()
259+
)
260+
.into(),
261+
Some(p) => (
262+
p.package_id(),
263+
ConflictReason::RequiredDependencyAsFeatures(dep.name_in_toml()),
264+
)
265+
.into(),
266+
});
278267
}
279268
let mut base = base.1.clone();
280269
base.extend(dep.features().iter());
@@ -331,28 +320,28 @@ impl Context {
331320
Ok(ret)
332321
}
333322

334-
pub fn resolve_replacements(&self) -> HashMap<PackageId, PackageId> {
335-
let mut replacements = HashMap::new();
336-
let mut cur = &self.resolve_replacements;
337-
while let Some(ref node) = cur.head {
338-
let (k, v) = node.0;
339-
replacements.insert(k, v);
340-
cur = &node.1;
341-
}
342-
replacements
323+
pub fn resolve_replacements(
324+
&self,
325+
registry: &RegistryQueryer<'_>,
326+
) -> HashMap<PackageId, PackageId> {
327+
self.activations
328+
.values()
329+
.filter_map(|(s, _)| registry.used_replacement_for(s.package_id()))
330+
.collect()
343331
}
344332

345333
pub fn graph(&self) -> Graph<PackageId, Vec<Dependency>> {
346334
let mut graph: Graph<PackageId, Vec<Dependency>> = Graph::new();
347-
let mut cur = &self.resolve_graph;
348-
while let Some(ref node) = cur.head {
349-
match node.0 {
350-
GraphNode::Add(ref p) => graph.add(p.clone()),
351-
GraphNode::Link(ref a, ref b, ref dep) => {
352-
graph.link(a.clone(), b.clone()).push(dep.clone());
353-
}
335+
self.activations
336+
.values()
337+
.for_each(|(r, _)| graph.add(r.package_id()));
338+
for i in self.parents.iter() {
339+
graph.add(*i);
340+
for (o, e) in self.parents.edges(i) {
341+
let old_link = graph.link(*o, *i);
342+
assert!(old_link.is_empty());
343+
*old_link = e.to_vec();
354344
}
355-
cur = &node.1;
356345
}
357346
graph
358347
}

src/cargo/core/resolver/errors.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ impl fmt::Display for ResolveError {
5050

5151
pub type ActivateResult<T> = Result<T, ActivateError>;
5252

53+
#[derive(Debug)]
5354
pub enum ActivateError {
5455
Fatal(failure::Error),
5556
Conflict(PackageId, ConflictReason),
@@ -126,7 +127,7 @@ pub(super) fn activation_error(
126127
msg.push_str(&describe_path(&cx.parents.path_to_bottom(p)));
127128
}
128129

129-
let (features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors
130+
let (features_errors, mut other_errors): (Vec<_>, Vec<_>) = other_errors
130131
.drain(..)
131132
.partition(|&(_, r)| r.is_missing_features());
132133

@@ -145,6 +146,29 @@ pub(super) fn activation_error(
145146
// p == parent so the full path is redundant.
146147
}
147148

149+
let (required_dependency_as_features_errors, other_errors): (Vec<_>, Vec<_>) = other_errors
150+
.drain(..)
151+
.partition(|&(_, r)| r.is_required_dependency_as_features());
152+
153+
for &(p, r) in required_dependency_as_features_errors.iter() {
154+
if let ConflictReason::RequiredDependencyAsFeatures(ref features) = *r {
155+
msg.push_str("\n\nthe package `");
156+
msg.push_str(&*p.name());
157+
msg.push_str("` depends on `");
158+
msg.push_str(&*dep.package_name());
159+
msg.push_str("`, with features: `");
160+
msg.push_str(features);
161+
msg.push_str("` but `");
162+
msg.push_str(&*dep.package_name());
163+
msg.push_str("` does not have these features.\n");
164+
msg.push_str(
165+
" It has a required dependency with that name, \
166+
but only optional dependencies can be used as features.\n",
167+
);
168+
}
169+
// p == parent so the full path is redundant.
170+
}
171+
148172
if !other_errors.is_empty() {
149173
msg.push_str(
150174
"\n\nall possible versions conflict with \

src/cargo/core/resolver/mod.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use crate::util::errors::CargoResult;
6262
use crate::util::profile;
6363

6464
use self::context::{Activations, Context};
65-
use self::types::{Candidate, ConflictMap, ConflictReason, DepsFrame, GraphNode};
65+
use self::types::{Candidate, ConflictMap, ConflictReason, DepsFrame};
6666
use self::types::{RcVecIter, RegistryQueryer, RemainingDeps, ResolverProgress};
6767

6868
pub use self::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
@@ -124,7 +124,6 @@ pub fn resolve(
124124
registry: &mut dyn Registry,
125125
try_to_use: &HashSet<PackageId>,
126126
config: Option<&Config>,
127-
print_warnings: bool,
128127
check_public_visible_dependencies: bool,
129128
) -> CargoResult<Resolve> {
130129
let cx = Context::new(check_public_visible_dependencies);
@@ -143,7 +142,7 @@ pub fn resolve(
143142
}
144143
let resolve = Resolve::new(
145144
cx.graph(),
146-
cx.resolve_replacements(),
145+
cx.resolve_replacements(&registry),
147146
cx.resolve_features
148147
.iter()
149148
.map(|(k, v)| (*k, v.iter().map(|x| x.to_string()).collect()))
@@ -157,18 +156,6 @@ pub fn resolve(
157156
check_duplicate_pkgs_in_lockfile(&resolve)?;
158157
trace!("resolved: {:?}", resolve);
159158

160-
// If we have a shell, emit warnings about required deps used as feature.
161-
if let Some(config) = config {
162-
if print_warnings {
163-
let mut shell = config.shell();
164-
let mut warnings = &cx.warnings;
165-
while let Some(ref head) = warnings.head {
166-
shell.warn(&head.0)?;
167-
warnings = &head.1;
168-
}
169-
}
170-
}
171-
172159
Ok(resolve)
173160
}
174161

@@ -613,8 +600,6 @@ fn activate(
613600
let candidate_pid = candidate.summary.package_id();
614601
if let Some((parent, dep)) = parent {
615602
let parent_pid = parent.package_id();
616-
cx.resolve_graph
617-
.push(GraphNode::Link(parent_pid, candidate_pid, dep.clone()));
618603
Rc::make_mut(
619604
// add a edge from candidate to parent in the parents graph
620605
cx.parents.link(candidate_pid, parent_pid),
@@ -675,8 +660,6 @@ fn activate(
675660

676661
let candidate = match candidate.replace {
677662
Some(replace) => {
678-
cx.resolve_replacements
679-
.push((candidate_pid, replace.package_id()));
680663
if cx.flag_activated(&replace, method)? && activated {
681664
return Ok(None);
682665
}

src/cargo/core/resolver/types.rs

Lines changed: 38 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,12 @@ pub struct RegistryQueryer<'a> {
9595
pub registry: &'a mut (dyn Registry + 'a),
9696
replacements: &'a [(PackageIdSpec, Dependency)],
9797
try_to_use: &'a HashSet<PackageId>,
98-
cache: HashMap<Dependency, Rc<Vec<Candidate>>>,
9998
// If set the list of dependency candidates will be sorted by minimal
10099
// versions first. That allows `cargo update -Z minimal-versions` which will
101100
// specify minimum dependency versions to be used.
102101
minimal_versions: bool,
102+
cache: HashMap<Dependency, Rc<Vec<Candidate>>>,
103+
used_replacements: HashMap<PackageId, PackageId>,
103104
}
104105

105106
impl<'a> RegistryQueryer<'a> {
@@ -112,12 +113,17 @@ impl<'a> RegistryQueryer<'a> {
112113
RegistryQueryer {
113114
registry,
114115
replacements,
115-
cache: HashMap::new(),
116116
try_to_use,
117117
minimal_versions,
118+
cache: HashMap::new(),
119+
used_replacements: HashMap::new(),
118120
}
119121
}
120122

123+
pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
124+
self.used_replacements.get(&p).map(|&r| (p, r))
125+
}
126+
121127
/// Queries the `registry` to return a list of candidates for `dep`.
122128
///
123129
/// This method is the location where overrides are taken into account. If
@@ -212,6 +218,23 @@ impl<'a> RegistryQueryer<'a> {
212218
for dep in summary.dependencies() {
213219
debug!("\t{} => {}", dep.package_name(), dep.version_req());
214220
}
221+
if let Some(r) = &replace {
222+
if let Some(old) = self
223+
.used_replacements
224+
.insert(summary.package_id(), r.package_id())
225+
{
226+
debug_assert_eq!(
227+
r.package_id(),
228+
old,
229+
"we are inconsistent about witch replacement is used for {:?}, \
230+
one time we used {:?}, \
231+
now we are adding {:?}.",
232+
summary.package_id(),
233+
old,
234+
r.package_id()
235+
)
236+
}
237+
}
215238

216239
candidate.replace = replace;
217240
}
@@ -403,6 +426,12 @@ pub enum ConflictReason {
403426
/// candidate we're activating didn't actually have the feature `foo`.
404427
MissingFeatures(String),
405428

429+
/// A dependency listed features that ended up being a required dependency.
430+
/// For example we tried to activate feature `foo` but the
431+
/// candidate we're activating didn't actually have the feature `foo`
432+
/// it had a dependency `foo` instead.
433+
RequiredDependencyAsFeatures(InternedString),
434+
406435
// TODO: needs more info for `activation_error`
407436
// TODO: needs more info for `find_candidate`
408437
/// pub dep error
@@ -423,6 +452,13 @@ impl ConflictReason {
423452
}
424453
false
425454
}
455+
456+
pub fn is_required_dependency_as_features(&self) -> bool {
457+
if let ConflictReason::RequiredDependencyAsFeatures(_) = *self {
458+
return true;
459+
}
460+
false
461+
}
426462
}
427463

428464
/// A list of packages that have gotten in the way of resolving a dependency.
@@ -481,50 +517,3 @@ where
481517
}
482518

483519
impl<T: Clone> ExactSizeIterator for RcVecIter<T> {}
484-
485-
pub struct RcList<T> {
486-
pub head: Option<Rc<(T, RcList<T>)>>,
487-
}
488-
489-
impl<T> RcList<T> {
490-
pub fn new() -> RcList<T> {
491-
RcList { head: None }
492-
}
493-
494-
pub fn push(&mut self, data: T) {
495-
let node = Rc::new((
496-
data,
497-
RcList {
498-
head: self.head.take(),
499-
},
500-
));
501-
self.head = Some(node);
502-
}
503-
}
504-
505-
// Not derived to avoid `T: Clone`
506-
impl<T> Clone for RcList<T> {
507-
fn clone(&self) -> RcList<T> {
508-
RcList {
509-
head: self.head.clone(),
510-
}
511-
}
512-
}
513-
514-
// Avoid stack overflows on drop by turning recursion into a loop
515-
impl<T> Drop for RcList<T> {
516-
fn drop(&mut self) {
517-
let mut cur = self.head.take();
518-
while let Some(head) = cur {
519-
match Rc::try_unwrap(head) {
520-
Ok((_data, mut next)) => cur = next.head.take(),
521-
Err(_) => break,
522-
}
523-
}
524-
}
525-
}
526-
527-
pub enum GraphNode {
528-
Add(PackageId),
529-
Link(PackageId, PackageId, Dependency),
530-
}

0 commit comments

Comments
 (0)