Skip to content

Fix AssetCapacity Eq/Ord semantics#1207

Merged
alexdewar merged 2 commits intomainfrom
fix-assetcapacity-semantics
Mar 19, 2026
Merged

Fix AssetCapacity Eq/Ord semantics#1207
alexdewar merged 2 commits intomainfrom
fix-assetcapacity-semantics

Conversation

@alexdewar
Copy link
Copy Markdown
Collaborator

@alexdewar alexdewar commented Mar 17, 2026

Description

Copilot had a go at this (#1206), but I didn't like the result.

The reason that Eq and Ord are inconsistent for AssetCapacity is because we are comparing continuous capacities with total_cmp, which handles all the weird edge cases of floating-point arithmetic, like NaNs and negative zeroes, giving them a consistent sort order. The default PartialEq implementation that we are deriving has the usual behaviour you get when comparing floating-point numbers (e.g. that comparing NaN with itself returns false), which is not the same.

The easy way around this is to just swap total_cmp for partial_cmp. I opted to just panic if either of the values is NaN, which is a case that shouldn't arise anyway.

Fixes #1162.

Type of change

  • Bug fix (non-breaking change to fix an issue)
  • New feature (non-breaking change to add functionality)
  • Refactoring (non-breaking, non-functional change to improve maintainability)
  • Optimization (non-breaking change to speed up the code)
  • Breaking change (whatever its nature)
  • Documentation (improve or add documentation)

Key checklist

  • All tests pass: $ cargo test
  • The documentation builds and looks OK: $ cargo doc
  • Update release notes for the latest release if this PR adds a new feature or fixes a bug
    present in the previous release

Further checks

  • Code is commented, particularly in hard-to-understand areas
  • Tests added that prove fix is effective or that feature works

Copilot AI review requested due to automatic review settings March 17, 2026 12:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates ordering behavior for AssetCapacity comparisons, specifically for continuous capacities, to change how floating-point values are compared.

Changes:

  • Replaced total_cmp with partial_cmp(...).expect(...) for AssetCapacity::Continuous inside Ord::cmp.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@alexdewar alexdewar requested review from Aurashk and tsmbland March 17, 2026 12:08
@alexdewar alexdewar marked this pull request as draft March 17, 2026 12:10
@alexdewar
Copy link
Copy Markdown
Collaborator Author

Hmm... I can make this tidier, actually. Lemme have another go.

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.29%. Comparing base (5389d44) to head (fb95c7a).
⚠️ Report is 34 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1207      +/-   ##
==========================================
+ Coverage   89.02%   89.29%   +0.26%     
==========================================
  Files          57       57              
  Lines        7838     7976     +138     
  Branches     7838     7976     +138     
==========================================
+ Hits         6978     7122     +144     
+ Misses        564      557       -7     
- Partials      296      297       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@alexdewar alexdewar force-pushed the fix-assetcapacity-semantics branch from 15db796 to 6269b0f Compare March 17, 2026 15:51
…pacity`

The implementations for these traits shouldn't panic and can cause UB if they do. The only thing we need them for is the `min()` method, but we can just implement this manually instead.

Fixes #1206.
@alexdewar alexdewar force-pushed the fix-assetcapacity-semantics branch from 6269b0f to f60f62a Compare March 17, 2026 15:53
@alexdewar
Copy link
Copy Markdown
Collaborator Author

I've had another go. I ended up going down a bit of a rabbit hole with this... it actually seems that having implementations of Eq and Ord which can panic is just a bad idea in general (there are parts of the standard library where this can cause UB, which we definitely want to avoid). PartialEq and PartialOrd better describe the semantics of our data type.

As we only only use Eq/Ord for the min method, I've just implemented one manually instead (which is what I think @tsmbland did before I suggested the Eq/Ord thing 😆).

@alexdewar alexdewar marked this pull request as ready for review March 17, 2026 16:01
Copilot AI review requested due to automatic review settings March 17, 2026 16:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates AssetCapacity comparisons to use a true partial ordering (returning None when capacities aren’t meaningfully comparable) and adds an explicit AssetCapacity::min helper that preserves the prior .min(...) call sites without relying on Ord.

Changes:

  • Added AssetCapacity::min that panics when a comparison is not meaningful.
  • Replaced the previous total ordering (Eq/Ord with total_cmp/panics) with a PartialOrd implementation that returns None for incomparable cases.
  • Adjusted discrete comparisons to only compare unit counts when unit sizes match.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 69 to +77
impl PartialOrd for AssetCapacity {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}

impl Ord for AssetCapacity {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(AssetCapacity::Continuous(a), AssetCapacity::Continuous(b)) => a.total_cmp(b),
(AssetCapacity::Continuous(a), AssetCapacity::Continuous(b)) => a.partial_cmp(b),
(AssetCapacity::Discrete(units1, size1), AssetCapacity::Discrete(units2, size2)) => {
Self::check_same_unit_size(*size1, *size2);
units1.cmp(units2)
// NB: Also returns `None` if either is NaN
(*size1 == *size2).then(|| units1.cmp(units2))
}
_ => panic!("Cannot compare different types of AssetCapacity ({self:?} and {other:?})"),
_ => None,
}
}
}

@alexdewar alexdewar enabled auto-merge March 19, 2026 16:30
@alexdewar alexdewar merged commit f4cdad4 into main Mar 19, 2026
8 checks passed
@alexdewar alexdewar deleted the fix-assetcapacity-semantics branch March 19, 2026 16:32
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.

Broken Eq/Ord semantics for AssetCapacity

3 participants