Add StableOrd trait as proposed in MCP 533.

The StableOrd trait can be used to mark types as having a stable
sort order across compilation sessions. Collections that sort their
items in a stable way can safely implement HashStable by
hashing items in sort order.
This commit is contained in:
Michael Woerister 2022-12-02 15:14:49 +01:00
parent 11663b1b48
commit 3a58309798
7 changed files with 96 additions and 36 deletions

View File

@ -9,6 +9,8 @@ use std::ops::{Add, AddAssign, Mul, RangeInclusive, Sub};
use std::str::FromStr; use std::str::FromStr;
use bitflags::bitflags; use bitflags::bitflags;
#[cfg(feature = "nightly")]
use rustc_data_structures::stable_hasher::StableOrd;
use rustc_index::vec::{Idx, IndexVec}; use rustc_index::vec::{Idx, IndexVec};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
use rustc_macros::HashStable_Generic; use rustc_macros::HashStable_Generic;
@ -403,6 +405,11 @@ pub struct Size {
raw: u64, raw: u64,
} }
// Safety: Ord is implement as just comparing numerical values and numerical values
// are not changed by (de-)serialization.
#[cfg(feature = "nightly")]
unsafe impl StableOrd for Size {}
// This is debug-printed a lot in larger structs, don't waste too much space there // This is debug-printed a lot in larger structs, don't waste too much space there
impl fmt::Debug for Size { impl fmt::Debug for Size {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@ -140,7 +140,7 @@ impl stable_hasher::StableHasherResult for Fingerprint {
} }
} }
impl_stable_hash_via_hash!(Fingerprint); impl_stable_ord_and_stable_hash_via_hash!(Fingerprint);
impl<E: Encoder> Encodable<E> for Fingerprint { impl<E: Encoder> Encodable<E> for Fingerprint {
#[inline] #[inline]

View File

@ -1,4 +1,4 @@
use crate::stable_hasher::{HashStable, StableHasher}; use crate::stable_hasher::{HashStable, StableHasher, StableOrd};
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::iter::FromIterator; use std::iter::FromIterator;
@ -308,7 +308,7 @@ impl<K: Ord, V> FromIterator<(K, V)> for SortedMap<K, V> {
} }
} }
impl<K: HashStable<CTX>, V: HashStable<CTX>, CTX> HashStable<CTX> for SortedMap<K, V> { impl<K: HashStable<CTX> + StableOrd, V: HashStable<CTX>, CTX> HashStable<CTX> for SortedMap<K, V> {
#[inline] #[inline]
fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) { fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
self.data.hash_stable(ctx, hasher); self.data.hash_stable(ctx, hasher);

View File

@ -120,13 +120,20 @@ where
self.items.hash(hasher) self.items.hash(hasher)
} }
} }
impl<I: Idx, K, V, C> HashStable<C> for SortedIndexMultiMap<I, K, V> impl<I: Idx, K, V, C> HashStable<C> for SortedIndexMultiMap<I, K, V>
where where
K: HashStable<C>, K: HashStable<C>,
V: HashStable<C>, V: HashStable<C>,
{ {
fn hash_stable(&self, ctx: &mut C, hasher: &mut StableHasher) { fn hash_stable(&self, ctx: &mut C, hasher: &mut StableHasher) {
self.items.hash_stable(ctx, hasher) let SortedIndexMultiMap {
items,
// We can ignore this field because it is not observable from the outside.
idx_sorted_by_item_key: _,
} = self;
items.hash_stable(ctx, hasher)
} }
} }

View File

