Ensure careful consideration is given by impls

Added an associated `const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED`
to the `StableOrd` trait to ensure that implementors carefully consider
whether the trait's contract is upheld, as incorrect implementations can
cause miscompilations.
This commit is contained in:
Alan Egerton 2024-06-22 07:11:42 +01:00
parent 114dd2061e
commit 0e73e7095a
No known key found for this signature in database
GPG key ID: 7D4C2F6C22122532
6 changed files with 60 additions and 13 deletions

View file

@ -425,11 +425,13 @@ pub struct Size {
raw: u64,
}
// Ord is implement as just comparing numerical values and numerical values
// are not changed by (de-)serialization.
#[cfg(feature = "nightly")]
impl StableOrd for Size {
const CAN_USE_UNSTABLE_SORT: bool = true;
// `Ord` is implemented as just comparing numerical values and numerical values
// are not changed by (de-)serialization.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
// This is debug-printed a lot in larger structs, don't waste too much space there

View file

@ -238,15 +238,21 @@ pub trait ToStableHashKey<HCX> {
/// The associated constant `CAN_USE_UNSTABLE_SORT` denotes whether
/// unstable sorting can be used for this type. Set to true if and
/// only if `a == b` implies `a` and `b` are fully indistinguishable.
///
/// **Be careful when implementing this trait, as an incorrect
/// implementation can cause miscompilation!**
pub trait StableOrd: Ord {
const CAN_USE_UNSTABLE_SORT: bool;
/// Marker to ensure that implementors have carefully considered
/// whether their `Ord` implementation obeys this trait's contract.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: ();
}
impl<T: StableOrd> StableOrd for &T {
const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
// Ordering of a reference is exactly that of the referent, and since
// the ordering of the referet is stable so must be the ordering of the
// reference.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
/// This is a companion trait to `StableOrd`. Some types like `Symbol` can be
@ -295,6 +301,10 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut $crate::stable_hasher::StableHas
impl $crate::stable_hasher::StableOrd for $t {
const CAN_USE_UNSTABLE_SORT: bool = true;
// Encoding and decoding doesn't change the bytes of trivial types
// and `Ord::cmp` depends only on those bytes.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
};
}
@ -332,6 +342,10 @@ fn hash_stable(&self, _: &mut CTX, hasher: &mut StableHasher) {
impl StableOrd for Hash128 {
const CAN_USE_UNSTABLE_SORT: bool = true;
// Encoding and decoding doesn't change the bytes of `Hash128`
// and `Ord::cmp` depends only on those bytes.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<CTX> HashStable<CTX> for ! {
@ -397,6 +411,10 @@ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
impl<T1: StableOrd, T2: StableOrd> StableOrd for (T1, T2) {
const CAN_USE_UNSTABLE_SORT: bool = T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT;
// Ordering of tuples is a pure function of their elements' ordering, and since
// the ordering of each element is stable so must be the ordering of the tuple.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<T1, T2, T3, CTX> HashStable<CTX> for (T1, T2, T3)
@ -416,6 +434,10 @@ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
impl<T1: StableOrd, T2: StableOrd, T3: StableOrd> StableOrd for (T1, T2, T3) {
const CAN_USE_UNSTABLE_SORT: bool =
T1::CAN_USE_UNSTABLE_SORT && T2::CAN_USE_UNSTABLE_SORT && T3::CAN_USE_UNSTABLE_SORT;
// Ordering of tuples is a pure function of their elements' ordering, and since
// the ordering of each element is stable so must be the ordering of the tuple.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<T1, T2, T3, T4, CTX> HashStable<CTX> for (T1, T2, T3, T4)
@ -439,6 +461,10 @@ impl<T1: StableOrd, T2: StableOrd, T3: StableOrd, T4: StableOrd> StableOrd for (
&& T2::CAN_USE_UNSTABLE_SORT
&& T3::CAN_USE_UNSTABLE_SORT
&& T4::CAN_USE_UNSTABLE_SORT;
// Ordering of tuples is a pure function of their elements' ordering, and since
// the ordering of each element is stable so must be the ordering of the tuple.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<T: HashStable<CTX>, CTX> HashStable<CTX> for [T] {
@ -533,6 +559,10 @@ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
impl StableOrd for &str {
const CAN_USE_UNSTABLE_SORT: bool = true;
// Encoding and decoding doesn't change the bytes of string slices
// and `Ord::cmp` depends only on those bytes.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<CTX> HashStable<CTX> for String {
@ -542,10 +572,12 @@ fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
}
}
// String comparison only depends on their contents and the
// contents are not changed by (de-)serialization.
impl StableOrd for String {
const CAN_USE_UNSTABLE_SORT: bool = true;
// String comparison only depends on their contents and the
// contents are not changed by (de-)serialization.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<HCX> ToStableHashKey<HCX> for String {
@ -571,9 +603,11 @@ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
}
}
// sort order of bools is not changed by (de-)serialization.
impl StableOrd for bool {
const CAN_USE_UNSTABLE_SORT: bool = true;
// sort order of bools is not changed by (de-)serialization.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<T, CTX> HashStable<CTX> for Option<T>
@ -591,9 +625,11 @@ fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
}
}
// the Option wrapper does not add instability to comparison.
impl<T: StableOrd> StableOrd for Option<T> {
const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
// the Option wrapper does not add instability to comparison.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>

View file

@ -165,10 +165,12 @@ impl ItemLocalId {
pub const INVALID: ItemLocalId = ItemLocalId::MAX;
}
// Ord is implement as just comparing the ItemLocalId's numerical
// values and these are not changed by (de-)serialization.
impl StableOrd for ItemLocalId {
const CAN_USE_UNSTABLE_SORT: bool = true;
// `Ord` is implemented as just comparing the ItemLocalId's numerical
// values and these are not changed by (de-)serialization.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`.

View file

@ -304,6 +304,9 @@ fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
impl StableOrd for WorkProductId {
// Fingerprint can use unstable (just a tuple of `u64`s), so WorkProductId can as well
const CAN_USE_UNSTABLE_SORT: bool = true;
// `WorkProductId` sort order is not affected by (de)serialization.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
// Some types are used a lot. Make sure they don't unintentionally get bigger.

View file

@ -491,9 +491,11 @@ pub enum OutputType {
DepInfo,
}
// Trivial C-Style enums have a stable sort order across compilation sessions.
impl StableOrd for OutputType {
const CAN_USE_UNSTABLE_SORT: bool = true;
// Trivial C-Style enums have a stable sort order across compilation sessions.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {

View file

@ -120,9 +120,11 @@ fn default() -> Self {
}
}
// `DefPathHash` sort order is not affected (de)serialization.
impl StableOrd for DefPathHash {
const CAN_USE_UNSTABLE_SORT: bool = true;
// `DefPathHash` sort order is not affected by (de)serialization.
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
}
/// A [`StableCrateId`] is a 64-bit hash of a crate name, together with all