Repository navigation
1.100 beta crater regression: Behavior change with #[derive(Ord)] and manual impl PartialOrd #164141
Description
Activity
- addedregression-from-stable-to-betaPerformance or correctness regression from stable to beta.Performance or correctness regression from stable to beta.C-bugCategory: This is a bug.Category: This is a bug.E-needs-bisectionCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustcCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustcE-needs-mcveCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable ExampleCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example
on Oct 11, 2026 - addedneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingI-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}Issue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
on Oct 11, 2026 - added a parent issue
on Oct 11, 2026 Probably one of the builtin macros changes did something. cc @cyrgani
Ordspecifically says to not combinederives and manualimpl, and there's even a clippy lint for this.Because Ord implies a stronger ordering relationship than PartialOrd, and both Ord and PartialOrd must agree, you must choose how to implement Ord first. You can choose to derive it, or implement it manually. If you derive it, you should derive all four traits. If you implement it manually, you should manually implement all four traits, based on the implementation of Ord.
It's likely that
Vec::iter().map().max()got some new specialization that's using thederivedOrdinstead of thePartialOrdimpl.I did a bisection and you're right.
searched toolchains 2fb4ed81d6a3131a5ba6d75fa1aeb15bc998a5f6 through fd98695850065878fa7de48808dca4f0901cefbb ******************************************************************************** Regression in c9b7f178899788fac53d942b82cf97665ee59aaa ******************************************************************************** Attempting to search unrolled perf builds Found commits ["9aa503aa", "369655d3", "02a976f5", "d3bf528c", "33c5df03"] installing 9aa503aa0d7d67f132376e5425174a869a273ace rust-std-nightly-x86_64-unknown-linux-gnu: 31.31 MB / 31.31 MB [========================================================] 100.00 % 11.32 MB/s testing... RESULT: 9aa503aa0d7d67f132376e5425174a869a273ace, ===> Compile error uninstalling 9aa503aa0d7d67f132376e5425174a869a273ace Regression in https://github.com/rust-lang/rust/commit/9aa503aa0d7d67f132376e5425174a869a273ace. Note that if it is a legacy rollup build, it might be available in https://github.com/rust-lang-ci/rust/commit/9aa503aa0d7d67f132376e5425174a869a273ace. The PR introducing the regression in this rollup is #160203: `Iterator::{min,max}(_by_key)` should use overridden `min`/…Regressed in #160203. cc @scottmcm (author), @JohnTitor (reviewer)
- addedA-iteratorsArea: IteratorsArea: IteratorsT-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.S-has-bisectionStatus: A bisection has been found for this issueStatus: A bisection has been found for this issueand removedE-needs-bisectionCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustcCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustcneeds-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triagingThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
on Oct 11, 2026 Minimal reproducer:
use std::cmp::Ordering; #[derive(PartialEq, Eq, Ord)] struct Thing(i32); impl PartialOrd for Thing { fn partial_cmp(&self, other: &Thing) -> Option<Ordering> { Some(self.0.cmp(&other.0).reverse()) } } fn main() { println!("{}", [Thing(1), Thing(2)].iter().max().unwrap().0); }
Outputs 2 on stable, but outputs 1 on beta.
It seems kinda odd that it would call
partial_cmpon beta. But I assume this is acceptable breakage?- addedS-has-mcveStatus: A Minimal Complete and Verifiable Example has been found for this issueStatus: A Minimal Complete and Verifiable Example has been found for this issueand removedE-needs-mcveCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable ExampleCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example
on Oct 11, 2026 Ah, it's already in the release notes: #161391
That PR causes the iterator to start calling
Ord::max, which is not overridden, so it uses the default impl which doesThing < Thing(using the<operator), which calls intopartial_cmp. Previously it wasreduceoverOrd::cmp, which has correct behavior from the derive.
This regression was discovered in the 1.100 beta crater run.
https://crater-reports.s3.amazonaws.com/beta-1.100-4/1.100.0-beta.1/gh/aprowe.equity-cli/log.txt
Possibly relevant code: https://github.com/aprowe/equity-cli/blob/481a9d0d48d7fd651c1afa667cd3ce210b3ac30d/src/poker_hand.rs#L63