diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 6d75b0fb8a0..afe26f80de8 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -245,6 +245,32 @@ unsafe impl StableOrd for &T { const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; } +/// This is a companion trait to `StableOrd`. Some types like `Symbol` can be +/// compared in a cross-session stable way, but their `Ord` implementation is +/// not stable. In such cases, a `StableOrd` implementation can be provided +/// to offer a lightweight way for stable sorting. (The more heavyweight option +/// is to sort via `ToStableHashKey`, but then sorting needs to have access to +/// a stable hashing context and `ToStableHashKey` can also be expensive as in +/// the case of `Symbol` where it has to allocate a `String`.) +/// +/// See the documentation of [StableOrd] for how stable sort order is defined. +/// The same definition applies here. Be careful when implementing this trait. +pub trait StableCompare { + const CAN_USE_UNSTABLE_SORT: bool; + + fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering; +} + +/// `StableOrd` denotes that the type's `Ord` implementation is stable, so +/// we can implement `StableCompare` by just delegating to `Ord`. +impl StableCompare for T { + const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT; + + fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.cmp(other) + } +} + /// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since /// that has the same requirements. /// diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs index 47c56eba7ad..2475713012d 100644 --- a/compiler/rustc_data_structures/src/unord.rs +++ b/compiler/rustc_data_structures/src/unord.rs @@ -14,7 +14,7 @@ use std::{ use crate::{ fingerprint::Fingerprint, - stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey}, + stable_hasher::{HashStable, StableCompare, StableHasher, ToStableHashKey}, }; /// `UnordItems` is the order-less version of `Iterator`. It only contains methods @@ -134,7 +134,7 @@ impl<'a, T: Copy + 'a, I: Iterator> UnordItems<&'a T, I> { } } -impl> UnordItems { +impl> UnordItems { pub fn into_sorted(self, hcx: &HCX) -> Vec where T: ToStableHashKey, @@ -147,13 +147,36 @@ impl> UnordItems { #[inline] pub fn into_sorted_stable_ord(self) -> Vec where - T: Ord + StableOrd, + T: StableCompare, { let mut items: Vec = self.0.collect(); if !T::CAN_USE_UNSTABLE_SORT { - items.sort(); + items.sort_by(T::stable_cmp); } else { - items.sort_unstable() + items.sort_unstable_by(T::stable_cmp) + } + items + } + + #[inline] + pub fn into_sorted_stable_ord_by_key(self, project_to_key: C) -> Vec + where + K: StableCompare, + C: for<'a> Fn(&'a T) -> &'a K, + { + let mut items: Vec = self.0.collect(); + if !K::CAN_USE_UNSTABLE_SORT { + items.sort_by(|a, b| { + let a_key = project_to_key(a); + let b_key = project_to_key(b); + a_key.stable_cmp(b_key) + }); + } else { + items.sort_unstable_by(|a, b| { + let a_key = project_to_key(a); + let b_key = project_to_key(b); + a_key.stable_cmp(b_key) + }); } items } @@ -268,16 +291,30 @@ impl UnordSet { } /// Returns the items of this set in stable sort order (as defined by - /// `StableOrd`). This method is much more efficient than + /// `StableCompare`). This method is much more efficient than /// `into_sorted` because it does not need to transform keys to their /// `ToStableHashKey` equivalent. #[inline] - pub fn to_sorted_stable_ord(&self) -> Vec + pub fn to_sorted_stable_ord(&self) -> Vec<&V> where - V: Ord + StableOrd + Clone, + V: StableCompare, { - let mut items: Vec = self.inner.iter().cloned().collect(); - items.sort_unstable(); + let mut items: Vec<&V> = self.inner.iter().collect(); + items.sort_unstable_by(|a, b| a.stable_cmp(*b)); + items + } + + /// Returns the items of this set in stable sort order (as defined by + /// `StableCompare`). This method is much more efficient than + /// `into_sorted` because it does not need to transform keys to their + /// `ToStableHashKey` equivalent. + #[inline] + pub fn into_sorted_stable_ord(self) -> Vec + where + V: StableCompare, + { + let mut items: Vec = self.inner.into_iter().collect(); + items.sort_unstable_by(V::stable_cmp); items } @@ -483,16 +520,16 @@ impl UnordMap { to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k) } - /// Returns the entries of this map in stable sort order (as defined by `StableOrd`). + /// Returns the entries of this map in stable sort order (as defined by `StableCompare`). /// This method can be much more efficient than `into_sorted` because it does not need /// to transform keys to their `ToStableHashKey` equivalent. #[inline] - pub fn to_sorted_stable_ord(&self) -> Vec<(K, &V)> + pub fn to_sorted_stable_ord(&self) -> Vec<(&K, &V)> where - K: Ord + StableOrd + Copy, + K: StableCompare, { - let mut items: Vec<(K, &V)> = self.inner.iter().map(|(&k, v)| (k, v)).collect(); - items.sort_unstable_by_key(|&(k, _)| k); + let mut items: Vec<_> = self.inner.iter().collect(); + items.sort_unstable_by(|(a, _), (b, _)| a.stable_cmp(*b)); items } @@ -510,6 +547,19 @@ impl UnordMap { to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |(k, _)| k) } + /// Returns the entries of this map in stable sort order (as defined by `StableCompare`). + /// This method can be much more efficient than `into_sorted` because it does not need + /// to transform keys to their `ToStableHashKey` equivalent. + #[inline] + pub fn into_sorted_stable_ord(self) -> Vec<(K, V)> + where + K: StableCompare, + { + let mut items: Vec<(K, V)> = self.inner.into_iter().collect(); + items.sort_unstable_by(|a, b| a.0.stable_cmp(&b.0)); + items + } + /// Returns the values of this map in stable sort order (as defined by K's /// `ToStableHashKey` implementation). /// diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 47fdd64796e..22a7ede601e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -913,7 +913,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for ((span, add_where_or_comma), obligations) in type_params.into_iter() { restrict_type_params = true; // #74886: Sort here so that the output is always the same. - let obligations = obligations.to_sorted_stable_ord(); + let obligations = obligations.into_sorted_stable_ord(); err.span_suggestion_verbose( span, format!( diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 47b9d5f6503..f6b05e1b35a 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -912,7 +912,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { drop_order: bool, ) -> MigrationWarningReason { MigrationWarningReason { - auto_traits: auto_trait_reasons.to_sorted_stable_ord(), + auto_traits: auto_trait_reasons.into_sorted_stable_ord(), drop_order, } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 719d85ed3db..c56a028321a 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -473,7 +473,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); let fcx_coercion_casts = fcx_typeck_results.coercion_casts().to_sorted_stable_ord(); - for local_id in fcx_coercion_casts { + for &local_id in fcx_coercion_casts { self.typeck_results.set_coercion_cast(local_id); } } diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index bdc935a5e3b..08b7d08bcc0 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -103,7 +103,7 @@ pub fn save_work_product_index( // deleted during invalidation. Some object files don't change their // content, they are just not needed anymore. let previous_work_products = dep_graph.previous_work_products(); - for (id, wp) in previous_work_products.to_sorted_stable_ord().iter() { + for (id, wp) in previous_work_products.to_sorted_stable_ord() { if !new_work_products.contains_key(id) { work_product::delete_workproduct_files(sess, wp); debug_assert!( diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index a25cfe68e0d..eed35326c45 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -9,7 +9,9 @@ pub use self::Level::*; use rustc_ast::node_id::NodeId; use rustc_ast::{AttrId, Attribute}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{ + HashStable, StableCompare, StableHasher, ToStableHashKey, +}; use rustc_error_messages::{DiagnosticMessage, MultiSpan}; use rustc_hir::HashStableContext; use rustc_hir::HirId; @@ -541,6 +543,14 @@ impl ToStableHashKey for LintId { } } +impl StableCompare for LintId { + const CAN_USE_UNSTABLE_SORT: bool = true; + + fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.lint_name_raw().cmp(&other.lint_name_raw()) + } +} + #[derive(Debug)] pub struct AmbiguityErrorDiag { pub msg: String, diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 58699c934b6..ad41a674dd8 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -527,7 +527,7 @@ impl<'a, V> LocalTableInContext<'a, V> { } pub fn items_in_stable_order(&self) -> Vec<(ItemLocalId, &'a V)> { - self.data.to_sorted_stable_ord() + self.data.items().map(|(&k, v)| (k, v)).into_sorted_stable_ord_by_key(|(k, _)| k) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 0b44071496e..d8d24851620 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -4,7 +4,9 @@ use rustc_arena::DroplessArena; use rustc_data_structures::fx::FxIndexSet; -use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{ + HashStable, StableCompare, StableHasher, ToStableHashKey, +}; use rustc_data_structures::sync::Lock; use rustc_macros::HashStable_Generic; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -2103,6 +2105,14 @@ impl ToStableHashKey for Symbol { } } +impl StableCompare for Symbol { + const CAN_USE_UNSTABLE_SORT: bool = true; + + fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering { + self.as_str().cmp(other.as_str()) + } +} + pub(crate) struct Interner(Lock); // The `&'static str`s in this type actually point into the arena.