Skip to content

Relax MSRV per requirement, not just per bucket - #51

Merged
jackmarsh merged 1 commit into
mainfrom
issue-50-msrv-conflict
Aug 24, 2026
Merged

Relax MSRV per requirement, not just per bucket#51
jackmarsh merged 1 commit into
mainfrom
issue-50-msrv-conflict

Conversation

@jackmarsh

Copy link
Copy Markdown
Collaborator

Closes #50. Found by running lock --upgrade --toolchain-version 1.70.0 over rust-corpus after #47 made MSRV filtering actually apply.

What it said

Error: version conflict:
dependencies of the request are unavailable no release of thiserror satisfies ^2.0.20

Not true. 2.0.20 exists. MSRV filtering removed it because it needs rustc 1.71, and the error never mentions rustc, so a reader on 1.70 goes hunting for a yanked release when their compiler is one version too old.

Why the existing escape hatch missed it

The relaxation was per bucket, firing only when filtering emptied one:

thiserror needs rustc
2.0.0 - 2.0.17 1.61
2.0.18 1.68
2.0.19 - 2.0.20 1.71

Eighteen releases survive on 1.70, so the bucket is not empty and nothing relaxes, even though every release satisfying ^2.0.20 was filtered out. A bucket can keep most of its releases and still lose every one a particular requirement asked for.

The change

A requirement that no supported release satisfies now takes the newest release that does, and says so:

warning: nothing that satisfies thiserror ^2.0.20 builds on rustc 1.70.0; taking 2.0.20, which needs rustc 1.71.0

This is the same choice the whole-bucket case already makes, and the existing comment states the principle: "refusing to resolve is worse than building something that may need a newer rustc".

Both halves are needed. The allowed range is built from filtered candidates, so bucket_range has to admit the relaxed version, and choose_version has to be able to return it. Fixing only the second does nothing, because an empty range is rejected before a version is ever chosen. I found that out by fixing the second one first and watching the error not move.

Result at rustc 1.70 on rust-corpus

Instead of stopping at thiserror it relaxes 140 crates, each named:

warning: nothing that satisfies tokio ^1.53.1 builds on rustc 1.70.0; taking 1.53.1, which needs rustc 1.71.0
warning: nothing that satisfies indexmap ^2.14.0 builds on rustc 1.70.0; taking 2.14.0, which needs rustc 1.85.0
warning: nothing that satisfies log ^0.4.33 builds on rustc 1.70.0; taking 0.4.33, which needs rustc 1.71.0

and gets far enough to reach a genuine conflict, reported as one with real version lists:

Because actix-http 3.x 3.13.3 depends on tokio-util 0.7.x 0.7.0 | ... | 0.7.16
and the request depends on tokio-util 0.7.x 0.7.19, the request is forbidden.

That is a real incompatibility: on 1.70, actix-http accepts tokio-util only up to 0.7.16 while the declarations require 0.7.19. Resolving a 2026 dependency set on a 2023 compiler is not always possible, and now it fails for the right reason.

No change at a current toolchain

  • rust-corpus at 1.97.1: nothing to do, no warnings, declarations byte-identical
  • rust-rules: nothing to do, no warnings

189 tests pass, including one covering the exact shape (a bucket that keeps most releases but loses every one satisfying the requirement), clippy clean, fmt clean.

Resolving rust-corpus for rustc 1.70 failed with

    Error: no release of thiserror satisfies ^2.0.20

which is not true. 2.0.20 exists. MSRV filtering removed it because it
needs rustc 1.71, and the error said nothing about rustc, so a reader
goes looking for a yanked release when the answer is that their compiler
is one version too old.

The existing relaxation is per bucket: it fires only when filtering
empties a bucket. thiserror 2.x keeps eighteen releases on 1.70 and
loses every one that satisfies ^2.0.20, so nothing relaxed.

A requirement that no supported release satisfies now takes the newest
release that does, and says which crate, which requirement, which
version and which rustc it needs. That is the same choice the
whole-bucket case already makes, applied where the requirement rather
than the bucket is what ran out.

The range is built from filtered candidates, so both halves are needed:
bucket_range has to admit the relaxed version, and choose_version has to
be able to return it.

At rustc 1.70 the corpus now relaxes 140 crates rather than stopping at
the first, and gets far enough to hit a real conflict: actix-http 3.13.3
accepts tokio-util up to 0.7.16 while the declarations require 0.7.19.
That one is a genuine incompatibility and is reported as one.

Nothing changes at a current toolchain. Both repos resolve with no
warnings and no movement.
@jackmarsh
jackmarsh merged commit 0c59067 into main Aug 24, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A version conflict caused by MSRV filtering does not say so

1 participant