Skip to content

fix(prerelease): preserve locked prerelease deps - #17464

Open
weihanglo wants to merge 9 commits into
rust-lang:masterfrom
weihanglo:precise-prerelease-pins
Open

weihanglo wants to merge 9 commits into
rust-lang:masterfrom
weihanglo:precise-prerelease-pins

Conversation

@weihanglo

@weihanglo weihanglo commented Sep 11, 2026

Copy link
Copy Markdown
Member

What does this PR try to resolve?

This implements the consumption side of for prerelease version pinning, addressing the issue mentioned in #13290 (comment) (and all the above bug reports)

The initial implementation only dealt with the the resolution creation side (cargo update --precise <prerelease>), but we never touched how the pinned prerelease versions in lockfile are consumed for subsequent cargo commands.

In order to use the pinned prerelease for subsequent cargo commands beyond cargo update, this adds a new standalone flag -Zprerelease. Previously it was under the umbrella flag -Zunstable-options in cargo update.

How to test and review this PR?

The concept of the implementation is simple: Introduce a new enum modeling version request match mode.

  • Default for the default semver matching semantics
  • Prerelease for additional pre-release matching

However, the area this PR touching is messy, and the lockfile consumption site is hard to identify. This is my best-effort work, though I am not sure if it is done 100% correct.

Here is how I checked whether it needs the default semantic or extra prerelease semantic:

  • If an OptVersionReq is Locked or Precise, just use the Default mode. Mode makes no difference if it is already exact. For example, code block under req.is_locked() condition doesn't need prerelease mode.
  • Prerelease mode can only come from lockfile or --precise. Otherwise we may accidentally select more candidates.

Future extensions

  • While We mostly only want to apply prerelease mode on lockfile consumption side + pre-existing cargo update --precise locations at this moment, in the future we can probably make this configurable via resolver.prerelease accepting deny|locked-only|fallback|preferred or else.

  • There are some other "extensions" like supporting [patch]/[replace]. I meant like whether a [patch] entry of a prerelease version is applicable. I deferred them to future PRs as it is not purely lockfile consumption fix and the RFC didn't mention that, so not going do it right now.

🤖 LLM disclosure: LLM helped me find some more edge cases in [patch]. Code are hand written though.

@rustbot rustbot added A-crate-dependencies Area: [dependencies] of any kind A-dependency-resolution Area: dependency resolution and the resolver A-directory-source Area: directory sources (vendoring) A-documenting-cargo-itself Area: Cargo's documentation A-registries Area: registries A-semver Area: semver specifications, version matching, etc. A-unstable Area: nightly unstable support Command-add S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 11, 2026
@rustbot

rustbot commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

r? @epage

rustbot has assigned @epage.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: @epage, @weihanglo
  • @epage, @weihanglo expanded to epage, weihanglo

@weihanglo weihanglo left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry this is not easy to review. Let me know if you want more smaller chunks.

View changes since this review

Comment thread src/workspace/registry.rs
// have a match whatsoever. Otherwise we need to check
// `[patch]`...
if !dep.matches_ignoring_source(id, mode) {
if !dep.matches_ignoring_source(id, VersionReqMatchMode::Default) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to stick to the default mode because we don't currently support matching prerelease from a [patch] entry

Comment thread src/workspace/registry.rs
assert!(!self.patches_locked);

let mode = VersionReqMatchMode::Default;
let mode = if self.gctx.cli_unstable().prerelease {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is asking whether a previously locked matches dependency requirement from a [patch] entry, not the version of [patch] itself.

@weihanglo

Copy link
Copy Markdown
Member Author

cc @celinval

@weihanglo weihanglo changed the title fix(prerelease): preserve locked prerelease deps from patches fix(prerelease): preserve locked prerelease deps Sep 11, 2026
@epage

epage commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Is this the right direction to go?

We could let people opt-in this feature. However, this feature is now behind -Zunstable-options for only cargo update. We could switch back to a dedicated -Zprerelease unstable flag, so people can opt-in this behavior for commands like cargo build and other build commands. What do people think?

I would have assumed the fix would be to do this.

@weihanglo

Copy link
Copy Markdown
Member Author

Yeah, that is what this PR is doing. Both consumption and generation side require -Zprerelease flag.

Were you thinking of a different implementation or anything I am missing?

@weihanglo
weihanglo force-pushed the precise-prerelease-pins branch from 97382f3 to dd76ab8 Compare September 14, 2026 18:48
@rustbot

This comment has been minimized.

@celinval celinval left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Thanks for doing this, @weihanglo!

View changes since this review

Comment thread doc/book/src/reference/unstable.md Outdated
@@ -1,4 +1,4 @@
# Unstable Features
# Unstable Featuresunstable.md

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo?

Suggested change
# Unstable Featuresunstable.md
# Unstable Features

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My neovim love pasting nonsense.

```

It's possible to update `my-dependency` to a pre-release with `update -Zunstable-options my-dependency --precise 0.1.2-pre.0`.
It's possible to update `my-dependency` to a pre-release with `update -Zprerelease my-dependency --precise 0.1.2-pre.0`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document that the same feature has to be enabled for subsequent cargo commands?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in dcd3c7a.
Let me know if it is good.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! Thank you

@weihanglo
weihanglo force-pushed the precise-prerelease-pins branch from dd76ab8 to dcd3c7a Compare September 15, 2026 23:51
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

`-Zunstable-options` only gates `cargo update` for generation side.
This prepare use it in consumption side,
for example, `cargo build -Zprerelease`.
The second resolution either replaces the locked prerelease,
or fails when another dep requires a non-prerelease requirement.
Use prerelease if they are in `Cargo.lock` already,
so locked prerelease survive subsequent resolution.
We use prelease match mode for `--precies` when either
"update to" or locked version is pre-release.
Otherwise the entire `--precise` will be ignored
because default mode doesn't match the locked prerelease version.
@weihanglo
weihanglo force-pushed the precise-prerelease-pins branch from dcd3c7a to c84bd1c Compare September 18, 2026 02:36
@rustbot

rustbot commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-crate-dependencies Area: [dependencies] of any kind A-dependency-resolution Area: dependency resolution and the resolver A-directory-source Area: directory sources (vendoring) A-documenting-cargo-itself Area: Cargo's documentation A-registries Area: registries A-semver Area: semver specifications, version matching, etc. A-unstable Area: nightly unstable support Command-add S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants