Skip to content

Commit e0bf0d3

Browse files
committed
Auto merge of rust-lang#119192 - michaelwoerister:mcp533-push, r=<try>
Replace a number of FxHashMaps/Sets with stable-iteration-order alternatives This PR replaces almost all of the remaining `FxHashMap`s in query results with either `FxIndexMap` or `UnordMap`. The only case that is missing is the `EffectiveVisibilities` struct which turned out to not be straightforward to transform. Once that is done too, we can remove the `HashStable` implementation from `HashMap`. The first commit adds the `StableCompare` trait which 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 `StableCompare` 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`. The rest of the commits are rather mechanical and don't overlap, so they are best reviewed individually. Part of [MCP 533](rust-lang/compiler-team#533).
2 parents ef1b78e + e71af93 commit e0bf0d3

File tree

34 files changed

+303
-149
lines changed

34 files changed

+303
-149
lines changed

compiler/rustc_codegen_ssa/src/back/symbol_export.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::base::allocator_kind_for_codegen;
33
use std::collections::hash_map::Entry::*;
44

55
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE};
6-
use rustc_data_structures::fx::FxHashMap;
6+
use rustc_data_structures::unord::UnordMap;
77
use rustc_hir::def::DefKind;
88
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
99
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -393,10 +393,10 @@ fn exported_symbols_provider_local(
393393
fn upstream_monomorphizations_provider(
394394
tcx: TyCtxt<'_>,
395395
(): (),
396-
) -> DefIdMap<FxHashMap<GenericArgsRef<'_>, CrateNum>> {
396+
) -> DefIdMap<UnordMap<GenericArgsRef<'_>, CrateNum>> {
397397
let cnums = tcx.crates(());
398398

399-
let mut instances: DefIdMap<FxHashMap<_, _>> = Default::default();
399+
let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
400400

401401
let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
402402

@@ -445,7 +445,7 @@ fn upstream_monomorphizations_provider(
445445
fn upstream_monomorphizations_for_provider(
446446
tcx: TyCtxt<'_>,
447447
def_id: DefId,
448-
) -> Option<&FxHashMap<GenericArgsRef<'_>, CrateNum>> {
448+
) -> Option<&UnordMap<GenericArgsRef<'_>, CrateNum>> {
449449
debug_assert!(!def_id.is_local());
450450
tcx.upstream_monomorphizations(()).get(&def_id)
451451
}
@@ -656,7 +656,7 @@ fn maybe_emutls_symbol_name<'tcx>(
656656
}
657657
}
658658

659-
fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
659+
fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<String> {
660660
// Build up a map from DefId to a `NativeLib` structure, where
661661
// `NativeLib` internally contains information about
662662
// `#[link(wasm_import_module = "...")]` for example.
@@ -665,9 +665,9 @@ fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, S
665665
let def_id_to_native_lib = native_libs
666666
.iter()
667667
.filter_map(|lib| lib.foreign_module.map(|id| (id, lib)))
668-
.collect::<FxHashMap<_, _>>();
668+
.collect::<DefIdMap<_>>();
669669

