Skip to content

Commit 5dde29c

Browse files
author
blake2-ppc
committed
std: Fix tuple lexicographical order
Use the definition, where R is <, <=, >=, or > [x, ..xs] R [y, ..ys] = if x != y { x R y } else { xs R ys } Previously, tuples would only implement < and derive the other comparisons from it; this is incorrect. Included are several testcases involving NaN comparisons that are now correct. Previously, tuples would consider an element equal if both a < b and b < a were false, this was also incorrect.
1 parent 86da55e commit 5dde29c

File tree

1 file changed

+28
-14
lines changed

1 file changed

+28
-14
lines changed

src/libstd/tuple.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,17 +197,23 @@ macro_rules! tuple_impls {
197197
}
198198

199199
#[cfg(not(test))]
200-
impl<$($T:Ord),+> Ord for ($($T,)+) {
200+
impl<$($T:Ord + Eq),+> Ord for ($($T,)+) {
201201
#[inline]
202202
fn lt(&self, other: &($($T,)+)) -> bool {
203-
lexical_lt!($(self.$get_ref_fn(), other.$get_ref_fn()),+)
203+
lexical_ord!(lt, $(self.$get_ref_fn(), other.$get_ref_fn()),+)
204204
}
205205
#[inline]
206-
fn le(&self, other: &($($T,)+)) -> bool { !(*other).lt(&(*self)) }
206+
fn le(&self, other: &($($T,)+)) -> bool {
207+
lexical_ord!(le, $(self.$get_ref_fn(), other.$get_ref_fn()),+)
208+
}
207209
#[inline]
208-
fn ge(&self, other: &($($T,)+)) -> bool { !(*self).lt(other) }
210+
fn ge(&self, other: &($($T,)+)) -> bool {
211+
lexical_ord!(ge, $(self.$get_ref_fn(), other.$get_ref_fn()),+)
212+
}
209213
#[inline]
210-
fn gt(&self, other: &($($T,)+)) -> bool { (*other).lt(&(*self)) }
214+
fn gt(&self, other: &($($T,)+)) -> bool {
215+
lexical_ord!(gt, $(self.$get_ref_fn(), other.$get_ref_fn()),+)
216+
}
211217
}
212218

213219
#[cfg(not(test))]
@@ -234,17 +240,16 @@ macro_rules! tuple_impls {
234240
}
235241
}
236242

237-
// Constructs an expression that performs a lexical less-than
238-
// ordering. The values are interleaved, so the macro invocation for
239-
// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_lt!(a1, b1, a2, b2,
243+
// Constructs an expression that performs a lexical ordering using method $rel.
244+
// The values are interleaved, so the macro invocation for
245+
// `(a1, a2, a3) < (b1, b2, b3)` would be `lexical_ord!(lt, a1, b1, a2, b2,
240246
// a3, b3)` (and similarly for `lexical_cmp`)
241-
macro_rules! lexical_lt {
242-
($a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {
243-
if *$a < *$b { true }
244-
else if !(*$b < *$a) { lexical_lt!($($rest_a, $rest_b),+) }
245-
else { false }
247+
macro_rules! lexical_ord {
248+
($rel: ident, $a:expr, $b:expr, $($rest_a:expr, $rest_b:expr),+) => {
249+
if *$a != *$b { lexical_ord!($rel, $a, $b) }
250+
else { lexical_ord!($rel, $($rest_a, $rest_b),+) }
246251
};
247-
($a:expr, $b:expr) => { *$a < *$b };
252+
($rel: ident, $a:expr, $b:expr) => { (*$a) . $rel ($b) };
248253
}
249254

250255
macro_rules! lexical_cmp {
@@ -436,6 +441,8 @@ mod tests {
436441
fn test_tuple_cmp() {
437442
let (small, big) = ((1u, 2u, 3u), (3u, 2u, 1u));
438443

444+
let nan = 0.0/0.0;
445+
439446
// Eq
440447
assert_eq!(small, small);
441448
assert_eq!(big, big);
@@ -456,6 +463,13 @@ mod tests {
456463
assert!(big >= small);
457464
assert!(big >= big);
458465

466+
assert!(!((1.0, 2.0) < (nan, 3.0)));
467+
assert!(!((1.0, 2.0) <= (nan, 3.0)));
468+
assert!(!((1.0, 2.0) > (nan, 3.0)));
469+
assert!(!((1.0, 2.0) >= (nan, 3.0)));
470+
assert!(((1.0, 2.0) < (2.0, nan)));
471+
assert!(!((2.0, 2.0) < (2.0, nan)));
472+
459473
// TotalEq
460474
assert!(small.equals(&small));
461475
assert!(big.equals(&big));

0 commit comments

Comments
 (0)