@ -219,7 +219,35 @@ pub trait ToStableHashKey<HCX> {
fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType; fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType;
} }
/// Implement HashStable by just calling `Hash::hash()`. /// Trait for marking a type as having a sort order that is
/// stable across compilation session boundaries. More formally:
///
/// ```txt
/// Ord::cmp(a1, b1) == Ord:cmp(a2, b2)
/// where a2 = decode(encode(a1, context1), context2)
/// b2 = decode(encode(b1, context1), context2)
/// ```
///
/// i.e. the result of `Ord::cmp` is not influenced by encoding
/// the values in one session and then decoding them in another
/// session.
///
/// This is trivially true for types where encoding and decoding
/// don't change the bytes of the values that are used during
/// comparison and comparison only depends on these bytes (as
/// opposed to some non-local state). Examples are u32, String,
/// Path, etc.
///
/// But it is not true for:
/// - `*const T` and `*mut T` because the values of these pointers
/// will change between sessions.
/// - `DefIndex`, `CrateNum`, `LocalDefId`, because their concrete
/// values depend on state that might be different between
/// compilation sessions.
pub unsafe trait StableOrd: Ord {}
/// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since
/// that has the same requirements.
/// ///
/// **WARNING** This is only valid for types that *really* don't need any context for fingerprinting. /// **WARNING** This is only valid for types that *really* don't need any context for fingerprinting.
/// But it is easy to misuse this macro (see [#96013](https://github.com/rust-lang/rust/issues/96013) /// But it is easy to misuse this macro (see [#96013](https://github.com/rust-lang/rust/issues/96013)
@ -227,7 +255,7 @@ pub trait ToStableHashKey<HCX> {
/// here in this module. /// here in this module.
/// ///
/// Use `#[derive(HashStable_Generic)]` instead. /// Use `#[derive(HashStable_Generic)]` instead.
macro_rules! impl_stable_hash_via_hash { macro_rules! impl_stable_ord_and_stable_hash_via_hash {
($t:ty) => { ($t:ty) => {
impl<CTX> $crate::stable_hasher::HashStable<CTX> for $t { impl<CTX> $crate::stable_hasher::HashStable<CTX> for $t {
#[inline] #[inline]
@ -235,26 +263,28 @@ macro_rules! impl_stable_hash_via_hash {
::std::hash::Hash::hash(self, hasher); ::std::hash::Hash::hash(self, hasher);
} }
} }
unsafe impl $crate::stable_hasher::StableOrd for $t {}
}; };
} }
impl_stable_hash_via_hash!(i8); impl_stable_ord_and_stable_hash_via_hash!(i8);
impl_stable_hash_via_hash!(i16); impl_stable_ord_and_stable_hash_via_hash!(i16);
impl_stable_hash_via_hash!(i32); impl_stable_ord_and_stable_hash_via_hash!(i32);
impl_stable_hash_via_hash!(i64); impl_stable_ord_and_stable_hash_via_hash!(i64);
impl_stable_hash_via_hash!(isize); impl_stable_ord_and_stable_hash_via_hash!(isize);
impl_stable_hash_via_hash!(u8); impl_stable_ord_and_stable_hash_via_hash!(u8);
impl_stable_hash_via_hash!(u16); impl_stable_ord_and_stable_hash_via_hash!(u16);
impl_stable_hash_via_hash!(u32); impl_stable_ord_and_stable_hash_via_hash!(u32);
impl_stable_hash_via_hash!(u64); impl_stable_ord_and_stable_hash_via_hash!(u64);
impl_stable_hash_via_hash!(usize); impl_stable_ord_and_stable_hash_via_hash!(usize);
impl_stable_hash_via_hash!(u128); impl_stable_ord_and_stable_hash_via_hash!(u128);
impl_stable_hash_via_hash!(i128); impl_stable_ord_and_stable_hash_via_hash!(i128);
impl_stable_hash_via_hash!(char); impl_stable_ord_and_stable_hash_via_hash!(char);
impl_stable_hash_via_hash!(()); impl_stable_ord_and_stable_hash_via_hash!(());
impl<CTX> HashStable<CTX> for ! { impl<CTX> HashStable<CTX> for ! {
fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) { fn hash_stable(&self, _ctx: &mut CTX, _hasher: &mut StableHasher) {
@ -444,6 +474,10 @@ impl<CTX> HashStable<CTX> for String {
} }
} }
// Safety: String comparison only depends on their contents and the
// contents are not changed by (de-)serialization.
unsafe impl StableOrd for String {}
impl<HCX> ToStableHashKey<HCX> for String { impl<HCX> ToStableHashKey<HCX> for String {
type KeyType = String; type KeyType = String;
#[inline] #[inline]
@ -459,6 +493,9 @@ impl<CTX> HashStable<CTX> for bool {
} }
} }
// Safety: sort order of bools is not changed by (de-)serialization.
unsafe impl StableOrd for bool {}
impl<T, CTX> HashStable<CTX> for Option<T> impl<T, CTX> HashStable<CTX> for Option<T>
where where
T: HashStable<CTX>, T: HashStable<CTX>,
@ -474,6 +511,9 @@ where
} }
} }
// Safety: the Option wrapper does not add instability to comparison.
unsafe impl<T: StableOrd> StableOrd for Option<T> {}
impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2> impl<T1, T2, CTX> HashStable<CTX> for Result<T1, T2>
where where
T1: HashStable<CTX>, T1: HashStable<CTX>,
@ -550,8 +590,8 @@ where
} }
} }
impl_stable_hash_via_hash!(::std::path::Path); impl_stable_ord_and_stable_hash_via_hash!(::std::path::Path);
impl_stable_hash_via_hash!(::std::path::PathBuf); impl_stable_ord_and_stable_hash_via_hash!(::std::path::PathBuf);
impl<K, V, R, HCX> HashStable<HCX> for ::std::collections::HashMap<K, V, R> impl<K, V, R, HCX> HashStable<HCX> for ::std::collections::HashMap<K, V, R>
where where
@ -584,27 +624,26 @@ where
impl<K, V, HCX> HashStable<HCX> for ::std::collections::BTreeMap<K, V> impl<K, V, HCX> HashStable<HCX> for ::std::collections::BTreeMap<K, V>
where where
K: ToStableHashKey<HCX>, K: HashStable<HCX> + StableOrd,
V: HashStable<HCX>, V: HashStable<HCX>,
{ {
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, (key, value)| { self.len().hash_stable(hcx, hasher);
let key = key.to_stable_hash_key(hcx); for entry in self.iter() {
key.hash_stable(hcx, hasher); entry.hash_stable(hcx, hasher);
value.hash_stable(hcx, hasher); }
});
} }
} }
impl<K, HCX> HashStable<HCX> for ::std::collections::BTreeSet<K> impl<K: StableOrd, HCX> HashStable<HCX> for ::std::collections::BTreeSet<K>
where where
K: ToStableHashKey<HCX>, K: HashStable<HCX> + StableOrd,
{ {
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
stable_hash_reduce(hcx, hasher, self.iter(), self.len(), |hasher, hcx, key| { self.len().hash_stable(hcx, hasher);
let key = key.to_stable_hash_key(hcx); for entry in self.iter() {
key.hash_stable(hcx, hasher); entry.hash_stable(hcx, hasher);
}); }
} }
} }

