You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If I read #155598 correctly, it only checks the outer container (Packet in this case) for if their PartialOrd+Ord are derived. But PacketValue's PartialOrd impl is incompatible to a derived Ord to begin with, because the last two match arms lift Deeper above what a derived Ord does, which goes by order of declaration. That way, the combined manual PartialOrd + incompatible derived Ord from PacketValue creep into the derived Packet impls, where #155598 applies an optimization that it thinks is safe but actually isn't.
and was only made visible (or only easily visible) with the changes to derive(Ord). There's a comment in that file about how the PartialOrd version is unsound.
Oh wait actually, is this just a violation of the PartialEq/PartialOrd relationship? Your minimized example derivesPartialEq which means that values of PacketValue are only considered equal if and only if their discriminants are equal and their values are equal. Your minimization's PartialOrd however, can return Some(Equal) for values where that's not the case.
The PartialOrd docs say:
a == b if and only if partial_cmp(a, b) == Some(Equal).
And they even say:
It’s easy to accidentally make them disagree by deriving some of the traits and manually implementing others.
Maybe the specialization isn't unsound, but makes it easier to observe problematic behavior when the PartialOrd and PartialEq contract is violated.
rah, github didnt load @lukaslueg's comment until just now, that is almost certainly the issue. It is a violation of the contract though, so it might be Fine(tm).
In the spdx crate, that's an incorrect implementation of PartialOrd, where they even explicitly have a #[allow(clippy::non_canonical_partial_ord_impl)]
In particular: PartialOrd only compares the id fields, where Ord compares the id and or_later fields.
This trait should only contain the comparison logic for a type if one plans on only implementing PartialOrd but not Ord. Otherwise the comparison logic should be in Ord and this trait implemented with Some(self.cmp(other)).
If Ord is also implemented for Self and Rhs, it must also be consistent with partial_cmp (see the documentation of that trait for the exact requirements). It’s easy to accidentally make them disagree by deriving some of the traits and manually implementing others.
[...]
Violating these requirements is a logic error. The behavior resulting from a logic error is not specified, [...]
This means that code where a manually-implemented PartialOrd disagrees with a derived Ord was, at the very least, relying on behavior the documentation never specifies, such that #155598 doesn't break any documented promise, since none was made.
Reacted by asquared31415, scottmcm, 许杰友 Jieyou Xu (Joe) and teor
Based on analyses above, and that an issue has been cut with spdx, I'm going to close this as "not a bug". Will revisit this during next week's triage meeting.
Activity
theemathas commentedon Jul 18, 2026
Smaller reproducer (assertion fails in beta but not stable):
theemathas commentedon Jul 18, 2026
Bisection results:
Regressed in #155598. cc @makai410 @nnethercote
theemathas commentedon Jul 18, 2026
This might also affect the
spdxcrate (which is quite a popular crate), although I have not yet investigated in detail.Relevant source code. Error log
lukaslueg commentedon Jul 18, 2026
If I read #155598 correctly, it only checks the outer container (
Packetin this case) for if theirPartialOrd+Ordare derived. ButPacketValue'sPartialOrdimpl is incompatible to a derivedOrdto begin with, because the last two match arms liftDeeperabove what a derivedOrddoes, which goes by order of declaration. That way, the combined manualPartialOrd+ incompatible derivedOrdfromPacketValuecreep into the derivedPacketimpls, where #155598 applies an optimization that it thinks is safe but actually isn't.asquared31415 commentedon Jul 18, 2026
Minimized further:
EDIT: i'm not sure that the following is true, see my next comment
I'm going to take a guess that thisOrdspecialization for slices (used by Vec), is unsound:rust/library/core/src/slice/cmp.rs
Lines 277 to 282 in b26c8ef
and was only made visible (or only easily visible) with the changes toderive(Ord). There's a comment in that file about how thePartialOrdversion is unsound.asquared31415 commentedon Jul 18, 2026
Oh wait actually, is this just a violation of the
PartialEq/PartialOrdrelationship? Your minimized example derivesPartialEqwhich means that values ofPacketValueare only considered equal if and only if their discriminants are equal and their values are equal. Your minimization'sPartialOrdhowever, can returnSome(Equal)for values where that's not the case.The
PartialOrddocs say:And they even say:
Maybe the specialization isn't unsound, but makes it easier to observe problematic behavior when the
PartialOrdandPartialEqcontract is violated.asquared31415 commentedon Jul 18, 2026
rah, github didnt load @lukaslueg's comment until just now, that is almost certainly the issue. It is a violation of the contract though, so it might be Fine(tm).
theemathas commentedon Jul 18, 2026
The regression in the
spdxcrate is concerning though, given the popularity of that crate.asquared31415 commentedon Jul 18, 2026
In the
spdxcrate, that's an incorrect implementation ofPartialOrd, where they even explicitly have a#[allow(clippy::non_canonical_partial_ord_impl)]In particular:
PartialOrdonly compares theidfields, whereOrdcompares theidandor_laterfields.lukaslueg commentedon Jul 18, 2026
The following panics on the playground on beta, but runs fine on stable.
derive_ord_xor_partial_ordrejects this, but the compiler does this optimization all the time, whilederive_ord_xor_partial_ordis optional/clippy.asquared31415 commentedon Jul 18, 2026
The offending
spdxcode was added in EmbarkStudios/spdx#4makai410 commentedon Jul 18, 2026
My two cents are:
As said in the documentation for the
std::cmp::PartialOrdtrait:This endorses the strategy taken in #155598.
Plus, the documentation also says:
This means that code where a manually-implemented
PartialOrddisagrees with a derivedOrdwas, at the very least, relying on behavior the documentation never specifies, such that #155598 doesn't break any documented promise, since none was made.jieyouxu commentedon Jul 19, 2026
Based on analyses above, and that an issue has been cut with
spdx, I'm going to close this as "not a bug". Will revisit this during next week's triage meeting.