Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bf1c98f

Browse files
committedMar 7, 2025
Add chaining versions of lt/le/gt/ge and use them in tuple PartialOrd
1 parent 1b91a33 commit bf1c98f

File tree

5 files changed

+168
-325
lines changed

5 files changed

+168
-325
lines changed
 

‎library/core/src/cmp.rs

+88
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ mod bytewise;
2929
pub(crate) use bytewise::BytewiseEq;
3030

3131
use self::Ordering::*;
32+
use crate::ops::ControlFlow::{self, Break, Continue};
3233

3334
/// Trait for comparisons using the equality operator.
3435
///
@@ -1436,6 +1437,54 @@ pub macro PartialOrd($item:item) {
14361437
/* compiler built-in */
14371438
}
14381439

1440+
/// Helpers for chaining together field PartialOrds into the full type's ordering.
1441+
///
1442+
/// If the two values are equal, returns `ControlFlow::Continue`.
1443+
/// If the two values are not equal, returns `ControlFlow::Break(self OP other)`.
1444+
///
1445+
/// This allows simple types like `i32` and `f64` to just emit two comparisons
1446+
/// directly, instead of needing to optimize the 3-way comparison.
1447+
///
1448+
/// Currently this is done using specialization, but it doesn't need that:
1449+
/// it could be provided methods on `PartialOrd` instead and work fine.
1450+
pub(crate) trait SpecChainingPartialOrd<Rhs> : PartialOrd<Rhs> {
1451+
fn spec_chain_lt(&self, other: &Rhs) -> ControlFlow<bool>;
1452+
fn spec_chain_le(&self, other: &Rhs) -> ControlFlow<bool>;
1453+
fn spec_chain_gt(&self, other: &Rhs) -> ControlFlow<bool>;
1454+
fn spec_chain_ge(&self, other: &Rhs) -> ControlFlow<bool>;
1455+
}
1456+
1457+
impl<T: PartialOrd<U>, U> SpecChainingPartialOrd<U> for T {
1458+
#[inline]
1459+
default fn spec_chain_lt(&self, other: &U) -> ControlFlow<bool> {
1460+
match PartialOrd::partial_cmp(self, other) {
1461+
Some(Equal) => Continue(()),
1462+
c => Break(c.is_some_and(Ordering::is_lt)),
1463+
}
1464+
}
1465+
#[inline]
1466+
default fn spec_chain_le(&self, other: &U) -> ControlFlow<bool> {
1467+
match PartialOrd::partial_cmp(self, other) {
1468+
Some(Equal) => Continue(()),
1469+
c => Break(c.is_some_and(Ordering::is_le)),
1470+
}
1471+
}
1472+
#[inline]
1473+
default fn spec_chain_gt(&self, other: &U) -> ControlFlow<bool> {
1474+
match PartialOrd::partial_cmp(self, other) {
1475+
Some(Equal) => Continue(()),
1476+
c => Break(c.is_some_and(Ordering::is_gt)),
1477+
}
1478+
}
1479+
#[inline]
1480+
default fn spec_chain_ge(&self, other: &U) -> ControlFlow<bool> {
1481+
match PartialOrd::partial_cmp(self, other) {
1482+
Some(Equal) => Continue(()),
1483+
c => Break(c.is_some_and(Ordering::is_ge)),
1484+
}
1485+
}
1486+
}
1487+
14391488
/// Compares and returns the minimum of two values.
14401489
///
14411490
/// Returns the first argument if the comparison determines them to be equal.
@@ -1731,6 +1780,7 @@ where
17311780
mod impls {
17321781
use crate::cmp::Ordering::{self, Equal, Greater, Less};
17331782
use crate::hint::unreachable_unchecked;
1783+
use crate::ops::ControlFlow::{self, Break, Continue};
17341784

17351785
macro_rules! partial_eq_impl {
17361786
($($t:ty)*) => ($(
@@ -1769,6 +1819,40 @@ mod impls {
17691819

17701820
eq_impl! { () bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 }
17711821

1822+
macro_rules! chaining_impl {
1823+
($t:ty) => {
1824+
// These implementations are the same for `Ord` or `PartialOrd` types
1825+
// because if either is NAN the `==` test will fail so we end up in
1826+
// the `Break` case and the comparison will correctly return `false`.
1827+
impl super::SpecChainingPartialOrd<$t> for $t {
1828+
#[inline]
1829+
fn spec_chain_lt(&self, other: &Self) -> ControlFlow<bool> {
1830+
let (lhs, rhs) = (*self, *other);
1831+
if lhs == rhs { Continue(()) }
1832+
else { Break(lhs < rhs) }
1833+
}
1834+
#[inline]
1835+
fn spec_chain_le(&self, other: &Self) -> ControlFlow<bool> {
1836+
let (lhs, rhs) = (*self, *other);
1837+
if lhs == rhs { Continue(()) }
1838+
else { Break(lhs <= rhs) }
1839+
}
1840+
#[inline]
1841+
fn spec_chain_gt(&self, other: &Self) -> ControlFlow<bool> {
1842+
let (lhs, rhs) = (*self, *other);
1843+
if lhs == rhs { Continue(()) }
1844+
else { Break(lhs > rhs) }
1845+
}
1846+
#[inline]
1847+
fn spec_chain_ge(&self, other: &Self) -> ControlFlow<bool> {
1848+
let (lhs, rhs) = (*self, *other);
1849+
if lhs == rhs { Continue(()) }
1850+
else { Break(lhs >= rhs) }
1851+
}
1852+
}
1853+
};
1854+
}
1855+
17721856
macro_rules! partial_ord_impl {
17731857
($($t:ty)*) => ($(
17741858
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1791,6 +1875,8 @@ mod impls {
17911875
#[inline(always)]
17921876
fn gt(&self, other: &$t) -> bool { (*self) > (*other) }
17931877
}
1878+
1879+
chaining_impl!($t);
17941880
)*)
17951881
}
17961882

@@ -1830,6 +1916,8 @@ mod impls {
18301916
fn gt(&self, other: &$t) -> bool { (*self) > (*other) }
18311917
}
18321918

1919+
chaining_impl!($t);
1920+
18331921
#[stable(feature = "rust1", since = "1.0.0")]
18341922
impl Ord for $t {
18351923
#[inline]

‎library/core/src/tuple.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// See core/src/primitive_docs.rs for documentation.
22

33
use crate::cmp::Ordering::{self, *};
4+
use crate::cmp::SpecChainingPartialOrd;
45
use crate::marker::{ConstParamTy_, StructuralPartialEq, UnsizedConstParamTy};
6+
use crate::ops::ControlFlow::{Break, Continue};
57

68
// Recursive macro for implementing n-ary tuple functions and operations
79
//
@@ -80,19 +82,19 @@ macro_rules! tuple_impls {
8082
}
8183
#[inline]
8284
fn lt(&self, other: &($($T,)+)) -> bool {
83-
lexical_ord!(lt, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
85+
lexical_ord!(lt, spec_chain_lt, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
8486
}
8587
#[inline]
8688
fn le(&self, other: &($($T,)+)) -> bool {
87-
lexical_ord!(le, Less, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
89+
lexical_ord!(le, spec_chain_le, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
8890
}
8991
#[inline]
9092
fn ge(&self, other: &($($T,)+)) -> bool {
91-
lexical_ord!(ge, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
93+
lexical_ord!(ge, spec_chain_ge, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
9294
}
9395
#[inline]
9496
fn gt(&self, other: &($($T,)+)) -> bool {
95-
lexical_ord!(gt, Greater, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
97+
lexical_ord!(gt, spec_chain_gt, $( ${ignore($T)} self.${index()}, other.${index()} ),+)
9698
}
9799
}
98100
}
@@ -171,15 +173,16 @@ macro_rules! maybe_tuple_doc {
171173
// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, opt_is_lt, a1, b1,
172174
// a2, b2, a3, b3)` (and similarly for `lexical_cmp`)
173175
//
174-
// `$ne_rel` is only used to determine the result after checking that they're
175-
// not equal, so `lt` and `le` can both just use `Less`.
176+
// `$chain_rel` is the method from `SpecChainingPartialOrd` to use for all but the
177+
// final value, to produce better results for simple primitives.
176178
macro_rules! lexical_ord {
177-
($rel: ident, $ne_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{
178-
let c = PartialOrd::partial_cmp(&$a, &$b);
179-
if c != Some(Equal) { c == Some($ne_rel) }
180-
else { lexical_ord!($rel, $ne_rel, $($rest_a, $rest_b),+) }
179+
($rel: ident, $chain_rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {{
180+
match SpecChainingPartialOrd::$chain_rel(&$a, &$b) {
181+
Break(val) => val,
182+
Continue(()) => lexical_ord!($rel, $chain_rel, $($rest_a, $rest_b),+),
183+
}
181184
}};
182-
($rel: ident, $ne_rel: ident, $a:expr, $b:expr) => {
185+
($rel: ident, $chain_rel: ident, $a:expr, $b:expr) => {
183186
// Use the specific method for the last element
184187
PartialOrd::$rel(&$a, &$b)
185188
};
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Failed to load comments.