View File

@ -1,5 +1,5 @@
use crate::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_ID}; use crate::def_id::{DefId, DefIndex, LocalDefId, CRATE_DEF_ID};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey};
use rustc_span::{def_id::DefPathHash, HashStableContext}; use rustc_span::{def_id::DefPathHash, HashStableContext};
use std::fmt; use std::fmt;
@ -146,6 +146,10 @@ impl ItemLocalId {
pub const INVALID: ItemLocalId = ItemLocalId::MAX; pub const INVALID: ItemLocalId = ItemLocalId::MAX;
} }
// Safety: Ord is implement as just comparing the LocalItemId's numerical
// values and these are not changed by (de-)serialization.
unsafe impl StableOrd for ItemLocalId {}
/// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`. /// The `HirId` corresponding to `CRATE_NODE_ID` and `CRATE_DEF_ID`.
pub const CRATE_HIR_ID: HirId = pub const CRATE_HIR_ID: HirId =
HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) }; HirId { owner: OwnerId { def_id: CRATE_DEF_ID }, local_id: ItemLocalId::from_u32(0) };

View File

@ -10,7 +10,7 @@ use crate::{lint, HashStableContext};
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stable_hasher::ToStableHashKey; use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey};
use rustc_target::abi::Align; use rustc_target::abi::Align;
use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo}; use rustc_target::spec::{PanicStrategy, SanitizerSet, SplitDebuginfo};
use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS}; use rustc_target::spec::{Target, TargetTriple, TargetWarnings, TARGETS};
@ -288,6 +288,9 @@ pub enum OutputType {
DepInfo, DepInfo,
} }
// Safety: Trivial C-Style enums have a stable sort order across compilation sessions.
unsafe impl StableOrd for OutputType {}
impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType { impl<HCX: HashStableContext> ToStableHashKey<HCX> for OutputType {
type KeyType = Self; type KeyType = Self;