Skip to main content

cargo/core/resolver/
dep_cache.rs

1//! There are 2 sources of facts for the resolver:
2//!
3//! - The `Registry` tells us for a `Dependency` what versions are available to fulfil it.
4//! - The `Summary` tells us for a version (and features) what dependencies need to be fulfilled for it to be activated.
5//!
6//! These constitute immutable facts, the soled ground truth that all other inference depends on.
7//! Theoretically this could all be enumerated ahead of time, but we want to be lazy and only
8//! look up things we need to. The compromise is to cache the results as they are computed.
9//!
10//! This module impl that cache in all the gory details
11
12use crate::core::resolver::context::ResolverContext;
13use crate::core::resolver::errors::describe_path_in_context;
14use crate::core::resolver::types::{ConflictReason, DepInfo, FeaturesSet};
15use crate::core::resolver::{
16    ActivateError, ActivateResult, CliFeatures, RequestedFeatures, ResolveOpts, VersionOrdering,
17    VersionPreferences,
18};
19use crate::core::{
20    Dependency, FeatureValue, PackageId, PackageIdSpec, PackageIdSpecQuery, Registry, Summary,
21};
22use crate::sources::IndexSummary;
23use crate::sources::source::QueryKind;
24use crate::util::LocalPollAdapter;
25use crate::util::closest_msg;
26use crate::util::errors::CargoResult;
27use crate::util::interning::{INTERNED_DEFAULT, InternedString};
28
29use crate::util::data_structures::{HashMap, HashSet};
30use anyhow::Context as _;
31use std::cell::RefCell;
32use std::collections::BTreeSet;
33use std::fmt::Write;
34use std::rc::Rc;
35use std::task::Poll;
36use tracing::debug;
37
38pub struct RegistryQueryerAsync<'a, T: Registry> {
39    pub registry: &'a T,
40    replacements: &'a [(PackageIdSpec, Dependency)],
41    version_prefs: &'a VersionPreferences,
42    /// all the cases we ended up using a supplied replacement
43    used_replacements: RefCell<HashMap<PackageId, Summary>>,
44}
45
46impl<'a, T: Registry> RegistryQueryerAsync<'a, T> {
47    pub fn new(
48        registry: &'a T,
49        replacements: &'a [(PackageIdSpec, Dependency)],
50        version_prefs: &'a VersionPreferences,
51    ) -> Self {
52        RegistryQueryerAsync {
53            registry,
54            replacements,
55            version_prefs,
56            used_replacements: RefCell::new(HashMap::default()),
57        }
58    }
59
60    /// Queries the `registry` to return a list of candidates for `dep`.
61    ///
62    /// This method is the location where overrides are taken into account. If
63    /// any candidates are returned which match an override then the override is
64    /// applied by performing a second query for what the override should
65    /// return.
66    async fn query(
67        &self,
68        key: &(Dependency, Option<VersionOrdering>),
69    ) -> CargoResult<Rc<Vec<Summary>>> {
70        let (dep, first_version) = key;
71        let mut summaries = Vec::new();
72        self.registry
73            .query(dep, QueryKind::Exact, &mut |s| match s {
74                IndexSummary::Candidate(summary) => {
75                    // Filter out versions that are too new,
76                    // unless pinned by a lock file or a `[patch]` entry.
77                    //
78                    // Unlike yanked, `cargo update --precise` does not opt in here
79                    // unless `resolver.incompatible-publish-age = "allow"` is set.
80                    let too_new = self.version_prefs.too_new(&summary).is_some();
81                    if !too_new || self.version_prefs.should_prefer(&summary.package_id()) {
82                        summaries.push(summary);
83                    }
84                }
85                // Prefer yanked only when
86                //
87                // * it is recorded in lock file or a `[patch]` entry
88                // * it is specified in `cargo update --precise`
89                IndexSummary::Yanked(summary) => {
90                    let pkg_id = summary.package_id();
91                    let allow_precise = pkg_id
92                        .source_id()
93                        .precise_registry_version(pkg_id.name().as_str())
94                        .is_some_and(|(_, to)| to == pkg_id.version());
95                    if allow_precise || self.version_prefs.should_prefer(&pkg_id) {
96                        summaries.push(summary);
97                    }
98                }
99                _ => {}
100            })
101            .await?;
102
103        for summary in summaries.iter() {
104            let mut potential_matches = self
105                .replacements
106                .iter()
107                .filter(|(spec, _)| spec.matches(summary.package_id()));
108
109            let Some((spec, dep)) = potential_matches.next() else {
110                continue;
111            };
112            debug!(
113                "found an override for {} {}",
114                dep.package_name(),
115                dep.version_req()
116            );
117
118            let mut summaries = self
119                .registry
120                .query_vec(dep, QueryKind::Exact)
121                .await?
122                .into_iter()
123                .filter_map(|s| match s {
124                    IndexSummary::Candidate(s) => Some(s),
125                    _ => None,
126                });
127            let s = summaries.next().ok_or_else(|| {
128                anyhow::format_err!(
129                    "no matching package for override `{}` found\n\
130                     location searched: {}\n\
131                     version required: {}",
132                    spec,
133                    dep.source_id(),
134                    dep.version_req()
135                )
136            })?;
137            let summaries = summaries.collect::<Vec<_>>();
138            if !summaries.is_empty() {
139                let bullets = summaries
140                    .iter()
141                    .map(|s| format!("  * {}", s.package_id()))
142                    .collect::<Vec<_>>();
143                return Err(anyhow::anyhow!(
144                    "the replacement specification `{}` matched \
145                     multiple packages:\n  * {}\n{}",
146                    spec,
147                    s.package_id(),
148                    bullets.join("\n")
149                ));
150            }
151
152            assert_eq!(
153                s.name(),
154                summary.name(),
155                "dependency should be hard coded to have the same name"
156            );
157            if s.version() != summary.version() {
158                return Err(anyhow::anyhow!(
159                    "replacement specification `{}` matched {} and tried to override it with {}\n\
160                     avoid matching unrelated packages by being more specific",
161                    spec,
162                    summary.version(),
163                    s.version(),
164                ));
165            }
166
167            let replace = if s.source_id() == summary.source_id() {
168                debug!("Preventing\n{:?}\nfrom replacing\n{:?}", summary, s);
169                None
170            } else {
171                Some(s)
172            };
173            let matched_spec = spec.clone();
174
175            // Make sure no duplicates
176            if let Some((spec, _)) = potential_matches.next() {
177                return Err(anyhow::anyhow!(
178                    "overlapping replacement specifications found:\n\n  \
179                     * {}\n  * {}\n\nboth specifications match: {}",
180                    matched_spec,
181                    spec,
182                    summary.package_id()
183                ));
184            }
185
186            for dep in summary.dependencies() {
187                debug!("\t{} => {}", dep.package_name(), dep.version_req());
188            }
189            if let Some(r) = replace {
190                self.used_replacements
191                    .borrow_mut()
192                    .insert(summary.package_id(), r);
193            }
194        }
195
196        self.version_prefs
197            .sort_summaries(&mut summaries, *first_version);
198        Ok(Rc::new(summaries))
199    }
200}
201
202/// Wrapper around RegistryQueryerAsync that provides
203/// caching and a Poll based interface using `LocalPollAdapter`.
204pub struct RegistryQueryer<'a, T: Registry> {
205    inner: Rc<RegistryQueryerAsync<'a, T>>,
206    poller: LocalPollAdapter<
207        'a,
208        Rc<RegistryQueryerAsync<'a, T>>,
209        (Dependency, Option<VersionOrdering>),
210        CargoResult<Rc<Vec<Summary>>>,
211    >,
212
213    /// a cache of `Dependency`s that are required for a `Summary`
214    ///
215    /// HACK: `first_version` is not kept in the cache key is it is 1:1 with
216    /// `parent.is_none()` (the first element of the cache key) as it doesn't change through
217    /// execution.
218    summary_cache: HashMap<
219        (Option<PackageId>, Summary, ResolveOpts),
220        (Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>, bool),
221    >,
222}
223
224impl<'a, T: Registry> RegistryQueryer<'a, T> {
225    pub fn new(
226        registry: &'a T,
227        replacements: &'a [(PackageIdSpec, Dependency)],
228        version_prefs: &'a VersionPreferences,
229    ) -> Self {
230        let inner = Rc::new(RegistryQueryerAsync::new(
231            registry,
232            replacements,
233            version_prefs,
234        ));
235        Self {
236            inner: inner.clone(),
237            poller: LocalPollAdapter::new(inner),
238            summary_cache: HashMap::default(),
239        }
240    }
241
242    pub fn registry(&self) -> &T {
243        self.inner.registry
244    }
245
246    pub fn version_prefs(&self) -> &VersionPreferences {
247        self.inner.version_prefs
248    }
249
250    pub fn query(
251        &mut self,
252        dep: &Dependency,
253        first_version: Option<VersionOrdering>,
254    ) -> Poll<CargoResult<Rc<Vec<Summary>>>> {
255        self.poller
256            .poll(RegistryQueryerAsync::query, (dep.clone(), first_version))
257    }
258
259    pub fn wait(&mut self) -> CargoResult<bool> {
260        let pending = self.poller.pending_count();
261        // Have all outstanding registry requests been completed?
262        let mut all_ready = self.poller.wait();
263        debug!(target: "cargo::core::resolver::restarting", pending);
264
265        // Remove cached summaries that we produced with incomplete information.
266        self.summary_cache.retain(|_, (_, r)| {
267            if !*r {
268                all_ready = false;
269            }
270            *r
271        });
272
273        Ok(all_ready)
274    }
275
276    pub fn used_replacement_for(&self, p: PackageId) -> Option<(PackageId, PackageId)> {
277        self.inner
278            .used_replacements
279            .borrow()
280            .get(&p)
281            .map(|r| (p, r.package_id()))
282    }
283
284    pub fn replacement_summary(&self, p: PackageId) -> Option<Summary> {
285        self.inner.used_replacements.borrow().get(&p).cloned()
286    }
287
288    /// Find out what dependencies will be added by activating `candidate`,
289    /// with features described in `opts`. Then look up in the `registry`
290    /// the candidates that will fulfil each of these dependencies, as it is the
291    /// next obvious question.
292    pub fn build_deps(
293        &mut self,
294        cx: &ResolverContext,
295        parent: Option<PackageId>,
296        candidate: &Summary,
297        opts: &ResolveOpts,
298        first_version: Option<VersionOrdering>,
299    ) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
300        // if we have calculated a result before, then we can just return it,
301        // as it is a "pure" query of its arguments.
302        if let Some(out) = self
303            .summary_cache
304            .get(&(parent, candidate.clone(), opts.clone()))
305        {
306            return Ok(out.0.clone());
307        }
308        // First, figure out our set of dependencies based on the requested set
309        // of features. This also calculates what features we're going to enable
310        // for our own dependencies.
311        let (used_features, deps) = resolve_features(parent, candidate, opts)?;
312
313        // Next, transform all dependencies into a list of possible candidates
314        // which can satisfy that dependency.
315        let mut all_ready = true;
316        let mut deps = deps
317            .into_iter()
318            .filter_map(|(dep, features)| match self.query(&dep, first_version) {
319                Poll::Ready(Ok(candidates)) => Some(Ok((dep, candidates, features))),
320                Poll::Pending => {
321                    all_ready = false;
322                    // we can ignore Pending deps, resolve will be repeatedly called
323                    // until there are none to ignore
324                    None
325                }
326                Poll::Ready(Err(e)) => Some(Err(e).with_context(|| {
327                    format!(
328                        "failed to get `{}` as a dependency of {}",
329                        dep.package_name(),
330                        describe_path_in_context(cx, &candidate.package_id()),
331                    )
332                })),
333            })
334            .collect::<CargoResult<Vec<DepInfo>>>()?;
335
336        // Attempt to resolve dependencies with fewer candidates before trying
337        // dependencies with more candidates. This way if the dependency with
338        // only one candidate can't be resolved we don't have to do a bunch of
339        // work before we figure that out.
340        deps.sort_by_key(|(_, a, _)| a.len());
341
342        let out = Rc::new((used_features, Rc::new(deps)));
343
344        // If we succeed we add the result to the cache so we can use it again next time.
345        // We don't cache the failure cases as they don't impl Clone.
346        self.summary_cache.insert(
347            (parent, candidate.clone(), opts.clone()),
348            (out.clone(), all_ready),
349        );
350
351        Ok(out)
352    }
353}
354
355/// Returns the features we ended up using and
356/// all dependencies and the features we want from each of them.
357pub fn resolve_features<'b>(
358    parent: Option<PackageId>,
359    s: &'b Summary,
360    opts: &'b ResolveOpts,
361) -> ActivateResult<(HashSet<InternedString>, Vec<(Dependency, FeaturesSet)>)> {
362    // First, filter by dev-dependencies.
363    let deps = s.dependencies();
364    let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps);
365
366    let reqs = build_requirements(parent, s, opts)?;
367    let mut ret = Vec::new();
368    let default_dep = BTreeSet::new();
369    let mut valid_dep_names = HashSet::default();
370
371    // Next, collect all actually enabled dependencies and their features.
372    for dep in deps {
373        // Skip optional dependencies, but not those enabled through a
374        // feature
375        if dep.is_optional() && !reqs.deps.contains_key(&dep.name_in_toml()) {
376            continue;
377        }
378        valid_dep_names.insert(dep.name_in_toml());
379        // So we want this dependency. Move the features we want from
380        // `feature_deps` to `ret` and register ourselves as using this
381        // name.
382        let mut base = reqs
383            .deps
384            .get(&dep.name_in_toml())
385            .unwrap_or(&default_dep)
386            .clone();
387        base.extend(dep.features().iter());
388        ret.push((dep.clone(), Rc::new(base)));
389    }
390
391    // This is a special case for command-line `--features
392    // dep_name/feat_name` where `dep_name` does not exist. All other
393    // validation is done either in `build_requirements` or
394    // `build_feature_map`.
395    if parent.is_none() {
396        for dep_name in reqs.deps.keys() {
397            if !valid_dep_names.contains(dep_name) {
398                let e = RequirementError::MissingDependency(*dep_name);
399                return Err(e.into_activate_error(parent, s));
400            }
401        }
402    }
403
404    Ok((reqs.into_features(), ret))
405}
406
407/// Takes requested features for a single package from the input `ResolveOpts` and
408/// recurses to find all requested features, dependencies and requested
409/// dependency features in a `Requirements` object, returning it to the resolver.
410fn build_requirements<'a, 'b: 'a>(
411    parent: Option<PackageId>,
412    s: &'a Summary,
413    opts: &'b ResolveOpts,
414) -> ActivateResult<Requirements<'a>> {
415    let mut reqs = Requirements::new(s);
416
417    let handle_default = |uses_default_features, reqs: &mut Requirements<'_>| {
418        if uses_default_features && s.features().contains_key("default") {
419            if let Err(e) = reqs.require_feature(INTERNED_DEFAULT) {
420                return Err(e.into_activate_error(parent, s));
421            }
422        }
423        Ok(())
424    };
425
426    match &opts.features {
427        RequestedFeatures::CliFeatures(CliFeatures {
428            features,
429            all_features,
430            uses_default_features,
431        }) => {
432            if *all_features {
433                for key in s.features().keys() {
434                    if let Err(e) = reqs.require_feature(*key) {
435                        return Err(e.into_activate_error(parent, s));
436                    }
437                }
438            }
439
440            for fv in features.iter() {
441                if let Err(e) = reqs.require_value(fv) {
442                    return Err(e.into_activate_error(parent, s));
443                }
444            }
445            handle_default(*uses_default_features, &mut reqs)?;
446        }
447        RequestedFeatures::DepFeatures {
448            features,
449            uses_default_features,
450        } => {
451            for feature in features.iter() {
452                if let Err(e) = reqs.require_feature(*feature) {
453                    return Err(e.into_activate_error(parent, s));
454                }
455            }
456            handle_default(*uses_default_features, &mut reqs)?;
457        }
458    }
459
460    Ok(reqs)
461}
462
463/// Set of feature and dependency requirements for a package.
464#[derive(Debug)]
465struct Requirements<'a> {
466    summary: &'a Summary,
467    /// The deps map is a mapping of dependency name to list of features enabled.
468    ///
469    /// The resolver will activate all of these dependencies, with the given
470    /// features enabled.
471    deps: HashMap<InternedString, BTreeSet<InternedString>>,
472    /// The set of features enabled on this package which is later used when
473    /// compiling to instruct the code what features were enabled.
474    features: HashSet<InternedString>,
475}
476
477/// An error for a requirement.
478///
479/// This will later be converted to an `ActivateError` depending on whether or
480/// not this is a dependency or a root package.
481enum RequirementError {
482    /// The package does not have the requested feature.
483    MissingFeature(InternedString),
484    /// The package does not have the requested dependency.
485    MissingDependency(InternedString),
486    /// A feature has a direct cycle to itself.
487    ///
488    /// Note that cycles through multiple features are allowed (but perhaps
489    /// they shouldn't be?).
490    Cycle(InternedString),
491}
492
493impl Requirements<'_> {
494    fn new(summary: &Summary) -> Requirements<'_> {
495        Requirements {
496            summary,
497            deps: HashMap::default(),
498            features: HashSet::default(),
499        }
500    }
501
502    fn into_features(self) -> HashSet<InternedString> {
503        self.features
504    }
505
506    fn require_dep_feature(
507        &mut self,
508        package: InternedString,
509        feat: InternedString,
510        weak: bool,
511    ) -> Result<(), RequirementError> {
512        // If `package` is indeed an optional dependency then we activate the
513        // feature named `package`, but otherwise if `package` is a required
514        // dependency then there's no feature associated with it.
515        if !weak
516            && self
517                .summary
518                .dependencies()
519                .iter()
520                .any(|dep| dep.name_in_toml() == package && dep.is_optional())
521        {
522            // This optional dependency may not have an implicit feature of
523            // the same name if the `dep:` syntax is used to avoid creating
524            // that implicit feature.
525            if self.summary.features().contains_key(&package) {
526                self.require_feature(package)?;
527            }
528        }
529        self.deps.entry(package).or_default().insert(feat);
530        Ok(())
531    }
532
533    fn require_dependency(&mut self, pkg: InternedString) {
534        self.deps.entry(pkg).or_default();
535    }
536
537    fn require_feature(&mut self, feat: InternedString) -> Result<(), RequirementError> {
538        if !self.features.insert(feat) {
539            // Already seen this feature.
540            return Ok(());
541        }
542
543        let Some(fvs) = self.summary.features().get(&feat) else {
544            return Err(RequirementError::MissingFeature(feat));
545        };
546
547        for fv in fvs {
548            if let FeatureValue::Feature(dep_feat) = fv {
549                if *dep_feat == feat {
550                    return Err(RequirementError::Cycle(feat));
551                }
552            }
553            self.require_value(fv)?;
554        }
555        Ok(())
556    }
557
558    fn require_value(&mut self, fv: &FeatureValue) -> Result<(), RequirementError> {
559        match fv {
560            FeatureValue::Feature(feat) => self.require_feature(*feat)?,
561            FeatureValue::Dep { dep_name } => self.require_dependency(*dep_name),
562            FeatureValue::DepFeature {
563                dep_name,
564                dep_feature,
565                // Weak features are always activated in the dependency
566                // resolver. They will be narrowed inside the new feature
567                // resolver.
568                weak,
569            } => self.require_dep_feature(*dep_name, *dep_feature, *weak)?,
570        };
571        Ok(())
572    }
573}
574
575impl RequirementError {
576    fn into_activate_error(self, parent: Option<PackageId>, summary: &Summary) -> ActivateError {
577        match self {
578            RequirementError::MissingFeature(feat) => {
579                let deps: Vec<_> = summary
580                    .dependencies()
581                    .iter()
582                    .filter(|dep| dep.name_in_toml() == feat)
583                    .collect();
584                if deps.is_empty() {
585                    return match parent {
586                        None => {
587                            let closest = closest_msg(
588                                &feat.as_str(),
589                                summary.features().keys(),
590                                |key| &key,
591                                "feature",
592                            );
593                            ActivateError::Fatal(anyhow::format_err!(
594                                "package `{}` does not have the feature `{}`{}",
595                                summary.package_id(),
596                                feat,
597                                closest
598                            ))
599                        }
600                        Some(p) => ActivateError::Conflict(p, ConflictReason::MissingFeature(feat)),
601                    };
602                }
603                if deps.iter().any(|dep| dep.is_optional()) {
604                    match parent {
605                        None => {
606                            let mut features =
607                                features_enabling_dependency_sorted(summary, feat).peekable();
608                            let mut suggestion = String::new();
609                            if features.peek().is_some() {
610                                suggestion = format!(
611                                    "\nDependency `{}` would be enabled by these features:",
612                                    feat
613                                );
614                                for feature in (&mut features).take(3) {
615                                    let _ = write!(&mut suggestion, "\n\t- `{}`", feature);
616                                }
617                                if features.peek().is_some() {
618                                    suggestion.push_str("\n\t  ...");
619                                }
620                            }
621                            ActivateError::Fatal(anyhow::format_err!(
622                                "\
623package `{}` does not have feature `{}`
624
625help: an optional dependency \
626with that name exists, but the `features` table includes it with the \"dep:\" \
627syntax so it does not have an implicit feature with that name{}",
628                                summary.package_id(),
629                                feat,
630                                suggestion
631                            ))
632                        }
633                        Some(p) => ActivateError::Conflict(
634                            p,
635                            ConflictReason::NonImplicitDependencyAsFeature(feat),
636                        ),
637                    }
638                } else {
639                    match parent {
640                        None => ActivateError::Fatal(anyhow::format_err!(
641                            "package `{}` does not have feature `{}`
642
643help: a dependency with that name exists but it is required dependency and only optional dependencies can be used as features.",
644                            summary.package_id(),
645                            feat,
646                        )),
647                        Some(p) => ActivateError::Conflict(
648                            p,
649                            ConflictReason::RequiredDependencyAsFeature(feat),
650                        ),
651                    }
652                }
653            }
654            RequirementError::MissingDependency(dep_name) => {
655                match parent {
656                    None => ActivateError::Fatal(anyhow::format_err!(
657                        "package `{}` does not have a dependency named `{}`",
658                        summary.package_id(),
659                        dep_name
660                    )),
661                    // This code path currently isn't used, since `foo/bar`
662                    // and `dep:` syntax is not allowed in a dependency.
663                    Some(p) => ActivateError::Conflict(p, ConflictReason::MissingFeature(dep_name)),
664                }
665            }
666            RequirementError::Cycle(feat) => ActivateError::Fatal(anyhow::format_err!(
667                "cyclic feature dependency: feature `{}` depends on itself",
668                feat
669            )),
670        }
671    }
672}
673
674/// Collect any features which enable the optional dependency "target_dep".
675///
676/// The returned value will be sorted.
677fn features_enabling_dependency_sorted(
678    summary: &Summary,
679    target_dep: InternedString,
680) -> impl Iterator<Item = InternedString> + '_ {
681    let iter = summary
682        .features()
683        .iter()
684        .filter(move |(_, values)| {
685            for value in *values {
686                match value {
687                    FeatureValue::Dep { dep_name }
688                    | FeatureValue::DepFeature {
689                        dep_name,
690                        weak: false,
691                        ..
692                    } if dep_name == &target_dep => return true,
693                    _ => (),
694                }
695            }
696            false
697        })
698        .map(|(name, _)| *name);
699    // iter is already sorted because it was constructed from a BTreeMap.
700    iter
701}