Skip to content

Commit 12ddadd

Browse files
authored
Unrolled build for rust-lang#115386
Rollup merge of rust-lang#115386 - RalfJung:partial-eq-chain, r=dtolnay PartialEq, PartialOrd: update and synchronize handling of transitive chains It was brought up in https://internals.rust-lang.org/t/total-equality-relations-as-std-eq-rhs/19232 that we currently have a gap in our `PartialEq` rules, which this PR aims to close: > For example, with PartialEq's conditions you may have a = b = c = d ≠ a (where a and c are of type A, b and d are of type B). The second commit fixes rust-lang#87067 by updating PartialOrd to handle the requirements the same way PartialEq does.
2 parents 8c0b4f6 + 61d1ebe commit 12ddadd

File tree

1 file changed

+52
-8
lines changed

1 file changed

+52
-8
lines changed

library/core/src/cmp.rs

+52-8
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ use self::Ordering::*;
6161
/// The equality relation `==` must satisfy the following conditions
6262
/// (for all `a`, `b`, `c` of type `A`, `B`, `C`):
6363
///
64-
/// - **Symmetric**: if `A: PartialEq<B>` and `B: PartialEq<A>`, then **`a == b`
64+
/// - **Symmetry**: if `A: PartialEq<B>` and `B: PartialEq<A>`, then **`a == b`
6565
/// implies `b == a`**; and
6666
///
67-
/// - **Transitive**: if `A: PartialEq<B>` and `B: PartialEq<C>` and `A:
67+
/// - **Transitivity**: if `A: PartialEq<B>` and `B: PartialEq<C>` and `A:
6868
/// PartialEq<C>`, then **`a == b` and `b == c` implies `a == c`**.
69+
/// This must also work for longer chains, such as when `A: PartialEq<B>`, `B: PartialEq<C>`,
70+
/// `C: PartialEq<D>`, and `A: PartialEq<D>` all exist.
6971
///
7072
/// Note that the `B: PartialEq<A>` (symmetric) and `A: PartialEq<C>`
7173
/// (transitive) impls are not forced to exist, but these requirements apply
@@ -76,6 +78,25 @@ use self::Ordering::*;
7678
/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
7779
/// methods.
7880
///
81+
/// ## Cross-crate considerations
82+
///
83+
/// Upholding the requirements stated above can become tricky when one crate implements `PartialEq`
84+
/// for a type of another crate (i.e., to allow comparing one of its own types with a type from the
85+
/// standard library). The recommendation is to never implement this trait for a foreign type. In
86+
/// other words, such a crate should do `impl PartialEq<ForeignType> for LocalType`, but it should
87+
/// *not* do `impl PartialEq<LocalType> for ForeignType`.
88+
///
89+
/// This avoids the problem of transitive chains that criss-cross crate boundaries: for all local
90+
/// types `T`, you may assume that no other crate will add `impl`s that allow comparing `T == U`. In
91+
/// other words, if other crates add `impl`s that allow building longer transitive chains `U1 == ...
92+
/// == T == V1 == ...`, then all the types that appear to the right of `T` must be types that the
93+
/// crate defining `T` already knows about. This rules out transitive chains where downstream crates
94+
/// can add new `impl`s that "stitch together" comparisons of foreign types in ways that violate
95+
/// transitivity.
96+
///
97+
/// Not having such foreign `impl`s also avoids forward compatibility issues where one crate adding
98+
/// more `PartialEq` implementations can cause build failures in downstream crates.
99+
///
79100
/// ## Derivable
80101
///
81102
/// This trait can be used with `#[derive]`. When `derive`d on structs, two
@@ -920,20 +941,43 @@ pub macro Ord($item:item) {
920941
/// easy to accidentally make them disagree by deriving some of the traits and manually
921942
/// implementing others.
922943
///
923-
/// The comparison must satisfy, for all `a`, `b` and `c`:
944+
/// The comparison relations must satisfy the following conditions
945+
/// (for all `a`, `b`, `c` of type `A`, `B`, `C`):
924946
///
925-
/// - transitivity: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
926-
/// - duality: `a < b` if and only if `b > a`.
947+
/// - **Transitivity**: if `A: PartialOrd<B>` and `B: PartialOrd<C>` and `A:
948+
/// PartialOrd<C>`, then `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
949+
/// This must also work for longer chains, such as when `A: PartialOrd<B>`, `B: PartialOrd<C>`,
950+
/// `C: PartialOrd<D>`, and `A: PartialOrd<D>` all exist.
951+
/// - **Duality**: if `A: PartialOrd<B>` and `B: PartialOrd<A>`, then `a < b` if and only if `b > a`.
927952
///
928-
/// Note that these requirements mean that the trait itself must be implemented symmetrically and
929-
/// transitively: if `T: PartialOrd<U>` and `U: PartialOrd<V>` then `U: PartialOrd<T>` and `T:
930-
/// PartialOrd<V>`.
953+
/// Note that the `B: PartialOrd<A>` (dual) and `A: PartialOrd<C>`
954+
/// (transitive) impls are not forced to exist, but these requirements apply
955+
/// whenever they do exist.
931956
///
932957
/// Violating these requirements is a logic error. The behavior resulting from a logic error is not
933958
/// specified, but users of the trait must ensure that such logic errors do *not* result in
934959
/// undefined behavior. This means that `unsafe` code **must not** rely on the correctness of these
935960
/// methods.
936961
///
962+
/// ## Cross-crate considerations
963+
///
964+
/// Upholding the requirements stated above can become tricky when one crate implements `PartialOrd`
965+
/// for a type of another crate (i.e., to allow comparing one of its own types with a type from the
966+
/// standard library). The recommendation is to never implement this trait for a foreign type. In
967+
/// other words, such a crate should do `impl PartialOrd<ForeignType> for LocalType`, but it should
968+
/// *not* do `impl PartialOrd<LocalType> for ForeignType`.
969+
///
970+
/// This avoids the problem of transitive chains that criss-cross crate boundaries: for all local
971+
/// types `T`, you may assume that no other crate will add `impl`s that allow comparing `T < U`. In
972+
/// other words, if other crates add `impl`s that allow building longer transitive chains `U1 < ...
973+
/// < T < V1 < ...`, then all the types that appear to the right of `T` must be types that the crate
974+
/// defining `T` already knows about. This rules out transitive chains where downstream crates can
975+
/// add new `impl`s that "stitch together" comparisons of foreign types in ways that violate
976+
/// transitivity.
977+
///
978+
/// Not having such foreign `impl`s also avoids forward compatibility issues where one crate adding
979+
/// more `PartialOrd` implementations can cause build failures in downstream crates.
980+
///
937981
/// ## Corollaries
938982
///
939983
/// The following corollaries follow from the above requirements:

0 commit comments

Comments
 (0)