670-
let mut ret = FxHashMap::default();
670+
let mut ret = DefIdMap::default();
671671
for (def_id, lib) in tcx.foreign_modules(cnum).iter() {
672672
let module = def_id_to_native_lib.get(def_id).and_then(|s| s.wasm_import_module());
673673
let Some(module) = module else { continue };

compiler/rustc_codegen_ssa/src/target_features.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::errors;
22
use rustc_ast::ast;
33
use rustc_attr::InstructionSetAttr;
4-
use rustc_data_structures::fx::FxHashMap;
54
use rustc_data_structures::fx::FxIndexSet;
5+
use rustc_data_structures::unord::UnordMap;
66
use rustc_errors::Applicability;
77
use rustc_hir::def::DefKind;
88
use rustc_hir::def_id::DefId;
@@ -18,7 +18,7 @@ use rustc_span::Span;
1818
pub fn from_target_feature(
1919
tcx: TyCtxt<'_>,
2020
attr: &ast::Attribute,
21-
supported_target_features: &FxHashMap<String, Option<Symbol>>,
21+
supported_target_features: &UnordMap<String, Option<Symbol>>,
2222
target_features: &mut Vec<Symbol>,
2323
) {
2424
let Some(list) = attr.meta_item_list() else { return };

compiler/rustc_data_structures/src/stable_hasher.rs

+26
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,32 @@ unsafe impl<T: StableOrd> StableOrd for &T {
245245
const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
246246
}
247247

248+
/// This is a companion trait to `StableOrd`. Some types like `Symbol` can be
249+
/// compared in a cross-session stable way, but their `Ord` implementation is
250+
/// not stable. In such cases, a `StableOrd` implementation can be provided
251+
/// to offer a lightweight way for stable sorting. (The more heavyweight option
252+
/// is to sort via `ToStableHashKey`, but then sorting needs to have access to
253+
/// a stable hashing context and `ToStableHashKey` can also be expensive as in
254+
/// the case of `Symbol` where it has to allocate a `String`.)
255+
///
256+
/// See the documentation of [StableOrd] for how stable sort order is defined.
257+
/// The same definition applies here. Be careful when implementing this trait.
258+
pub trait StableCompare {
259+
const CAN_USE_UNSTABLE_SORT: bool;
260+
261+
fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering;
262+
}
263+
264+
/// `StableOrd` denotes that the type's `Ord` implementation is stable, so
265+
/// we can implement `StableCompare` by just delegating to `Ord`.
266+
impl<T: StableOrd> StableCompare for T {
267+
const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
268+
269+
fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering {
270+
self.cmp(other)
271+
}
272+
}
273+
248274
/// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since
249275
/// that has the same requirements.
250276
///

compiler/rustc_data_structures/src/unord.rs

+95-27
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@
33
//! as required by the query system.
44
55
use rustc_hash::{FxHashMap, FxHashSet};
6-
use smallvec::SmallVec;
76
use std::{
8-
borrow::Borrow,
7+
borrow::{Borrow, BorrowMut},
98
collections::hash_map::Entry,
109
hash::Hash,
1110
iter::{Product, Sum},
@@ -14,7 +13,7 @@ use std::{
1413

1514
use crate::{
1615
fingerprint::Fingerprint,
17-
stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey},
16+
stable_hasher::{HashStable, StableCompare, StableHasher, ToStableHashKey},
1817
};
1918

2019
/// `UnordItems` is the order-less version of `Iterator`. It only contains methods
@@ -134,36 +133,78 @@ impl<'a, T: Copy + 'a, I: Iterator<Item = &'a T>> UnordItems<&'a T, I> {
134133
}
135134
}
136135

137-
impl<T: Ord, I: Iterator<Item = T>> UnordItems<T, I> {
136+
impl<T, I: Iterator<Item = T>> UnordItems<T, I> {
137+
#[inline]
138138
pub fn into_sorted<HCX>(self, hcx: &HCX) -> Vec<T>
139139
where
140140
T: ToStableHashKey<HCX>,
141141
{
142-
let mut items: Vec<T> = self.0.collect();
143-
items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
144-
items
142+
self.collect_sorted(hcx, true)
145143
}
146144

147145
#[inline]
148146
pub fn into_sorted_stable_ord(self) -> Vec<T>
149147
where
150-
T: Ord + StableOrd,
148+
T: StableCompare,
149+
{
150+
self.collect_stable_ord_by_key(|x| x)
151+
}
152+
153+
#[inline]
154+
pub fn into_sorted_stable_ord_by_key<K, C>(self, project_to_key: C) -> Vec<T>
155+
where
156+
K: StableCompare,
157+
C: for<'a> Fn(&'a T) -> &'a K,
151158
{
152-
let mut items: Vec<T> = self.0.collect();
153-
if !T::CAN_USE_UNSTABLE_SORT {
154-
items.sort();
155-
} else {
156-
items.sort_unstable()
159+
self.collect_stable_ord_by_key(project_to_key)
160+
}
161+
162+
#[inline]
163+
pub fn collect_sorted<HCX, C>(self, hcx: &HCX, cache_sort_key: bool) -> C
164+
where
165+
T: ToStableHashKey<HCX>,
166+
C: FromIterator<T> + BorrowMut<[T]>,
167+
{
168+
let mut items: C = self.0.collect();
169+
170+
let slice = items.borrow_mut();
171+
if slice.len() > 1 {
172+
if cache_sort_key {
173+
slice.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
174+
} else {
175+
slice.sort_by_key(|x| x.to_stable_hash_key(hcx));
176+
}
157177
}
178+
158179
items
159180
}
160181

161-
pub fn into_sorted_small_vec<HCX, const LEN: usize>(self, hcx: &HCX) -> SmallVec<[T; LEN]>
182+
#[inline]
183+
pub fn collect_stable_ord_by_key<K, C, P>(self, project_to_key: P) -> C
162184
where
163-
T: ToStableHashKey<HCX>,
185+
K: StableCompare,
186+
P: for<'a> Fn(&'a T) -> &'a K,
187+
C: FromIterator<T> + BorrowMut<[T]>,
164188
{
165-
let mut items: SmallVec<[T; LEN]> = self.0.collect();
166-
items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
189+
let mut items: C = self.0.collect();
190+
191+
let slice = items.borrow_mut();
192+
if slice.len() > 1 {
193+
if !K::CAN_USE_UNSTABLE_SORT {
194+
slice.sort_by(|a, b| {
195+
let a_key = project_to_key(a);
196+
let b_key = project_to_key(b);
197+
a_key.stable_cmp(b_key)
198+
});
199+
} else {
200+
slice.sort_unstable_by(|a, b| {
201+
let a_key = project_to_key(a);
202+
let b_key = project_to_key(b);
203+
a_key.stable_cmp(b_key)
204+
});
205+
}
206+
}
207+
167208
items
168209
}
169210
}
@@ -268,16 +309,30 @@ impl<V: Eq + Hash> UnordSet<V> {
268309
}
269310

270311
/// Returns the items of this set in stable sort order (as defined by
271-
/// `StableOrd`). This method is much more efficient than
312+
/// `StableCompare`). This method is much more efficient than
272313
/// `into_sorted` because it does not need to transform keys to their
273314
/// `ToStableHashKey` equivalent.
274315
#[inline]
275-
pub fn to_sorted_stable_ord(&self) -> Vec<V>
316+
pub fn to_sorted_stable_ord(&self) -> Vec<&V>
276317
where
277-
V: Ord + StableOrd + Clone,
318+
V: StableCompare,
278319
{
279-
let mut items: Vec<V> = self.inner.iter().cloned().collect();
280-
items.sort_unstable();
320+
let mut items: Vec<&V> = self.inner.iter().collect();
321+
items.sort_unstable_by(|a, b| a.stable_cmp(*b));
322+
items
323+
}
324+
325+
/// Returns the items of this set in stable sort order (as defined by
326+
/// `StableCompare`). This method is much more efficient than
327+
/// `into_sorted` because it does not need to transform keys to their
328+
/// `ToStableHashKey` equivalent.
329+
#[inline]
330+
pub fn into_sorted_stable_ord(self) -> Vec<V>
331+
where
332+
V: StableCompare,
333+
{
334+
let mut items: Vec<V> = self.inner.into_iter().collect();
335+
items.sort_unstable_by(V::stable_cmp);
281336
items
282337
}
283338

@@ -483,16 +538,16 @@ impl<K: Eq + Hash, V> UnordMap<K, V> {
483538
to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k)
484539
}
485540

486-
/// Returns the entries of this map in stable sort order (as defined by `StableOrd`).
541+
/// Returns the entries of this map in stable sort order (as defined by `StableCompare`).
487542
/// This method can be much more efficient than `into_sorted` because it does not need
488543
/// to transform keys to their `ToStableHashKey` equivalent.
489544
#[inline]
490-
pub fn to_sorted_stable_ord(&self) -> Vec<(K, &V)>
545+
pub fn to_sorted_stable_ord(&self) -> Vec<(&K, &V)>
491546
where
492-
K: Ord + StableOrd + Copy,
547+
K: StableCompare,
493548
{
494-
let mut items: Vec<(K, &V)> = self.inner.iter().map(|(&k, v)| (k, v)).collect();
495-
items.sort_unstable_by_key(|&(k, _)| k);
549+
let mut items: Vec<_> = self.inner.iter().collect();
550+
items.sort_unstable_by(|(a, _), (b, _)| a.stable_cmp(*b));
496551
items
497552
}
498553

@@ -510,6 +565,19 @@ impl<K: Eq + Hash, V> UnordMap<K, V> {
510565
to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |(k, _)| k)
511566
}
512567

