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