From f877fb44fe0618f4e1382f60ab94a493f7da6d68 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 8 Apr 2026 17:51:50 +0200 Subject: [PATCH 01/15] rdr: add RDRHashes field to CrateRoot --- compiler/rustc_metadata/src/rmeta/encoder.rs | 1 + compiler/rustc_metadata/src/rmeta/mod.rs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c77711354f459..511570bd3b4ad 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -775,6 +775,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { expn_hashes, def_path_hash_map, specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), + rdr_hashes: RDRHashes { public_api_hash: tcx.crate_hash(LOCAL_CRATE) }, }) }); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c7b2eaa15ebfb..68fb1de13e235 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -206,6 +206,7 @@ pub(crate) struct ProcMacroData { #[derive(MetadataEncodable, BlobDecodable)] pub(crate) struct CrateHeader { pub(crate) triple: TargetTuple, + /// Hash of the crate contents, including private items pub(crate) hash: Svh, pub(crate) name: Symbol, /// Whether this is the header for a proc-macro crate. @@ -297,6 +298,21 @@ pub(crate) struct CrateRoot { symbol_mangling_version: SymbolManglingVersion, specialization_enabled_in: bool, + + rdr_hashes: RDRHashes, +} + +/// Hashes used for the feature [relink don't rebuild](https://github.com/rust-lang/compiler-team/issues/790) +/// +/// This struct is not final. For example it might be +/// beneficial for `cargo check` and `cargo build` to use different hashes for early cutoff. `check` doesn't really +/// depend on spans of inlined/monomorphized mir, it also doesn't need private types used in them. While a full build will need it for code and debug info generation +/// +/// All hashes here are equal to the hash from the crate header (the `crate_hash` query) when the feature is disabled. +#[derive(MetadataEncodable, LazyDecodable)] +struct RDRHashes { + /// Hash of the "public api". It tries to exclude things which cannot change the output in dependents, allowing to skip their rustc invocation, which can be quite expensive even if nothing has changed (macro expansion, checking that nothing changed in the query dependency graph is always executed) + public_api_hash: Svh, } /// On-disk representation of `DefId`. From 27034fccf5ea92e6532432b3339817dd7ed7c825 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 20 Apr 2026 19:13:12 +0200 Subject: [PATCH 02/15] rdr: add public_api_hash unstable option --- compiler/rustc_session/src/options.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index a4e9a89a78c7a..383e326156cd1 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2564,6 +2564,8 @@ options! { "use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"), profiler_runtime: String = (String::from("profiler_builtins"), parse_string, [TRACKED], "name of the profiler runtime crate to automatically inject (default: `profiler_builtins`)"), + public_api_hash: bool = (false, parse_bool, [TRACKED], + "track public api hash instead of full crate hash in queries that read from rmeta of the dependencies"), query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing (default: no)"), randomize_layout: bool = (false, parse_bool, [TRACKED], From 56124084a1a0831b4b6be728f727898dd37d3b50 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 20 Apr 2026 12:36:29 +0200 Subject: [PATCH 03/15] derive HashStable for TargetModifier --- compiler/rustc_session/src/options.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 383e326156cd1..024a8711d31cf 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -11,7 +11,7 @@ use rustc_errors::{ColorConfig, TerminalUrl}; use rustc_feature::UnstableFeatures; use rustc_hashes::Hash64; use rustc_hir::attrs::CollapseMacroDebuginfo; -use rustc_macros::{BlobDecodable, Encodable}; +use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_span::{RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm}; use rustc_target::spec::{ @@ -76,7 +76,8 @@ pub struct ExtendedTargetModifierInfo { /// A recorded -Zopt_name=opt_value (or -Copt_name=opt_value) /// which alter the ABI or effectiveness of exploit mitigations. -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Encodable, BlobDecodable)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Encodable, BlobDecodable, HashStable_Generic)] pub struct TargetModifier { /// Option enum value pub opt: OptionsTargetModifiers, @@ -183,7 +184,8 @@ macro_rules! top_level_options { )* } ) => { - #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, BlobDecodable)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] + #[derive(Encodable, BlobDecodable, HashStable_Generic)] pub enum OptionsTargetModifiers { $( $( @@ -493,7 +495,8 @@ macro_rules! options { )* } - #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Encodable, BlobDecodable)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] + #[derive(Encodable, BlobDecodable, HashStable_Generic)] pub enum $tmod_enum { $( $( $tmod_variant, )? From 9db913153f739964aea89c26b95e0493d0feb304 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 27 Apr 2026 10:16:19 +0200 Subject: [PATCH 04/15] derive HashStable for DeniedPartialMitigation --- .../rustc_session/src/options/mitigation_coverage.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_session/src/options/mitigation_coverage.rs b/compiler/rustc_session/src/options/mitigation_coverage.rs index dbe989100d567..1a4fea0f45c8b 100644 --- a/compiler/rustc_session/src/options/mitigation_coverage.rs +++ b/compiler/rustc_session/src/options/mitigation_coverage.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::str::FromStr; -use rustc_macros::{BlobDecodable, Encodable}; +use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic}; use rustc_span::edition::Edition; use rustc_target::spec::StackProtector; @@ -9,7 +9,8 @@ use crate::Session; use crate::config::Options; use crate::options::CFGuard; -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, BlobDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Encodable, BlobDecodable, HashStable_Generic)] pub enum DeniedPartialMitigationLevel { // Enabled(false) should be the bottom of the Ord hierarchy Enabled(bool), @@ -133,7 +134,8 @@ macro_rules! intersperse { macro_rules! denied_partial_mitigations { ([$self:ident] enum $kind:ident {$(($name:ident, $text:expr, $since:ident, $code:expr)),*}) => { - #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Encodable, BlobDecodable)] + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] + #[derive(Encodable, BlobDecodable, HashStable_Generic)] pub enum DeniedPartialMitigationKind { $($name),* } @@ -211,7 +213,8 @@ denied_partial_mitigations! { /// A mitigation that cannot be partially enabled (see /// [RFC 3855](https://github.com/rust-lang/rfcs/pull/3855)), but are currently enabled for this /// crate. -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, BlobDecodable)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Encodable, BlobDecodable, HashStable_Generic)] pub struct DeniedPartialMitigation { pub kind: DeniedPartialMitigationKind, pub level: DeniedPartialMitigationLevel, From 9132e4b6e22c09dae6b7ea30c277a47dc35a5174 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 27 Apr 2026 10:19:03 +0200 Subject: [PATCH 05/15] rdr: implement rmeta public api hash as the stable hash of (almost) all encoded data --- compiler/rustc_metadata/src/rmeta/encoder.rs | 794 +++++++++++------- .../src/rmeta/encoder/public_api_hasher.rs | 438 ++++++++++ compiler/rustc_metadata/src/rmeta/mod.rs | 182 ++-- compiler/rustc_metadata/src/rmeta/table.rs | 115 ++- 4 files changed, 1138 insertions(+), 391 deletions(-) create mode 100644 compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 511570bd3b4ad..fee6a5a5c0206 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1,4 +1,5 @@ use std::borrow::Borrow; +use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fs::File; use std::io::{Read, Seek, Write}; @@ -21,9 +22,9 @@ use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; -use rustc_middle::ty::AssocContainer; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; +use rustc_middle::ty::{AssocContainer, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; @@ -35,10 +36,13 @@ use rustc_span::{ }; use tracing::{debug, instrument, trace}; +use self::public_api_hasher::{PublicApiHasher, PublicApiHashingContext}; use crate::eii::EiiMapEncodedKeyValue; use crate::errors::{FailCreateFileEncoder, FailWriteFile}; use crate::rmeta::*; +pub(super) mod public_api_hasher; + pub(super) struct EncodeContext<'a, 'tcx> { opaque: opaque::FileEncoder, tcx: TyCtxt<'tcx>, @@ -75,7 +79,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { macro_rules! empty_proc_macro { ($self:ident) => { if $self.is_proc_macro { - return LazyArray::default(); + return Default::default(); } }; } @@ -393,11 +397,19 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. macro_rules! record { - ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident) => {{ + record!($self.$tables.$table[$def_id] <- $value, $hcx, $value) + }}; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr) => {{ { let value = $value; let lazy = $self.lazy(value); - $self.$tables.$table.set_some($def_id.index, lazy); + $self.$tables.$table.set_some_hashed( + $def_id.index, + lazy, + ($def_id, $hashed_value), + $hcx, + ); } }}; } @@ -405,21 +417,39 @@ macro_rules! record { // Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_array($value))`, which would // normally need extra variables to avoid errors about multiple mutable borrows. macro_rules! record_array { - ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident) => { + record_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => {{ { let value = $value; - let lazy = $self.lazy_array(value); - $self.$tables.$table.set_some($def_id.index, lazy); + let mut hasher = $self.$tables.$table.iter_hasher(); + let lazy = $self.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)); + $self.$tables.$table.set_some_hashed( + $def_id.index, + lazy, + ($def_id, hasher.finish()), + $hcx, + ); } }}; } macro_rules! record_defaulted_array { - ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{ + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident) => { + record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => {{ { let value = $value; - let lazy = $self.lazy_array(value); - $self.$tables.$table.set($def_id.index, lazy); + let mut hasher = $self.$tables.$table.iter_hasher(); + let lazy = $self.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)); + $self.$tables.$table.set_hashed( + $def_id.index, + lazy, + ($def_id, hasher.finish()), + $hcx, + ); } }}; } @@ -510,6 +540,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } fn encode_def_path_table(&mut self) { + // The contents of def_keys and def_path hashes is let table = self.tcx.def_path_table(); if self.is_proc_macro { for def_index in std::iter::once(CRATE_DEF_INDEX) @@ -517,14 +548,18 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { let def_key = self.lazy(table.def_key(def_index)); let def_path_hash = table.def_path_hash(def_index); - self.tables.def_keys.set_some(def_index, def_key); - self.tables.def_path_hashes.set(def_index, def_path_hash.local_hash().as_u64()); + self.tables.def_keys.set_some_unhashed(def_index, def_key); + self.tables + .def_path_hashes + .set_unhashed(def_index, def_path_hash.local_hash().as_u64()); } } else { for (def_index, def_key, def_path_hash) in table.enumerated_keys_and_path_hashes() { let def_key = self.lazy(def_key); - self.tables.def_keys.set_some(def_index, def_key); - self.tables.def_path_hashes.set(def_index, def_path_hash.local_hash().as_u64()); + self.tables.def_keys.set_some_unhashed(def_index, def_key); + self.tables + .def_path_hashes + .set_unhashed(def_index, def_path_hash.local_hash().as_u64()); } } } @@ -533,7 +568,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())) } - fn encode_source_map(&mut self) -> LazyTable>> { + fn encode_source_map( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + public_api_hasher: &mut PublicApiHasher, + ) -> LazyTable>> { let source_map = self.tcx.sess.source_map(); let all_source_files = source_map.files(); @@ -542,7 +581,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // is done. let required_source_files = self.required_source_files.take().unwrap(); - let mut adapted = TableBuilder::default(); + let mut adapted = TableBuilder::, _, _>::default(); let local_crate_stable_id = self.tcx.stable_crate_id(LOCAL_CRATE); @@ -595,13 +634,47 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let on_disk_index: u32 = on_disk_index.try_into().expect("cannot export more than U32_MAX files"); - adapted.set_some(on_disk_index, self.lazy(adapted_source_file)); + adapted.set_some_hashed( + on_disk_index, + self.lazy(&adapted_source_file), + { + let SourceFile { + name, + src, + src_hash, + checksum_hash, + external_src, + start_pos, + normalized_source_len, + unnormalized_source_len, + lines, + multibyte_chars, + normalized_pos, + stable_id, + cnum, + } = &adapted_source_file; + // not encoded + let _ = (src, external_src, start_pos); + // hashed as adapted_source_file.lines() + let _ = lines; + // hashed with stable_id + let _ = name; + ( + (src_hash, checksum_hash, normalized_source_len, unnormalized_source_len), + (adapted_source_file.lines(), multibyte_chars, stable_id, normalized_pos), + cnum, + ) + }, + hcx, + ); } + adapted.hash_public_api(public_api_hasher, hcx); adapted.encode(&mut self.opaque) } - fn encode_crate_root(&mut self) -> LazyValue { + fn encode_crate_root(&mut self, hcx: &mut PublicApiHashingContext<'_>) -> LazyValue { + let mut public_api_hasher = PublicApiHasher::default(); let tcx = self.tcx; let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); @@ -646,13 +719,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let traits = stat!("traits", || self.encode_traits()); // Encode the def IDs of impls, for coherence checking. - let impls = stat!("impls", || self.encode_impls()); + let (impls, trait_impls_map) = stat!("impls", || self.encode_impls(hcx)); let incoherent_impls = stat!("incoherent-impls", || self.encode_incoherent_impls()); - _ = stat!("mir", || self.encode_mir()); + _ = stat!("mir", || self.encode_mir(hcx)); - _ = stat!("def-ids", || self.encode_def_ids()); + _ = stat!("def-ids", || self.encode_def_ids(hcx)); let interpret_alloc_index = stat!("interpret-alloc-index", || { let mut interpret_alloc_index = Vec::new(); @@ -680,8 +753,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Encode the proc macro data. This affects `tables`, so we need to do this before we // encode the tables. This overwrites def_keys, so it must happen after // encode_def_path_table. - let proc_macro_data = stat!("proc-macro-data", || self.encode_proc_macros()); + let proc_macro_data = stat!("proc-macro-data", || self.encode_proc_macros(hcx)); + self.tables.hash_public_api(&mut public_api_hasher, hcx); let tables = stat!("tables", || self.tables.encode(&mut self.opaque)); let debugger_visualizers = @@ -707,77 +781,89 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the incremental cache. If this causes us to deserialize a `Span`, then we may load // additional `SyntaxContext`s into the global `HygieneData`. Therefore, we need to encode // the hygiene data last to ensure that we encode any `SyntaxContext`s that might be used. - let (syntax_contexts, expn_data, expn_hashes) = stat!("hygiene", || self.encode_hygiene()); + let (syntax_contexts, expn_data, expn_hashes) = + stat!("hygiene", || self.encode_hygiene(hcx, &mut public_api_hasher)); let def_path_hash_map = stat!("def-path-hash-map", || self.encode_def_path_hash_map()); // Encode source_map. This needs to be done last, because encoding `Span`s tells us which // `SourceFiles` we actually need to encode. - let source_map = stat!("source-map", || self.encode_source_map()); + let source_map = + stat!("source-map", || self.encode_source_map(hcx, &mut public_api_hasher)); let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers()); let denied_partial_mitigations = stat!("denied-partial-mitigations", || self .encode_enabled_denied_partial_mitigations()); - let root = stat!("final", || { - let attrs = tcx.hir_krate_attrs(); - self.lazy(CrateRoot { - header: CrateHeader { - name: tcx.crate_name(LOCAL_CRATE), - triple: tcx.sess.opts.target_triple.clone(), - hash: tcx.crate_hash(LOCAL_CRATE), - is_proc_macro_crate: proc_macro_data.is_some(), - is_stub: false, - }, - extra_filename: tcx.sess.opts.cg.extra_filename.clone(), - stable_crate_id: tcx.stable_crate_id(LOCAL_CRATE), - required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), - panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, - edition: tcx.sess.edition(), - has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), - has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), - has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), - has_default_lib_allocator: find_attr!(attrs, DefaultLibAllocator), - externally_implementable_items, - proc_macro_data, - debugger_visualizers, - compiler_builtins: find_attr!(attrs, CompilerBuiltins), - needs_allocator: find_attr!(attrs, NeedsAllocator), - needs_panic_runtime: find_attr!(attrs, NeedsPanicRuntime), - no_builtins: find_attr!(attrs, NoBuiltins), - panic_runtime: find_attr!(attrs, PanicRuntime), - profiler_runtime: find_attr!(attrs, ProfilerRuntime), - symbol_mangling_version: tcx.sess.opts.get_symbol_mangling_version(), - - crate_deps, - dylib_dependency_formats, - lib_features, - stability_implications, - lang_items, - diagnostic_items, - lang_items_missing, - stripped_cfg_items, - native_libraries, - foreign_modules, - source_map, - target_modifiers, - denied_partial_mitigations, - traits, - impls, - incoherent_impls, - exportable_items, - stable_order_of_exportable_impls, - exported_non_generic_symbols, - exported_generic_symbols, - interpret_alloc_index, - tables, - syntax_contexts, - expn_data, - expn_hashes, - def_path_hash_map, - specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), - rdr_hashes: RDRHashes { public_api_hash: tcx.crate_hash(LOCAL_CRATE) }, - }) - }); + let attrs = tcx.hir_krate_attrs(); + let mut crate_root = CrateRoot { + header: CrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + hash: tcx.crate_hash(LOCAL_CRATE), + is_proc_macro_crate: proc_macro_data.is_some(), + is_stub: false, + }, + extra_filename: tcx.sess.opts.cg.extra_filename.clone(), + stable_crate_id: tcx.def_path_hash(LOCAL_CRATE.as_def_id()).stable_crate_id(), + required_panic_strategy: tcx.required_panic_strategy(LOCAL_CRATE), + panic_in_drop_strategy: tcx.sess.opts.unstable_opts.panic_in_drop, + edition: tcx.sess.edition(), + has_global_allocator: tcx.has_global_allocator(LOCAL_CRATE), + has_alloc_error_handler: tcx.has_alloc_error_handler(LOCAL_CRATE), + has_panic_handler: tcx.has_panic_handler(LOCAL_CRATE), + has_default_lib_allocator: find_attr!(attrs, DefaultLibAllocator), + externally_implementable_items, + proc_macro_data, + debugger_visualizers, + compiler_builtins: find_attr!(attrs, CompilerBuiltins), + needs_allocator: find_attr!(attrs, NeedsAllocator), + needs_panic_runtime: find_attr!(attrs, NeedsPanicRuntime), + no_builtins: find_attr!(attrs, NoBuiltins), + panic_runtime: find_attr!(attrs, PanicRuntime), + profiler_runtime: find_attr!(attrs, ProfilerRuntime), + symbol_mangling_version: tcx.sess.opts.get_symbol_mangling_version(), + + crate_deps, + dylib_dependency_formats, + lib_features, + stability_implications, + lang_items, + diagnostic_items, + lang_items_missing, + stripped_cfg_items, + native_libraries, + foreign_modules, + source_map, + target_modifiers, + denied_partial_mitigations, + traits, + impls, + incoherent_impls, + exportable_items, + stable_order_of_exportable_impls, + exported_non_generic_symbols, + exported_generic_symbols, + interpret_alloc_index, + tables, + syntax_contexts, + expn_data, + expn_hashes, + def_path_hash_map, + specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), + rdr_hashes: RDRHashes { public_api_hash: tcx.crate_hash(LOCAL_CRATE) }, + }; + public_api_hasher::hash_crate_root_public_api( + &mut public_api_hasher, + hcx, + &crate_root, + self.tcx, + trait_impls_map, + &self.interpret_allocs, + ); + crate_root.rdr_hashes.public_api_hash = + public_api_hasher.finish(hcx).unwrap_or(crate_root.header.hash); + + let root = stat!("final", || { self.lazy(crate_root) }); let total_bytes = self.position(); @@ -1394,7 +1480,7 @@ fn assoc_item_has_value<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - fn encode_attrs(&mut self, def_id: LocalDefId) { + fn encode_attrs(&mut self, def_id: LocalDefId, hcx: &mut PublicApiHashingContext<'_>) { let tcx = self.tcx; let mut state = AnalyzeAttrState { is_exported: tcx.effective_visibilities(()).is_exported(def_id), @@ -1405,17 +1491,22 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .iter() .filter(|attr| analyze_attr(*attr, &mut state)); - record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter); + record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter, hcx); let mut attr_flags = AttrFlags::empty(); if state.is_doc_hidden { attr_flags |= AttrFlags::IS_DOC_HIDDEN; } - self.tables.attr_flags.set(def_id.local_def_index, attr_flags); + self.tables.attr_flags.set_hashed( + def_id.local_def_index, + attr_flags, + (def_id, attr_flags.bits()), + hcx, + ); } - fn encode_def_ids(&mut self) { - self.encode_info_for_mod(CRATE_DEF_ID); + fn encode_def_ids(&mut self, hcx: &mut PublicApiHashingContext<'_>) { + self.encode_info_for_mod(CRATE_DEF_ID, hcx); // Proc-macro crates only export proc-macro items, which are looked // up using `proc_macro_data` @@ -1428,7 +1519,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for local_id in tcx.iter_local_def_id() { let def_id = local_id.to_def_id(); let def_kind = tcx.def_kind(local_id); - self.tables.def_kind.set_some(def_id.index, def_kind); + self.tables.def_kind.set_some_local_hashed(local_id, def_kind, hcx); // The `DefCollector` will sometimes create unnecessary `DefId`s // for trivial const arguments which are directly lowered to @@ -1461,195 +1552,209 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { && let hir::Node::Field(field) = tcx.hir_node_by_def_id(local_id) && let Some(anon) = field.default { - record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id()); + record!(self.tables.default_fields[def_id] <- anon.def_id.to_def_id(), hcx); } if should_encode_span(def_kind) { let def_span = tcx.def_span(local_id); - record!(self.tables.def_span[def_id] <- def_span); + record!(self.tables.def_span[def_id] <- def_span, hcx); } if should_encode_attrs(def_kind) { - self.encode_attrs(local_id); + self.encode_attrs(local_id, hcx); } if should_encode_expn_that_defined(def_kind) { - record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id)); + record!(self.tables.expn_that_defined[def_id] <- self.tcx.expn_that_defined(def_id), hcx); } if should_encode_span(def_kind) && let Some(ident_span) = tcx.def_ident_span(def_id) { - record!(self.tables.def_ident_span[def_id] <- ident_span); + record!(self.tables.def_ident_span[def_id] <- ident_span, hcx); } if def_kind.has_codegen_attrs() { - record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id)); + record!(self.tables.codegen_fn_attrs[def_id] <- self.tcx.codegen_fn_attrs(def_id), hcx); } if should_encode_visibility(def_kind) { - let vis = - self.tcx.local_visibility(local_id).map_id(|def_id| def_id.local_def_index); - record!(self.tables.visibility[def_id] <- vis); + let vis = tcx.local_visibility(local_id); + record!(self.tables.visibility[def_id] <- vis.map_id(|def_id| def_id.local_def_index), hcx, vis); } if should_encode_stability(def_kind) { - self.encode_stability(def_id); - self.encode_const_stability(def_id); - self.encode_default_body_stability(def_id); - self.encode_deprecation(def_id); + self.encode_stability(def_id, hcx); + self.encode_const_stability(def_id, hcx); + self.encode_default_body_stability(def_id, hcx); + self.encode_deprecation(def_id, hcx); } if should_encode_variances(tcx, def_id, def_kind) { let v = self.tcx.variances_of(def_id); - record_array!(self.tables.variances_of[def_id] <- v); + record_array!(self.tables.variances_of[def_id] <- v, hcx); } if should_encode_fn_sig(def_kind) { - record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id)); + record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id), hcx); } if should_encode_generics(def_kind) { let g = tcx.generics_of(def_id); - record!(self.tables.generics_of[def_id] <- g); - record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id)); + record!(self.tables.generics_of[def_id] <- g, hcx); + record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id), hcx); let inferred_outlives = self.tcx.inferred_outlives_of(def_id); - record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives); + record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives, hcx); for param in &g.own_params { if let ty::GenericParamDefKind::Const { has_default: true, .. } = param.kind { let default = self.tcx.const_param_default(param.def_id); - record!(self.tables.const_param_default[param.def_id] <- default); + record!(self.tables.const_param_default[param.def_id] <- default, hcx); } } } if tcx.is_conditionally_const(def_id) { - record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id)); + record!(self.tables.const_conditions[def_id] <- self.tcx.const_conditions(def_id), hcx); } if should_encode_type(tcx, local_id, def_kind) { - record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id)); + record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id), hcx); } if should_encode_constness(def_kind) { let constness = self.tcx.constness(def_id); - self.tables.constness.set(def_id.index, constness); + self.tables.constness.set_local_hashed(def_id.expect_local(), constness, hcx); } if let DefKind::Fn | DefKind::AssocFn = def_kind { let asyncness = tcx.asyncness(def_id); - self.tables.asyncness.set(def_id.index, asyncness); - record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id)); + self.tables.asyncness.set_local_hashed(def_id.expect_local(), asyncness, hcx); + record_array!(self.tables.fn_arg_idents[def_id] <- tcx.fn_arg_idents(def_id), hcx); } if let Some(name) = tcx.intrinsic(def_id) { - record!(self.tables.intrinsic[def_id] <- name); + record!(self.tables.intrinsic[def_id] <- name, hcx); } if let DefKind::TyParam = def_kind { let default = self.tcx.object_lifetime_default(def_id); - record!(self.tables.object_lifetime_default[def_id] <- default); + record!(self.tables.object_lifetime_default[def_id] <- default, hcx); } if let DefKind::Trait = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id), hcx); record_defaulted_array!(self.tables.explicit_super_predicates_of[def_id] <- - self.tcx.explicit_super_predicates_of(def_id).skip_binder()); + self.tcx.explicit_super_predicates_of(def_id).skip_binder(), hcx); record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- - self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); + self.tcx.explicit_implied_predicates_of(def_id).skip_binder(), hcx); let module_children = self.tcx.module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().map(|child| child.res.def_id().index)); + module_children.iter().map(|child| child.res.def_id()), hcx, + |def_id| def_id.index); if self.tcx.is_const_trait(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] - <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); + <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder(), hcx); } } if let DefKind::TraitAlias = def_kind { - record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); + record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id), hcx); record_defaulted_array!(self.tables.explicit_super_predicates_of[def_id] <- - self.tcx.explicit_super_predicates_of(def_id).skip_binder()); + self.tcx.explicit_super_predicates_of(def_id).skip_binder(), hcx); record_defaulted_array!(self.tables.explicit_implied_predicates_of[def_id] <- - self.tcx.explicit_implied_predicates_of(def_id).skip_binder()); + self.tcx.explicit_implied_predicates_of(def_id).skip_binder(), hcx); } if let DefKind::Trait | DefKind::Impl { .. } = def_kind { let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id); record_array!(self.tables.associated_item_or_field_def_ids[def_id] <- - associated_item_def_ids.iter().map(|&def_id| { + associated_item_def_ids.iter(), hcx, |&def_id| { assert!(def_id.is_local()); def_id.index - }) + } ); for &def_id in associated_item_def_ids { - self.encode_info_for_assoc_item(def_id); + self.encode_info_for_assoc_item(def_id, hcx); } } if let DefKind::Closure | DefKind::SyntheticCoroutineBody = def_kind && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id) { - self.tables.coroutine_kind.set(def_id.index, Some(coroutine_kind)) + self.tables.coroutine_kind.set_local_hashed( + def_id.expect_local(), + Some(coroutine_kind), + hcx, + ) } if def_kind == DefKind::Closure && tcx.type_of(def_id).skip_binder().is_coroutine_closure() { let coroutine_for_closure = self.tcx.coroutine_for_closure(def_id); - self.tables - .coroutine_for_closure - .set_some(def_id.index, coroutine_for_closure.into()); + self.tables.coroutine_for_closure.set_hashed( + def_id.index, + Some(coroutine_for_closure.into()), + (def_id, coroutine_for_closure), + hcx, + ); // If this async closure has a by-move body, record it too. if tcx.needs_coroutine_by_move_body_def_id(coroutine_for_closure) { - self.tables.coroutine_by_move_body_def_id.set_some( + self.tables.coroutine_by_move_body_def_id.set_hashed( coroutine_for_closure.index, - self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into(), + Some(self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure).into()), + ( + coroutine_for_closure, + self.tcx.coroutine_by_move_body_def_id(coroutine_for_closure), + ), + hcx, ); } } if let DefKind::Static { .. } = def_kind { if !self.tcx.is_foreign_item(def_id) { let data = self.tcx.eval_static_initializer(def_id).unwrap(); - record!(self.tables.eval_static_initializer[def_id] <- data); + record!(self.tables.eval_static_initializer[def_id] <- data, hcx); } } if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind { - self.encode_info_for_adt(local_id); + self.encode_info_for_adt(local_id, hcx); } if let DefKind::Mod = def_kind { - self.encode_info_for_mod(local_id); + self.encode_info_for_mod(local_id, hcx); } if let DefKind::Macro(_) = def_kind { - self.encode_info_for_macro(local_id); + self.encode_info_for_macro(local_id, hcx); } if let DefKind::TyAlias = def_kind { - self.tables - .type_alias_is_lazy - .set(def_id.index, self.tcx.type_alias_is_lazy(def_id)); + self.tables.type_alias_is_lazy.set_local_hashed( + def_id.expect_local(), + self.tcx.type_alias_is_lazy(def_id), + hcx, + ); } if let DefKind::OpaqueTy = def_kind { - self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_self_bounds(def_id); - record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); - self.encode_precise_capturing_args(def_id); + self.encode_explicit_item_bounds(def_id, hcx); + self.encode_explicit_item_self_bounds(def_id, hcx); + record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id), hcx); + self.encode_precise_capturing_args(def_id, hcx); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] - <- tcx.explicit_implied_const_bounds(def_id).skip_binder()); + <- tcx.explicit_implied_const_bounds(def_id).skip_binder(), hcx); } } if let DefKind::AnonConst = def_kind { - record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id)); + record!(self.tables.anon_const_kind[def_id] <- self.tcx.anon_const_kind(def_id), hcx); } if should_encode_const_of_item(self.tcx, def_id, def_kind) { - record!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id)); + record!(self.tables.const_of_item[def_id] <- self.tcx.const_of_item(def_id), hcx); } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) { - record!(self.tables.trait_impl_trait_tys[def_id] <- table); + record!(self.tables.trait_impl_trait_tys[def_id] <- table, hcx); } if let DefKind::Impl { .. } | DefKind::Trait = def_kind { let table = tcx.associated_types_for_impl_traits_in_trait_or_impl(def_id); - record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table); + record!(self.tables.associated_types_for_impl_traits_in_trait_or_impl[def_id] <- table, hcx); } } for (def_id, impls) in &tcx.crate_inherent_impls(()).0.inherent_impls { - record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter().map(|def_id| { + record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter(), hcx, |def_id| { assert!(def_id.is_local()); def_id.index - })); + }); } for (def_id, res_map) in &tcx.resolutions(()).doc_link_resolutions { - record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map); + record!(self.tables.doc_link_resolutions[def_id.to_def_id()] <- res_map, hcx); } for (def_id, traits) in &tcx.resolutions(()).doc_link_traits_in_scope { - record_array!(self.tables.doc_link_traits_in_scope[def_id.to_def_id()] <- traits); + record_array!(self.tables.doc_link_traits_in_scope[def_id.to_def_id()] <- traits, hcx); } } @@ -1667,20 +1772,24 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { )) } - #[instrument(level = "trace", skip(self))] - fn encode_info_for_adt(&mut self, local_def_id: LocalDefId) { + #[instrument(level = "trace", skip(self, hcx))] + fn encode_info_for_adt( + &mut self, + local_def_id: LocalDefId, + hcx: &mut PublicApiHashingContext<'_>, + ) { let def_id = local_def_id.to_def_id(); let tcx = self.tcx; let adt_def = tcx.adt_def(def_id); - record!(self.tables.repr_options[def_id] <- adt_def.repr()); + record!(self.tables.repr_options[def_id] <- adt_def.repr(), hcx); let params_in_repr = self.tcx.params_in_repr(def_id); - record!(self.tables.params_in_repr[def_id] <- params_in_repr); + record!(self.tables.params_in_repr[def_id] <- params_in_repr, hcx); if adt_def.is_enum() { let module_children = tcx.module_children_local(local_def_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().map(|child| child.res.def_id().index)); + module_children.iter().map(|child| child.res.def_id()), hcx, |def_id| def_id.index); } else { // For non-enum, there is only one variant, and its def_id is the adt's. debug_assert_eq!(adt_def.variants().len(), 1); @@ -1695,35 +1804,43 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)), is_non_exhaustive: variant.is_field_list_non_exhaustive(), }; - record!(self.tables.variant_data[variant.def_id] <- data); + record!( + self.tables.variant_data[variant.def_id] <- data, + hcx, + (idx, variant.discr, variant.ctor, variant.is_field_list_non_exhaustive()) + ); record_array!(self.tables.associated_item_or_field_def_ids[variant.def_id] <- variant.fields.iter().map(|f| { assert!(f.did.is_local()); - f.did.index - })); + f.did + }), hcx, |def_id| def_id.index); for field in &variant.fields { - self.tables.safety.set(field.did.index, field.safety); + self.tables.safety.set_local_hashed(field.did.expect_local(), field.safety, hcx); } if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { let fn_sig = tcx.fn_sig(ctor_def_id); // FIXME only encode signature for ctor_def_id - record!(self.tables.fn_sig[variant.def_id] <- fn_sig); + record!(self.tables.fn_sig[variant.def_id] <- fn_sig, hcx); } } if let Some(destructor) = tcx.adt_destructor(local_def_id) { - record!(self.tables.adt_destructor[def_id] <- destructor); + record!(self.tables.adt_destructor[def_id] <- destructor, hcx); } if let Some(destructor) = tcx.adt_async_destructor(local_def_id) { - record!(self.tables.adt_async_destructor[def_id] <- destructor); + record!(self.tables.adt_async_destructor[def_id] <- destructor, hcx); } } - #[instrument(level = "debug", skip(self))] - fn encode_info_for_mod(&mut self, local_def_id: LocalDefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_info_for_mod( + &mut self, + local_def_id: LocalDefId, + hcx: &mut PublicApiHashingContext<'_>, + ) { let tcx = self.tcx; let def_id = local_def_id.to_def_id(); @@ -1734,16 +1851,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // items - we encode information about proc-macros later on. if self.is_proc_macro { // Encode this here because we don't do it in encode_def_ids. - record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id)); + record!(self.tables.expn_that_defined[def_id] <- tcx.expn_that_defined(local_def_id), hcx); } else { let module_children = tcx.module_children_local(local_def_id); record_array!(self.tables.module_children_non_reexports[def_id] <- module_children.iter().filter(|child| child.reexport_chain.is_empty()) - .map(|child| child.res.def_id().index)); + .map(|child| child.res.def_id()), hcx, |def_id| def_id.index); record_defaulted_array!(self.tables.module_children_reexports[def_id] <- - module_children.iter().filter(|child| !child.reexport_chain.is_empty())); + module_children.iter().filter(|child| !child.reexport_chain.is_empty()), hcx); let ambig_module_children = tcx .resolutions(()) @@ -1751,64 +1868,80 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .get(&local_def_id) .map_or_default(|v| &v[..]); record_defaulted_array!(self.tables.ambig_module_children[def_id] <- - ambig_module_children); + ambig_module_children, hcx); } } - fn encode_explicit_item_bounds(&mut self, def_id: DefId) { + fn encode_explicit_item_bounds( + &mut self, + def_id: DefId, + hcx: &mut PublicApiHashingContext<'_>, + ) { debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id); let bounds = self.tcx.explicit_item_bounds(def_id).skip_binder(); - record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); + record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds, hcx); } - fn encode_explicit_item_self_bounds(&mut self, def_id: DefId) { + fn encode_explicit_item_self_bounds( + &mut self, + def_id: DefId, + hcx: &mut PublicApiHashingContext<'_>, + ) { debug!("EncodeContext::encode_explicit_item_self_bounds({:?})", def_id); let bounds = self.tcx.explicit_item_self_bounds(def_id).skip_binder(); - record_defaulted_array!(self.tables.explicit_item_self_bounds[def_id] <- bounds); + record_defaulted_array!(self.tables.explicit_item_self_bounds[def_id] <- bounds, hcx); } - #[instrument(level = "debug", skip(self))] - fn encode_info_for_assoc_item(&mut self, def_id: DefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_info_for_assoc_item(&mut self, def_id: DefId, hcx: &mut PublicApiHashingContext<'_>) { let tcx = self.tcx; let item = tcx.associated_item(def_id); if matches!(item.container, AssocContainer::Trait | AssocContainer::TraitImpl(_)) { - self.tables.defaultness.set(def_id.index, item.defaultness(tcx)); + self.tables.defaultness.set_local_hashed( + def_id.expect_local(), + item.defaultness(tcx), + hcx, + ); } - record!(self.tables.assoc_container[def_id] <- item.container); + record!(self.tables.assoc_container[def_id] <- item.container, hcx); if let AssocContainer::Trait = item.container && item.is_type() { - self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_self_bounds(def_id); + self.encode_explicit_item_bounds(def_id, hcx); + self.encode_explicit_item_self_bounds(def_id, hcx); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] - <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); + <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder(), hcx); } } if let ty::AssocKind::Type { data: ty::AssocTypeData::Rpitit(rpitit_info) } = item.kind { - record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info); + record!(self.tables.opt_rpitit_info[def_id] <- rpitit_info, hcx); if matches!(rpitit_info, ty::ImplTraitInTraitData::Trait { .. }) { record_array!( self.tables.assumed_wf_types_for_rpitit[def_id] - <- self.tcx.assumed_wf_types_for_rpitit(def_id) + <- self.tcx.assumed_wf_types_for_rpitit(def_id), hcx ); - self.encode_precise_capturing_args(def_id); + self.encode_precise_capturing_args(def_id, hcx); } } } - fn encode_precise_capturing_args(&mut self, def_id: DefId) { + fn encode_precise_capturing_args( + &mut self, + def_id: DefId, + hcx: &mut PublicApiHashingContext<'_>, + ) { let Some(precise_capturing_args) = self.tcx.rendered_precise_capturing_args(def_id) else { return; }; - record_array!(self.tables.rendered_precise_capturing_args[def_id] <- precise_capturing_args); + record_array!(self.tables.rendered_precise_capturing_args[def_id] <- precise_capturing_args, hcx); } - fn encode_mir(&mut self) { + fn encode_mir(&mut self, hcx: &mut PublicApiHashingContext<'_>) { if self.is_proc_macro { return; } @@ -1825,53 +1958,55 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debug!("EntryBuilder::encode_mir({:?})", def_id); if encode_opt { - record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id)); - self.tables - .cross_crate_inlinable - .set(def_id.to_def_id().index, self.tcx.cross_crate_inlinable(def_id)); + record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id), hcx); + self.tables.cross_crate_inlinable.set_local_hashed( + def_id, + self.tcx.cross_crate_inlinable(def_id), + hcx, + ); record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()] - <- tcx.closure_saved_names_of_captured_variables(def_id)); + <- tcx.closure_saved_names_of_captured_variables(def_id), hcx); if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses, hcx); } } let mut is_trivial = false; if encode_const { if let Some((val, ty)) = tcx.trivial_const(def_id) { is_trivial = true; - record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty)); + record!(self.tables.trivial_const[def_id.to_def_id()] <- (val, ty), hcx); } else { is_trivial = false; - record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id)); + record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id), hcx); } // FIXME(generic_const_exprs): this feels wrong to have in `encode_mir` let abstract_const = tcx.thir_abstract_const(def_id); if let Ok(Some(abstract_const)) = abstract_const { - record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const); + record!(self.tables.thir_abstract_const[def_id.to_def_id()] <- abstract_const, hcx); } if should_encode_const(tcx.def_kind(def_id)) { let qualifs = tcx.mir_const_qualif(def_id); - record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs); + record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs, hcx); let body = tcx.hir_maybe_body_owned_by(def_id); if let Some(body) = body { let const_data = rendered_const(self.tcx, &body, def_id); - record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data); + record!(self.tables.rendered_const[def_id.to_def_id()] <- &const_data, hcx); } } } if !is_trivial { - record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id)); + record!(self.tables.promoted_mir[def_id.to_def_id()] <- tcx.promoted_mir(def_id), hcx); } if self.tcx.is_coroutine(def_id.to_def_id()) && let Some(witnesses) = tcx.mir_coroutine_witnesses(def_id) { - record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses); + record!(self.tables.mir_coroutine_witnesses[def_id.to_def_id()] <- witnesses, hcx); } } @@ -1885,59 +2020,63 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { for &local_def_id in tcx.mir_keys(()) { if let DefKind::AssocFn | DefKind::Fn = tcx.def_kind(local_def_id) { record_array!(self.tables.deduced_param_attrs[local_def_id.to_def_id()] <- - self.tcx.deduced_param_attrs(local_def_id.to_def_id())); + self.tcx.deduced_param_attrs(local_def_id.to_def_id()), hcx); } } } } - #[instrument(level = "debug", skip(self))] - fn encode_stability(&mut self, def_id: DefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_stability(&mut self, def_id: DefId, hcx: &mut PublicApiHashingContext<'_>) { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_stability(def_id) { - record!(self.tables.lookup_stability[def_id] <- stab) + record!(self.tables.lookup_stability[def_id] <- stab, hcx) } } } - #[instrument(level = "debug", skip(self))] - fn encode_const_stability(&mut self, def_id: DefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_const_stability(&mut self, def_id: DefId, hcx: &mut PublicApiHashingContext<'_>) { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_const_stability(def_id) { - record!(self.tables.lookup_const_stability[def_id] <- stab) + record!(self.tables.lookup_const_stability[def_id] <- stab, hcx) } } } - #[instrument(level = "debug", skip(self))] - fn encode_default_body_stability(&mut self, def_id: DefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_default_body_stability( + &mut self, + def_id: DefId, + hcx: &mut PublicApiHashingContext<'_>, + ) { // The query lookup can take a measurable amount of time in crates with many items. Check if // the stability attributes are even enabled before using their queries. if self.feat.staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked { if let Some(stab) = self.tcx.lookup_default_body_stability(def_id) { - record!(self.tables.lookup_default_body_stability[def_id] <- stab) + record!(self.tables.lookup_default_body_stability[def_id] <- stab, hcx) } } } - #[instrument(level = "debug", skip(self))] - fn encode_deprecation(&mut self, def_id: DefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_deprecation(&mut self, def_id: DefId, hcx: &mut PublicApiHashingContext<'_>) { if let Some(depr) = self.tcx.lookup_deprecation(def_id) { - record!(self.tables.lookup_deprecation_entry[def_id] <- depr); + record!(self.tables.lookup_deprecation_entry[def_id] <- depr, hcx); } } - #[instrument(level = "debug", skip(self))] - fn encode_info_for_macro(&mut self, def_id: LocalDefId) { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_info_for_macro(&mut self, def_id: LocalDefId, hcx: &mut PublicApiHashingContext<'_>) { let tcx = self.tcx; let (_, macro_def, _) = tcx.hir_expect_item(def_id).expect_macro(); - self.tables.is_macro_rules.set(def_id.local_def_index, macro_def.macro_rules); - record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body); + self.tables.is_macro_rules.set_local_hashed(def_id, macro_def.macro_rules, hcx); + record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body, hcx); } fn encode_native_libraries(&mut self) -> LazyArray { @@ -1952,23 +2091,43 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(foreign_modules.iter().map(|(_, m)| m).cloned()) } - fn encode_hygiene(&mut self) -> (SyntaxContextTable, ExpnDataTable, ExpnHashTable) { - let mut syntax_contexts: TableBuilder<_, _> = Default::default(); - let mut expn_data_table: TableBuilder<_, _> = Default::default(); - let mut expn_hash_table: TableBuilder<_, _> = Default::default(); + fn encode_hygiene( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + public_api_hasher: &mut PublicApiHasher, + ) -> (SyntaxContextTable, ExpnDataTable, ExpnHashTable) { + let mut syntax_contexts: TableBuilder, _, _> = Default::default(); + let mut expn_data_table: TableBuilder, _, _> = Default::default(); + let mut expn_hash_table: TableBuilder, _, _> = Default::default(); + let hcx = RefCell::new(hcx); self.hygiene_ctxt.encode( &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), |(this, syntax_contexts, _, _), index, ctxt_data| { - syntax_contexts.set_some(index, this.lazy(ctxt_data)); + syntax_contexts.set_some_hashed( + index, + this.lazy(ctxt_data), + ctxt_data, + &mut hcx.borrow_mut(), + ); }, |(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| { if let Some(index) = index.as_local() { - expn_data_table.set_some(index.as_raw(), this.lazy(expn_data)); - expn_hash_table.set_some(index.as_raw(), this.lazy(hash)); + expn_data_table.set_some_hashed( + index.as_raw(), + this.lazy(expn_data), + index, + &mut hcx.borrow_mut(), + ); + // don't need to hash it since it is already included with `expn_data_table` + expn_hash_table.set_some_unhashed(index.as_raw(), this.lazy(hash)); } }, ); + let hcx = hcx.into_inner(); + syntax_contexts.hash_public_api(public_api_hasher, hcx); + expn_data_table.hash_public_api(public_api_hasher, hcx); + expn_hash_table.hash_public_api(public_api_hasher, hcx); ( syntax_contexts.encode(&mut self.opaque), @@ -1977,7 +2136,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ) } - fn encode_proc_macros(&mut self) -> Option { + fn encode_proc_macros( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Option { let is_proc_macro = self.tcx.crate_types().contains(&CrateType::ProcMacro); if is_proc_macro { let tcx = self.tcx; @@ -1986,24 +2148,24 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let macros = self.lazy_array(tcx.resolutions(()).proc_macros.iter().map(|p| p.local_def_index)); for (i, span) in self.tcx.sess.proc_macro_quoted_spans() { - let span = self.lazy(span); - self.tables.proc_macro_quoted_spans.set_some(i, span); + let encoded_span = self.lazy(span); + self.tables.proc_macro_quoted_spans.set_some_hashed(i, encoded_span, span, hcx); } - self.tables.def_kind.set_some(LOCAL_CRATE.as_def_id().index, DefKind::Mod); - record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id())); - self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local()); - let vis = tcx.local_visibility(CRATE_DEF_ID).map_id(|def_id| def_id.local_def_index); - record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis); + self.tables.def_kind.set_some_local_hashed(CRATE_DEF_ID, DefKind::Mod, hcx); + record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id()), hcx); + self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local(), hcx); + let vis = tcx.local_visibility(CRATE_DEF_ID); + record!(self.tables.visibility[LOCAL_CRATE.as_def_id()] <- vis.map_id(|def_id| def_id.local_def_index), hcx, vis); if let Some(stability) = stability { - record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability); + record!(self.tables.lookup_stability[LOCAL_CRATE.as_def_id()] <- stability, hcx); } - self.encode_deprecation(LOCAL_CRATE.as_def_id()); + self.encode_deprecation(LOCAL_CRATE.as_def_id(), hcx); if let Some(res_map) = tcx.resolutions(()).doc_link_resolutions.get(&CRATE_DEF_ID) { - record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map); + record!(self.tables.doc_link_resolutions[LOCAL_CRATE.as_def_id()] <- res_map, hcx); } if let Some(traits) = tcx.resolutions(()).doc_link_traits_in_scope.get(&CRATE_DEF_ID) { - record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits); + record_array!(self.tables.doc_link_traits_in_scope[LOCAL_CRATE.as_def_id()] <- traits, hcx); } // Normally, this information is encoded when we walk the items @@ -2034,15 +2196,23 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind.into())); - self.tables.proc_macro.set_some(def_id.index, macro_kind); - self.encode_attrs(id); - record!(self.tables.def_keys[def_id] <- def_key); - record!(self.tables.def_ident_span[def_id] <- span); - record!(self.tables.def_span[def_id] <- span); - record!(self.tables.visibility[def_id] <- ty::Visibility::Public); + self.tables.def_kind.set_some_local_hashed( + def_id.expect_local(), + DefKind::Macro(macro_kind.into()), + hcx, + ); + self.tables.proc_macro.set_some_local_hashed( + def_id.expect_local(), + macro_kind, + hcx, + ); + self.encode_attrs(id, hcx); + record!(self.tables.def_keys[def_id] <- def_key, hcx, def_id); + record!(self.tables.def_ident_span[def_id] <- span, hcx); + record!(self.tables.def_span[def_id] <- span, hcx); + record!(self.tables.visibility[def_id] <- Visibility::Public, hcx, Visibility::::Public); if let Some(stability) = stability { - record!(self.tables.lookup_stability[def_id] <- stability); + record!(self.tables.lookup_stability[def_id] <- stability, hcx); } } @@ -2069,22 +2239,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_crate_deps(&mut self) -> LazyArray { empty_proc_macro!(self); - let deps = self - .tcx - .crates(()) - .iter() - .map(|&cnum| { - let dep = CrateDep { - name: self.tcx.crate_name(cnum), - hash: self.tcx.crate_hash(cnum), - host_hash: self.tcx.crate_host_hash(cnum), - kind: self.tcx.crate_dep_kind(cnum), - extra_filename: self.tcx.extra_filename(cnum).clone(), - is_private: self.tcx.is_private_dep(cnum), - }; - (cnum, dep) - }) - .collect::>(); + let deps = crate_deps(self.tcx).collect::>(); { // Sanity-check the crate numbers @@ -2165,11 +2320,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } /// Encodes an index, mapping each trait to its (local) implementations. - #[instrument(level = "debug", skip(self))] - fn encode_impls(&mut self) -> LazyArray { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_impls( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> (LazyArray, FxIndexMap)>>) { empty_proc_macro!(self); let tcx = self.tcx; - let mut trait_impls: FxIndexMap)>> = + let mut trait_impls_map: FxIndexMap)>> = FxIndexMap::default(); for id in tcx.hir_free_items() { @@ -2180,9 +2338,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if of_trait { let header = tcx.impl_trait_header(def_id); - record!(self.tables.impl_trait_header[def_id] <- header); + record!(self.tables.impl_trait_header[def_id] <- header, hcx); - self.tables.defaultness.set(def_id.index, tcx.defaultness(def_id)); + self.tables.defaultness.set_local_hashed( + def_id.expect_local(), + tcx.defaultness(def_id), + hcx, + ); let trait_ref = header.trait_ref.instantiate_identity().skip_norm_wip(); let simplified_self_ty = fast_reject::simplify_type( @@ -2190,7 +2352,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { trait_ref.self_ty(), TreatParams::InstantiateWithInfer, ); - trait_impls + trait_impls_map .entry(trait_ref.def_id) .or_default() .push((id.owner_id.def_id.local_def_index, simplified_self_ty)); @@ -2199,27 +2361,32 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let Ok(mut an) = trait_def.ancestors(tcx, def_id) && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - self.tables.impl_parent.set_some(def_id.index, parent.into()); + self.tables.impl_parent.set_hashed( + def_id.index, + Some(parent.into()), + (def_id, parent), + hcx, + ); } // if this is an impl of `CoerceUnsized`, create its // "unsized info", else just store None if tcx.is_lang_item(trait_ref.def_id, LangItem::CoerceUnsized) { let coerce_unsized_info = tcx.coerce_unsized_info(def_id).unwrap(); - record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info); + record!(self.tables.coerce_unsized_info[def_id] <- coerce_unsized_info, hcx); } } } - let trait_impls: Vec<_> = trait_impls - .into_iter() + let trait_impls: Vec<_> = trait_impls_map + .iter() .map(|(trait_def_id, impls)| TraitImpls { trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index), - impls: self.lazy_array(&impls), + impls: self.lazy_array(impls), }) .collect(); - self.lazy_array(&trait_impls) + (self.lazy_array(&trait_impls), trait_impls_map) } #[instrument(level = "debug", skip(self))] @@ -2272,16 +2439,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_dylib_dependency_formats(&mut self) -> LazyArray> { empty_proc_macro!(self); - let formats = self.tcx.dependency_formats(()); - if let Some(arr) = formats.get(&CrateType::Dylib) { - return self.lazy_array(arr.iter().skip(1 /* skip LOCAL_CRATE */).map( - |slot| match *slot { - Linkage::NotLinked | Linkage::IncludedFromDylib => None, - - Linkage::Dynamic => Some(LinkagePreference::RequireDynamic), - Linkage::Static => Some(LinkagePreference::RequireStatic), - }, - )); + if let Some(arr) = dylib_dependency_formats(self.tcx) { + return self.lazy_array(arr.map(|(_id, slot)| slot)); } LazyArray::default() } @@ -2485,21 +2644,29 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { dep_node, tcx, || { - with_encode_metadata_header(tcx, path, |ecx| { - // Encode all the entries and extra information in the crate, - // culminating in the `CrateRoot` which points to all of it. - let root = ecx.encode_crate_root(); - - // Flush buffer to ensure backing file has the correct size. - ecx.opaque.flush(); - // Record metadata size for self-profiling - tcx.prof.artifact_size( - "crate_metadata", - "crate_metadata", - ecx.opaque.file().metadata().unwrap().len(), - ); + tcx.with_stable_hashing_context(|hcx| { + let is_proc_macro = tcx.crate_types().contains(&CrateType::ProcMacro); + let hash_public_api = tcx.sess.opts.unstable_opts.public_api_hash + & !is_proc_macro + & tcx.sess.opts.incremental.is_some(); + let mut hcx = PublicApiHashingContext::new(hash_public_api, hcx); + + with_encode_metadata_header(tcx, path, |ecx| { + // Encode all the entries and extra information in the crate, + // culminating in the `CrateRoot` which points to all of it. + let root = ecx.encode_crate_root(&mut hcx); + + // Flush buffer to ensure backing file has the correct size. + ecx.opaque.flush(); + // Record metadata size for self-profiling + tcx.prof.artifact_size( + "crate_metadata", + "crate_metadata", + ecx.opaque.file().metadata().unwrap().len(), + ); - root.position.get() + root.position.get() + }); }) }, None, @@ -2688,3 +2855,36 @@ pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: &hir::Body<'_>, def_id: Loc } } } + +fn crate_deps(tcx: TyCtxt<'_>) -> impl Iterator + '_ { + tcx.crates(()).iter().map(move |&cnum| { + let dep = CrateDep { + name: tcx.crate_name(cnum), + hash: tcx.crate_hash(cnum), + host_hash: tcx.crate_host_hash(cnum), + kind: tcx.crate_dep_kind(cnum), + extra_filename: tcx.extra_filename(cnum).clone(), + is_private: tcx.is_private_dep(cnum), + }; + (cnum, dep) + }) +} + +fn dylib_dependency_formats( + tcx: TyCtxt<'_>, +) -> Option)>> { + let formats = tcx.dependency_formats(()); + formats.get(&CrateType::Dylib).map(|arr| { + arr.iter().enumerate().skip(1 /* skip LOCAL_CRATE */).map(|(i, slot)| { + ( + CrateNum::new(i), + match *slot { + Linkage::NotLinked | Linkage::IncludedFromDylib => None, + + Linkage::Dynamic => Some(LinkagePreference::RequireDynamic), + Linkage::Static => Some(LinkagePreference::RequireStatic), + }, + ) + }) + }) +} diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs new file mode 100644 index 0000000000000..86a68b4bfd220 --- /dev/null +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -0,0 +1,438 @@ +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::svh::Svh; +use rustc_hir::def_id::{DefId, DefIndex, LOCAL_CRATE, LocalDefId}; +use rustc_index::Idx; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; +use rustc_middle::mir::interpret; +use rustc_middle::ty::TyCtxt; + +use super::{CrateDep, CrateHeader, CrateRoot, SimplifiedType, TargetTuple}; + +#[derive(Default)] +pub(crate) struct PublicApiHasher(StableHasher); + +impl PublicApiHasher { + pub(crate) fn finish(self, hcx: &mut PublicApiHashingContext<'_>) -> Option { + hcx.hash_public_api.then(|| Svh::new(self.0.finish())) + } + + pub(crate) fn digest<'a, T: HashStable>>( + &mut self, + value: T, + hcx: &mut PublicApiHashingContext<'a>, + ) { + if hcx.hash_public_api { + value.hash_stable(&mut hcx.hcx, &mut self.0); + } + } + fn digest_iter<'a, I>(&mut self, values: I, hcx: &mut PublicApiHashingContext<'a>) + where + I: IntoIterator, + I::Item: HashStable>, + { + if hcx.hash_public_api { + for value in values { + self.digest(value, hcx); + } + } + } +} + +pub(crate) trait TablePublicApiHasher: Default { + type IterHasher; + + fn digest<'a, V>(&mut self, index: I, value: V, hcx: &mut PublicApiHashingContext<'a>) + where + V: HashStable>; + fn finish(&self) -> Option; + + fn iter_hasher(&self) -> Self::IterHasher; +} + +pub(crate) struct RDRHashAll { + hash: Fingerprint, + _marker: std::marker::PhantomData, +} + +impl Default for RDRHashAll { + fn default() -> Self { + Self { hash: Fingerprint::ZERO, _marker: Default::default() } + } +} + +pub(crate) struct PublicApiHashingContext<'a> { + hcx: StableHashingContext<'a>, + hash_public_api: bool, +} + +impl<'a> PublicApiHashingContext<'a> { + pub(crate) fn new(hash_public_api: bool, hcx: StableHashingContext<'a>) -> Self { + Self { hash_public_api, hcx } + } +} + +impl TablePublicApiHasher for RDRHashAll { + type IterHasher = OrderedIterHasher; + fn digest<'a, V>(&mut self, index: I, value: V, hcx: &mut PublicApiHashingContext<'a>) + where + V: HashStable>, + { + if !hcx.hash_public_api { + return; + } + let mut hasher = StableHasher::default(); + // add the non-stable hash of the index here to hash the order of items without storing them and iterating over it later + (index.index(), value).hash_stable(&mut hcx.hcx, &mut hasher); + let hash: Fingerprint = hasher.finish(); + self.hash = self.hash.combine_commutative(hash); + } + + fn finish(&self) -> Option { + Some(self.hash) + } + + fn iter_hasher(&self) -> Self::IterHasher { + OrderedIterHasher::default() + } +} + +#[derive(Default)] +pub(crate) struct OrderedIterHasher(StableHasher); + +impl OrderedIterHasher { + pub(crate) fn inspect_digest<'a: 'b, 'b, I>( + &'b mut self, + iter: I, + hcx: &'b mut PublicApiHashingContext<'a>, + ) -> impl Iterator + 'b + where + I: IntoIterator + 'b, + I::Item: HashStable>, + { + iter.into_iter().inspect(move |item: &I::Item| { + if hcx.hash_public_api { + item.hash_stable(&mut hcx.hcx, &mut self.0) + } + }) + } + + pub(crate) fn finish(self) -> Fingerprint { + self.0.finish() + } +} + +pub(crate) struct RDRHashNone(std::marker::PhantomData); + +impl Default for RDRHashNone { + fn default() -> Self { + RDRHashNone(Default::default()) + } +} + +impl TablePublicApiHasher for RDRHashNone { + type IterHasher = RDRHashNone<()>; + fn digest<'a, V>(&mut self, _index: I, _value: V, _hcx: &mut PublicApiHashingContext<'a>) + where + V: HashStable>, + { + } + + fn iter_hasher(&self) -> Self::IterHasher { + Default::default() + } + + fn finish(&self) -> Option { + None + } +} + +pub(crate) fn hash_crate_root_public_api( + hasher: &mut PublicApiHasher, + hcx: &mut PublicApiHashingContext<'_>, + root: &CrateRoot, + tcx: TyCtxt<'_>, + encoded_impls: FxIndexMap)>>, + interpret_allocs: &FxIndexSet, +) { + if !hcx.hash_public_api { + return; + } + let CrateRoot { + header, + extra_filename, + stable_crate_id, + required_panic_strategy, + panic_in_drop_strategy, + edition, + has_global_allocator, + has_alloc_error_handler, + has_panic_handler, + has_default_lib_allocator, + externally_implementable_items, + crate_deps, + dylib_dependency_formats, + lib_features, + stability_implications, + lang_items, + lang_items_missing, + stripped_cfg_items, + diagnostic_items, + native_libraries, + foreign_modules, + traits, + impls, + incoherent_impls, + interpret_alloc_index, + proc_macro_data, + // hashed before encoding tables + tables: _, + debugger_visualizers, + exportable_items, + stable_order_of_exportable_impls, + exported_non_generic_symbols, + exported_generic_symbols, + // hashed in `encode_hygiene` + syntax_contexts: _, + // hashed in `encode_hygiene` + expn_data: _, + // hashed in `encode_hygiene` + expn_hashes: _, + def_path_hash_map, + // hashed in `encode_source_map` + source_map: _, + target_modifiers, + compiler_builtins, + needs_allocator, + needs_panic_runtime, + no_builtins, + panic_runtime, + profiler_runtime, + symbol_mangling_version, + specialization_enabled_in, + denied_partial_mitigations, + // We are constructing this right now, no need to include it + rdr_hashes: _, + } = root; + let CrateHeader { triple, hash, name, is_proc_macro_crate, is_stub } = header; + // FIXME do we need this? + hasher.digest(std::mem::discriminant(triple), hcx); + match triple { + TargetTuple::TargetTuple(triple) => hasher.digest(triple, hcx), + TargetTuple::TargetJson { + // this is discarded during serialization + path_for_rustdoc: _, + tuple, + contents, + } => { + hasher.digest(tuple, hcx); + hasher.digest(contents, hcx); + } + } + // FIXME do we need this? + hasher.digest(hash, hcx); + // FIXME do we need this? + hasher.digest(name, hcx); + // FIXME do we need this? + hasher.digest(is_proc_macro_crate, hcx); + // FIXME do we need this? + hasher.digest(is_stub, hcx); + + // FIXME do we need this? + hasher.digest(extra_filename, hcx); + // FIXME do we need this? + hasher.digest(stable_crate_id, hcx); + // FIXME do we need this? + hasher.digest(required_panic_strategy, hcx); + // FIXME do we need this? + hasher.digest(panic_in_drop_strategy, hcx); + // FIXME do we need this? + hasher.digest(edition, hcx); + // FIXME do we need this? + hasher.digest(has_global_allocator, hcx); + // FIXME do we need this? + hasher.digest(has_alloc_error_handler, hcx); + // FIXME do we need this? + hasher.digest(has_panic_handler, hcx); + // FIXME do we need this? + hasher.digest(has_default_lib_allocator, hcx); + + // FIXME do we need this? + let _ = externally_implementable_items; + hasher.digest(tcx.externally_implementable_items(LOCAL_CRATE), hcx); + + // We can always ignore this during public api hashing. For proc macros we use the full crate hash as the public api hash. For non-proc macros this is always None. + assert!(proc_macro_data.is_none()); + + // FIXME do we need this? + let _ = debugger_visualizers; + hasher.digest_iter( + tcx.debugger_visualizers(LOCAL_CRATE) + .iter() + // Erase the path since it may contain privacy sensitive data + // that we don't want to end up in crate metadata. + // The path is only needed for the local crate because of + // `--emit dep-info`. + .map(DebuggerVisualizerFile::path_erased), + hcx, + ); + + // FIXME do we need this? + hasher.digest(compiler_builtins, hcx); + // FIXME do we need this? + hasher.digest(needs_allocator, hcx); + // FIXME do we need this? + hasher.digest(needs_panic_runtime, hcx); + // FIXME do we need this? + hasher.digest(no_builtins, hcx); + // FIXME do we need this? + hasher.digest(panic_runtime, hcx); + // FIXME do we need this? + hasher.digest(profiler_runtime, hcx); + // FIXME do we need this? + hasher.digest(symbol_mangling_version, hcx); + + // FIXME do we need this? + let _ = crate_deps; + // FIXME should we sort this? + for (id, dep) in super::crate_deps(tcx) { + // FIXME do we need this? + hasher.digest(id, hcx); + + let CrateDep { name, hash, host_hash, kind, extra_filename, is_private } = dep; + // FIXME do we need this? + hasher.digest(name, hcx); + // FIXME do we need this? + hasher.digest(hash, hcx); + // FIXME do we need this? + hasher.digest(host_hash, hcx); + // FIXME do we need this? + hasher.digest(kind, hcx); + // FIXME do we need this? + hasher.digest(extra_filename, hcx); + // FIXME do we need this? + hasher.digest(is_private, hcx); + } + + // FIXME do we need this? + let _ = dylib_dependency_formats; + if let Some(arr) = super::dylib_dependency_formats(tcx) { + let arr: Vec<_> = arr.map(|(id, format)| (tcx.stable_crate_id(id), format)).collect(); + // FIXME should we sort this? + hasher.digest(arr, hcx); + } + + // FIXME do we need this? + let _ = lib_features; + hasher.digest(tcx.lib_features(LOCAL_CRATE).to_sorted_vec(), hcx); + + // FIXME do we need this + let _ = stability_implications; + hasher.digest(tcx.stability_implications(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = lang_items; + hasher.digest_iter( + tcx.lang_items() + .iter() + .filter_map(|(lang_item, def_id)| def_id.as_local().map(|id| (id, lang_item))), + hcx, + ); + + // FIXME do we need this? + let _ = lang_items_missing; + let missing = tcx.lang_items().missing.clone(); + // FIXME should we sort this? + // missing.sort_unstable_by_key(|lang_item| *lang_item as u32); + hasher.digest_iter(missing, hcx); + + // FIXME do we need this? + let _ = diagnostic_items; + let diagnostic_items = &tcx.diagnostic_items(LOCAL_CRATE).name_to_id; + // FIXME should we sort this? + hasher.digest_iter(diagnostic_items, hcx); + + // FIXME do we need this? + let _ = stripped_cfg_items; + // FIXME should we sort this? + hasher.digest(tcx.stripped_cfg_items(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = native_libraries; + // FIXME should we sort this? + hasher.digest(tcx.native_libraries(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = foreign_modules; + // FIXME should we sort this? + hasher.digest(tcx.foreign_modules(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = target_modifiers; + // FIXME should we sort this? + hasher.digest(tcx.sess.opts.gather_target_modifiers(), hcx); + + // FIXME do we need this? + let _ = traits; + // FIXME should we sort this? + hasher.digest(tcx.traits(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = impls; + // FIXME should we sort this? + let impls = encoded_impls.into_iter().map(|(id, impls)| { + ( + id, + // FIXME should we sort this? + impls + .into_iter() + .map(|(ind, value)| (LocalDefId { local_def_index: ind }, value)) + .collect::>(), + ) + }); + hasher.digest_iter(impls, hcx); + + // FIXME do we need this? + let _ = incoherent_impls; + let incoherent_impls = &tcx.crate_inherent_impls(()).0.incoherent_impls; + // FIXME should we sort this? + hasher.digest(incoherent_impls, hcx); + + // FIXME do we need this? + let _ = exportable_items; + // FIXME should we sort this? + hasher.digest(tcx.exportable_items(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = stable_order_of_exportable_impls; + // FIXME should we sort this? + hasher.digest(tcx.stable_order_of_exportable_impls(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = exported_non_generic_symbols; + // FIXME should we sort this? + hasher.digest(tcx.exported_non_generic_symbols(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = exported_generic_symbols; + // FIXME should we sort this? + hasher.digest(tcx.exported_generic_symbols(LOCAL_CRATE), hcx); + + // FIXME do we need this? + let _ = interpret_alloc_index; + // FIXME should we sort this? + hasher.digest_iter(interpret_allocs.iter().map(|alloc_id| tcx.global_alloc(*alloc_id)), hcx); + + // FIXME do we need this? + let _ = def_path_hash_map; + hasher.digest_iter(tcx.iter_local_def_id(), hcx); + + // FIXME do we need this? + hasher.digest(*specialization_enabled_in, hcx); + + // FIXME do we need this? + let _ = denied_partial_mitigations; + hasher.digest(tcx.sess.gather_enabled_denied_partial_mitigations(), hcx); +} diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 68fb1de13e235..cb4970861d14a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -46,6 +46,9 @@ use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; use crate::eii::EiiMapEncodedKeyValue; +use crate::rmeta::encoder::public_api_hasher::{ + PublicApiHasher, PublicApiHashingContext, RDRHashAll, RDRHashNone, +}; mod decoder; mod def_path_hash_map; @@ -364,8 +367,8 @@ pub(crate) struct IncoherentImpls { /// Define `LazyTables` and `TableBuilders` at the same time. macro_rules! define_tables { ( - - defaulted: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+ - - optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+ + - defaulted: $($name1:ident: Table<$HASH1:ident, $IDX1:ty, $T1:ty>,)+ + - optional: $($name2:ident: Table<$HASH2:ident, $IDX2:ty, $T2:ty>,)+ ) => { #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct LazyTables { @@ -375,8 +378,8 @@ macro_rules! define_tables { #[derive(Default)] struct TableBuilders { - $($name1: TableBuilder<$IDX1, $T1>,)+ - $($name2: TableBuilder<$IDX2, Option<$T2>>,)+ + $($name1: TableBuilder<$HASH1<$IDX1>, $IDX1, $T1>,)+ + $($name2: TableBuilder<$HASH2<$IDX2>, $IDX2, Option<$T2>>,)+ } impl TableBuilders { @@ -386,110 +389,119 @@ macro_rules! define_tables { $($name2: self.$name2.encode(buf),)+ } } + + fn hash_public_api(&self, hasher: &mut PublicApiHasher, hcx: &mut PublicApiHashingContext<'_>) { + $(self.$name1.hash_public_api(hasher, hcx);)+ + $(self.$name2.hash_public_api(hasher, hcx);)+ + } } } } define_tables! { - defaulted: - intrinsic: Table>>, - is_macro_rules: Table, - type_alias_is_lazy: Table, - attr_flags: Table, + intrinsic: Table>>, + is_macro_rules: Table, + type_alias_is_lazy: Table, + attr_flags: Table, // The u64 is the crate-local part of the DefPathHash. All hashes in this crate have the same // StableCrateId, so we omit encoding those into the table. // // Note also that this table is fully populated (no gaps) as every DefIndex should have a // corresponding DefPathHash. - def_path_hashes: Table, - explicit_item_bounds: Table, Span)>>, - explicit_item_self_bounds: Table, Span)>>, - inferred_outlives_of: Table, Span)>>, - explicit_super_predicates_of: Table, Span)>>, - explicit_implied_predicates_of: Table, Span)>>, - explicit_implied_const_bounds: Table, Span)>>, - inherent_impls: Table>, - opt_rpitit_info: Table>>, + // + // We don't need to include this in the hash, hashing `def_path_hash_map` takes care of it. + def_path_hashes: Table, + explicit_item_bounds: Table, Span)>>, + explicit_item_self_bounds: Table, Span)>>, + inferred_outlives_of: Table, Span)>>, + explicit_super_predicates_of: Table, Span)>>, + explicit_implied_predicates_of: Table, Span)>>, + explicit_implied_const_bounds: Table, Span)>>, + inherent_impls: Table>, + opt_rpitit_info: Table>>, // Reexported names are not associated with individual `DefId`s, // e.g. a glob import can introduce a lot of names, all with the same `DefId`. // That's why the encoded list needs to contain `ModChild` structures describing all the names // individually instead of `DefId`s. - module_children_reexports: Table>, - ambig_module_children: Table>, - cross_crate_inlinable: Table, - asyncness: Table, - constness: Table, - safety: Table, - defaultness: Table, + module_children_reexports: Table>, + ambig_module_children: Table>, + cross_crate_inlinable: Table, + asyncness: Table, + constness: Table, + safety: Table, + defaultness: Table, - optional: - attributes: Table>, + attributes: Table>, // For non-reexported names in a module every name is associated with a separate `DefId`, // so we can take their names, visibilities etc from other encoded tables. - module_children_non_reexports: Table>, - associated_item_or_field_def_ids: Table>, - def_kind: Table, - visibility: Table>>, - def_span: Table>, - def_ident_span: Table>, - lookup_stability: Table>, - lookup_const_stability: Table>, - lookup_default_body_stability: Table>, - lookup_deprecation_entry: Table>, - explicit_predicates_of: Table>>, - generics_of: Table>, - type_of: Table>>>, - variances_of: Table>, - fn_sig: Table>>>, - codegen_fn_attrs: Table>, - impl_trait_header: Table>>, - const_param_default: Table>>>, - object_lifetime_default: Table>, - optimized_mir: Table>>, - mir_for_ctfe: Table>>, - trivial_const: Table)>>, - closure_saved_names_of_captured_variables: Table>>, - mir_coroutine_witnesses: Table>>, - promoted_mir: Table>>>, - thir_abstract_const: Table>>>, - impl_parent: Table, - const_conditions: Table>>, + module_children_non_reexports: Table>, + associated_item_or_field_def_ids: Table>, + def_kind: Table, + visibility: Table>>, + def_span: Table>, + def_ident_span: Table>, + lookup_stability: Table>, + lookup_const_stability: Table>, + lookup_default_body_stability: Table>, + lookup_deprecation_entry: Table>, + explicit_predicates_of: Table>>, + generics_of: Table>, + type_of: Table>>>, + variances_of: Table>, + fn_sig: Table>>>, + codegen_fn_attrs: Table>, + impl_trait_header: Table>>, + const_param_default: Table>>>, + object_lifetime_default: Table>, + optimized_mir: Table>>, + mir_for_ctfe: Table>>, + trivial_const: Table)>>, + closure_saved_names_of_captured_variables: Table>>, + mir_coroutine_witnesses: Table>>, + promoted_mir: Table>>>, + thir_abstract_const: Table>>>, + impl_parent: Table, + const_conditions: Table>>, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? - coerce_unsized_info: Table>, - mir_const_qualif: Table>, - rendered_const: Table>, - rendered_precise_capturing_args: Table>>, - fn_arg_idents: Table>>, - coroutine_kind: Table, - coroutine_for_closure: Table, - adt_destructor: Table>, - adt_async_destructor: Table>, - coroutine_by_move_body_def_id: Table, - eval_static_initializer: Table>>, - trait_def: Table>, - expn_that_defined: Table>, - default_fields: Table>, - params_in_repr: Table>>, - repr_options: Table>, + coerce_unsized_info: Table>, + mir_const_qualif: Table>, + rendered_const: Table>, + rendered_precise_capturing_args: Table>>, + fn_arg_idents: Table>>, + coroutine_kind: Table, + coroutine_for_closure: Table, + adt_destructor: Table>, + adt_async_destructor: Table>, + coroutine_by_move_body_def_id: Table, + eval_static_initializer: Table>>, + trait_def: Table>, + expn_that_defined: Table>, + default_fields: Table>, + params_in_repr: Table>>, + repr_options: Table>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire // `DefPathTable` up front, since we may only ever use a few // definitions from any given crate. - def_keys: Table>, - proc_macro_quoted_spans: Table>, - variant_data: Table>, - assoc_container: Table>, - macro_definition: Table>, - proc_macro: Table, - deduced_param_attrs: Table>, - trait_impl_trait_tys: Table>>>>, - doc_link_resolutions: Table>, - doc_link_traits_in_scope: Table>, - assumed_wf_types_for_rpitit: Table, Span)>>, - opaque_ty_origin: Table>>, - anon_const_kind: Table>, - const_of_item: Table>>>, - associated_types_for_impl_traits_in_trait_or_impl: Table>>>, + // + // We don't need to include this in the hash, hashing `def_path_hash_map` takes care of it. + def_keys: Table>, + proc_macro_quoted_spans: Table>, + variant_data: Table>, + assoc_container: Table>, + macro_definition: Table>, + proc_macro: Table, + deduced_param_attrs: Table>, + trait_impl_trait_tys: Table>>>>, + doc_link_resolutions: Table>, + doc_link_traits_in_scope: Table>, + assumed_wf_types_for_rpitit: Table, Span)>>, + opaque_ty_origin: Table>>, + anon_const_kind: Table>, + const_of_item: Table>>>, + associated_types_for_impl_traits_in_trait_or_impl: Table>>>, } #[derive(TyEncodable, TyDecodable)] @@ -502,7 +514,7 @@ struct VariantData { } bitflags::bitflags! { - #[derive(Default)] + #[derive(Default, Clone, Copy)] pub struct AttrFlags: u8 { const IS_DOC_HIDDEN = 1 << 0; } diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 26c5908563777..7e40ea8f1a9f8 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,7 +1,11 @@ +use rustc_data_structures::stable_hasher::HashStable; use rustc_hir::def::CtorOf; +use rustc_hir::def_id::LocalDefId; use rustc_index::Idx; +use rustc_middle::ich::StableHashingContext; use crate::rmeta::decoder::MetaBlob; +use crate::rmeta::encoder::public_api_hasher::{PublicApiHashingContext, TablePublicApiHasher}; use crate::rmeta::*; pub(super) trait IsDefault: Default { @@ -436,34 +440,127 @@ impl FixedSizeEncoding for Option> { } /// Helper for constructing a table's serialization (also see `Table`). -pub(super) struct TableBuilder { +pub(super) struct TableBuilder, I: Idx, T: FixedSizeEncoding> { width: usize, blocks: IndexVec, - _marker: PhantomData, + hasher: H, + _marker: PhantomData<(T, H)>, } -impl Default for TableBuilder { +impl, I: Idx, T: FixedSizeEncoding> Default for TableBuilder { fn default() -> Self { - TableBuilder { width: 0, blocks: Default::default(), _marker: PhantomData } + TableBuilder { + width: 0, + blocks: Default::default(), + hasher: Default::default(), + _marker: PhantomData, + } } } -impl TableBuilder> +impl, I: Idx, const N: usize, T> TableBuilder> where Option: FixedSizeEncoding, { - pub(crate) fn set_some(&mut self, i: I, value: T) { - self.set(i, Some(value)) + pub(crate) fn set_some_hashed<'a, HashedT>( + &mut self, + i: I, + value: T, + hashed: HashedT, + hcx: &mut PublicApiHashingContext<'a>, + ) where + HashedT: HashStable>, + { + self.hasher.digest(i, hashed, hcx); + self.set(i, Some(value)); + } +} + +impl, const N: usize, T: FixedSizeEncoding> + TableBuilder +{ + pub(crate) fn set_local_hashed<'a>( + &mut self, + i: LocalDefId, + value: T, + hcx: &mut PublicApiHashingContext<'a>, + ) where + T: HashStable> + Copy, + { + self.hasher.digest(i.local_def_index, (i, value), hcx); + self.set(i.local_def_index, value); } } -impl> TableBuilder { +impl, const N: usize, T> TableBuilder> +where + Option: FixedSizeEncoding, +{ + pub(crate) fn set_some_local_hashed<'a>( + &mut self, + i: LocalDefId, + value: T, + hcx: &mut PublicApiHashingContext<'a>, + ) where + T: HashStable> + Copy, + { + self.hasher.digest(i.local_def_index, (i, value), hcx); + self.set(i.local_def_index, Some(value)); + } +} + +impl> + TableBuilder, I, T> +{ + pub(super) fn set_unhashed(&mut self, i: I, value: T) { + self.set(i, value); + } +} + +impl TableBuilder, I, Option> +where + Option: FixedSizeEncoding, +{ + pub(super) fn set_some_unhashed(&mut self, i: I, value: T) { + self.set(i, Some(value)); + } +} + +impl, I: Idx, const N: usize, T: FixedSizeEncoding> + TableBuilder +{ + pub(super) fn hash_public_api( + &self, + public_api_hasher: &mut PublicApiHasher, + hcx: &mut PublicApiHashingContext<'_>, + ) { + if let Some(hash) = self.hasher.finish() { + public_api_hasher.digest(hash, hcx); + } + } + pub(super) fn iter_hasher(&self) -> H::IterHasher { + self.hasher.iter_hasher() + } + + pub(super) fn set_hashed<'a, HashedT>( + &mut self, + i: I, + value: T, + hashed: HashedT, + hcx: &mut PublicApiHashingContext<'a>, + ) where + HashedT: HashStable>, + { + self.hasher.digest(i, hashed, hcx); + self.set(i, value); + } + /// Sets the table value if it is not default. /// ATTENTION: For optimization default values are simply ignored by this function, because /// right now metadata tables never need to reset non-default values to default. If such need /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced /// for doing that explicitly. - pub(crate) fn set(&mut self, i: I, value: T) { + pub(super) fn set(&mut self, i: I, value: T) { #[cfg(debug_assertions)] { debug_assert!( From 1ce1fe315cfc3a4fe1386e16e58628e7efa22e79 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 25 Apr 2026 11:09:22 +0200 Subject: [PATCH 06/15] rdr: hash spans as if they had no parents (spans are encoded into the rmeta without parents) --- .../src/stable_hasher.rs | 1 + compiler/rustc_metadata/src/rmeta/encoder.rs | 3 +- .../src/rmeta/encoder/public_api_hasher.rs | 2 +- compiler/rustc_middle/src/ich/hcx.rs | 68 +++++++++++++------ 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index ce3d781712515..82def6a1e98ee 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -601,4 +601,5 @@ where #[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] pub struct HashingControls { pub hash_spans: bool, + pub hash_spans_as_parentless: bool, } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index fee6a5a5c0206..6547064f82c80 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2644,7 +2644,8 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { dep_node, tcx, || { - tcx.with_stable_hashing_context(|hcx| { + tcx.with_stable_hashing_context(|mut hcx| { + hcx.set_hash_spans_as_parentless(true); let is_proc_macro = tcx.crate_types().contains(&CrateType::ProcMacro); let hash_public_api = tcx.sess.opts.unstable_opts.public_api_hash & !is_proc_macro diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs index 86a68b4bfd220..e16a817d61c4b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -64,7 +64,7 @@ impl Default for RDRHashAll { } pub(crate) struct PublicApiHashingContext<'a> { - hcx: StableHashingContext<'a>, + pub(crate) hcx: StableHashingContext<'a>, hash_public_api: bool, } diff --git a/compiler/rustc_middle/src/ich/hcx.rs b/compiler/rustc_middle/src/ich/hcx.rs index e316cbc81bebe..d6bacfb4592df 100644 --- a/compiler/rustc_middle/src/ich/hcx.rs +++ b/compiler/rustc_middle/src/ich/hcx.rs @@ -6,7 +6,7 @@ use rustc_hir::definitions::DefPathHash; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::{CachingSourceMapView, DUMMY_SP, HashStableContext, Pos, Span}; +use rustc_span::{CachingSourceMapView, DUMMY_SP, HashStableContext, Pos, Span, SpanData}; // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we // initialize it lazily. @@ -37,10 +37,18 @@ impl<'a> StableHashingContext<'a> { untracked, incremental_ignore_spans: sess.opts.unstable_opts.incremental_ignore_spans, caching_source_map: CachingSourceMap::Unused(sess.source_map()), - hashing_controls: HashingControls { hash_spans: hash_spans_initial }, + hashing_controls: HashingControls { + hash_spans: hash_spans_initial, + hash_spans_as_parentless: false, + }, } } + #[inline] + pub fn set_hash_spans_as_parentless(&mut self, hash_spans_as_parentless: bool) { + self.hashing_controls.hash_spans_as_parentless = hash_spans_as_parentless; + } + #[inline] pub fn while_hashing_spans(&mut self, hash_spans: bool, f: F) { let prev_hash_spans = self.hashing_controls.hash_spans; @@ -69,31 +77,18 @@ impl<'a> StableHashingContext<'a> { pub fn hashing_controls(&self) -> HashingControls { self.hashing_controls } -} -impl<'a> HashStableContext for StableHashingContext<'a> { - /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that - /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`). - /// Instead, we hash the (file name, line, column) triple, which stays the same even if the - /// containing `SourceFile` has moved within the `SourceMap`. - /// - /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets. - /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we - /// avoid doing it twice when the span starts and ends in the same file, which is almost always - /// the case. - /// - /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. - #[inline] - fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher) { + fn hash_span_data(&mut self, span: SpanData, hasher: &mut StableHasher) { const TAG_VALID_SPAN: u8 = 0; const TAG_INVALID_SPAN: u8 = 1; const TAG_RELATIVE_SPAN: u8 = 2; - if !self.hashing_controls().hash_spans { + if span.parent.is_some() && self.hashing_controls().hash_spans_as_parentless { + let mut span = span; + span.parent = None; + self.hash_span_data(span, hasher); return; } - - let span = span.data_untracked(); span.ctxt.hash_stable(self, hasher); span.parent.hash_stable(self, hasher); @@ -155,6 +150,29 @@ impl<'a> HashStableContext for StableHashingContext<'a> { Hash::hash(&col_line, hasher); Hash::hash(&len, hasher); } +} + +impl<'a> HashStableContext for StableHashingContext<'a> { + /// Hashes a span in a stable way. We can't directly hash the span's `BytePos` fields (that + /// would be similar to hashing pointers, since those are just offsets into the `SourceMap`). + /// Instead, we hash the (file name, line, column) triple, which stays the same even if the + /// containing `SourceFile` has moved within the `SourceMap`. + /// + /// Also note that we are hashing byte offsets for the column, not unicode codepoint offsets. + /// For the purpose of the hash that's sufficient. Also, hashing filenames is expensive so we + /// avoid doing it twice when the span starts and ends in the same file, which is almost always + /// the case. + /// + /// IMPORTANT: changes to this method should be reflected in implementations of `SpanEncoder`. + #[inline] + fn span_hash_stable(&mut self, span: Span, hasher: &mut StableHasher) { + if !self.hashing_controls().hash_spans { + return; + } + + let span = span.data_untracked(); + self.hash_span_data(span, hasher); + } #[inline] fn def_path_hash(&self, def_id: DefId) -> DefPathHash { @@ -172,7 +190,15 @@ impl<'a> HashStableContext for StableHashingContext<'a> { #[inline] fn assert_default_hashing_controls(&self, msg: &str) { let hashing_controls = self.hashing_controls; - let HashingControls { hash_spans } = hashing_controls; + let HashingControls { + hash_spans, + // This is only used for public api hashing of rmeta. + // + // The expn hashes are encoded in the rmeta and all spans are encoded without their + // parent in rmeta. If it is ok to give the information like this to dependent crates + // it should be ok to ignore this setting here. + hash_spans_as_parentless: _, + } = hashing_controls; // Note that we require that `hash_spans` be the inverse of the global `-Z // incremental-ignore-spans` option. Normally, this option is disabled, in which case From bc16057c93189f3cedadb7a6f80cac8f7547b92f Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 21 Apr 2026 09:22:50 +0200 Subject: [PATCH 07/15] rdr: add the public_api_hash query. Queries provided by rmeta now depend on public_api_hash instead of crate_hash --- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 9 +++++---- compiler/rustc_middle/src/queries.rs | 7 +++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a00fb59963ac2..4f199603523c7 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -144,12 +144,12 @@ macro_rules! provide_one { let ($def_id, $other) = def_id_arg.into_args(); assert!(!$def_id.is_local()); - // External query providers call `crate_hash` in order to register a dependency - // on the crate metadata. The exception is `crate_hash` itself, which obviously + // External query providers call `public_api_hash` in order to register a dependency + // on the crate metadata. The exception is `public_api_hash` itself, which obviously // doesn't need to do this (and can't, as it would cause a query cycle). use rustc_middle::dep_graph::DepKind; - if DepKind::$name != DepKind::crate_hash && $tcx.dep_graph.is_fully_enabled() { - $tcx.ensure_ok().crate_hash($def_id.krate); + if DepKind::$name != DepKind::public_api_hash && $tcx.dep_graph.is_fully_enabled() { + $tcx.ensure_ok().public_api_hash($def_id.krate); } let cstore = CStore::from_tcx($tcx); @@ -366,6 +366,7 @@ provide! { tcx, def_id, other, cdata, native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } crate_hash => { cdata.root.header.hash } + public_api_hash => { cdata.root.rdr_hashes.public_api_hash } crate_host_hash => { cdata.host_hash } crate_name => { cdata.root.header.name } num_extern_def_ids => { cdata.num_def_ids() } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 7c6ab642b2736..d7d73a77b8526 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2045,6 +2045,13 @@ rustc_queries! { separate_provide_extern } + /// Returns the public api hash from a dependency metadata. Does not work for the local crate. + query public_api_hash(_: CrateNum) -> Svh { + eval_always + desc { "looking up the hash a crate" } + separate_provide_extern + } + /// Gets the hash for the host proc macro. Used to support -Z dual-proc-macro. query crate_host_hash(_: CrateNum) -> Option { eval_always From 280b75e5e669d1ec0326d08e6c8bca660e3a5b79 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 22 Apr 2026 14:57:54 +0200 Subject: [PATCH 08/15] rdr: remove crate_hash from public api hash --- .../src/rmeta/encoder/public_api_hasher.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs index e16a817d61c4b..3e392813a7e49 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -216,7 +216,14 @@ pub(crate) fn hash_crate_root_public_api( // We are constructing this right now, no need to include it rdr_hashes: _, } = root; - let CrateHeader { triple, hash, name, is_proc_macro_crate, is_stub } = header; + let CrateHeader { + triple, + // We don't want the full crate hash in the public api hash + hash: _, + name, + is_proc_macro_crate, + is_stub, + } = header; // FIXME do we need this? hasher.digest(std::mem::discriminant(triple), hcx); match triple { @@ -231,8 +238,7 @@ pub(crate) fn hash_crate_root_public_api( hasher.digest(contents, hcx); } } - // FIXME do we need this? - hasher.digest(hash, hcx); + // FIXME do we need this? hasher.digest(name, hcx); // FIXME do we need this? From 68bf2d49cd5e3b2b02c3726a5650cdae32d7a96c Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 22 Apr 2026 16:17:20 +0200 Subject: [PATCH 09/15] rdr: add public api hash debug logs --- .../src/rmeta/decoder/cstore_impl.rs | 5 ++++- compiler/rustc_metadata/src/rmeta/encoder.rs | 1 + .../src/rmeta/encoder/public_api_hasher.rs | 21 ++++++++++++++++++- compiler/rustc_metadata/src/rmeta/mod.rs | 4 ++-- 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 4f199603523c7..e9d37fbb4159a 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -366,7 +366,10 @@ provide! { tcx, def_id, other, cdata, native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } crate_hash => { cdata.root.header.hash } - public_api_hash => { cdata.root.rdr_hashes.public_api_hash } + public_api_hash => { { + tracing::debug!("public api hash: {} {}", cdata.root.header.name, cdata.root.rdr_hashes.public_api_hash); + cdata.root.rdr_hashes.public_api_hash + } } crate_host_hash => { cdata.host_hash } crate_name => { cdata.root.header.name } num_extern_def_ids => { cdata.num_def_ids() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 6547064f82c80..bf9b13ae19462 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -862,6 +862,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ); crate_root.rdr_hashes.public_api_hash = public_api_hasher.finish(hcx).unwrap_or(crate_root.header.hash); + debug!("public api hash: {}", crate_root.rdr_hashes.public_api_hash); let root = stat!("final", || { self.lazy(crate_root) }); diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs index 3e392813a7e49..5b9ccdefa0748 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -8,10 +8,11 @@ use rustc_middle::ich::StableHashingContext; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::mir::interpret; use rustc_middle::ty::TyCtxt; +use tracing::debug; use super::{CrateDep, CrateHeader, CrateRoot, SimplifiedType, TargetTuple}; -#[derive(Default)] +#[derive(Default, Debug)] pub(crate) struct PublicApiHasher(StableHasher); impl PublicApiHasher { @@ -333,10 +334,12 @@ pub(crate) fn hash_crate_root_public_api( // FIXME do we need this? let _ = lib_features; hasher.digest(tcx.lib_features(LOCAL_CRATE).to_sorted_vec(), hcx); + debug!("lib_features: {:?}", hasher); // FIXME do we need this let _ = stability_implications; hasher.digest(tcx.stability_implications(LOCAL_CRATE), hcx); + debug!("stability_implications: {:?}", hasher); // FIXME do we need this? let _ = lang_items; @@ -346,6 +349,7 @@ pub(crate) fn hash_crate_root_public_api( .filter_map(|(lang_item, def_id)| def_id.as_local().map(|id| (id, lang_item))), hcx, ); + debug!("lang_items: {:?}", hasher); // FIXME do we need this? let _ = lang_items_missing; @@ -353,37 +357,44 @@ pub(crate) fn hash_crate_root_public_api( // FIXME should we sort this? // missing.sort_unstable_by_key(|lang_item| *lang_item as u32); hasher.digest_iter(missing, hcx); + debug!("lang_items_missing: {:?}", hasher); // FIXME do we need this? let _ = diagnostic_items; let diagnostic_items = &tcx.diagnostic_items(LOCAL_CRATE).name_to_id; // FIXME should we sort this? hasher.digest_iter(diagnostic_items, hcx); + debug!("diagnostic_items: {:?}", hasher); // FIXME do we need this? let _ = stripped_cfg_items; // FIXME should we sort this? hasher.digest(tcx.stripped_cfg_items(LOCAL_CRATE), hcx); + debug!("stripped_cfg_items: {:?}", hasher); // FIXME do we need this? let _ = native_libraries; // FIXME should we sort this? hasher.digest(tcx.native_libraries(LOCAL_CRATE), hcx); + debug!("native_libraries: {:?}", hasher); // FIXME do we need this? let _ = foreign_modules; // FIXME should we sort this? hasher.digest(tcx.foreign_modules(LOCAL_CRATE), hcx); + debug!("foreign_modules: {:?}", hasher); // FIXME do we need this? let _ = target_modifiers; // FIXME should we sort this? hasher.digest(tcx.sess.opts.gather_target_modifiers(), hcx); + debug!("target_modifiers: {:?}", hasher); // FIXME do we need this? let _ = traits; // FIXME should we sort this? hasher.digest(tcx.traits(LOCAL_CRATE), hcx); + debug!("traits: {:?}", hasher); // FIXME do we need this? let _ = impls; @@ -399,41 +410,49 @@ pub(crate) fn hash_crate_root_public_api( ) }); hasher.digest_iter(impls, hcx); + debug!("impls: {:?}", hasher); // FIXME do we need this? let _ = incoherent_impls; let incoherent_impls = &tcx.crate_inherent_impls(()).0.incoherent_impls; // FIXME should we sort this? hasher.digest(incoherent_impls, hcx); + debug!("incoherent_impls: {:?}", hasher); // FIXME do we need this? let _ = exportable_items; // FIXME should we sort this? hasher.digest(tcx.exportable_items(LOCAL_CRATE), hcx); + debug!("exportable_items: {:?}", hasher); // FIXME do we need this? let _ = stable_order_of_exportable_impls; // FIXME should we sort this? hasher.digest(tcx.stable_order_of_exportable_impls(LOCAL_CRATE), hcx); + debug!("stable_order_of_exportable_impls: {:?}", hasher); // FIXME do we need this? let _ = exported_non_generic_symbols; // FIXME should we sort this? hasher.digest(tcx.exported_non_generic_symbols(LOCAL_CRATE), hcx); + debug!("exported_non_generic_symbols: {:?}", hasher); // FIXME do we need this? let _ = exported_generic_symbols; // FIXME should we sort this? hasher.digest(tcx.exported_generic_symbols(LOCAL_CRATE), hcx); + debug!("exported_generic_symbols: {:?}", hasher); // FIXME do we need this? let _ = interpret_alloc_index; // FIXME should we sort this? hasher.digest_iter(interpret_allocs.iter().map(|alloc_id| tcx.global_alloc(*alloc_id)), hcx); + debug!("interpret_alloc_index: {:?}", hasher); // FIXME do we need this? let _ = def_path_hash_map; hasher.digest_iter(tcx.iter_local_def_id(), hcx); + debug!("def_path_hash_map: {:?}", hasher); // FIXME do we need this? hasher.digest(*specialization_enabled_in, hcx); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index cb4970861d14a..f3319fe69ba9a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -391,8 +391,8 @@ macro_rules! define_tables { } fn hash_public_api(&self, hasher: &mut PublicApiHasher, hcx: &mut PublicApiHashingContext<'_>) { - $(self.$name1.hash_public_api(hasher, hcx);)+ - $(self.$name2.hash_public_api(hasher, hcx);)+ + $(self.$name1.hash_public_api(hasher, hcx); tracing::debug!("{}: {hasher:?}", stringify!($name1));)+ + $(self.$name2.hash_public_api(hasher, hcx); tracing::debug!("{}: {hasher:?}", stringify!($name2));)+ } } } From 3170ab0ccf00041842ed778cb89174f35bd2b592 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 30 Apr 2026 12:06:26 +0200 Subject: [PATCH 10/15] rdr: add rustc_public_hash_unchanged and rustc_public_hash_changed attributes for testing --- .../src/attributes/rustc_internal.rs | 103 ++++++++++++++++-- compiler/rustc_attr_parsing/src/context.rs | 1 + .../src/session_diagnostics.rs | 2 +- compiler/rustc_feature/src/builtin_attrs.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 10 ++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_incremental/src/persist/mod.rs | 1 + .../src/persist/rdr_hashes.rs | 88 +++++++++++++++ .../rustc_incremental/src/persist/save.rs | 3 +- compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_query_system/src/ich/mod.rs | 21 ++++ compiler/rustc_span/src/symbol.rs | 2 + 12 files changed, 224 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_incremental/src/persist/rdr_hashes.rs create mode 100644 compiler/rustc_query_system/src/ich/mod.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 1d35923339eb7..446f82adf6e9b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -4,7 +4,7 @@ use rustc_ast::{LitIntType, LitKind, MetaItemLit}; use rustc_hir::LangItem; use rustc_hir::attrs::{ BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior, - DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcMirKind, + DivergingFallbackBehavior, RDRFields, RustcCleanAttribute, RustcCleanQueries, RustcMirKind, }; use rustc_span::Symbol; @@ -12,7 +12,7 @@ use super::prelude::*; use super::util::parse_single_integer; use crate::errors; use crate::session_diagnostics::{ - AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem, + AttributeRequiresOpt, FieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem, }; pub(crate) struct RustcMainParser; @@ -250,11 +250,11 @@ fn parse_cgu_fields( } let Some((cfg, _)) = cfg else { - cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg }); + cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg }); return None; }; let Some((module, _)) = module else { - cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module }); + cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module }); return None; }; let kind = if let Some((kind, span)) = kind { @@ -274,11 +274,7 @@ fn parse_cgu_fields( } else { // return None so that an unwrap for the attributes that need it is ok. if accepts_kind { - cx.emit_err(CguFieldsMissing { - span: args.span, - name: &cx.attr_path, - field: sym::kind, - }); + cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::kind }); return None; }; @@ -288,6 +284,95 @@ fn parse_cgu_fields( Some((cfg, module, kind)) } +fn parse_rdr_fields( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, +) -> Option<(Symbol, Symbol)> { + let args = cx.expect_list(args, cx.attr_span)?; + + let mut cfg = None::<(Symbol, Span)>; + let mut crate_name = None::<(Symbol, Span)>; + + for arg in args.mixed() { + let Some(arg) = arg.meta_item() else { + cx.adcx().expected_name_value(args.span, None); + continue; + }; + + let res = match arg.ident().map(|i| i.name) { + Some(sym::cfg) => &mut cfg, + Some(sym::crate_name) => &mut crate_name, + _ => { + cx.adcx() + .expected_specific_argument(arg.path().span(), &[sym::cfg, sym::crate_name]); + continue; + } + }; + + let Some(i) = arg.args().name_value() else { + cx.adcx().expected_name_value(arg.span(), None); + continue; + }; + + let Some(str) = i.value_as_str() else { + cx.adcx().expected_string_literal(i.value_span, Some(i.value_as_lit())); + continue; + }; + + if res.is_some() { + cx.adcx().duplicate_key(arg.span(), arg.ident().unwrap().name); + continue; + } + + *res = Some((str, i.value_span)); + } + + let Some((cfg, _)) = cfg else { + cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg }); + return None; + }; + let Some((crate_name, _)) = crate_name else { + cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::crate_name }); + return None; + }; + + Some((cfg, crate_name)) +} + +#[derive(Default)] +pub(crate) struct RustcRDRTestAttributeParser { + items: ThinVec<(Span, RDRFields)>, +} + +impl AttributeParser for RustcRDRTestAttributeParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::rustc_public_hash_changed], + template!(List: &[r#"cfg = "...", crate_name = "...""#]), + |this, cx, args| { + this.items.extend(parse_rdr_fields(cx, args).map(|(cfg, crate_name)| { + (cx.attr_span, RDRFields { cfg, crate_name, changed: true }) + })); + }, + ), + ( + &[sym::rustc_public_hash_unchanged], + template!(List: &[r#"cfg = "...", crate_name = "...""#]), + |this, cx, args| { + this.items.extend(parse_rdr_fields(cx, args).map(|(cfg, crate_name)| { + (cx.attr_span, RDRFields { cfg, crate_name, changed: false }) + })); + }, + ), + ]; + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + Some(AttributeKind::RustcRDRTestAttr(self.items)) + } +} + #[derive(Default)] pub(crate) struct RustcCguTestAttributeParser { items: ThinVec<(Span, CguFields)>, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 08fd785fb60b7..a573c297d0a5f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -162,6 +162,7 @@ attribute_parsers!( RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, + RustcRDRTestAttributeParser, StabilityParser, UsedParser, // tidy-alphabetical-end diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 0a9c96033257d..1e1593ac2423d 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -50,7 +50,7 @@ pub(crate) struct DocAliasStartEnd<'a> { #[derive(Diagnostic)] #[diag("`#[{$name})]` is missing a `{$field}` argument")] -pub(crate) struct CguFieldsMissing<'a> { +pub(crate) struct FieldsMissing<'a> { #[primary_span] pub span: Span, pub name: &'a AttrPath, diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 053caee258f7b..a7a7a34b63d00 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -772,6 +772,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ rustc_attr!(TEST, rustc_partition_reused), rustc_attr!(TEST, rustc_partition_codegened), rustc_attr!(TEST, rustc_expected_cgu_reuse), + rustc_attr!(TEST, rustc_public_hash_changed), + rustc_attr!(TEST, rustc_public_hash_unchanged), rustc_attr!(TEST, rustc_dump_symbol_name), rustc_attr!(TEST, rustc_dump_def_path), rustc_attr!(TEST, rustc_mir), diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index bab86a25e3cbb..9ce4aacf7e364 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -69,6 +69,13 @@ pub enum CguFields { ExpectedCguReuse { cfg: Symbol, module: Symbol, kind: CguKind }, } +#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)] +pub struct RDRFields { + pub crate_name: Symbol, + pub cfg: Symbol, + pub changed: bool, +} + #[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)] #[derive(HashStable_Generic, Encodable, Decodable)] pub enum DivergingFallbackBehavior { @@ -1562,6 +1569,9 @@ pub enum AttributeKind { /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). RustcPubTransparent(Span), + /// Represents `#[rustc_public_hash_changed]` and `#[rustc_public_hash_unchanged]`. + RustcRDRTestAttr(ThinVec<(Span, RDRFields)>), + /// Represents `#[rustc_reallocator]` RustcReallocator, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 41e466a9d16b7..3396297e3d522 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -177,6 +177,7 @@ impl AttributeKind { RustcPreserveUbChecks => No, RustcProcMacroDecls => No, RustcPubTransparent(..) => Yes, + RustcRDRTestAttr(..) => No, RustcReallocator => No, RustcRegions => No, RustcReservationImpl(..) => Yes, diff --git a/compiler/rustc_incremental/src/persist/mod.rs b/compiler/rustc_incremental/src/persist/mod.rs index c25c5f1ea47fb..48a69a6d9132b 100644 --- a/compiler/rustc_incremental/src/persist/mod.rs +++ b/compiler/rustc_incremental/src/persist/mod.rs @@ -7,6 +7,7 @@ mod data; mod file_format; mod fs; mod load; +mod rdr_hashes; mod save; mod work_product; diff --git a/compiler/rustc_incremental/src/persist/rdr_hashes.rs b/compiler/rustc_incremental/src/persist/rdr_hashes.rs new file mode 100644 index 0000000000000..7c71ab5e1c49e --- /dev/null +++ b/compiler/rustc_incremental/src/persist/rdr_hashes.rs @@ -0,0 +1,88 @@ +use rustc_hir::attrs::RDRFields; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::find_attr; +use rustc_macros::Diagnostic; +use rustc_middle::dep_graph::{DepKind, DepNode}; +use rustc_middle::ty::TyCtxt; +use rustc_span::{Span, Symbol}; +use tracing::debug; + +pub(crate) fn check_rdr_test_attrs(tcx: TyCtxt<'_>) { + // can't add the attributes without opting into this feature + if !tcx.features().rustc_attrs() { + return; + } + for &(span, fields) in + find_attr!(tcx.hir_attrs(rustc_hir::CRATE_HIR_ID), RustcRDRTestAttr(e) => e) + .into_iter() + .flatten() + { + assert_dependency_public_hash(tcx, span, fields); + } +} + +#[derive(Diagnostic)] +#[diag("found rdr hash attribute but `-Zquery-dep-graph` was not specified")] +pub(crate) struct MissingQueryDepGraph { + #[primary_span] + pub span: Span, +} + +/// Scan for a `cfg="foo"` attribute and check whether we have a +/// cfg flag called `foo`. +fn check_config(tcx: TyCtxt<'_>, value: Symbol) -> bool { + let config = &tcx.sess.config; + debug!("check_config(config={:?}, value={:?})", config, value); + if config.iter().any(|&(name, _)| name == value) { + debug!("check_config: matched"); + return true; + } + debug!("check_config: no match found"); + false +} + +fn assert_dependency_public_hash(tcx: TyCtxt<'_>, span: Span, fields: RDRFields) { + if !tcx.sess.opts.unstable_opts.query_dep_graph { + tcx.dcx().emit_fatal(MissingQueryDepGraph { span }); + } + + let crate_num = tcx + .crates(()) + .iter() + .copied() + .find(|&cnum| tcx.crate_name(cnum).as_str() == fields.crate_name.as_str()) + .unwrap_or_else(|| { + tcx.dcx().span_fatal( + span, + format!("crate `{}` not found in dependencies", fields.crate_name), + ) + }); + if crate_num == LOCAL_CRATE { + tcx.dcx().span_fatal(span, "expected the name of a dependency crate"); + } + + if !check_config(tcx, fields.cfg) { + debug!("check_attr: config does not match, ignoring attr"); + return; + } + + let green = !fields.changed; + let dep_node = DepNode::construct(tcx, DepKind::public_api_hash, &crate_num); + let is_green = tcx.dep_graph.is_green(&dep_node); + let is_red = tcx.dep_graph.is_red(&dep_node); + if !is_red && !is_green { + tcx.dcx().span_fatal(span, "dependency color is neither red or green!"); + } + + if green && !is_green { + tcx.dcx().span_fatal( + span, + "expected dependency to be unchanged (green) but it was changed (red)", + ); + } else if !green && is_green { + tcx.dcx().span_fatal( + span, + "expected dependency to have changed (red) but it was unchanged (green)", + ); + } +} diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index ab695dddf06e6..fbe6b7c640d99 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -12,7 +12,7 @@ use tracing::debug; use super::data::*; use super::fs::*; -use super::{clean, file_format, work_product}; +use super::{clean, file_format, rdr_hashes, work_product}; use crate::assert_dep_graph::assert_dep_graph; use crate::errors; @@ -41,6 +41,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { sess.time("assert_dep_graph", || assert_dep_graph(tcx)); sess.time("check_clean", || clean::check_clean_annotations(tcx)); + sess.time("check_rdr_hashes", || rdr_hashes::check_rdr_test_attrs(tcx)); par_join( move || { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 204206c167331..fde13630d22af 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -341,6 +341,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) | AttributeKind::RustcPreserveUbChecks | AttributeKind::RustcProcMacroDecls + | AttributeKind::RustcRDRTestAttr(..) | AttributeKind::RustcReallocator | AttributeKind::RustcRegions | AttributeKind::RustcReservationImpl(..) diff --git a/compiler/rustc_query_system/src/ich/mod.rs b/compiler/rustc_query_system/src/ich/mod.rs new file mode 100644 index 0000000000000..ca36b0ad2318e --- /dev/null +++ b/compiler/rustc_query_system/src/ich/mod.rs @@ -0,0 +1,21 @@ +//! ICH - Incremental Compilation Hash + +use rustc_span::{Symbol, sym}; + +pub use self::hcx::StableHashingContext; + +mod hcx; +mod impls_syntax; + +pub const IGNORED_ATTRIBUTES: &[Symbol] = &[ + sym::cfg_trace, // FIXME should this really be ignored? + sym::rustc_if_this_changed, + sym::rustc_then_this_would_need, + sym::rustc_dirty, + sym::rustc_clean, + sym::rustc_partition_reused, + sym::rustc_partition_codegened, + sym::rustc_expected_cgu_reuse, + sym::rustc_public_hash_unchanged, + sym::rustc_public_hash_changed, +]; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 4cacdbd3408a5..8ca065c575ee8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1794,6 +1794,8 @@ symbols! { rustc_proc_macro_decls, rustc_promotable, rustc_pub_transparent, + rustc_public_hash_changed, + rustc_public_hash_unchanged, rustc_reallocator, rustc_regions, rustc_reservation_impl, From a5d94e26a1901f0221d6fc4abba947864091c972 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 26 Apr 2026 13:05:47 +0200 Subject: [PATCH 11/15] rdr: add incremental test exercising rustc_public_hash_changed and rustc_public_hash_unchanged attributes --- .../public_api_hash_attributes/auxiliary/dep.rs | 14 ++++++++++++++ .../rdr/public_api_hash_attributes/main.rs | 17 +++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/incremental/rdr/public_api_hash_attributes/auxiliary/dep.rs create mode 100644 tests/incremental/rdr/public_api_hash_attributes/main.rs diff --git a/tests/incremental/rdr/public_api_hash_attributes/auxiliary/dep.rs b/tests/incremental/rdr/public_api_hash_attributes/auxiliary/dep.rs new file mode 100644 index 0000000000000..3976be6eac81b --- /dev/null +++ b/tests/incremental/rdr/public_api_hash_attributes/auxiliary/dep.rs @@ -0,0 +1,14 @@ +//@ compile-flags: -Z public-api-hash + +#![crate_name = "dep"] +#![crate_type = "rlib"] + +#[cfg(any(cpass1, cpass2))] +pub fn generic(v: T) { + panic!("{v:?}"); +} + +#[cfg(any(cpass3, bpass4, bfail5))] +pub fn generic(v: T) { + panic!("{v:?}"); +} diff --git a/tests/incremental/rdr/public_api_hash_attributes/main.rs b/tests/incremental/rdr/public_api_hash_attributes/main.rs new file mode 100644 index 0000000000000..7f9a567a42775 --- /dev/null +++ b/tests/incremental/rdr/public_api_hash_attributes/main.rs @@ -0,0 +1,17 @@ +//@ revisions: cpass1 cpass2 cpass3 bpass4 bfail5 +//@ compile-flags: -Z query-dep-graph -Z public-api-hash +//@ aux-build: dep.rs +//@ ignore-backends: gcc + +#![feature(rustc_attrs)] +#![crate_type = "bin"] +#![rustc_public_hash_unchanged(crate_name = "dep", cfg = "cpass2")] +#![rustc_public_hash_changed(crate_name = "dep", cfg = "cpass3")] +#![rustc_public_hash_changed(crate_name = "dep", cfg = "bfail5")] +//[bfail5]~^ ERROR expected dependency to have changed (red) but it was unchanged (green) + +extern crate dep; + +fn main() { + dep::generic::(1); +} From 8d4b5db6a54a00fced694bb456a29ed5fd54c27f Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 29 Apr 2026 09:24:14 +0200 Subject: [PATCH 12/15] rdr: use public_api_hash when hashing dependencies --- .../src/rmeta/encoder/public_api_hasher.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs index 5b9ccdefa0748..3f503158cfc77 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -311,8 +311,16 @@ pub(crate) fn hash_crate_root_public_api( let CrateDep { name, hash, host_hash, kind, extra_filename, is_private } = dep; // FIXME do we need this? hasher.digest(name, hcx); - // FIXME do we need this? - hasher.digest(hash, hcx); + // FIXME hashing the full public_api_hash of all dependencies is too sensitive to change. + // + // One of the major goals of RDR is to allow early cutoff, hashing the public_api_hash of + // all dependencies goes against that. Changing public api in an indirect dependency should + // only cause recompiles in the dependencies actually using them. We should only include + // parts of this hash that this crate somehow reexports, making it available to downstream + // crates without directly depending on the reexported crate. (What "reexports" means here + // needs careful consideration.) + let _ = hash; + hasher.digest(tcx.public_api_hash(id), hcx); // FIXME do we need this? hasher.digest(host_hash, hcx); // FIXME do we need this? From bd4ea044adfe498242e59821605aff297accbc2b Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 29 Apr 2026 09:42:14 +0200 Subject: [PATCH 13/15] rdr: elaborate on how EII-s should be included in the public api hash --- .../rustc_metadata/src/rmeta/encoder/public_api_hasher.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs index 3f503158cfc77..8f32dc4df4930 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -266,7 +266,12 @@ pub(crate) fn hash_crate_root_public_api( // FIXME do we need this? hasher.digest(has_default_lib_allocator, hcx); - // FIXME do we need this? + // Changing externally implementable items should cause recompiles in all downstream crates. + // FIXME EiiDecl and EiiImpl contain spans. Should changing the span of these items cause + // recompiles? + // FIXME eii-s are collected in `rustc_metadata::eii::collect`. We should probably stable sort + // that there to make the query result more stable (but sorting might be useless if this + // should be sensitive to span changes) let _ = externally_implementable_items; hasher.digest(tcx.externally_implementable_items(LOCAL_CRATE), hcx); From dcfff37f536ad90d1249dd69e94e375e3d649c7d Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 29 Apr 2026 10:25:27 +0200 Subject: [PATCH 14/15] rdr: document the process of hashing new fields added to CrateRoot --- .../src/rmeta/encoder/public_api_hasher.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs index 8f32dc4df4930..37d7a3419dae3 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -161,6 +161,34 @@ pub(crate) fn hash_crate_root_public_api( if !hcx.hash_public_api { return; } + // This must be matched exhaustively to help avoid subtle miscompiles when introducing new data + // into rmeta. + // + // When a new field is added to `CrateRoot` the process of handling it in this function + // should be the following: + // 1. stable hash the values/query result serialized into the rmeta. For example if a new field + // named `let my_new_field = self.encode_my_new_field();` is added to `CrateRoot` it + // should be added here as + // ``` + // // FIXME do we need this // or a comment about why we need this + // let _ = my_new_field; + // hasher.digest(tcx.query_my_new_field(), hcx); // or any other code that was that was used + // // to get the encoded value in + // // `encode_my_attribute_list`. We could + // // decode it instead, but that is expensive. + // ``` + // if the field was simple enough, like a bool, which does not need to be encoded + // separately: + // ``` + // // FIXME do we need this // or a comment about why we need this + // hasher.digest(my_new_field); + // ``` + // + // 2. Add the comment "When changed, make sure to update the hashing in + // `hash_crate_root_public_api`" into `encode_my_new_field` + // 3. Only remove/change what is hashed in a separate PR. Removing items just from the hash + // should be done with extreme scrutiny. A better way might be to sort the query result + // in its provider, or filter which values we encode. That also helps with rmeta size. let CrateRoot { header, extra_filename, From 981dee444b94f45b5e40e7709b8fda45dd5ebeda Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 29 Apr 2026 11:07:08 +0200 Subject: [PATCH 15/15] rdr: add comments indicating that public api hashing must be updated when changing some rmeta encoder functions --- compiler/rustc_metadata/src/rmeta/encoder.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index bf9b13ae19462..9f43f59a684c0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -727,6 +727,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { _ = stat!("def-ids", || self.encode_def_ids(hcx)); + // When changed, make sure to update the hashing in `hash_crate_root_public_api` let interpret_alloc_index = stat!("interpret-alloc-index", || { let mut interpret_alloc_index = Vec::new(); let mut n = 0; @@ -767,6 +768,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { stat!("exportable-items", || self.encode_stable_order_of_exportable_impls()); // Encode exported symbols info. This is prefetched in `encode_metadata`. + // When changed, make sure to update the hashing in `hash_crate_root_public_api` let (exported_non_generic_symbols, exported_generic_symbols) = stat!("exported-symbols", || { ( @@ -790,7 +792,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // `SourceFiles` we actually need to encode. let source_map = stat!("source-map", || self.encode_source_map(hcx, &mut public_api_hasher)); + // When changed, make sure to update the hashing in `hash_crate_root_public_api` let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers()); + // When changed, make sure to update the hashing in `hash_crate_root_public_api` let denied_partial_mitigations = stat!("denied-partial-mitigations", || self .encode_enabled_denied_partial_mitigations()); @@ -1759,6 +1763,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_externally_implementable_items(&mut self) -> LazyArray { empty_proc_macro!(self); let externally_implementable_items = self.tcx.externally_implementable_items(LOCAL_CRATE); @@ -2080,12 +2085,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record!(self.tables.macro_definition[def_id.to_def_id()] <- &*macro_def.body, hcx); } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_native_libraries(&mut self) -> LazyArray { empty_proc_macro!(self); let used_libraries = self.tcx.native_libraries(LOCAL_CRATE); self.lazy_array(used_libraries.iter()) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_foreign_modules(&mut self) -> LazyArray { empty_proc_macro!(self); let foreign_modules = self.tcx.foreign_modules(LOCAL_CRATE); @@ -2223,6 +2230,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_debugger_visualizers(&mut self) -> LazyArray { empty_proc_macro!(self); self.lazy_array( @@ -2237,6 +2245,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_crate_deps(&mut self) -> LazyArray { empty_proc_macro!(self); @@ -2277,6 +2286,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(lib_features.to_sorted_vec()) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> { empty_proc_macro!(self); let tcx = self.tcx; @@ -2285,6 +2295,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(sorted.into_iter().map(|(k, v)| (*k, *v))) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> { empty_proc_macro!(self); let tcx = self.tcx; @@ -2292,6 +2303,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(diagnostic_items.iter().map(|(&name, def_id)| (name, def_id.index))) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_lang_items(&mut self) -> LazyArray<(DefIndex, LangItem)> { empty_proc_macro!(self); let lang_items = self.tcx.lang_items().iter(); @@ -2300,12 +2312,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { })) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_lang_items_missing(&mut self) -> LazyArray { empty_proc_macro!(self); let tcx = self.tcx; self.lazy_array(&tcx.lang_items().missing) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_stripped_cfg_items(&mut self) -> LazyArray> { self.lazy_array( self.tcx @@ -2315,12 +2329,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_traits(&mut self) -> LazyArray { empty_proc_macro!(self); self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index)) } /// Encodes an index, mapping each trait to its (local) implementations. + // When changed, make sure to update the hashing in `hash_crate_root_public_api` #[instrument(level = "debug", skip(self, hcx))] fn encode_impls( &mut self, @@ -2391,6 +2407,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_incoherent_impls(&mut self) -> LazyArray { empty_proc_macro!(self); let tcx = self.tcx; @@ -2409,11 +2426,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(&all_impls) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_exportable_items(&mut self) -> LazyArray { empty_proc_macro!(self); self.lazy_array(self.tcx.exportable_items(LOCAL_CRATE).iter().map(|def_id| def_id.index)) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_stable_order_of_exportable_impls(&mut self) -> LazyArray<(DefIndex, usize)> { empty_proc_macro!(self); let stable_order_of_exportable_impls = @@ -2438,6 +2457,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.lazy_array(exported_symbols.iter().cloned()) } + // When changed, make sure to update the hashing in `hash_crate_root_public_api` fn encode_dylib_dependency_formats(&mut self) -> LazyArray> { empty_proc_macro!(self); if let Some(arr) = dylib_dependency_formats(self.tcx) {