568+
/// Returns the entries of this map in stable sort order (as defined by `StableCompare`).
569+
/// This method can be much more efficient than `into_sorted` because it does not need
570+
/// to transform keys to their `ToStableHashKey` equivalent.
571+
#[inline]
572+
pub fn into_sorted_stable_ord(self) -> Vec<(K, V)>
573+
where
574+
K: StableCompare,
575+
{
576+
let mut items: Vec<(K, V)> = self.inner.into_iter().collect();
577+
items.sort_unstable_by(|a, b| a.0.stable_cmp(&b.0));
578+
items
579+
}
580+
513581
/// Returns the values of this map in stable sort order (as defined by K's
514582
/// `ToStableHashKey` implementation).
515583
///

compiler/rustc_errors/src/diagnostic.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{
44
SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
55
};
66
use rustc_data_structures::fx::FxHashMap;
7+
use rustc_data_structures::unord::UnordMap;
78
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
89
use rustc_error_messages::FluentValue;
910
use rustc_lint_defs::{Applicability, LintExpectationId};
@@ -280,7 +281,7 @@ impl Diagnostic {
280281

281282
pub(crate) fn update_unstable_expectation_id(
282283
&mut self,
283-
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
284+
unstable_to_stable: &UnordMap<LintExpectationId, LintExpectationId>,
284285
) {
285286
if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) =
286287
&mut self.level

compiler/rustc_errors/src/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ extern crate self as rustc_errors;
2929

3030
pub use emitter::ColorConfig;
3131

32+
use rustc_data_structures::unord::UnordMap;
3233
use rustc_lint_defs::LintExpectationId;
3334
use Level::*;
3435

3536
use emitter::{is_case_difference, DynEmitter, Emitter, EmitterWriter};
3637
use registry::Registry;
37-
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
38+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
3839
use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
3940
use rustc_data_structures::sync::{Lock, Lrc};
4041
use rustc_data_structures::AtomicRef;
@@ -1367,7 +1368,7 @@ impl DiagCtxt {
13671368

13681369
pub fn update_unstable_expectation_id(
13691370
&self,
1370-
unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
1371+
unstable_to_stable: &UnordMap<LintExpectationId, LintExpectationId>,
13711372
) {
13721373
let mut inner = self.inner.borrow_mut();
13731374
let diags = std::mem::take(&mut inner.unstable_expect_diagnostics);

compiler/rustc_hir_analysis/src/astconv/errors.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::errors::{
66
use crate::fluent_generated as fluent;
77
use crate::traits::error_reporting::report_object_safety_error;
88
use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
9+
use rustc_data_structures::unord::UnordMap;
910
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
1011
use rustc_hir as hir;
1112
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -673,7 +674,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
673674
}))
674675
})
675676
.flatten()
676-
.collect::<FxHashMap<Symbol, &ty::AssocItem>>();
677+
.collect::<UnordMap<Symbol, &ty::AssocItem>>();
677678

