From 0dde9186493b43dc2bc4e87c03d5030f735c8697 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 21 Sep 2026 14:48:10 +0300 Subject: [PATCH 1/6] Deterministic def path hash map encoding --- Cargo.lock | 1 + compiler/rustc_hir_id/src/definitions.rs | 68 ++++++++++++++++++- compiler/rustc_interface/src/passes.rs | 2 + compiler/rustc_metadata/Cargo.toml | 1 + .../src/rmeta/def_path_hash_map.rs | 33 ++++++--- compiler/rustc_middle/src/ty/context.rs | 4 +- 6 files changed, 94 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e57835c9cfef..44c2db1e941a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4415,6 +4415,7 @@ dependencies = [ "rustc_expand", "rustc_feature", "rustc_fs_util", + "rustc_hashes", "rustc_hir", "rustc_hir_pretty", "rustc_index", diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 50f7a15b19adf..a06e8336db2e4 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -8,6 +8,7 @@ use std::fmt::{self, Write}; use std::hash::Hash; use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hash::StableHasher; use rustc_hashes::Hash64; use rustc_index::IndexVec; @@ -46,13 +47,68 @@ impl LocalDefIdMap { } } +/// Struct that contains two maps: `det_part` is used at the earlier stages of compilation +/// (see where `commit_end_of_determinism` is called, at the moment of writing +/// it is after prefetch of `hir_crate_items` in `run_required_analysis`), `non_det_part` is used +/// when def ids are allocated non-deterministically (in parallel compiler), +/// i.e., order of serialized pairs may be different. +/// In order to preserve deterministic output of the compiler we need to deterministically encode this map, so we use +/// `SortedMap` to store the mapping between local hashes and def indices, which gives us +/// deterministic iteration when encoding crate metadata. For encoding/decoding details see +/// `compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs`. +#[derive(Debug, Default)] +pub struct DefPathToIndexMap { + pub det_part: DefPathHashMap, + pub non_det_part: SortedMap, + non_det_mode: bool, +} + +impl DefPathToIndexMap { + #[inline] + pub fn get(&self, hash: &Hash64) -> Option { + match self.det_part.get(hash) { + Some(index) => Some(index), + None => { + if self.non_det_mode { + self.non_det_part.get(hash).copied() + } else { + None + } + } + } + } + + /// This insert function does not behave like regular `insert` of a `HashMap`, + /// as the return value is used only for printing information about existing + /// def index for local hash before panicking. So we can do not actually insert + /// def index into `det_part` when we are in non-deterministic mode. + #[inline] + pub fn insert(&mut self, hash: &Hash64, index: DefIndex) -> Option { + match self.non_det_mode { + false => self.det_part.insert(hash, &index), + true => { + if let Some(existing) = self.det_part.get(hash) { + return Some(existing); + } + + self.non_det_part.insert(*hash, index) + } + } + } + + #[inline] + pub fn switch_to_non_det_mode(&mut self) { + self.non_det_mode = true; + } +} + #[derive(Debug)] pub struct Definitions { stable_crate_id: StableCrateId, def_id_to_key: IndexVec, // We do only store the local hash, as all the definitions are from the current crate. def_path_hashes: IndexVec, - def_path_hash_to_index: DefPathHashMap, + def_path_hash_to_index: DefPathToIndexMap, } /// A unique identifier that we can use to lookup a definition @@ -249,6 +305,12 @@ pub enum DefPathData { } impl Definitions { + /// This function indicates that def ids allocations are non-deterministic after + /// it was called. + pub fn commit_end_of_determinism(&mut self) { + self.def_path_hash_to_index.switch_to_non_det_mode(); + } + #[inline(always)] pub fn def_key(&self, id: LocalDefId) -> DefKey { self.def_id_to_key[id] @@ -323,7 +385,7 @@ impl Definitions { // Check for hash collisions of DefPathHashes. These should be // exceedingly rare. if let Some(existing) = - self.def_path_hash_to_index.insert(&local_hash, &def_id.local_def_index) + self.def_path_hash_to_index.insert(&local_hash, def_id.local_def_index) { let def_path1 = self.def_path(LocalDefId { local_def_index: existing }); let def_path2 = self.def_path(def_id); @@ -415,7 +477,7 @@ impl Definitions { .map(|local_def_index| LocalDefId { local_def_index }) } - pub fn def_path_hash_to_def_index_map(&self) -> &DefPathHashMap { + pub fn def_path_hash_to_def_index_map(&self) -> &DefPathToIndexMap { &self.def_path_hash_to_index } diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2316e80b6e945..8a3a149066e6a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -1099,6 +1099,8 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { // to use `hir_crate_items`. tcx.ensure_done().hir_crate_items(()); + tcx.untracked().definitions.write().commit_end_of_determinism(); + rustc_passes::delegation::check_glob_and_list_delegations_target_expr(tcx); let sess = tcx.sess; diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index a2ef8454ed6a6..cbcbe4a2ac551 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } rustc_fs_util = { path = "../rustc_fs_util" } +rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 949d7630f6736..1092d56a331f6 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -1,5 +1,8 @@ use rustc_data_structures::owned_slice::OwnedSlice; -use rustc_hir::def_path_hash_map::{Config as HashMapConfig, DefPathHashMap}; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_hashes::Hash64; +use rustc_hir::def_path_hash_map::Config as HashMapConfig; +use rustc_hir::definitions::DefPathToIndexMap; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::def_id::{DefIndex, DefPathHash}; @@ -7,8 +10,8 @@ use crate::rmeta::EncodeContext; use crate::rmeta::decoder::BlobDecodeContext; pub(crate) enum DefPathHashMapRef<'tcx> { - OwnedFromMetadata(odht::HashTable), - BorrowedFromTcx(&'tcx DefPathHashMap), + OwnedFromMetadata(odht::HashTable, SortedMap), + BorrowedFromTcx(&'tcx DefPathToIndexMap), } impl DefPathHashMapRef<'_> { @@ -17,8 +20,14 @@ impl DefPathHashMapRef<'_> { &self, def_path_hash: &DefPathHash, ) -> Option { - match *self { - DefPathHashMapRef::OwnedFromMetadata(ref map) => map.get(&def_path_hash.local_hash()), + match self { + DefPathHashMapRef::OwnedFromMetadata(det_map, non_det_map) => { + let hash = &def_path_hash.local_hash(); + match det_map.get(hash) { + Some(index) => Some(index), + None => non_det_map.get(hash).copied(), + } + } DefPathHashMapRef::BorrowedFromTcx(_) => { panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization") } @@ -29,12 +38,14 @@ impl DefPathHashMapRef<'_> { impl<'a, 'tcx> Encodable> for DefPathHashMapRef<'tcx> { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { match *self { - DefPathHashMapRef::BorrowedFromTcx(def_path_hash_map) => { - let bytes = def_path_hash_map.raw_bytes(); + DefPathHashMapRef::BorrowedFromTcx(map) => { + let bytes = map.det_part.raw_bytes(); e.emit_usize(bytes.len()); e.emit_raw_bytes(bytes); + + map.non_det_part.range(..).encode(e); } - DefPathHashMapRef::OwnedFromMetadata(_) => { + DefPathHashMapRef::OwnedFromMetadata(..) => { panic!("DefPathHashMap::OwnedFromMetadata variant only exists for deserialization") } } @@ -55,6 +66,10 @@ impl<'a> Decodable> for DefPathHashMapRef<'static> { let inner = odht::HashTable::from_raw_bytes(o).unwrap_or_else(|e| { panic!("decode error: {e}"); }); - DefPathHashMapRef::OwnedFromMetadata(inner) + + let elements = Vec::<(Hash64, DefIndex)>::decode(d); + let non_det_map = SortedMap::from_presorted_elements(elements); + + DefPathHashMapRef::OwnedFromMetadata(inner, non_det_map) } } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 9d0b334b63fbd..d5cfaf570bafe 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -1377,9 +1377,7 @@ impl<'tcx> TyCtxt<'tcx> { self.untracked.definitions.freeze() } - pub fn def_path_hash_to_def_index_map( - self, - ) -> &'tcx rustc_hir::def_path_hash_map::DefPathHashMap { + pub fn def_path_hash_to_def_index_map(self) -> &'tcx rustc_hir::definitions::DefPathToIndexMap { // Create a dependency to the crate to be sure we re-execute this when the amount of // definitions change. self.ensure_ok().hir_crate_items(()); From c57dbadb03995c00217b91306e416c5fc2de08d7 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 25 Sep 2026 08:40:20 +0300 Subject: [PATCH 2/6] Review: use Option instead of a bool flag, inline `switch_to_non_det_mode` --- compiler/rustc_hir_id/src/definitions.rs | 26 +++++-------------- .../src/rmeta/def_path_hash_map.rs | 2 +- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index a06e8336db2e4..8f60f8ce3a01c 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -59,8 +59,7 @@ impl LocalDefIdMap { #[derive(Debug, Default)] pub struct DefPathToIndexMap { pub det_part: DefPathHashMap, - pub non_det_part: SortedMap, - non_det_mode: bool, + pub non_det_part: Option>, } impl DefPathToIndexMap { @@ -68,13 +67,7 @@ impl DefPathToIndexMap { pub fn get(&self, hash: &Hash64) -> Option { match self.det_part.get(hash) { Some(index) => Some(index), - None => { - if self.non_det_mode { - self.non_det_part.get(hash).copied() - } else { - None - } - } + None => self.non_det_part.as_ref().and_then(|map| map.get(hash).copied()), } } @@ -84,22 +77,17 @@ impl DefPathToIndexMap { /// def index into `det_part` when we are in non-deterministic mode. #[inline] pub fn insert(&mut self, hash: &Hash64, index: DefIndex) -> Option { - match self.non_det_mode { - false => self.det_part.insert(hash, &index), - true => { + match self.non_det_part.as_mut() { + None => self.det_part.insert(hash, &index), + Some(map) => { if let Some(existing) = self.det_part.get(hash) { return Some(existing); } - self.non_det_part.insert(*hash, index) + map.insert(*hash, index) } } } - - #[inline] - pub fn switch_to_non_det_mode(&mut self) { - self.non_det_mode = true; - } } #[derive(Debug)] @@ -308,7 +296,7 @@ impl Definitions { /// This function indicates that def ids allocations are non-deterministic after /// it was called. pub fn commit_end_of_determinism(&mut self) { - self.def_path_hash_to_index.switch_to_non_det_mode(); + self.def_path_hash_to_index.non_det_part = Some(Default::default()); } #[inline(always)] diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 1092d56a331f6..923c07cd764c9 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -43,7 +43,7 @@ impl<'a, 'tcx> Encodable> for DefPathHashMapRef<'tcx> { e.emit_usize(bytes.len()); e.emit_raw_bytes(bytes); - map.non_det_part.range(..).encode(e); + map.non_det_part.as_ref().unwrap_or(&Default::default()).range(..).encode(e); } DefPathHashMapRef::OwnedFromMetadata(..) => { panic!("DefPathHashMap::OwnedFromMetadata variant only exists for deserialization") From 1b504b3a0b30df3b18224def8e102f815901e3fa Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 25 Sep 2026 08:48:08 +0300 Subject: [PATCH 3/6] Review: use `or_else` instead of matches --- compiler/rustc_hir_id/src/definitions.rs | 7 +++---- compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 8f60f8ce3a01c..263ea4a026e2d 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -65,10 +65,9 @@ pub struct DefPathToIndexMap { impl DefPathToIndexMap { #[inline] pub fn get(&self, hash: &Hash64) -> Option { - match self.det_part.get(hash) { - Some(index) => Some(index), - None => self.non_det_part.as_ref().and_then(|map| map.get(hash).copied()), - } + self.det_part + .get(hash) + .or_else(|| self.non_det_part.as_ref().and_then(|map| map.get(hash).copied())) } /// This insert function does not behave like regular `insert` of a `HashMap`, diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 923c07cd764c9..7f625d59fa784 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -23,10 +23,7 @@ impl DefPathHashMapRef<'_> { match self { DefPathHashMapRef::OwnedFromMetadata(det_map, non_det_map) => { let hash = &def_path_hash.local_hash(); - match det_map.get(hash) { - Some(index) => Some(index), - None => non_det_map.get(hash).copied(), - } + det_map.get(hash).or_else(|| non_det_map.get(hash).copied()) } DefPathHashMapRef::BorrowedFromTcx(_) => { panic!("DefPathHashMap::BorrowedFromTcx variant only exists for serialization") From 956cc84bae470d2a674a3e2a0f2f769173741e50 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 25 Sep 2026 08:53:18 +0300 Subject: [PATCH 4/6] Review: pass hash by value --- compiler/rustc_hir_id/src/definitions.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 263ea4a026e2d..d177630e7a8fc 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -64,10 +64,10 @@ pub struct DefPathToIndexMap { impl DefPathToIndexMap { #[inline] - pub fn get(&self, hash: &Hash64) -> Option { + pub fn get(&self, hash: Hash64) -> Option { self.det_part - .get(hash) - .or_else(|| self.non_det_part.as_ref().and_then(|map| map.get(hash).copied())) + .get(&hash) + .or_else(|| self.non_det_part.as_ref().and_then(|map| map.get(&hash).copied())) } /// This insert function does not behave like regular `insert` of a `HashMap`, @@ -75,15 +75,15 @@ impl DefPathToIndexMap { /// def index for local hash before panicking. So we can do not actually insert /// def index into `det_part` when we are in non-deterministic mode. #[inline] - pub fn insert(&mut self, hash: &Hash64, index: DefIndex) -> Option { + pub fn insert(&mut self, hash: Hash64, index: DefIndex) -> Option { match self.non_det_part.as_mut() { - None => self.det_part.insert(hash, &index), + None => self.det_part.insert(&hash, &index), Some(map) => { - if let Some(existing) = self.det_part.get(hash) { + if let Some(existing) = self.det_part.get(&hash) { return Some(existing); } - map.insert(*hash, index) + map.insert(hash, index) } } } @@ -372,7 +372,7 @@ impl Definitions { // Check for hash collisions of DefPathHashes. These should be // exceedingly rare. if let Some(existing) = - self.def_path_hash_to_index.insert(&local_hash, def_id.local_def_index) + self.def_path_hash_to_index.insert(local_hash, def_id.local_def_index) { let def_path1 = self.def_path(LocalDefId { local_def_index: existing }); let def_path2 = self.def_path(def_id); @@ -460,7 +460,7 @@ impl Definitions { pub fn local_def_path_hash_to_def_id(&self, hash: DefPathHash) -> Option { debug_assert!(hash.stable_crate_id() == self.stable_crate_id); self.def_path_hash_to_index - .get(&hash.local_hash()) + .get(hash.local_hash()) .map(|local_def_index| LocalDefId { local_def_index }) } From 5e0bd1ac99ff370970a30fb2640cac7fc62eccdc Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 28 Sep 2026 12:26:05 +0300 Subject: [PATCH 5/6] Address review comments --- compiler/rustc_hir_id/src/definitions.rs | 38 +++++++++---------- .../src/rmeta/def_path_hash_map.rs | 8 +++- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index d177630e7a8fc..734f149f3bc77 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -47,27 +47,27 @@ impl LocalDefIdMap { } } -/// Struct that contains two maps: `det_part` is used at the earlier stages of compilation -/// (see where `commit_end_of_determinism` is called, at the moment of writing -/// it is after prefetch of `hir_crate_items` in `run_required_analysis`), `non_det_part` is used -/// when def ids are allocated non-deterministically (in parallel compiler), -/// i.e., order of serialized pairs may be different. -/// In order to preserve deterministic output of the compiler we need to deterministically encode this map, so we use -/// `SortedMap` to store the mapping between local hashes and def indices, which gives us -/// deterministic iteration when encoding crate metadata. For encoding/decoding details see -/// `compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs`. #[derive(Debug, Default)] pub struct DefPathToIndexMap { - pub det_part: DefPathHashMap, - pub non_det_part: Option>, + /// Stores mapping from local hash to def indices which are allocated when the order + /// of their allocation is deterministic (see where `commit_end_of_determinism` is called, + /// at the moment of writing it is after prefetch of `hir_crate_items` in `run_required_analysis`). + pub before_parallel_alloc: DefPathHashMap, + + /// Stores the same mapping as previous map but is used after the allocation order of def indices + /// is not deterministic, so when serializing metadata we have a ready to use sorted by stable local + /// hash (which does not change between compiler invocations) mapping. We use it for relatively small number + /// of definitions, so the majority of them would be stored in `DefPathHashMap`, which makes insertion + /// and serialization costs of `SortedMap` acceptable. + pub after_parallel_alloc: Option>, } impl DefPathToIndexMap { #[inline] pub fn get(&self, hash: Hash64) -> Option { - self.det_part + self.before_parallel_alloc .get(&hash) - .or_else(|| self.non_det_part.as_ref().and_then(|map| map.get(&hash).copied())) + .or_else(|| self.after_parallel_alloc.as_ref().and_then(|map| map.get(&hash).copied())) } /// This insert function does not behave like regular `insert` of a `HashMap`, @@ -76,10 +76,10 @@ impl DefPathToIndexMap { /// def index into `det_part` when we are in non-deterministic mode. #[inline] pub fn insert(&mut self, hash: Hash64, index: DefIndex) -> Option { - match self.non_det_part.as_mut() { - None => self.det_part.insert(&hash, &index), + match self.after_parallel_alloc.as_mut() { + None => self.before_parallel_alloc.insert(&hash, &index), Some(map) => { - if let Some(existing) = self.det_part.get(&hash) { + if let Some(existing) = self.before_parallel_alloc.get(&hash) { return Some(existing); } @@ -292,10 +292,10 @@ pub enum DefPathData { } impl Definitions { - /// This function indicates that def ids allocations are non-deterministic after - /// it was called. + /// This function indicates that the order of def id allocations + /// may be non-deterministic after it was called. pub fn commit_end_of_determinism(&mut self) { - self.def_path_hash_to_index.non_det_part = Some(Default::default()); + self.def_path_hash_to_index.after_parallel_alloc = Some(Default::default()); } #[inline(always)] diff --git a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs index 7f625d59fa784..61ec7ad4cf7b3 100644 --- a/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs +++ b/compiler/rustc_metadata/src/rmeta/def_path_hash_map.rs @@ -36,11 +36,15 @@ impl<'a, 'tcx> Encodable> for DefPathHashMapRef<'tcx> { fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) { match *self { DefPathHashMapRef::BorrowedFromTcx(map) => { - let bytes = map.det_part.raw_bytes(); + let bytes = map.before_parallel_alloc.raw_bytes(); e.emit_usize(bytes.len()); e.emit_raw_bytes(bytes); - map.non_det_part.as_ref().unwrap_or(&Default::default()).range(..).encode(e); + map.after_parallel_alloc + .as_ref() + .expect("must be set before metadata encoding") + .range(..) + .encode(e); } DefPathHashMapRef::OwnedFromMetadata(..) => { panic!("DefPathHashMap::OwnedFromMetadata variant only exists for deserialization") From 2f77d206ab26535798a3157d0d72646bd6f3655b Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 28 Sep 2026 13:02:51 +0300 Subject: [PATCH 6/6] Assert that `commit_end_of_determinism` is called only once --- compiler/rustc_hir_id/src/definitions.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_hir_id/src/definitions.rs b/compiler/rustc_hir_id/src/definitions.rs index 734f149f3bc77..426a4e0109153 100644 --- a/compiler/rustc_hir_id/src/definitions.rs +++ b/compiler/rustc_hir_id/src/definitions.rs @@ -295,7 +295,10 @@ impl Definitions { /// This function indicates that the order of def id allocations /// may be non-deterministic after it was called. pub fn commit_end_of_determinism(&mut self) { - self.def_path_hash_to_index.after_parallel_alloc = Some(Default::default()); + assert!( + self.def_path_hash_to_index.after_parallel_alloc.replace(Default::default()).is_none(), + "this function should be called only once" + ) } #[inline(always)]