diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index a1ed993b7d9bf..961b54ee0aec9 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -25,8 +25,8 @@ #![stable(feature = "rust1", since = "1.0.0")] -mod bytewise; -pub(crate) use bytewise::BytewiseEq; +mod spec; +pub(crate) use spec::{AlwaysApplicableOrd, BytewiseEq, UnsignedBytewiseOrd}; use self::Ordering::*; @@ -817,7 +817,9 @@ impl<T: Clone> Clone for Reverse<T> { #[doc(alias = ">=")] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "Ord"] -pub trait Ord: Eq + PartialOrd<Self> { +pub trait Ord<#[unstable(feature = "generic_ord", issue = "none")] Rhs: ?Sized = Self>: + Eq + PartialOrd<Rhs> +{ /// This method returns an [`Ordering`] between `self` and `other`. /// /// By convention, `self.cmp(&other)` returns the ordering matching the expression @@ -835,7 +837,7 @@ pub trait Ord: Eq + PartialOrd<Self> { #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "ord_cmp_method"] - fn cmp(&self, other: &Self) -> Ordering; + fn cmp(&self, other: &Rhs) -> Ordering; /// Compares and returns the maximum of two values. /// @@ -851,11 +853,12 @@ pub trait Ord: Eq + PartialOrd<Self> { #[inline] #[must_use] #[rustc_diagnostic_item = "cmp_ord_max"] - fn max(self, other: Self) -> Self + fn max(self, other: Rhs) -> Self where Self: Sized, + Rhs: Sized + Into<Self>, { - max_by(self, other, Ord::cmp) + if self > other { self } else { other.into() } } /// Compares and returns the minimum of two values. @@ -872,11 +875,12 @@ pub trait Ord: Eq + PartialOrd<Self> { #[inline] #[must_use] #[rustc_diagnostic_item = "cmp_ord_min"] - fn min(self, other: Self) -> Self + fn min(self, other: Rhs) -> Self where Self: Sized, + Rhs: Sized + Into<Self>, { - min_by(self, other, Ord::cmp) + if self <= other { self } else { other.into() } } /// Restrict a value to a certain interval. @@ -898,16 +902,16 @@ pub trait Ord: Eq + PartialOrd<Self> { #[must_use] #[inline] #[stable(feature = "clamp", since = "1.50.0")] - fn clamp(self, min: Self, max: Self) -> Self + fn clamp(self, min: Rhs, max: Rhs) -> Self where Self: Sized, - Self: PartialOrd, + Rhs: Sized + PartialOrd + Into<Self>, { assert!(min <= max); if self < min { - min + min.into() } else if self > max { - max + max.into() } else { self } @@ -1692,12 +1696,12 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: ?Sized> Ord for &A + impl<A: ?Sized, B: ?Sized> Ord<&B> for &A where - A: Ord, + A: Ord<B>, { #[inline] - fn cmp(&self, other: &Self) -> Ordering { + fn cmp(&self, other: &&B) -> Ordering { Ord::cmp(*self, *other) } } @@ -1747,12 +1751,12 @@ mod impls { } } #[stable(feature = "rust1", since = "1.0.0")] - impl<A: ?Sized> Ord for &mut A + impl<A: ?Sized, B: ?Sized> Ord<&mut B> for &mut A where - A: Ord, + A: Ord<B>, { #[inline] - fn cmp(&self, other: &Self) -> Ordering { + fn cmp(&self, other: &&mut B) -> Ordering { Ord::cmp(*self, *other) } } diff --git a/library/core/src/cmp/bytewise.rs b/library/core/src/cmp/spec.rs similarity index 67% rename from library/core/src/cmp/bytewise.rs rename to library/core/src/cmp/spec.rs index a06a5227fe285..354c397b57d3a 100644 --- a/library/core/src/cmp/bytewise.rs +++ b/library/core/src/cmp/spec.rs @@ -1,3 +1,4 @@ +use crate::ascii; use crate::num::NonZero; /// Types where `==` & `!=` are equivalent to comparing their underlying bytes. @@ -80,3 +81,40 @@ macro_rules! is_bytewise_comparable_array_length { // error: specializing impl repeats parameter `N` // so just do it for a couple of plausibly-common ones. is_bytewise_comparable_array_length!(0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64); + +/// Marks that a type should be treated as an unsigned byte for comparisons. +/// +/// # Safety +/// * The type must be readable as an `u8`, meaning it has to have the same +/// layout as `u8` and always be initialized. +/// * For every `x` and `y` of this type, `Ord(x, y)` must return the same +/// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. +#[rustc_specialization_trait] +pub(crate) unsafe trait UnsignedBytewiseOrd<Rhs = Self>: Ord<Rhs> + Sized {} + +unsafe impl UnsignedBytewiseOrd for bool {} +unsafe impl UnsignedBytewiseOrd for u8 {} +unsafe impl UnsignedBytewiseOrd for NonZero<u8> {} +unsafe impl UnsignedBytewiseOrd for Option<NonZero<u8>> {} +unsafe impl UnsignedBytewiseOrd for ascii::Char {} + +/// Marks that a type's [`Ord`] impl can always be used instead of its [`PartialOrd`] impl, +/// so that we can specialize slice `Ord`. +#[rustc_specialization_trait] +pub(crate) trait AlwaysApplicableOrd<Rhs: ?Sized = Self>: Ord<Rhs> {} + +macro_rules! always_applicable_ord { + ($($t:ty,)*) => { + $(impl AlwaysApplicableOrd for $t {})* + } +} + +always_applicable_ord! { + u8, u16, u32, u64, u128, usize, + i8, i16, i32, i64, i128, isize, + bool, char, +} + +// to ensure soundness, these must have differing types +impl<T: ?Sized, U: ?Sized> AlwaysApplicableOrd<&U> for &T where T: AlwaysApplicableOrd<U> {} +impl<T: ?Sized, U: ?Sized> AlwaysApplicableOrd<&mut U> for &mut T where T: AlwaysApplicableOrd<U> {} diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 1769612def0a5..bc39240922739 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -1,10 +1,9 @@ //! Comparison traits for `[T]`. use super::{from_raw_parts, memchr}; -use crate::cmp::{self, BytewiseEq, Ordering}; +use crate::cmp::{self, AlwaysApplicableOrd, BytewiseEq, Ordering, UnsignedBytewiseOrd}; use crate::intrinsics::compare_bytes; -use crate::num::NonZero; -use crate::{ascii, mem}; +use crate::mem; #[stable(feature = "rust1", since = "1.0.0")] impl<T, U> PartialEq<[U]> for [T] @@ -20,51 +19,32 @@ where } } -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: Eq> Eq for [T] {} - -/// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: Ord> Ord for [T] { - fn cmp(&self, other: &[T]) -> Ordering { - SliceOrd::compare(self, other) - } -} - -/// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). -#[stable(feature = "rust1", since = "1.0.0")] -impl<T: PartialOrd> PartialOrd for [T] { - fn partial_cmp(&self, other: &[T]) -> Option<Ordering> { - SlicePartialOrd::partial_compare(self, other) - } -} - #[doc(hidden)] // intermediate trait for specialization of slice's PartialEq -trait SlicePartialEq<B> { - fn equal(&self, other: &[B]) -> bool; +trait SlicePartialEq<B>: Sized { + fn equal(left: &[Self], right: &[B]) -> bool; - fn not_equal(&self, other: &[B]) -> bool { - !self.equal(other) + fn not_equal(left: &[Self], right: &[B]) -> bool { + !Self::equal(left, right) } } // Generic slice equality -impl<A, B> SlicePartialEq<B> for [A] +impl<A, B> SlicePartialEq<B> for A where A: PartialEq<B>, { - default fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { + default fn equal(left: &[A], right: &[B]) -> bool { + if left.len() != right.len() { return false; } // Implemented as explicit indexing rather // than zipped iterators for performance reasons. // See PR https://github.com/rust-lang/rust/pull/116846 - for idx in 0..self.len() { + for idx in 0..left.len() { // bound checks are optimized away - if self[idx] != other[idx] { + if left[idx] != right[idx] { return false; } } @@ -75,32 +55,49 @@ where // When each element can be compared byte-wise, we can compare all the bytes // from the whole size in one call to the intrinsics. -impl<A, B> SlicePartialEq<B> for [A] +impl<A, B> SlicePartialEq<B> for A where A: BytewiseEq<B>, { - fn equal(&self, other: &[B]) -> bool { - if self.len() != other.len() { + fn equal(left: &[A], right: &[B]) -> bool { + if left.len() != right.len() { return false; } // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. // The two slices have been checked to have the same size above. unsafe { - let size = mem::size_of_val(self); - compare_bytes(self.as_ptr() as *const u8, other.as_ptr() as *const u8, size) == 0 + let size = mem::size_of_val(left); + compare_bytes(left.as_ptr() as *const u8, right.as_ptr() as *const u8, size) == 0 } } } +#[stable(feature = "rust1", since = "1.0.0")] +impl<T: Eq> Eq for [T] {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<T, U> PartialOrd<[U]> for [T] +where + T: PartialOrd<U>, +{ + fn partial_cmp(&self, other: &[U]) -> Option<Ordering> { + SlicePartialOrd::partial_compare(self, other) + } +} + #[doc(hidden)] // intermediate trait for specialization of slice's PartialOrd -trait SlicePartialOrd: Sized { - fn partial_compare(left: &[Self], right: &[Self]) -> Option<Ordering>; +trait SlicePartialOrd<B>: Sized { + fn partial_compare(left: &[Self], right: &[B]) -> Option<Ordering>; } -impl<A: PartialOrd> SlicePartialOrd for A { - default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { +/// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). +impl<A, B> SlicePartialOrd<B> for A +where + A: PartialOrd<B>, +{ + default fn partial_compare(left: &[A], right: &[B]) -> Option<Ordering> { let l = cmp::min(left.len(), right.len()); // Slice to the loop iteration range to enable bound check @@ -119,52 +116,37 @@ impl<A: PartialOrd> SlicePartialOrd for A { } } -// This is the impl that we would like to have. Unfortunately it's not sound. -// See `partial_ord_slice.rs`. -/* -impl<A> SlicePartialOrd for A +impl<A, B> SlicePartialOrd<B> for A where - A: Ord, + A: AlwaysApplicableOrd<B>, { - default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { - Some(SliceOrd::compare(left, right)) - } -} -*/ - -impl<A: AlwaysApplicableOrd> SlicePartialOrd for A { - fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { + fn partial_compare(left: &[A], right: &[B]) -> Option<Ordering> { Some(SliceOrd::compare(left, right)) } } -#[rustc_specialization_trait] -trait AlwaysApplicableOrd: SliceOrd + Ord {} - -macro_rules! always_applicable_ord { - ($([$($p:tt)*] $t:ty,)*) => { - $(impl<$($p)*> AlwaysApplicableOrd for $t {})* +#[stable(feature = "rust1", since = "1.0.0")] +impl<T> Ord for [T] +where + T: Ord, +{ + fn cmp(&self, other: &[T]) -> Ordering { + SliceOrd::compare(self, other) } } -always_applicable_ord! { - [] u8, [] u16, [] u32, [] u64, [] u128, [] usize, - [] i8, [] i16, [] i32, [] i64, [] i128, [] isize, - [] bool, [] char, - [T: ?Sized] *const T, [T: ?Sized] *mut T, - [T: AlwaysApplicableOrd] &T, - [T: AlwaysApplicableOrd] &mut T, - [T: AlwaysApplicableOrd] Option<T>, -} - #[doc(hidden)] // intermediate trait for specialization of slice's Ord -trait SliceOrd: Sized { - fn compare(left: &[Self], right: &[Self]) -> Ordering; +trait SliceOrd<B>: Sized + Ord<B> { + fn compare(left: &[Self], right: &[B]) -> Ordering; } -impl<A: Ord> SliceOrd for A { - default fn compare(left: &[Self], right: &[Self]) -> Ordering { +/// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). +impl<A, B> SliceOrd<B> for A +where + A: Ord<B>, +{ + default fn compare(left: &[A], right: &[B]) -> Ordering { let l = cmp::min(left.len(), right.len()); // Slice to the loop iteration range to enable bound check @@ -183,27 +165,14 @@ impl<A: Ord> SliceOrd for A { } } -/// Marks that a type should be treated as an unsigned byte for comparisons. -/// -/// # Safety -/// * The type must be readable as an `u8`, meaning it has to have the same -/// layout as `u8` and always be initialized. -/// * For every `x` and `y` of this type, `Ord(x, y)` must return the same -/// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. -#[rustc_specialization_trait] -unsafe trait UnsignedBytewiseOrd {} - -unsafe impl UnsignedBytewiseOrd for bool {} -unsafe impl UnsignedBytewiseOrd for u8 {} -unsafe impl UnsignedBytewiseOrd for NonZero<u8> {} -unsafe impl UnsignedBytewiseOrd for Option<NonZero<u8>> {} -unsafe impl UnsignedBytewiseOrd for ascii::Char {} - // `compare_bytes` compares a sequence of unsigned bytes lexicographically, so // use it if the requirements for `UnsignedBytewiseOrd` are fulfilled. -impl<A: Ord + UnsignedBytewiseOrd> SliceOrd for A { +impl<A, B> SliceOrd<B> for A +where + A: UnsignedBytewiseOrd<B>, +{ #[inline] - fn compare(left: &[Self], right: &[Self]) -> Ordering { + fn compare(left: &[A], right: &[B]) -> Ordering { // Since the length of a slice is always less than or equal to // isize::MAX, this never underflows. let diff = left.len() as isize - right.len() as isize; @@ -225,6 +194,7 @@ impl<A: Ord + UnsignedBytewiseOrd> SliceOrd for A { } } +// trait for specialization of `slice::contains` pub(super) trait SliceContains: Sized { fn slice_contains(&self, x: &[Self]) -> bool; }