678679
let mut names = names
679680
.into_iter()
@@ -709,7 +710,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
709710
let mut where_constraints = vec![];
710711
let mut already_has_generics_args_suggestion = false;
711712
for (span, assoc_items) in &associated_types {
712-
let mut names: FxHashMap<_, usize> = FxHashMap::default();
713+
let mut names: UnordMap<_, usize> = Default::default();
713714
for item in assoc_items {
714715
types_count += 1;
715716
*names.entry(item.name).or_insert(0) += 1;

compiler/rustc_hir_analysis/src/check/compare_impl_item.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::potentially_plural_count;
22
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
3-
use hir::def_id::{DefId, LocalDefId};
3+
use hir::def_id::{DefId, DefIdMap, LocalDefId};
44
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
55
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
66
use rustc_hir as hir;
@@ -478,7 +478,7 @@ fn compare_asyncness<'tcx>(
478478
pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
479479
tcx: TyCtxt<'tcx>,
480480
impl_m_def_id: LocalDefId,
481-
) -> Result<&'tcx FxHashMap<DefId, ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed> {
481+
) -> Result<&'tcx DefIdMap<ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed> {
482482
let impl_m = tcx.opt_associated_item(impl_m_def_id.to_def_id()).unwrap();
483483
let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
484484
let impl_trait_ref =
@@ -706,7 +706,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
706706
);
707707
ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
708708

709-
let mut remapped_types = FxHashMap::default();
709+
let mut remapped_types = DefIdMap::default();
710710
for (def_id, (ty, args)) in collected_types {
711711
match infcx.fully_resolve((ty, args)) {
712712
Ok((ty, args)) => {

0 commit comments

Comments
 (0)