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