From 7db12409a8efe1fc9b2e4d387c774b4b45aba328 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 8 Apr 2026 17:51:50 +0200 Subject: [PATCH 01/50] 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 a0db004b7f4c4..d9efdc4293eda 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 a3645a5556bf3..2045cab3f7959 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 aac2ec224407c6de79775e7d0c632a67d0f5fd32 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 20 Apr 2026 19:13:12 +0200 Subject: [PATCH 02/50] 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 51b2635a4188e..95ca52b5e0da4 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 2e274d5cd9b3810c520aac4ca81a6e9ca72bb947 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 20 Apr 2026 12:36:29 +0200 Subject: [PATCH 03/50] 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 95ca52b5e0da4..bd9365015f7a4 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, StableHash}; 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, StableHash)] 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, StableHash)] 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, StableHash)] pub enum $tmod_enum { $( $( $tmod_variant, )? From ab1fd6cf9061129193d78e09b949143843509ffb Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 27 Apr 2026 10:16:19 +0200 Subject: [PATCH 04/50] 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..44b8db32ad021 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, StableHash}; 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, StableHash)] 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, StableHash)] 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, StableHash)] pub struct DeniedPartialMitigation { pub kind: DeniedPartialMitigationKind, pub level: DeniedPartialMitigationLevel, From b3c3fdad6e6745d648c2be0b0ef4d5f7d21ccc90 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 7 May 2026 10:23:32 +0200 Subject: [PATCH 05/50] implement StableHash for TargetTuple --- compiler/rustc_target/src/spec/mod.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b1a9c4b3cde6a..0a9bcb528ed66 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -51,6 +51,7 @@ use rustc_abi::{ TargetDataLayoutError, }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use rustc_error_messages::{DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; use rustc_fs_util::try_canonicalize; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; @@ -3932,6 +3933,22 @@ impl Hash for TargetTuple { } } +impl StableHash for TargetTuple { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + match self { + TargetTuple::TargetTuple(tuple) => { + 0u8.stable_hash(hcx, hasher); + tuple.stable_hash(hcx, hasher) + } + TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { + 1u8.stable_hash(hcx, hasher); + tuple.stable_hash(hcx, hasher); + contents.stable_hash(hcx, hasher) + } + } + } +} + // Use a manual implementation to prevent encoding the target json file path in the crate metadata impl Encodable for TargetTuple { fn encode(&self, s: &mut S) { From c46e842f8b5791a69f55daea11bab52ed33771c0 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 8 May 2026 15:09:39 +0200 Subject: [PATCH 06/50] tidy: move TargetTuple to a new file --- compiler/rustc_target/src/spec/mod.rs | 164 +----------------- .../rustc_target/src/spec/target_tuple.rs | 163 +++++++++++++++++ 2 files changed, 167 insertions(+), 160 deletions(-) create mode 100644 compiler/rustc_target/src/spec/target_tuple.rs diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 0a9bcb528ed66..321781e29f840 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -40,22 +40,19 @@ use core::result::Result; use std::borrow::Cow; use std::collections::BTreeMap; -use std::hash::{Hash, Hasher}; +use std::fmt; +use std::hash::Hash; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::{fmt, io}; use rustc_abi::{ Align, CVariadicStatus, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutError, }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use rustc_error_messages::{DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; -use rustc_fs_util::try_canonicalize; use rustc_macros::{BlobDecodable, Decodable, Encodable, StableHash}; -use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_span::{Symbol, kw, sym}; use serde_json::Value; use tracing::debug; @@ -68,11 +65,13 @@ pub mod crt_objects; mod abi_map; mod base; mod json; +mod target_tuple; pub use abi_map::{AbiMap, AbiMapping}; pub use base::apple; pub use base::avr::ef_avr_arch; pub use json::json_schema; +pub use target_tuple::TargetTuple; /// Linker is called through a C/C++ compiler. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -3888,158 +3887,3 @@ impl Target { Symbol::intern(&self.vendor) } } - -/// Either a target tuple string or a path to a JSON file. -#[derive(Clone, Debug)] -pub enum TargetTuple { - TargetTuple(String), - TargetJson { - /// Warning: This field may only be used by rustdoc. Using it anywhere else will lead to - /// inconsistencies as it is discarded during serialization. - path_for_rustdoc: PathBuf, - tuple: String, - contents: String, - }, -} - -// Use a manual implementation to ignore the path field -impl PartialEq for TargetTuple { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::TargetTuple(l0), Self::TargetTuple(r0)) => l0 == r0, - ( - Self::TargetJson { path_for_rustdoc: _, tuple: l_tuple, contents: l_contents }, - Self::TargetJson { path_for_rustdoc: _, tuple: r_tuple, contents: r_contents }, - ) => l_tuple == r_tuple && l_contents == r_contents, - _ => false, - } - } -} - -// Use a manual implementation to ignore the path field -impl Hash for TargetTuple { - fn hash(&self, state: &mut H) -> () { - match self { - TargetTuple::TargetTuple(tuple) => { - 0u8.hash(state); - tuple.hash(state) - } - TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { - 1u8.hash(state); - tuple.hash(state); - contents.hash(state) - } - } - } -} - -impl StableHash for TargetTuple { - fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { - match self { - TargetTuple::TargetTuple(tuple) => { - 0u8.stable_hash(hcx, hasher); - tuple.stable_hash(hcx, hasher) - } - TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { - 1u8.stable_hash(hcx, hasher); - tuple.stable_hash(hcx, hasher); - contents.stable_hash(hcx, hasher) - } - } - } -} - -// Use a manual implementation to prevent encoding the target json file path in the crate metadata -impl Encodable for TargetTuple { - fn encode(&self, s: &mut S) { - match self { - TargetTuple::TargetTuple(tuple) => { - s.emit_u8(0); - s.emit_str(tuple); - } - TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { - s.emit_u8(1); - s.emit_str(tuple); - s.emit_str(contents); - } - } - } -} - -impl Decodable for TargetTuple { - fn decode(d: &mut D) -> Self { - match d.read_u8() { - 0 => TargetTuple::TargetTuple(d.read_str().to_owned()), - 1 => TargetTuple::TargetJson { - path_for_rustdoc: PathBuf::new(), - tuple: d.read_str().to_owned(), - contents: d.read_str().to_owned(), - }, - _ => { - panic!("invalid enum variant tag while decoding `TargetTuple`, expected 0..2"); - } - } - } -} - -impl TargetTuple { - /// Creates a target tuple from the passed target tuple string. - pub fn from_tuple(tuple: &str) -> Self { - TargetTuple::TargetTuple(tuple.into()) - } - - /// Creates a target tuple from the passed target path. - pub fn from_path(path: &Path) -> Result { - let canonicalized_path = try_canonicalize(path)?; - let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { - io::Error::new( - io::ErrorKind::InvalidInput, - format!("target path {canonicalized_path:?} is not a valid file: {err}"), - ) - })?; - let tuple = canonicalized_path - .file_stem() - .expect("target path must not be empty") - .to_str() - .expect("target path must be valid unicode") - .to_owned(); - Ok(TargetTuple::TargetJson { path_for_rustdoc: canonicalized_path, tuple, contents }) - } - - /// Returns a string tuple for this target. - /// - /// If this target is a path, the file name (without extension) is returned. - pub fn tuple(&self) -> &str { - match *self { - TargetTuple::TargetTuple(ref tuple) | TargetTuple::TargetJson { ref tuple, .. } => { - tuple - } - } - } - - /// Returns an extended string tuple for this target. - /// - /// If this target is a path, a hash of the path is appended to the tuple returned - /// by `tuple()`. - pub fn debug_tuple(&self) -> String { - use std::hash::DefaultHasher; - - match self { - TargetTuple::TargetTuple(tuple) => tuple.to_owned(), - TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents: content } => { - let mut hasher = DefaultHasher::new(); - content.hash(&mut hasher); - let hash = hasher.finish(); - format!("{tuple}-{hash}") - } - } - } -} - -impl fmt::Display for TargetTuple { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.debug_tuple()) - } -} - -into_diag_arg_using_display!(&TargetTuple); diff --git a/compiler/rustc_target/src/spec/target_tuple.rs b/compiler/rustc_target/src/spec/target_tuple.rs new file mode 100644 index 0000000000000..6a6116a3e92ba --- /dev/null +++ b/compiler/rustc_target/src/spec/target_tuple.rs @@ -0,0 +1,163 @@ +use std::hash::{Hash, Hasher}; +use std::path::{Path, PathBuf}; +use std::{fmt, io}; + +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; +use rustc_error_messages::into_diag_arg_using_display; +use rustc_fs_util::try_canonicalize; +use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; + +/// Either a target tuple string or a path to a JSON file. +#[derive(Clone, Debug)] +pub enum TargetTuple { + TargetTuple(String), + TargetJson { + /// Warning: This field may only be used by rustdoc. Using it anywhere else will lead to + /// inconsistencies as it is discarded during serialization. + path_for_rustdoc: PathBuf, + tuple: String, + contents: String, + }, +} + +// Use a manual implementation to ignore the path field +impl PartialEq for TargetTuple { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::TargetTuple(l0), Self::TargetTuple(r0)) => l0 == r0, + ( + Self::TargetJson { path_for_rustdoc: _, tuple: l_tuple, contents: l_contents }, + Self::TargetJson { path_for_rustdoc: _, tuple: r_tuple, contents: r_contents }, + ) => l_tuple == r_tuple && l_contents == r_contents, + _ => false, + } + } +} + +// Use a manual implementation to ignore the path field +impl Hash for TargetTuple { + fn hash(&self, state: &mut H) -> () { + match self { + TargetTuple::TargetTuple(tuple) => { + 0u8.hash(state); + tuple.hash(state) + } + TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { + 1u8.hash(state); + tuple.hash(state); + contents.hash(state) + } + } + } +} + +impl StableHash for TargetTuple { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + match self { + TargetTuple::TargetTuple(tuple) => { + 0u8.stable_hash(hcx, hasher); + tuple.stable_hash(hcx, hasher) + } + TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { + 1u8.stable_hash(hcx, hasher); + tuple.stable_hash(hcx, hasher); + contents.stable_hash(hcx, hasher) + } + } + } +} + +// Use a manual implementation to prevent encoding the target json file path in the crate metadata +impl Encodable for TargetTuple { + fn encode(&self, s: &mut S) { + match self { + TargetTuple::TargetTuple(tuple) => { + s.emit_u8(0); + s.emit_str(tuple); + } + TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents } => { + s.emit_u8(1); + s.emit_str(tuple); + s.emit_str(contents); + } + } + } +} + +impl Decodable for TargetTuple { + fn decode(d: &mut D) -> Self { + match d.read_u8() { + 0 => TargetTuple::TargetTuple(d.read_str().to_owned()), + 1 => TargetTuple::TargetJson { + path_for_rustdoc: PathBuf::new(), + tuple: d.read_str().to_owned(), + contents: d.read_str().to_owned(), + }, + _ => { + panic!("invalid enum variant tag while decoding `TargetTuple`, expected 0..2"); + } + } + } +} + +impl TargetTuple { + /// Creates a target tuple from the passed target tuple string. + pub fn from_tuple(tuple: &str) -> Self { + TargetTuple::TargetTuple(tuple.into()) + } + + /// Creates a target tuple from the passed target path. + pub fn from_path(path: &Path) -> Result { + let canonicalized_path = try_canonicalize(path)?; + let contents = std::fs::read_to_string(&canonicalized_path).map_err(|err| { + io::Error::new( + io::ErrorKind::InvalidInput, + format!("target path {canonicalized_path:?} is not a valid file: {err}"), + ) + })?; + let tuple = canonicalized_path + .file_stem() + .expect("target path must not be empty") + .to_str() + .expect("target path must be valid unicode") + .to_owned(); + Ok(TargetTuple::TargetJson { path_for_rustdoc: canonicalized_path, tuple, contents }) + } + + /// Returns a string tuple for this target. + /// + /// If this target is a path, the file name (without extension) is returned. + pub fn tuple(&self) -> &str { + match *self { + TargetTuple::TargetTuple(ref tuple) | TargetTuple::TargetJson { ref tuple, .. } => { + tuple + } + } + } + + /// Returns an extended string tuple for this target. + /// + /// If this target is a path, a hash of the path is appended to the tuple returned + /// by `tuple()`. + pub fn debug_tuple(&self) -> String { + use std::hash::DefaultHasher; + + match self { + TargetTuple::TargetTuple(tuple) => tuple.to_owned(), + TargetTuple::TargetJson { path_for_rustdoc: _, tuple, contents: content } => { + let mut hasher = DefaultHasher::new(); + content.hash(&mut hasher); + let hash = hasher.finish(); + format!("{tuple}-{hash}") + } + } + } +} + +impl fmt::Display for TargetTuple { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.debug_tuple()) + } +} + +into_diag_arg_using_display!(&TargetTuple); From 346cfd4418cd1aae481bc414f3ffcac24f4a4f76 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 27 Apr 2026 10:19:03 +0200 Subject: [PATCH 07/50] rdr: implement rmeta public api hash as the stable hash of (almost) all encoded data --- compiler/rustc_metadata/src/rmeta/encoder.rs | 1076 +++++++++++------ .../src/rmeta/encoder/public_api_hasher.rs | 396 ++++++ compiler/rustc_metadata/src/rmeta/mod.rs | 207 ++-- compiler/rustc_metadata/src/rmeta/table.rs | 127 +- 4 files changed, 1315 insertions(+), 491 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 d9efdc4293eda..53c5212706840 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,16 @@ use rustc_span::{ }; use tracing::{debug, instrument, trace}; +use self::public_api_hasher::{ + HashableCrateHeader, HashableCrateRoot, Hashed, NoneIfHashed, 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 +82,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 +400,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,25 +420,62 @@ 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, + ); } }}; } +macro_rules! hashed_lazy_array { + ($self:ident, $values:expr, $hcx:ident, $encode_map:expr) => {{ + { + let mut hasher = PublicApiHasher::default(); + let array = $self.lazy_array($values.into_iter().map(|v| { + hasher.digest(&v, $hcx); + $encode_map(v) + })); + Hashed { value: array, hash: hasher.finish($hcx) } + } + }}; + ($self:ident, $values:expr, $hcx:ident) => { + hashed_lazy_array!($self, $values, $hcx, |v| v) + }; + ($self:ident, $values:expr, $hcx:ident,) => { + hashed_lazy_array!($self, $values, $hcx, |v| v) + }; +} + impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn emit_lazy_distance(&mut self, position: NonZero) { let pos = position.get(); @@ -510,6 +562,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,23 +570,39 @@ 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()); } } } - fn encode_def_path_hash_map(&mut self) -> LazyValue> { - self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())) - } - - fn encode_source_map(&mut self) -> LazyTable>> { + fn encode_def_path_hash_map( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed>> { + let value = self + .lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())); + // an ordered hash of all local defids encapsulates all information contained in a reverse + // mapping as well. + let mut hasher = PublicApiHasher::default(); + hasher.digest_iter(self.tcx.iter_local_def_id(), hcx); + Hashed { hash: hasher.finish(hcx), value } + } + + fn encode_source_map( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed>>> { let source_map = self.tcx.sess.source_map(); let all_source_files = source_map.files(); @@ -542,7 +611,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 +664,45 @@ 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.encode(&mut self.opaque) + adapted.encode(&mut self.opaque, hcx) } - fn encode_crate_root(&mut self) -> LazyValue { + fn encode_crate_root(&mut self, hcx: &mut PublicApiHashingContext<'_>) -> LazyValue { let tcx = self.tcx; let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); @@ -618,45 +719,49 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { stats.push(("preamble", self.position())); let externally_implementable_items = stat!("externally-implementable-items", || self - .encode_externally_implementable_items()); + .encode_externally_implementable_items(hcx)); - let (crate_deps, dylib_dependency_formats) = - stat!("dep", || (self.encode_crate_deps(), self.encode_dylib_dependency_formats())); + let (crate_deps, dylib_dependency_formats) = stat!("dep", || ( + self.encode_crate_deps(hcx), + self.encode_dylib_dependency_formats(hcx) + )); - let lib_features = stat!("lib-features", || self.encode_lib_features()); + let lib_features = stat!("lib-features", || self.encode_lib_features(hcx)); let stability_implications = - stat!("stability-implications", || self.encode_stability_implications()); + stat!("stability-implications", || self.encode_stability_implications(hcx)); let (lang_items, lang_items_missing) = stat!("lang-items", || { - (self.encode_lang_items(), self.encode_lang_items_missing()) + (self.encode_lang_items(hcx), self.encode_lang_items_missing(hcx)) }); - let stripped_cfg_items = stat!("stripped-cfg-items", || self.encode_stripped_cfg_items()); + let stripped_cfg_items = + stat!("stripped-cfg-items", || self.encode_stripped_cfg_items(hcx)); - let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items()); + let diagnostic_items = stat!("diagnostic-items", || self.encode_diagnostic_items(hcx)); - let native_libraries = stat!("native-libs", || self.encode_native_libraries()); + let native_libraries = stat!("native-libs", || self.encode_native_libraries(hcx)); - let foreign_modules = stat!("foreign-modules", || self.encode_foreign_modules()); + let foreign_modules = stat!("foreign-modules", || self.encode_foreign_modules(hcx)); _ = stat!("def-path-table", || self.encode_def_path_table()); // Encode the def IDs of traits, for rustdoc and diagnostics. - let traits = stat!("traits", || self.encode_traits()); + let traits = stat!("traits", || self.encode_traits(hcx)); // Encode the def IDs of impls, for coherence checking. - let impls = stat!("impls", || self.encode_impls()); + let impls = stat!("impls", || self.encode_impls(hcx)); - let incoherent_impls = stat!("incoherent-impls", || self.encode_incoherent_impls()); + let incoherent_impls = stat!("incoherent-impls", || self.encode_incoherent_impls(hcx)); - _ = 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(); let mut n = 0; + let mut hasher = PublicApiHasher::default(); trace!("beginning to encode alloc ids"); loop { let new_n = self.interpret_allocs.len(); @@ -670,34 +775,38 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let id = self.interpret_allocs[idx]; let pos = self.position() as u64; interpret_alloc_index.push(pos); + hasher.digest(tcx.global_alloc(id), hcx); interpret::specialized_encode_alloc_id(self, tcx, id); } n = new_n; } - self.lazy_array(interpret_alloc_index) + Hashed { value: self.lazy_array(interpret_alloc_index), hash: hasher.finish(hcx) } }); // 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)); - let tables = stat!("tables", || self.tables.encode(&mut self.opaque)); + let tables = stat!("tables", || self.tables.encode(&mut self.opaque, hcx)); let debugger_visualizers = - stat!("debugger-visualizers", || self.encode_debugger_visualizers()); + stat!("debugger-visualizers", || self.encode_debugger_visualizers(hcx)); - let exportable_items = stat!("exportable-items", || self.encode_exportable_items()); + let exportable_items = stat!("exportable-items", || self.encode_exportable_items(hcx)); let stable_order_of_exportable_impls = - stat!("exportable-items", || self.encode_stable_order_of_exportable_impls()); + stat!("exportable-items", || self.encode_stable_order_of_exportable_impls(hcx)); // Encode exported symbols info. This is prefetched in `encode_metadata`. let (exported_non_generic_symbols, exported_generic_symbols) = stat!("exported-symbols", || { ( - self.encode_exported_symbols(tcx.exported_non_generic_symbols(LOCAL_CRATE)), - self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE)), + self.encode_exported_symbols( + tcx.exported_non_generic_symbols(LOCAL_CRATE), + hcx, + ), + self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE), hcx), ) }); @@ -707,77 +816,77 @@ 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)); - let def_path_hash_map = stat!("def-path-hash-map", || self.encode_def_path_hash_map()); + let def_path_hash_map = stat!("def-path-hash-map", || self.encode_def_path_hash_map(hcx)); // 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 target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers()); + let source_map = stat!("source-map", || self.encode_source_map(hcx)); + let target_modifiers = stat!("target-modifiers", || self.encode_target_modifiers(hcx)); 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) }, - }) - }); + .encode_enabled_denied_partial_mitigations(hcx)); + + let attrs = tcx.hir_krate_attrs(); + let crate_root = HashableCrateRoot { + header: HashableCrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + 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: NoneIfHashed { value: 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), + }; + let crate_root = crate_root.into_crate_root(self.tcx, hcx); + + let root = stat!("final", || { self.lazy(crate_root) }); let total_bytes = self.position(); @@ -1394,7 +1503,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 +1514,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 +1542,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,226 +1575,249 @@ 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.collect_return_position_impl_trait_in_trait_tys[def_id] <- table); + record!(self.tables.collect_return_position_impl_trait_in_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); } } - fn encode_externally_implementable_items(&mut self) -> LazyArray { + fn encode_externally_implementable_items( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let externally_implementable_items = self.tcx.externally_implementable_items(LOCAL_CRATE); - self.lazy_array(externally_implementable_items.iter().map( - |(foreign_item, (decl, impls))| { + hashed_lazy_array!( + self, + externally_implementable_items.iter().map(|(foreign_item, (decl, impls))| { ( *foreign_item, (decl.clone(), impls.iter().map(|(impl_did, i)| (*impl_did, *i)).collect()), ) - }, - )) + },), + hcx + ) } - #[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 +1832,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 +1879,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 +1896,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 +1986,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,99 +2048,128 @@ 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 { + fn encode_native_libraries( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let used_libraries = self.tcx.native_libraries(LOCAL_CRATE); - self.lazy_array(used_libraries.iter()) + hashed_lazy_array!(self, used_libraries, hcx) } - fn encode_foreign_modules(&mut self) -> LazyArray { + fn encode_foreign_modules( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let foreign_modules = self.tcx.foreign_modules(LOCAL_CRATE); - self.lazy_array(foreign_modules.iter().map(|(_, m)| m).cloned()) + hashed_lazy_array!(self, foreign_modules.iter().map(|(_, m)| m), hcx) } - 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<'_>, + ) -> (Hashed, Hashed, Hashed) { + 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.encode(&mut self.opaque), - expn_data_table.encode(&mut self.opaque), - expn_hash_table.encode(&mut self.opaque), + syntax_contexts.encode(&mut self.opaque, hcx), + expn_data_table.encode(&mut self.opaque, hcx), + expn_hash_table.encode(&mut self.opaque, hcx), ) } - 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 +2178,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 +2226,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); } } @@ -2052,9 +2252,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - fn encode_debugger_visualizers(&mut self) -> LazyArray { + fn encode_debugger_visualizers( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); - self.lazy_array( + + hashed_lazy_array!( + self, self.tcx .debugger_visualizers(LOCAL_CRATE) .iter() @@ -2063,28 +2268,17 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // The path is only needed for the local crate because of // `--emit dep-info`. .map(DebuggerVisualizerFile::path_erased), + hcx, ) } - fn encode_crate_deps(&mut self) -> LazyArray { + fn encode_crate_deps( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { 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 @@ -2099,77 +2293,120 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the assumption that they are numbered 1 to n. // FIXME (#2166): This is not nearly enough to support correct versioning // but is enough to get transitive crate dependencies working. - self.lazy_array(deps.iter().map(|(_, dep)| dep)) + hashed_lazy_array!(self, deps.iter().map(|(_, dep)| dep), hcx) } - fn encode_target_modifiers(&mut self) -> LazyArray { + fn encode_target_modifiers( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; - self.lazy_array(tcx.sess.opts.gather_target_modifiers()) + hashed_lazy_array!(self, tcx.sess.opts.gather_target_modifiers(), hcx) } - fn encode_enabled_denied_partial_mitigations(&mut self) -> LazyArray { + fn encode_enabled_denied_partial_mitigations( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; - self.lazy_array(tcx.sess.gather_enabled_denied_partial_mitigations()) + hashed_lazy_array!(self, tcx.sess.gather_enabled_denied_partial_mitigations(), hcx) } - fn encode_lib_features(&mut self) -> LazyArray<(Symbol, FeatureStability)> { + fn encode_lib_features( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; let lib_features = tcx.lib_features(LOCAL_CRATE); - self.lazy_array(lib_features.to_sorted_vec()) + hashed_lazy_array!(self, lib_features.to_sorted_vec(), hcx) } - fn encode_stability_implications(&mut self) -> LazyArray<(Symbol, Symbol)> { + fn encode_stability_implications( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; let implications = tcx.stability_implications(LOCAL_CRATE); let sorted = implications.to_sorted_stable_ord(); - self.lazy_array(sorted.into_iter().map(|(k, v)| (*k, *v))) + hashed_lazy_array!(self, sorted.into_iter().map(|(k, v)| (*k, *v)), hcx) } - fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> { + fn encode_diagnostic_items( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; let diagnostic_items = &tcx.diagnostic_items(LOCAL_CRATE).name_to_id; - self.lazy_array(diagnostic_items.iter().map(|(&name, def_id)| (name, def_id.index))) + hashed_lazy_array!( + self, + diagnostic_items.iter().map(|(name, id)| (*name, *id)), + hcx, + |(name, def_id): (Symbol, DefId)| (name, def_id.index) + ) } - fn encode_lang_items(&mut self) -> LazyArray<(DefIndex, LangItem)> { + fn encode_lang_items( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let lang_items = self.tcx.lang_items().iter(); - self.lazy_array(lang_items.filter_map(|(lang_item, def_id)| { - def_id.as_local().map(|id| (id.local_def_index, lang_item)) - })) + hashed_lazy_array!( + self, + lang_items.filter(|(_lang_item, def_id)| { def_id.is_local() }), + hcx, + |(lang_item, id): (LangItem, DefId)| (id.index, lang_item) + ) } - fn encode_lang_items_missing(&mut self) -> LazyArray { + fn encode_lang_items_missing( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; - self.lazy_array(&tcx.lang_items().missing) + hashed_lazy_array!(self, &tcx.lang_items().missing, hcx) } - fn encode_stripped_cfg_items(&mut self) -> LazyArray> { - self.lazy_array( - self.tcx - .stripped_cfg_items(LOCAL_CRATE) - .into_iter() - .map(|item| item.clone().map_scope_id(|def_id| def_id.index)), + fn encode_stripped_cfg_items( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed>> { + hashed_lazy_array!( + self, + self.tcx.stripped_cfg_items(LOCAL_CRATE), + hcx, + |item: &StrippedCfgItem| item.clone().map_scope_id(|def_id| def_id.index) ) } - fn encode_traits(&mut self) -> LazyArray { + fn encode_traits( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); - self.lazy_array(self.tcx.traits(LOCAL_CRATE).iter().map(|def_id| def_id.index)) + hashed_lazy_array!( + self, + self.tcx.traits(LOCAL_CRATE).iter().copied(), + hcx, + |def_id: DefId| def_id.index + ) } /// 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<'_>, + ) -> Hashed> { 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 +2417,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,68 +2431,102 @@ 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)); + .push((id.owner_id.def_id, simplified_self_ty)); let trait_def = tcx.trait_def(trait_ref.def_id); 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() - .map(|(trait_def_id, impls)| TraitImpls { - trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index), - impls: self.lazy_array(&impls), + let mut hasher = PublicApiHasher::default(); + let trait_impls: Vec<_> = trait_impls_map + .iter() + .map(|(trait_def_id, impls)| { + hasher.digest(trait_def_id, hcx); + hasher.digest(impls, hcx); + TraitImpls { + trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index), + impls: self.lazy_array(impls.iter().map(|(id, ty)| (id.local_def_index, *ty))), + } }) .collect(); - self.lazy_array(&trait_impls) + Hashed { value: self.lazy_array(trait_impls), hash: hasher.finish(hcx) } } - #[instrument(level = "debug", skip(self))] - fn encode_incoherent_impls(&mut self) -> LazyArray { + #[instrument(level = "debug", skip(self, hcx))] + fn encode_incoherent_impls( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let tcx = self.tcx; + let mut hasher = PublicApiHasher::default(); let all_impls: Vec<_> = tcx .crate_inherent_impls(()) .0 .incoherent_impls .iter() .map(|(&simp, impls)| IncoherentImpls { - self_ty: self.lazy(simp), - impls: self.lazy_array(impls.iter().map(|def_id| def_id.local_def_index)), + self_ty: self.lazy({ + hasher.digest(simp, hcx); + simp + }), + impls: self.lazy_array({ + hasher.digest(impls, hcx); + impls.iter().map(|def_id| def_id.local_def_index) + }), }) .collect(); - self.lazy_array(&all_impls) + Hashed { value: self.lazy_array(&all_impls), hash: hasher.finish(hcx) } } - fn encode_exportable_items(&mut self) -> LazyArray { + fn encode_exportable_items( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); - self.lazy_array(self.tcx.exportable_items(LOCAL_CRATE).iter().map(|def_id| def_id.index)) + hashed_lazy_array!( + self, + self.tcx.exportable_items(LOCAL_CRATE).iter().copied(), + hcx, + |def_id: DefId| { def_id.index } + ) } - fn encode_stable_order_of_exportable_impls(&mut self) -> LazyArray<(DefIndex, usize)> { + fn encode_stable_order_of_exportable_impls( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { empty_proc_macro!(self); let stable_order_of_exportable_impls = self.tcx.stable_order_of_exportable_impls(LOCAL_CRATE); - self.lazy_array( - stable_order_of_exportable_impls.iter().map(|(def_id, idx)| (def_id.index, *idx)), + hashed_lazy_array!( + self, + stable_order_of_exportable_impls.iter().map(|(id, idx)| (*id, *idx)), + hcx, + |(def_id, idx): (DefId, usize)| (def_id.index, idx) ) } @@ -2264,26 +2539,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_exported_symbols( &mut self, exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)], - ) -> LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)> { + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed, SymbolExportInfo)>> { empty_proc_macro!(self); - self.lazy_array(exported_symbols.iter().cloned()) + hashed_lazy_array!(self, exported_symbols, hcx) } - fn encode_dylib_dependency_formats(&mut self) -> LazyArray> { + fn encode_dylib_dependency_formats( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed>> { 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), - }, - )); - } - LazyArray::default() + let arr = dylib_dependency_formats(self.tcx).into_iter().flatten(); + hashed_lazy_array!(self, arr.map(|(_id, slot)| slot), hcx) } } @@ -2485,21 +2754,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 +2965,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..7e65e5c8f9f5f --- /dev/null +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -0,0 +1,396 @@ +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; +use rustc_data_structures::svh::Svh; +use rustc_hir::LangItem; +use rustc_hir::attrs::StrippedCfgItem; +use rustc_hir::def_id::DefIndex; +use rustc_index::Idx; +use rustc_macros::StableHash; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use rustc_middle::middle::lib_features::FeatureStability; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; +use rustc_session::config::{SymbolManglingVersion, TargetModifier}; +use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; +use rustc_span::Symbol; +use rustc_span::def_id::{LOCAL_CRATE, StableCrateId}; +use rustc_span::edition::Edition; +use rustc_target::spec::PanicStrategy; + +use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; +use crate::rmeta::{ + DefPathHashMapRef, EiiMapEncodedKeyValue, ExpnDataTable, ExpnHashTable, IncoherentImpls, + LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, RDRHashes, SyntaxContextTable, + TraitImpls, +}; + +#[derive(Default)] +pub(crate) struct PublicApiHasher(StableHasher); + +impl PublicApiHasher { + pub(crate) fn finish(self, hcx: &mut PublicApiHashingContext<'_>) -> Option { + hcx.hash_public_api.then(|| self.0.finish()) + } + + pub(crate) fn digest<'a, T: StableHash>( + &mut self, + value: T, + hcx: &mut PublicApiHashingContext<'a>, + ) { + if hcx.hash_public_api { + value.stable_hash(&mut hcx.hcx, &mut self.0); + } + } + pub(crate) fn digest_iter<'a, I>(&mut self, values: I, hcx: &mut PublicApiHashingContext<'a>) + where + I: IntoIterator, + I::Item: StableHash, + { + if hcx.hash_public_api { + for value in values { + self.digest(value, hcx); + } + } + } +} + +pub(crate) trait TablePublicApiHasher: Default { + type IterHasher; + + fn digest(&mut self, index: I, value: V, hcx: &mut PublicApiHashingContext<'_>) + where + V: StableHash; + fn finish(&self, hcx: &mut PublicApiHashingContext<'_>) -> 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(&mut self, index: I, value: V, hcx: &mut PublicApiHashingContext<'_>) + where + V: StableHash, + { + 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).stable_hash(&mut hcx.hcx, &mut hasher); + let hash: Fingerprint = hasher.finish(); + self.hash = self.hash.combine_commutative(hash); + } + + fn finish(&self, hcx: &mut PublicApiHashingContext<'_>) -> Option { + hcx.hash_public_api.then_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: StableHash, + { + iter.into_iter().inspect(move |item: &I::Item| { + if hcx.hash_public_api { + item.stable_hash(&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(&mut self, _index: I, _value: V, _hcx: &mut PublicApiHashingContext<'_>) + where + V: StableHash, + { + } + + fn iter_hasher(&self) -> Self::IterHasher { + Default::default() + } + + fn finish(&self, hcx: &mut PublicApiHashingContext<'_>) -> Option { + hcx.hash_public_api.then_some(Fingerprint::ZERO) + } +} + +#[derive(Default)] +pub(crate) struct Hashed { + pub(crate) hash: Option, + pub(crate) value: T, +} + +impl StableHash for Hashed { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + self.hash.expect("Hash must be present when stable hashing!").stable_hash(hcx, hasher); + } +} + +pub(crate) struct NoneIfHashed { + pub(super) value: Option, +} + +impl StableHash for NoneIfHashed { + fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { + assert!(self.value.is_none()); + 0u32.stable_hash(hcx, hasher); + } +} + +#[derive(StableHash)] +pub(crate) struct HashableCrateHeader { + // FIXME do we need to hash this? + pub(crate) triple: TargetTuple, + // FIXME do we need to hash this? + pub(crate) name: Symbol, + // FIXME do we need to hash this? + pub(crate) is_proc_macro_crate: bool, + // FIXME do we need to hash this? + pub(crate) is_stub: bool, +} + +#[derive(StableHash)] +pub(crate) struct HashableCrateRoot { + // FIXME do we need to hash this? + pub(crate) header: HashableCrateHeader, + + // FIXME do we need to hash this? + pub(crate) extra_filename: String, + // FIXME do we need to hash this? + pub(crate) stable_crate_id: StableCrateId, + // FIXME do we need to hash this? + pub(crate) required_panic_strategy: Option, + // FIXME do we need to hash this? + pub(crate) panic_in_drop_strategy: PanicStrategy, + // FIXME do we need to hash this? + pub(crate) edition: Edition, + // FIXME do we need to hash this? + pub(crate) has_global_allocator: bool, + // FIXME do we need to hash this? + pub(crate) has_alloc_error_handler: bool, + // FIXME do we need to hash this? + pub(crate) has_panic_handler: bool, + // FIXME do we need to hash this? + pub(crate) has_default_lib_allocator: bool, + // FIXME do we need to hash this? + pub(crate) externally_implementable_items: Hashed>, + + // FIXME do we need to hash this? + pub(crate) crate_deps: Hashed>, + // FIXME do we need to hash this? + pub(crate) dylib_dependency_formats: Hashed>>, + // FIXME do we need to hash this? + pub(crate) lib_features: Hashed>, + // FIXME do we need to hash this? + pub(crate) stability_implications: Hashed>, + // FIXME do we need to hash this? + pub(crate) lang_items: Hashed>, + // FIXME do we need to hash this? + pub(crate) lang_items_missing: Hashed>, + // FIXME do we need to hash this? + pub(crate) stripped_cfg_items: Hashed>>, + // FIXME do we need to hash this? + pub(crate) diagnostic_items: Hashed>, + // FIXME do we need to hash this? + pub(crate) native_libraries: Hashed>, + // FIXME do we need to hash this? + pub(crate) foreign_modules: Hashed>, + // FIXME do we need to hash this? + pub(crate) traits: Hashed>, + // FIXME do we need to hash this? + pub(crate) impls: Hashed>, + // FIXME do we need to hash this? + pub(crate) incoherent_impls: Hashed>, + // FIXME do we need to hash this? + pub(crate) interpret_alloc_index: Hashed>, + // FIXME do we need to hash this? + pub(crate) proc_macro_data: NoneIfHashed, + + // FIXME do we need to hash this? + pub(crate) tables: Hashed, + // FIXME do we need to hash this? + pub(crate) debugger_visualizers: Hashed>, + + // FIXME do we need to hash this? + pub(crate) exportable_items: Hashed>, + // FIXME do we need to hash this? + pub(crate) stable_order_of_exportable_impls: Hashed>, + // FIXME do we need to hash this? + pub(crate) exported_non_generic_symbols: + Hashed, SymbolExportInfo)>>, + // FIXME do we need to hash this? + pub(crate) exported_generic_symbols: + Hashed, SymbolExportInfo)>>, + + // FIXME do we need to hash this? + pub(crate) syntax_contexts: Hashed, + // FIXME do we need to hash this? + pub(crate) expn_data: Hashed, + // FIXME do we need to hash this? + pub(crate) expn_hashes: Hashed, + + // FIXME do we need to hash this? + pub(crate) def_path_hash_map: Hashed>>, + + // FIXME do we need to hash this? + pub(crate) source_map: Hashed>>>, + // FIXME do we need to hash this? + pub(crate) target_modifiers: Hashed>, + // FIXME do we need to hash this? + pub(crate) denied_partial_mitigations: Hashed>, + + // FIXME do we need to hash this? + pub(crate) compiler_builtins: bool, + // FIXME do we need to hash this? + pub(crate) needs_allocator: bool, + // FIXME do we need to hash this? + pub(crate) needs_panic_runtime: bool, + // FIXME do we need to hash this? + pub(crate) no_builtins: bool, + // FIXME do we need to hash this? + pub(crate) panic_runtime: bool, + // FIXME do we need to hash this? + pub(crate) profiler_runtime: bool, + // FIXME do we need to hash this? + pub(crate) symbol_mangling_version: SymbolManglingVersion, + + // FIXME do we need to hash this? + pub(crate) specialization_enabled_in: bool, +} + +impl HashableCrateRoot { + pub(super) fn into_crate_root( + self, + tcx: TyCtxt<'_>, + hcx: &mut PublicApiHashingContext<'_>, + ) -> CrateRoot { + let rdr_hashes = if hcx.hash_public_api { + assert!(!self.header.is_proc_macro_crate); + let mut hasher = StableHasher::default(); + self.stable_hash(&mut hcx.hcx, &mut hasher); + let public_api_hash = Svh::new(hasher.finish()); + RDRHashes { public_api_hash } + } else { + let hash = tcx.crate_hash(LOCAL_CRATE); + RDRHashes { public_api_hash: hash } + }; + let header = self.header; + let header = CrateHeader { + triple: header.triple, + hash: rdr_hashes.public_api_hash, + name: header.name, + is_proc_macro_crate: header.is_proc_macro_crate, + is_stub: header.is_stub, + }; + CrateRoot { + header, + + extra_filename: self.extra_filename, + stable_crate_id: self.stable_crate_id, + required_panic_strategy: self.required_panic_strategy, + panic_in_drop_strategy: self.panic_in_drop_strategy, + edition: self.edition, + has_global_allocator: self.has_global_allocator, + has_alloc_error_handler: self.has_alloc_error_handler, + has_panic_handler: self.has_panic_handler, + has_default_lib_allocator: self.has_default_lib_allocator, + externally_implementable_items: self.externally_implementable_items.value, + + crate_deps: self.crate_deps.value, + dylib_dependency_formats: self.dylib_dependency_formats.value, + lib_features: self.lib_features.value, + stability_implications: self.stability_implications.value, + lang_items: self.lang_items.value, + lang_items_missing: self.lang_items_missing.value, + stripped_cfg_items: self.stripped_cfg_items.value, + diagnostic_items: self.diagnostic_items.value, + native_libraries: self.native_libraries.value, + foreign_modules: self.foreign_modules.value, + traits: self.traits.value, + impls: self.impls.value, + incoherent_impls: self.incoherent_impls.value, + interpret_alloc_index: self.interpret_alloc_index.value, + proc_macro_data: self.proc_macro_data.value, + + tables: self.tables.value, + debugger_visualizers: self.debugger_visualizers.value, + + exportable_items: self.exportable_items.value, + stable_order_of_exportable_impls: self.stable_order_of_exportable_impls.value, + exported_non_generic_symbols: self.exported_non_generic_symbols.value, + exported_generic_symbols: self.exported_generic_symbols.value, + + syntax_contexts: self.syntax_contexts.value, + expn_data: self.expn_data.value, + expn_hashes: self.expn_hashes.value, + + def_path_hash_map: self.def_path_hash_map.value, + + source_map: self.source_map.value, + target_modifiers: self.target_modifiers.value, + denied_partial_mitigations: self.denied_partial_mitigations.value, + + compiler_builtins: self.compiler_builtins, + needs_allocator: self.needs_allocator, + needs_panic_runtime: self.needs_panic_runtime, + no_builtins: self.no_builtins, + panic_runtime: self.panic_runtime, + profiler_runtime: self.profiler_runtime, + symbol_mangling_version: self.symbol_mangling_version, + + specialization_enabled_in: self.specialization_enabled_in, + + rdr_hashes, + } + } +} diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2045cab3f7959..3d18aeebfd563 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -21,7 +21,8 @@ use rustc_hir::{PreciseCapturingArgKind, attrs}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_macros::{ - BlobDecodable, Decodable, Encodable, LazyDecodable, MetadataEncodable, TyDecodable, TyEncodable, + BlobDecodable, Decodable, Encodable, LazyDecodable, MetadataEncodable, StableHash, TyDecodable, + TyEncodable, }; use rustc_middle::metadata::{AmbigModChild, ModChild}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; @@ -46,6 +47,9 @@ use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; use crate::eii::EiiMapEncodedKeyValue; +use crate::rmeta::encoder::public_api_hasher::{ + Hashed, PublicApiHasher, PublicApiHashingContext, RDRHashAll, RDRHashNone, +}; mod decoder; mod def_path_hash_map; @@ -339,7 +343,7 @@ impl RawDefId { } } -#[derive(Encodable, BlobDecodable)] +#[derive(Encodable, BlobDecodable, StableHash)] pub(crate) struct CrateDep { pub name: Symbol, pub hash: Svh, @@ -364,8 +368,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,16 +379,31 @@ 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 { - fn encode(&self, buf: &mut FileEncoder) -> LazyTables { - LazyTables { - $($name1: self.$name1.encode(buf),)+ - $($name2: self.$name2.encode(buf),)+ - } + fn encode( + &self, + buf: &mut FileEncoder, + hcx: &mut PublicApiHashingContext<'_> + ) -> Hashed + { + let mut hasher = PublicApiHasher::default(); + let tables = LazyTables { + $($name1: { + let table = self.$name1.encode(buf, hcx); + hasher.digest(&table, hcx); + table.value + },)+ + $($name2: { + let table = self.$name2.encode(buf, hcx); + hasher.digest(&table, hcx); + table.value + },)+ + }; + Hashed { hash: hasher.finish(hcx), value: tables } } } } @@ -392,104 +411,108 @@ macro_rules! define_tables { 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>, - collect_return_position_impl_trait_in_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>, + collect_return_position_impl_trait_in_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 +525,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..f84ac0efb313a 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,7 +1,12 @@ +use rustc_data_structures::stable_hasher::StableHash; use rustc_hir::def::CtorOf; +use rustc_hir::def_id::LocalDefId; use rustc_index::Idx; use crate::rmeta::decoder::MetaBlob; +use crate::rmeta::encoder::public_api_hasher::{ + Hashed, PublicApiHashingContext, TablePublicApiHasher, +}; use crate::rmeta::*; pub(super) trait IsDefault: Default { @@ -436,34 +441,118 @@ 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: StableHash, + { + 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: StableHash + 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: StableHash + 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 iter_hasher(&self) -> H::IterHasher { + self.hasher.iter_hasher() + } + + pub(super) fn set_hashed( + &mut self, + i: I, + value: T, + hashed: HashedT, + hcx: &mut PublicApiHashingContext<'_>, + ) where + HashedT: StableHash, + { + 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!( @@ -486,7 +575,11 @@ impl> TableBui } } - pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable { + pub(crate) fn encode( + &self, + buf: &mut FileEncoder, + hcx: &mut PublicApiHashingContext<'_>, + ) -> Hashed> { let pos = buf.position(); let width = self.width; @@ -496,12 +589,14 @@ impl> TableBui width }); } - - LazyTable::from_position_and_encoded_size( - NonZero::new(pos).unwrap(), - width, - self.blocks.len(), - ) + Hashed { + value: LazyTable::from_position_and_encoded_size( + NonZero::new(pos).unwrap(), + width, + self.blocks.len(), + ), + hash: self.hasher.finish(hcx), + } } } From 04c13624bd48d142cd9e89f4a9002756d1fe0e77 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 8 May 2026 15:20:44 +0200 Subject: [PATCH 08/50] Add public api hash debug logs --- .../src/rmeta/encoder/public_api_hasher.rs | 21 +++++++++++++++++-- compiler/rustc_metadata/src/rmeta/mod.rs | 6 ++++++ 2 files changed, 25 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 7e65e5c8f9f5f..a81df0d7b0840 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -1,3 +1,5 @@ +use std::fmt; + use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::svh::Svh; @@ -18,6 +20,7 @@ use rustc_span::Symbol; use rustc_span::def_id::{LOCAL_CRATE, StableCrateId}; use rustc_span::edition::Edition; use rustc_target::spec::PanicStrategy; +use tracing::debug; use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; use crate::rmeta::{ @@ -170,6 +173,12 @@ pub(crate) struct Hashed { pub(crate) value: T, } +impl fmt::Debug for Hashed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.hash.as_ref().unwrap().fmt(f) + } +} + impl StableHash for Hashed { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { self.hash.expect("Hash must be present when stable hashing!").stable_hash(hcx, hasher); @@ -180,6 +189,12 @@ pub(crate) struct NoneIfHashed { pub(super) value: Option, } +impl fmt::Debug for NoneIfHashed { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + None::<()>.fmt(f) + } +} + impl StableHash for NoneIfHashed { fn stable_hash(&self, hcx: &mut Hcx, hasher: &mut StableHasher) { assert!(self.value.is_none()); @@ -187,7 +202,7 @@ impl StableHash for NoneIfHashed { } } -#[derive(StableHash)] +#[derive(StableHash, Debug)] pub(crate) struct HashableCrateHeader { // FIXME do we need to hash this? pub(crate) triple: TargetTuple, @@ -199,7 +214,7 @@ pub(crate) struct HashableCrateHeader { pub(crate) is_stub: bool, } -#[derive(StableHash)] +#[derive(StableHash, Debug)] pub(crate) struct HashableCrateRoot { // FIXME do we need to hash this? pub(crate) header: HashableCrateHeader, @@ -319,6 +334,8 @@ impl HashableCrateRoot { let mut hasher = StableHasher::default(); self.stable_hash(&mut hcx.hcx, &mut hasher); let public_api_hash = Svh::new(hasher.finish()); + debug!("Hashed crate root: {self:#x?}"); + debug!("public api hash: {}", public_api_hash); RDRHashes { public_api_hash } } else { let 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 3d18aeebfd563..47223f61c24db 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -394,11 +394,17 @@ macro_rules! define_tables { let tables = LazyTables { $($name1: { let table = self.$name1.encode(buf, hcx); + if let Some(hash) = table.hash { + tracing::debug!("{}: {hash:x?}", stringify!($name1)); + } hasher.digest(&table, hcx); table.value },)+ $($name2: { let table = self.$name2.encode(buf, hcx); + if let Some(hash) = table.hash { + tracing::debug!("{}: {hash:x?}", stringify!($name2)); + } hasher.digest(&table, hcx); table.value },)+ From cfdfcc5d06ef99eb3b91b79d16ebf30262fdff14 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 8 May 2026 09:20:10 +0200 Subject: [PATCH 09/50] 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 a81df0d7b0840..a6b7220cf2321 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -237,7 +237,12 @@ pub(crate) struct HashableCrateRoot { pub(crate) has_panic_handler: bool, // FIXME do we need to hash this? pub(crate) has_default_lib_allocator: bool, - // FIXME do we need to hash 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) pub(crate) externally_implementable_items: Hashed>, // FIXME do we need to hash this? From 5989a84658fc68a9cec8b962124d0070c0d01e29 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 8 May 2026 10:21:22 +0200 Subject: [PATCH 10/50] rdr: document the process of hashing new fields added to CrateRoot --- .../src/rmeta/encoder/public_api_hasher.rs | 26 +++++++++++++++++++ compiler/rustc_metadata/src/rmeta/mod.rs | 3 +++ 2 files changed, 29 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 a6b7220cf2321..87b4538ecc390 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -214,6 +214,32 @@ pub(crate) struct HashableCrateHeader { pub(crate) is_stub: bool, } +/// Stable hashable version of [`CrateRoot`] we use to calculate the public api hash. When adding +/// new fields to `CrateRoot`, it is important to include it in the public api hash. Not doing so +/// can cause silent miscompiles. This struct helps make sure the compiler does not allow forgetting +/// the hashing of new items added to rmeta and makes hashing them a local problem with a +/// straightforward solution. +/// +/// When adding a new field to `CrateRoot` we have 3 cases: it is encoded directly, bools for +/// example, it is added as a `LazyValue` or `LazyArray`, or it is added as a new table to +/// `LazyTables` +/// 1. When encoded directly, the type likely implements `StableHash` or it can be derived for it. +/// Simply adding it as-is to `HashableCrateRoot` then moving it into `CrateRoot` in +/// `into_crate_root` should do the trick. +/// 2. When encoded as some kind of lazy value, if one tries to do the same as in 1, the compiler +/// will complain that it does not implement `StableHash`, so it cannot derive +/// `StableHash` for `HashableCrateRoot`. In this case one should wrap it in [`Hashed`] and hash +/// it where it was encoded. the `hash_lazy_array` macro can help with this for simple arrays. +/// 3. When added to `LazyTables` in the `define_tables!` macro, simply defining its type as +/// `Table` will take care of hashing it. +/// +/// In all 3 cases a `// FIXME do we need to hash this?` comment should be included with the new +/// field to note that it wasn't reviewed from the public api hash point of view. It might need +/// stable sorting, or removing parts or all of it from the hash. However, removing anything from +/// the public api hash should be done with extreme scrutiny. In most cases one can likely improve +/// it by stable sorting before encoding, or removing unneeded items before encoding. If it does +/// not need to be in the public api, it likely does not need to be in the rmeta at all. This also +/// improves rmeta sizes. #[derive(StableHash, Debug)] pub(crate) struct HashableCrateRoot { // FIXME do we need to hash this? diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 47223f61c24db..314f4bbbff510 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -415,6 +415,9 @@ macro_rules! define_tables { } } +// When adding a new field, use `RDRHashAll` as the hasher and include a `// FIXME do we need to +// hash this comment?` to note that it wasn't reviewed for public api hashing. The docs of +// `HashableCrateHeader` contains more information about public api hashing. define_tables! { - defaulted: intrinsic: Table>>, From f8e21df8980e52c43da96150efe54f379260c003 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 8 May 2026 10:24:37 +0200 Subject: [PATCH 11/50] rdr: document hashed_lazy_array --- compiler/rustc_metadata/src/rmeta/encoder.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 53c5212706840..939e5907e18c0 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -457,6 +457,18 @@ macro_rules! record_defaulted_array { }}; } +/// Stable hashes an iterator while encoding it as a LazyArray. +/// +/// The two forms it accepts are +/// ```text +/// hashed_lazy_array!(self, hashed_iterator, hcx) +/// ``` +/// and +/// ```text +/// hashed_lazy_array!(self, hashed_iterator, hcx, map) +/// ``` +/// `map` maps from hashed value returned from hashed_iterator to the encoded value. This is +/// mostly used to map `LocalDefId`-s to `DefIndex` in the encoded values. macro_rules! hashed_lazy_array { ($self:ident, $values:expr, $hcx:ident, $encode_map:expr) => {{ { From 504f8595a9d019e284faf9b96a94ed63f3b1490a Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 25 Apr 2026 11:09:22 +0200 Subject: [PATCH 12/50] 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 | 6 +- .../src/rmeta/encoder/public_api_hasher.rs | 2 +- compiler/rustc_middle/src/ich.rs | 69 +++++++++++++------ 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs index 931317f80110b..4a5cb051a698b 100644 --- a/compiler/rustc_data_structures/src/stable_hasher.rs +++ b/compiler/rustc_data_structures/src/stable_hasher.rs @@ -620,4 +620,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 939e5907e18c0..8ee5b5b44c449 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -340,6 +340,9 @@ impl<'a, 'tcx> Encodable> for SpanData { // Encode the start position relative to the file start, so we profit more from the // variable-length integer encoding. + // IMPORTANT: if this is ever changed, the public api span hashing must be updated. It + // currently uses the `hash_spans_as_parentless` option to make sure spans are hashed not + // relative to their parent, but relative to their file. let lo = self.lo - source_file.start_pos; // Encode length which is usually less than span.hi and profits more @@ -2766,7 +2769,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 87b4538ecc390..f09192c77a3e1 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -82,7 +82,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.rs b/compiler/rustc_middle/src/ich.rs index 2b57a5e6aa226..7307482167ca7 100644 --- a/compiler/rustc_middle/src/ich.rs +++ b/compiler/rustc_middle/src/ich.rs @@ -7,7 +7,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_session::Session; use rustc_session::cstore::Untracked; use rustc_span::source_map::SourceMap; -use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span}; +use rustc_span::{CachingSourceMapView, DUMMY_SP, Pos, Span, SpanData}; // Very often, we are hashing something that does not need the `CachingSourceMapView`, so we // initialize it lazily. @@ -38,10 +38,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; @@ -70,32 +78,19 @@ impl<'a> StableHashingContext<'a> { pub fn hashing_controls(&self) -> HashingControls { self.hashing_controls } -} -impl<'a> StableHashCtxt 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, raw_span: RawSpan, 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::from_raw_span(raw_span); - let span = span.data_untracked(); span.ctxt.stable_hash(self, hasher); span.parent.stable_hash(self, hasher); @@ -157,6 +152,30 @@ impl<'a> StableHashCtxt for StableHashingContext<'a> { Hash::hash(&col_line, hasher); Hash::hash(&len, hasher); } +} + +impl<'a> StableHashCtxt 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, raw_span: RawSpan, hasher: &mut StableHasher) { + if !self.hashing_controls().hash_spans { + return; + } + + let span = Span::from_raw_span(raw_span); + let span = span.data_untracked(); + self.hash_span_data(span, hasher); + } #[inline] fn def_path_hash(&self, raw_def_id: RawDefId) -> RawDefPathHash { @@ -176,7 +195,15 @@ impl<'a> StableHashCtxt 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 294e8a32c231aeb2f9abee4f5af54f352f5d9391 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 21 Apr 2026 09:22:50 +0200 Subject: [PATCH 13/50] 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 cbd6afd68473a..986cf8f2ea20a 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); @@ -367,6 +367,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 f8200badb345b..458ea63ca243d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2039,6 +2039,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 f3a45e4619c0c0fd2eaf391ebc8f960b59f96562 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 30 Apr 2026 12:06:26 +0200 Subject: [PATCH 14/50] rdr: add rustc_public_hash_unchanged and rustc_public_hash_changed attributes for testing --- .../src/attributes/rustc_internal.rs | 93 +++++++++++++++++-- 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, 214 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 859293bf33a1e..f7857527708f7 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; @@ -218,11 +218,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 { @@ -242,11 +242,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; }; @@ -256,6 +252,85 @@ fn parse_cgu_fields( Some((cfg, module, kind)) } +fn parse_rdr_fields(cx: &mut AcceptContext<'_, '_>, 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((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else { + continue; + }; + + let res = match ident.name { + sym::cfg => &mut cfg, + sym::crate_name => &mut crate_name, + _ => { + cx.adcx().expected_specific_argument(ident.span, &[sym::cfg, sym::crate_name]); + continue; + } + }; + + let Some(str) = arg.value_as_str() else { + cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit())); + continue; + }; + + if res.is_some() { + cx.adcx().duplicate_key(ident.span.to(arg.args_span()), ident.name); + continue; + } + + *res = Some((str, arg.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<'_, '_>) -> 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 bf4989b83200b..4efd3f265a916 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -145,6 +145,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 58bf12855ad6e..51a615a580eed 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -762,6 +762,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 2cdcf75d00be7..4f5c7e0f2aab5 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, StableHash, PrintAttribute)] +pub struct RDRFields { + pub crate_name: Symbol, + pub cfg: Symbol, + pub changed: bool, +} + #[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)] #[derive(StableHash, Encodable, Decodable)] pub enum DivergingFallbackBehavior { @@ -1520,6 +1527,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 f2b528d1dcdc2..2e3bdc2eb55e4 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -175,6 +175,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 f1596a0685768..2030ba9bf3a38 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -339,6 +339,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 90f6bf669a2bc..76920b6e79f56 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1797,6 +1797,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 53ced96d6c8834689f28f64de90f3a456f102172 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 26 Apr 2026 13:05:47 +0200 Subject: [PATCH 15/50] 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 949b4d4596d7c909bd01fdc507e76e6db9d21472 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Wed, 29 Apr 2026 09:24:14 +0200 Subject: [PATCH 16/50] rdr: use public_api_hash when hashing dependencies --- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8ee5b5b44c449..3e4912c657377 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2986,7 +2986,7 @@ 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), + hash: tcx.public_api_hash(cnum), host_hash: tcx.crate_host_hash(cnum), kind: tcx.crate_dep_kind(cnum), extra_filename: tcx.extra_filename(cnum).clone(), From bdf7e0b20235a27135e7a0cba0fda97592c14142 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 5 May 2026 20:14:04 +0200 Subject: [PATCH 17/50] rdr: use public_api_hash in the rmeta headers --- .../src/rmeta/decoder/cstore_impl.rs | 28 ++++++++++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 45 +++++++++++-------- .../src/rmeta/encoder/public_api_hasher.rs | 4 +- compiler/rustc_metadata/src/rmeta/mod.rs | 5 +++ compiler/rustc_middle/src/hir/map.rs | 10 ++++- compiler/rustc_middle/src/queries.rs | 4 ++ 6 files changed, 71 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 986cf8f2ea20a..051b96fa872c9 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -18,6 +18,7 @@ use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; use rustc_serialize::Decoder; use rustc_session::StableCrateId; +use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStore, ExternCrate}; use rustc_span::hygiene::ExpnId; use rustc_span::{Span, Symbol, kw}; @@ -147,8 +148,14 @@ macro_rules! provide_one { // 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). + // + // The `crate_hash` query must not depend on public_api_hash, since it might change when + // the `public_api_hash` does not change. use rustc_middle::dep_graph::DepKind; - if DepKind::$name != DepKind::public_api_hash && $tcx.dep_graph.is_fully_enabled() { + if ((DepKind::$name != DepKind::public_api_hash) + & (DepKind::$name != DepKind::crate_hash)) + && $tcx.dep_graph.is_fully_enabled() + { $tcx.ensure_ok().public_api_hash($def_id.krate); } @@ -366,8 +373,23 @@ 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_hash => { + if tcx.sess.opts.unstable_opts.public_api_hash { + assert!( + // We could allow all binary targets, they should always get a rustc invocation for + // linking, but there is no reason to (yet). + tcx.crate_types().contains(&CrateType::ProcMacro), + "Calling the crate_hash query for dependencies outside of proc-macro crates is unsound!" + ); + cdata.root.rdr_hashes.private_hash + } else { + cdata.root.header.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 3e4912c657377..0f7c35696d33f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -717,7 +717,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { adapted.encode(&mut self.opaque, hcx) } - fn encode_crate_root(&mut self, hcx: &mut PublicApiHashingContext<'_>) -> LazyValue { + fn encode_crate_root( + &mut self, + hcx: &mut PublicApiHashingContext<'_>, + ) -> (LazyValue, Svh) { let tcx = self.tcx; let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); @@ -900,6 +903,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), }; let crate_root = crate_root.into_crate_root(self.tcx, hcx); + let hash = crate_root.header.hash; let root = stat!("final", || { self.lazy(crate_root) }); @@ -966,7 +970,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { eprint!("{s}"); } - root + (root, hash) } } @@ -2713,22 +2717,6 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { // there's no need to do dep-graph tracking for any of it. tcx.dep_graph.assert_ignored(); - // Generate the metadata stub manually, as that is a small file compared to full metadata. - if let Some(ref_path) = ref_path { - let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub"); - - with_encode_metadata_header(tcx, ref_path, |ecx| { - let header: LazyValue = ecx.lazy(CrateHeader { - name: tcx.crate_name(LOCAL_CRATE), - triple: tcx.sess.opts.target_triple.clone(), - hash: tcx.crate_hash(LOCAL_CRATE), - is_proc_macro_crate: false, - is_stub: true, - }); - header.position.get() - }) - } - let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata"); let dep_node = tcx.metadata_dep_node(); @@ -2763,6 +2751,8 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { ); } + let mut hash = tcx.crate_hash(LOCAL_CRATE); + // Perform metadata encoding inside a task, so the dep-graph can check if any encoded // information changes, and maybe reuse the work product. tcx.dep_graph.with_task( @@ -2780,7 +2770,8 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { 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); + let (root, header_hash) = ecx.encode_crate_root(&mut hcx); + hash = header_hash; // Flush buffer to ensure backing file has the correct size. ecx.opaque.flush(); @@ -2797,6 +2788,22 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { }, None, ); + + // Generate the metadata stub manually, as that is a small file compared to full metadata. + if let Some(ref_path) = ref_path { + let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub"); + + with_encode_metadata_header(tcx, ref_path, |ecx| { + let header: LazyValue = ecx.lazy(CrateHeader { + name: tcx.crate_name(LOCAL_CRATE), + triple: tcx.sess.opts.target_triple.clone(), + hash, + is_proc_macro_crate: false, + is_stub: true, + }); + header.position.get() + }) + } } fn with_encode_metadata_header( 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 f09192c77a3e1..1d97978c5c701 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -367,10 +367,10 @@ impl HashableCrateRoot { let public_api_hash = Svh::new(hasher.finish()); debug!("Hashed crate root: {self:#x?}"); debug!("public api hash: {}", public_api_hash); - RDRHashes { public_api_hash } + RDRHashes { public_api_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) } } else { let hash = tcx.crate_hash(LOCAL_CRATE); - RDRHashes { public_api_hash: hash } + RDRHashes { public_api_hash: hash, private_hash: hash } }; let header = self.header; let header = CrateHeader { diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 314f4bbbff510..57ab9934509f3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -317,6 +317,11 @@ pub(crate) struct CrateRoot { 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, + + /// This is the previous crate_hash, but the dependencies are only hashed using their public + /// api hash. Proc macros combine this from all dependencies to derive their full hash, which + /// is also their public api hash. + private_hash: Svh, } /// On-disk representation of `DefId`. diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index bda9b3a47849e..9b10b866fcaae 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -14,6 +14,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_hir::intravisit::Visitor; use rustc_hir::*; use rustc_hir_pretty as pprust_hir; +use rustc_session::config::CrateType; use rustc_span::def_id::StableCrateId; use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol, kw, with_metavar_spans}; @@ -1197,12 +1198,19 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { } fn upstream_crates(tcx: TyCtxt<'_>) -> Vec<(StableCrateId, Svh)> { + // proc macros must use the private hash of all crates, since the code running is part of its + // public api. When the crate is a library, it cannot use the crate_hash of dependencies with + // public api hash enabled. This crate's rustc invocation might be skipped if only the public + // api hash of dependencies change, which could lead to incorrect hashes in the rmeta. + let use_public_hash = tcx.sess.opts.unstable_opts.public_api_hash + && !tcx.crate_types().contains(&CrateType::ProcMacro); let mut upstream_crates: Vec<_> = tcx .crates(()) .iter() .map(|&cnum| { let stable_crate_id = tcx.stable_crate_id(cnum); - let hash = tcx.crate_hash(cnum); + let hash = + if use_public_hash { tcx.public_api_hash(cnum) } else { tcx.crate_hash(cnum) }; (stable_crate_id, hash) }) .collect(); diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 458ea63ca243d..c15f86a2f7a7c 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2033,6 +2033,10 @@ rustc_queries! { // until `tcx.untracked().definitions.freeze()` has been called, otherwise if incremental // compilation is enabled calculating this hash can freeze this structure too early in // compilation and cause subsequent crashes when attempting to write to `definitions` + // + // When the public-api-hash unstable options is enabled, this only contains the public hash of + // dependencies, unless this crate is a proc macro, then it contains the private hashes to make + // sure its hash changes with any code changes in dependencies. query crate_hash(_: CrateNum) -> Svh { eval_always desc { "looking up the hash a crate" } From 5fe704b50d3c7a1c3f86f67148593d46d3b3a107 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 8 May 2026 13:56:40 +0200 Subject: [PATCH 18/50] rdr: move the hashes to crate header, properly handle private and public hashes in the resolver When the resolver resolved transitive dependecies, it used the `crate_hash` of dependecies saved inside the rmeta of downstream crates to locate them. With public api hashing enabled, it is not sound to save that hash into downstream crates. Only the public hash can be saved. This modifies the locator to find all crates with the given public hash, but look for conflicing crates using the (public, private) hash pair. So if there are multiple crates with the same public hash, but different private hash (which should only happen if there was some kind of hash collision while making the public hashes or the StableCrateId which is included in it), it will be reported as having multiple candidates for the crate. --- compiler/rustc_metadata/src/creader.rs | 28 ++++++++++---- compiler/rustc_metadata/src/locator.rs | 16 ++++---- compiler/rustc_metadata/src/rmeta/decoder.rs | 21 +++++++--- .../src/rmeta/decoder/cstore_impl.rs | 23 +++++------ compiler/rustc_metadata/src/rmeta/encoder.rs | 17 +++++---- .../src/rmeta/encoder/public_api_hasher.rs | 20 +++++----- compiler/rustc_metadata/src/rmeta/mod.rs | 38 +++++++++---------- 7 files changed, 91 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 9a756337335c0..ddad64e6b3804 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -119,7 +119,8 @@ impl<'a> std::fmt::Debug for CrateDump<'a> { for (cnum, data) in self.0.iter_crate_data() { writeln!(fmt, " name: {}", data.name())?; writeln!(fmt, " cnum: {cnum}")?; - writeln!(fmt, " hash: {}", data.hash())?; + writeln!(fmt, " private hash: {}", data.private_hash())?; + writeln!(fmt, " public hash: {}", data.public_hash())?; writeln!(fmt, " reqd: {:?}", data.dep_kind())?; writeln!(fmt, " priv: {:?}", data.is_private_dep())?; let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source(); @@ -551,8 +552,8 @@ impl CStore { } } - fn existing_match(&self, name: Symbol, hash: Option) -> Option { - let hash = hash?; + fn existing_match(&self, name: Symbol, public_hash: Option) -> Option { + let public_hash = public_hash?; for (cnum, data) in self.iter_crate_data() { if data.name() != name { @@ -560,10 +561,14 @@ impl CStore { continue; } - if hash == data.hash() { + if public_hash == data.public_hash() { return Some(cnum); } else { - debug!("actual hash {} did not match expected {}", hash, data.hash()); + debug!( + "actual public hash {} did not match expected {}", + public_hash, + data.public_hash() + ); } } @@ -606,7 +611,15 @@ impl CStore { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); - let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); + let host_hash = host_lib.as_ref().map(|lib| { + let host_root = lib.metadata.get_root(); + assert_eq!( + host_root.public_hash(), + host_root.private_hash(), + "Mismatched public and private hash for proc macro!" + ); + host_root.public_hash() + }); let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep); // Claim this crate number and cache it @@ -877,7 +890,8 @@ impl CStore { let root = library.metadata.get_root(); let mut result = LoadResult::Loaded(library); for (cnum, data) in self.iter_crate_data() { - if data.name() == root.name() && root.hash() == data.hash() { + if data.name() == root.name() && root.public_hash() == data.public_hash() { + assert!(root.private_hash() == data.private_hash()); assert!(locator.hash.is_none()); info!("load success, going to previous cnum: {}", cnum); result = LoadResult::Previous(cnum); diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 5af60e9f19da7..0092d4caa765a 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -236,7 +236,7 @@ use tracing::{debug, info}; use crate::creader::{Library, MetadataLoader}; use crate::errors; -use crate::rmeta::{METADATA_HEADER, MetadataBlob, rustc_version}; +use crate::rmeta::{CrateHashes, METADATA_HEADER, MetadataBlob, rustc_version}; #[derive(Clone)] pub(crate) struct CrateLocator<'a> { @@ -489,10 +489,10 @@ impl<'a> CrateLocator<'a> { // search is being performed for. let mut libraries = FxIndexMap::default(); for (_hash, (rlibs, rmetas, dylibs, interfaces)) in candidates { - if let Some((svh, lib)) = + if let Some((hashes, lib)) = self.extract_lib(crate_rejections, rlibs, rmetas, dylibs, interfaces)? { - libraries.insert(svh, lib); + libraries.insert(hashes, lib); } } @@ -526,7 +526,7 @@ impl<'a> CrateLocator<'a> { rmetas: FxIndexSet, dylibs: FxIndexSet, interfaces: FxIndexSet, - ) -> Result, CrateError> { + ) -> Result, CrateError> { let mut slot = None; // Order here matters, rmeta should come first. // @@ -575,7 +575,7 @@ impl<'a> CrateLocator<'a> { crate_rejections: &mut CrateRejections, m: FxIndexSet, flavor: CrateFlavor, - slot: &mut Option<(Svh, MetadataBlob, PathBuf, CrateFlavor)>, + slot: &mut Option<(CrateHashes, MetadataBlob, PathBuf, CrateFlavor)>, ) -> Result, CrateError> { // If we are producing an rlib, and we've already loaded metadata, then // we should not attempt to discover further crate sources (unless we're @@ -700,7 +700,7 @@ impl<'a> CrateLocator<'a> { crate_rejections: &mut CrateRejections, metadata: &MetadataBlob, libpath: &Path, - ) -> Option { + ) -> Option { let header = metadata.get_header(); if header.is_proc_macro_crate != self.is_proc_macro { info!( @@ -724,7 +724,7 @@ impl<'a> CrateLocator<'a> { return None; } - let hash = header.hash; + let hash = header.hashes.public_hash; if let Some(expected_hash) = self.hash { if hash != expected_hash { info!("Rejecting via hash: expected {} got {}", expected_hash, hash); @@ -735,7 +735,7 @@ impl<'a> CrateLocator<'a> { } } - Some(hash) + Some(header.hashes) } fn find_commandline_library( diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 230ce5686e979..eb8a1ba579d5f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -759,10 +759,11 @@ impl MetadataBlob { writeln!(out, "name {}{}", root.name(), root.extra_filename)?; writeln!( out, - "hash {} stable_crate_id {:?}", - root.hash(), + "private hash {} stable_crate_id {:?}", + root.private_hash(), root.stable_crate_id )?; + writeln!(out, "public_hash {}", root.public_hash())?; writeln!(out, "proc_macro {:?}", root.proc_macro_data.is_some())?; writeln!(out, "triple {}", root.header.triple.tuple())?; writeln!(out, "edition {}", root.edition)?; @@ -941,8 +942,12 @@ impl CrateRoot { self.header.name } - pub(crate) fn hash(&self) -> Svh { - self.header.hash + pub(crate) fn private_hash(&self) -> Svh { + self.header.hashes.private_hash + } + + pub(crate) fn public_hash(&self) -> Svh { + self.header.hashes.public_hash } pub(crate) fn stable_crate_id(&self) -> StableCrateId { @@ -2033,8 +2038,12 @@ impl CrateMetadata { self.root.header.name } - pub(crate) fn hash(&self) -> Svh { - self.root.header.hash + pub(crate) fn private_hash(&self) -> Svh { + self.root.header.hashes.private_hash + } + + pub(crate) fn public_hash(&self) -> Svh { + self.root.header.hashes.public_hash } pub(crate) fn has_async_drops(&self) -> bool { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 051b96fa872c9..01143533dd8b8 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -374,21 +374,18 @@ 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 => { - if tcx.sess.opts.unstable_opts.public_api_hash { - assert!( - // We could allow all binary targets, they should always get a rustc invocation for - // linking, but there is no reason to (yet). - tcx.crate_types().contains(&CrateType::ProcMacro), - "Calling the crate_hash query for dependencies outside of proc-macro crates is unsound!" - ); - cdata.root.rdr_hashes.private_hash - } else { - cdata.root.header.hash - } + assert!( + // We could allow all binary targets, they should always get a rustc invocation for + // linking, but there is no reason to (yet). + !tcx.sess.opts.unstable_opts.public_api_hash + || tcx.crate_types().contains(&CrateType::ProcMacro), + "Calling the crate_hash query for dependencies outside of proc-macro crates is unsound!" + ); + cdata.root.header.hashes.private_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 + tracing::debug!("public api hash: {} {}", cdata.root.header.name, cdata.root.header.hashes.public_hash); + cdata.root.header.hashes.public_hash } } crate_host_hash => { cdata.host_hash } crate_name => { cdata.root.header.name } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 0f7c35696d33f..cb2d15b170182 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -720,7 +720,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_crate_root( &mut self, hcx: &mut PublicApiHashingContext<'_>, - ) -> (LazyValue, Svh) { + ) -> (LazyValue, CrateHashes) { let tcx = self.tcx; let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); @@ -903,7 +903,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), }; let crate_root = crate_root.into_crate_root(self.tcx, hcx); - let hash = crate_root.header.hash; + let hashes = crate_root.header.hashes; let root = stat!("final", || { self.lazy(crate_root) }); @@ -970,7 +970,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { eprint!("{s}"); } - (root, hash) + (root, hashes) } } @@ -2751,7 +2751,10 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { ); } - let mut hash = tcx.crate_hash(LOCAL_CRATE); + let mut hashes = CrateHashes { + private_hash: tcx.crate_hash(LOCAL_CRATE), + public_hash: tcx.crate_hash(LOCAL_CRATE), + }; // Perform metadata encoding inside a task, so the dep-graph can check if any encoded // information changes, and maybe reuse the work product. @@ -2770,8 +2773,8 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { 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, header_hash) = ecx.encode_crate_root(&mut hcx); - hash = header_hash; + let (root, crate_hashes) = ecx.encode_crate_root(&mut hcx); + hashes = crate_hashes; // Flush buffer to ensure backing file has the correct size. ecx.opaque.flush(); @@ -2797,7 +2800,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { let header: LazyValue = ecx.lazy(CrateHeader { name: tcx.crate_name(LOCAL_CRATE), triple: tcx.sess.opts.target_triple.clone(), - hash, + hashes, is_proc_macro_crate: false, is_stub: true, }); 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 1d97978c5c701..75fe45353bfce 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -24,9 +24,9 @@ use tracing::debug; use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; use crate::rmeta::{ - DefPathHashMapRef, EiiMapEncodedKeyValue, ExpnDataTable, ExpnHashTable, IncoherentImpls, - LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, RDRHashes, SyntaxContextTable, - TraitImpls, + CrateHashes, DefPathHashMapRef, EiiMapEncodedKeyValue, ExpnDataTable, ExpnHashTable, + IncoherentImpls, LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, + SyntaxContextTable, TraitImpls, }; #[derive(Default)] @@ -360,22 +360,22 @@ impl HashableCrateRoot { tcx: TyCtxt<'_>, hcx: &mut PublicApiHashingContext<'_>, ) -> CrateRoot { - let rdr_hashes = if hcx.hash_public_api { + let hashes = if hcx.hash_public_api { assert!(!self.header.is_proc_macro_crate); let mut hasher = StableHasher::default(); self.stable_hash(&mut hcx.hcx, &mut hasher); - let public_api_hash = Svh::new(hasher.finish()); + let public_hash = Svh::new(hasher.finish()); debug!("Hashed crate root: {self:#x?}"); - debug!("public api hash: {}", public_api_hash); - RDRHashes { public_api_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) } + debug!("public api hash: {}", public_hash); + CrateHashes { public_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) } } else { let hash = tcx.crate_hash(LOCAL_CRATE); - RDRHashes { public_api_hash: hash, private_hash: hash } + CrateHashes { public_hash: hash, private_hash: hash } }; let header = self.header; let header = CrateHeader { triple: header.triple, - hash: rdr_hashes.public_api_hash, + hashes, name: header.name, is_proc_macro_crate: header.is_proc_macro_crate, is_stub: header.is_stub, @@ -437,8 +437,6 @@ impl HashableCrateRoot { symbol_mangling_version: self.symbol_mangling_version, specialization_enabled_in: self.specialization_enabled_in, - - rdr_hashes, } } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 57ab9934509f3..4fa7a6e2617f0 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -210,8 +210,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) hashes: CrateHashes, pub(crate) name: Symbol, /// Whether this is the header for a proc-macro crate. /// @@ -302,26 +301,25 @@ 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, - - /// This is the previous crate_hash, but the dependencies are only hashed using their public - /// api hash. Proc macros combine this from all dependencies to derive their full hash, which - /// is also their public api hash. - private_hash: Svh, +/// All hashes here are equal to the hash from the crate header (the `crate_hash` query) when the public-api-hash unstable feature is disabled. +#[derive(MetadataEncodable, BlobDecodable, Clone, Copy, Eq, PartialEq, Hash)] +pub(crate) struct CrateHashes { + /// Hash of the crate contents, including private items. For proc macros this includes the + /// private hashes of all dependencies. When `public-api-hash` is enabled, for other crate + /// types than proc macro, it only includes the public hash of dependencies. This is only + /// readable by queries in downstream dependencies if the crate querying is a proc macro. + pub(crate) private_hash: Svh, + /// Hash of most data in rmeta. same as `private_hash` if the `public-api-hash` option is + /// disabled. + /// + /// The public hash contains `StableCrateId`, so two crates in the dependency graph should not + /// have the same public hash just because they have the same "public api". This is asserted + /// while loading: if two crates have the same public hash but different private hashes, the + /// resolver reports that there are multiple candidates available for a crate and compilation + /// aborts. + pub(crate) public_hash: Svh, } /// On-disk representation of `DefId`. From d292f0ba0476777c521e65e5cc4c9406d9fbb417 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 9 May 2026 08:43:43 +0200 Subject: [PATCH 19/50] rdr: add hash FIXME comments to LazyTables fields --- compiler/rustc_metadata/src/rmeta/mod.rs | 78 ++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4fa7a6e2617f0..1df0b3e9e8fff 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -423,9 +423,13 @@ macro_rules! define_tables { // `HashableCrateHeader` contains more information about public api hashing. define_tables! { - defaulted: + // FIXME do we need to hash this? intrinsic: Table>>, + // FIXME do we need to hash this? is_macro_rules: Table, + // FIXME do we need to hash this? type_alias_is_lazy: Table, + // FIXME do we need to hash this? 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. @@ -435,74 +439,134 @@ define_tables! { // // We don't need to include this in the hash, hashing `def_path_hash_map` takes care of it. def_path_hashes: Table, + // FIXME do we need to hash this? explicit_item_bounds: Table, Span)>>, + // FIXME do we need to hash this? explicit_item_self_bounds: Table, Span)>>, + // FIXME do we need to hash this? inferred_outlives_of: Table, Span)>>, + // FIXME do we need to hash this? explicit_super_predicates_of: Table, Span)>>, + // FIXME do we need to hash this? explicit_implied_predicates_of: Table, Span)>>, + // FIXME do we need to hash this? explicit_implied_const_bounds: Table, Span)>>, + // FIXME do we need to hash this? inherent_impls: Table>, + // FIXME do we need to hash this? 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. + // FIXME do we need to hash this? module_children_reexports: Table>, + // FIXME do we need to hash this? ambig_module_children: Table>, + // FIXME do we need to hash this? cross_crate_inlinable: Table, + // FIXME do we need to hash this? asyncness: Table, + // FIXME do we need to hash this? constness: Table, + // FIXME do we need to hash this? safety: Table, + // FIXME do we need to hash this? defaultness: Table, - optional: + // FIXME do we need to hash this? 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. + // FIXME do we need to hash this? module_children_non_reexports: Table>, + // FIXME do we need to hash this? associated_item_or_field_def_ids: Table>, + // FIXME do we need to hash this? def_kind: Table, + // FIXME do we need to hash this? visibility: Table>>, + // FIXME do we need to hash this? def_span: Table>, + // FIXME do we need to hash this? def_ident_span: Table>, + // FIXME do we need to hash this? lookup_stability: Table>, + // FIXME do we need to hash this? lookup_const_stability: Table>, + // FIXME do we need to hash this? lookup_default_body_stability: Table>, + // FIXME do we need to hash this? lookup_deprecation_entry: Table>, + // FIXME do we need to hash this? explicit_predicates_of: Table>>, + // FIXME do we need to hash this? generics_of: Table>, + // FIXME do we need to hash this? type_of: Table>>>, + // FIXME do we need to hash this? variances_of: Table>, + // FIXME do we need to hash this? fn_sig: Table>>>, + // FIXME do we need to hash this? codegen_fn_attrs: Table>, + // FIXME do we need to hash this? impl_trait_header: Table>>, + // FIXME do we need to hash this? const_param_default: Table>>>, + // FIXME do we need to hash this? object_lifetime_default: Table>, + // FIXME do we need to hash this? optimized_mir: Table>>, + // FIXME do we need to hash this? mir_for_ctfe: Table>>, + // FIXME do we need to hash this? trivial_const: Table)>>, + // FIXME do we need to hash this? closure_saved_names_of_captured_variables: Table>>, + // FIXME do we need to hash this? mir_coroutine_witnesses: Table>>, + // FIXME do we need to hash this? promoted_mir: Table>>>, + // FIXME do we need to hash this? thir_abstract_const: Table>>>, + // FIXME do we need to hash this? impl_parent: Table, + // FIXME do we need to hash this? const_conditions: Table>>, // FIXME(eddyb) perhaps compute this on the fly if cheap enough? + // FIXME do we need to hash this? coerce_unsized_info: Table>, + // FIXME do we need to hash this? mir_const_qualif: Table>, + // FIXME do we need to hash this? rendered_const: Table>, + // FIXME do we need to hash this? rendered_precise_capturing_args: Table>>, + // FIXME do we need to hash this? fn_arg_idents: Table>>, + // FIXME do we need to hash this? coroutine_kind: Table, + // FIXME do we need to hash this? coroutine_for_closure: Table, + // FIXME do we need to hash this? adt_destructor: Table>, + // FIXME do we need to hash this? adt_async_destructor: Table>, + // FIXME do we need to hash this? coroutine_by_move_body_def_id: Table, + // FIXME do we need to hash this? eval_static_initializer: Table>>, + // FIXME do we need to hash this? trait_def: Table>, + // FIXME do we need to hash this? expn_that_defined: Table>, + // FIXME do we need to hash this? default_fields: Table>, + // FIXME do we need to hash this? params_in_repr: Table>>, + // FIXME do we need to hash this? repr_options: Table>, // `def_keys` and `def_path_hashes` represent a lazy version of a // `DefPathTable`. This allows us to avoid deserializing an entire @@ -511,19 +575,33 @@ define_tables! { // // We don't need to include this in the hash, hashing `def_path_hash_map` takes care of it. def_keys: Table>, + // FIXME do we need to hash this? proc_macro_quoted_spans: Table>, + // FIXME do we need to hash this? variant_data: Table>, + // FIXME do we need to hash this? assoc_container: Table>, + // FIXME do we need to hash this? macro_definition: Table>, + // FIXME do we need to hash this? proc_macro: Table, + // FIXME do we need to hash this? deduced_param_attrs: Table>, + // FIXME do we need to hash this? collect_return_position_impl_trait_in_trait_tys: Table>>>>, + // FIXME do we need to hash this? doc_link_resolutions: Table>, + // FIXME do we need to hash this? doc_link_traits_in_scope: Table>, + // FIXME do we need to hash this? assumed_wf_types_for_rpitit: Table, Span)>>, + // FIXME do we need to hash this? opaque_ty_origin: Table>>, + // FIXME do we need to hash this? anon_const_kind: Table>, + // FIXME do we need to hash this? const_of_item: Table>>>, + // FIXME do we need to hash this? associated_types_for_impl_traits_in_trait_or_impl: Table>>>, } From 0380de978ebd8dea2f73fa5b97f1c399ebaef805 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 10 May 2026 09:16:10 +0200 Subject: [PATCH 20/50] record encoded crate nums --- compiler/rustc_metadata/src/rmeta/encoder.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index cb2d15b170182..160c03b9e6e7a 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -74,6 +74,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: &'a HygieneEncodeContext, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, + encoded_crate_nums: Option>, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -149,6 +150,9 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { panic!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate"); } self.emit_u32(crate_num.as_u32()); + if let Some(encoded_crate_nums) = self.encoded_crate_nums.as_mut() { + encoded_crate_nums[crate_num] = true; + } } fn encode_def_index(&mut self, def_index: DefIndex) { @@ -2770,7 +2774,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { & tcx.sess.opts.incremental.is_some(); let mut hcx = PublicApiHashingContext::new(hash_public_api, hcx); - with_encode_metadata_header(tcx, path, |ecx| { + with_encode_metadata_header(tcx, path, hash_public_api, |ecx| { // Encode all the entries and extra information in the crate, // culminating in the `CrateRoot` which points to all of it. let (root, crate_hashes) = ecx.encode_crate_root(&mut hcx); @@ -2796,7 +2800,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { if let Some(ref_path) = ref_path { let _prof_timer = tcx.prof.verbose_generic_activity("generate_crate_metadata_stub"); - with_encode_metadata_header(tcx, ref_path, |ecx| { + with_encode_metadata_header(tcx, ref_path, false, |ecx| { let header: LazyValue = ecx.lazy(CrateHeader { name: tcx.crate_name(LOCAL_CRATE), triple: tcx.sess.opts.target_triple.clone(), @@ -2812,6 +2816,7 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { fn with_encode_metadata_header( tcx: TyCtxt<'_>, path: &Path, + hash_public_api: bool, f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize, ) { let mut encoder = opaque::FileEncoder::new(path) @@ -2843,6 +2848,8 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, symbol_index_table: Default::default(), + encoded_crate_nums: hash_public_api + .then(|| IndexVec::from_elem_n(false, tcx.crates(()).len() + 1)), }; // Encode the rustc version string in a predictable location. From aa0a2b848b7975df9219b142df5d04ae466eba9e Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 10 May 2026 11:37:49 +0200 Subject: [PATCH 21/50] rdr: more comments of what to include where --- .../src/rmeta/encoder/public_api_hasher.rs | 53 +++++++++++++++++++ 1 file changed, 53 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 75fe45353bfce..5c0fadd9335b1 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -243,25 +243,35 @@ pub(crate) struct HashableCrateHeader { #[derive(StableHash, Debug)] pub(crate) struct HashableCrateRoot { // FIXME do we need to hash this? + // everything_downstream pub(crate) header: HashableCrateHeader, // FIXME do we need to hash this? + // everything downstream pub(crate) extra_filename: String, // FIXME do we need to hash this? + // everything_downstream pub(crate) stable_crate_id: StableCrateId, // FIXME do we need to hash this? + // everything_downstream, right? pub(crate) required_panic_strategy: Option, // FIXME do we need to hash this? + // everything_downstream, right? pub(crate) panic_in_drop_strategy: PanicStrategy, // FIXME do we need to hash this? + // macro expansion in downstream crates uses it, only relevant for publicly reachable macros pub(crate) edition: Edition, // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally pub(crate) has_global_allocator: bool, // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally pub(crate) has_alloc_error_handler: bool, // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally pub(crate) has_panic_handler: bool, // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally pub(crate) has_default_lib_allocator: bool, // 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 @@ -269,88 +279,131 @@ pub(crate) struct HashableCrateRoot { // 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) + // everything_downstream, there can only be a single one of each eii globally pub(crate) externally_implementable_items: Hashed>, // FIXME do we need to hash this? + // we only need the everything_downstream hashes from here included in our everything_downstream pub(crate) crate_deps: Hashed>, // FIXME do we need to hash this? + // lets say everything_downstream, but not sure pub(crate) dylib_dependency_formats: Hashed>>, // FIXME do we need to hash this? + // lets say everything_downstream, but not sure pub(crate) lib_features: Hashed>, // FIXME do we need to hash this? + // lets say everything_downstream, but not sure pub(crate) stability_implications: Hashed>, // FIXME do we need to hash this? + // everything_downstream, lang items impls are checked for uniqueness globally pub(crate) lang_items: Hashed>, // FIXME do we need to hash this? + // everything_downstream, lang items impls are checked for uniqueness globally pub(crate) lang_items_missing: Hashed>, // FIXME do we need to hash this? + // private_hash, only used when we are creating error diagnostics pub(crate) stripped_cfg_items: Hashed>>, // FIXME do we need to hash this? + // lets say everything_downstream for now, I think this is mostly used in the std pub(crate) diagnostic_items: Hashed>, // FIXME do we need to hash this? + // let say everything_downstream pub(crate) native_libraries: Hashed>, // FIXME do we need to hash this? + // dont know what this is used for. doc says `extern` blocks pub(crate) foreign_modules: Hashed>, // FIXME do we need to hash this? + // the traits defined in this crate. Definitely not needed in everything_downstream as is + // maybe we can leak private traits through MIR? pub(crate) traits: Hashed>, // FIXME do we need to hash this? + // the traits impls in this crate. Definitely not needed in everything_downstream as is + // how is it needed? pub(crate) impls: Hashed>, // FIXME do we need to hash this? + // what is this used for pub(crate) incoherent_impls: Hashed>, // FIXME do we need to hash this? + // what is this used for, some kind of const eval? pub(crate) interpret_alloc_index: Hashed>, // FIXME do we need to hash this? + // proc macro, ignored pub(crate) proc_macro_data: NoneIfHashed, // FIXME do we need to hash this? + // do it by table pub(crate) tables: Hashed, // FIXME do we need to hash this? + // likely don't only private. Do debuggers use this when debugging the linked binary? Then this + // is private_hash pub(crate) debugger_visualizers: Hashed>, // FIXME do we need to hash this? + // what is this, the ones marked a `pub`? pub(crate) exportable_items: Hashed>, // FIXME do we need to hash this? + // what is this extactly, used for diagnostics pub(crate) stable_order_of_exportable_impls: Hashed>, // FIXME do we need to hash this? + // what is this pub(crate) exported_non_generic_symbols: Hashed, SymbolExportInfo)>>, // FIXME do we need to hash this? + // i think this is used to find upstream_monomorphizations or what pub(crate) exported_generic_symbols: Hashed, SymbolExportInfo)>>, // FIXME do we need to hash this? + // some kind of macro expansion stuff? pub(crate) syntax_contexts: Hashed, // FIXME do we need to hash this? + // some kind of macro expansion stuff? pub(crate) expn_data: Hashed, // FIXME do we need to hash this? + // some kind of macro expansion stuff? pub(crate) expn_hashes: Hashed, // FIXME do we need to hash this? + // we stbale hash all localdefids currently to get this. Likely only need the ones that are + // somehow reachable pub(crate) def_path_hash_map: Hashed>>, // FIXME do we need to hash this? + // used for diagnostics and line info generation in codegen? To map span data to line/column + // data + // we need this while the spans are encoded like that. pub(crate) source_map: Hashed>>>, // FIXME do we need to hash this? + // everything_downstream, prbably not changing much pub(crate) target_modifiers: Hashed>, // FIXME do we need to hash this? + // everything_downstream? pub(crate) denied_partial_mitigations: Hashed>, // FIXME do we need to hash this? + // everything_downstream? pub(crate) compiler_builtins: bool, // FIXME do we need to hash this? + // everything_downstream? pub(crate) needs_allocator: bool, // FIXME do we need to hash this? + // everything_downstream? pub(crate) needs_panic_runtime: bool, // FIXME do we need to hash this? + // everything_downstream? pub(crate) no_builtins: bool, // FIXME do we need to hash this? + // everything_downstream? pub(crate) panic_runtime: bool, // FIXME do we need to hash this? + // everything_downstream? pub(crate) profiler_runtime: bool, // FIXME do we need to hash this? + // everything_downstream? pub(crate) symbol_mangling_version: SymbolManglingVersion, // FIXME do we need to hash this? + // everything_downstream? pub(crate) specialization_enabled_in: bool, } From 69d2f86b9972d7c6de5e882222aa6a7644d374db Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 11 May 2026 15:00:20 +0200 Subject: [PATCH 22/50] rdr: start building reachable graph --- compiler/rustc_metadata/src/rmeta/encoder.rs | 96 +++++++++++++++---- .../src/rmeta/encoder/public_api_hasher.rs | 55 ++++++++++- 2 files changed, 131 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 160c03b9e6e7a..4b054bcb7eddc 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -42,6 +42,7 @@ use self::public_api_hasher::{ }; use crate::eii::EiiMapEncodedKeyValue; use crate::errors::{FailCreateFileEncoder, FailWriteFile}; +use crate::rmeta::encoder::public_api_hasher::{LocalDefIdGraphBuilder, RecordMode}; use crate::rmeta::*; pub(super) mod public_api_hasher; @@ -74,7 +75,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: &'a HygieneEncodeContext, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, - encoded_crate_nums: Option>, + local_def_id_graph_builder: Option>, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -150,18 +151,20 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { panic!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate"); } self.emit_u32(crate_num.as_u32()); - if let Some(encoded_crate_nums) = self.encoded_crate_nums.as_mut() { - encoded_crate_nums[crate_num] = true; - } } fn encode_def_index(&mut self, def_index: DefIndex) { + // This is treated as if we are encoding a LocalDefId for now self.emit_u32(def_index.as_u32()); + self.record_encoded_local_def_id(def_index); } fn encode_def_id(&mut self, def_id: DefId) { def_id.krate.encode(self); - def_id.index.encode(self); + self.emit_u32(def_id.index.as_u32()); + if def_id.is_local() { + self.record_encoded_local_def_id(def_id.index); + } } fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { @@ -408,12 +411,20 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // 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, $hcx:ident) => {{ - record!($self.$tables.$table[$def_id] <- $value, $hcx, $value) + record!($self.$tables.$table[$def_id] <- $value, $hcx, $value, RecordMode::All) + }}; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, RecordMode::$mode:tt ) => {{ + record!($self.$tables.$table[$def_id] <- $value, $hcx, $value, RecordMode::$mode) + }}; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr ) => {{ + record!($self.$tables.$table[$def_id] <- $value, $hcx, $hashed_value, RecordMode::All) }}; - ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr) => {{ + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr, $record_mode:expr) => {{ { let value = $value; - let lazy = $self.lazy(value); + let lazy = $self.with_record_mode_and_index($record_mode, $def_id.index, |this| + this.lazy(value) + ); $self.$tables.$table.set_some_hashed( $def_id.index, lazy, @@ -428,13 +439,18 @@ macro_rules! record { // 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, $hcx:ident) => { - record_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v) + record_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v, RecordMode::All) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => { + record_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::All) }; - ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => {{ + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr, $record_mode:expr) => {{ { let value = $value; let mut hasher = $self.$tables.$table.iter_hasher(); - let lazy = $self.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)); + let lazy = $self.with_record_mode_and_index($record_mode, $def_id.index, |this| + this.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)) + ); $self.$tables.$table.set_some_hashed( $def_id.index, lazy, @@ -447,13 +463,18 @@ macro_rules! record_array { macro_rules! record_defaulted_array { ($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) + record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v, RecordMode::All) }; - ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => {{ + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => { + record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::All) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr, $record_mode:expr) => {{ { let value = $value; let mut hasher = $self.$tables.$table.iter_hasher(); - let lazy = $self.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)); + let lazy = $self.with_record_mode_and_index($record_mode, $def_id.index, |this| + this.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)) + ); $self.$tables.$table.set_hashed( $def_id.index, lazy, @@ -496,6 +517,44 @@ macro_rules! hashed_lazy_array { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { + fn with_record_mode(&mut self, mode: RecordMode, f: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + if let Some(local_def_id_graph_builder) = self.local_def_id_graph_builder.as_mut() { + let old = local_def_id_graph_builder.record_mode; + local_def_id_graph_builder.record_mode = mode; + let res = f(self); + self.local_def_id_graph_builder.as_mut().unwrap().record_mode = old; + res + } else { + f(self) + } + } + + fn with_record_mode_and_index(&mut self, mode: RecordMode, from: DefIndex, f: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + if let Some(local_def_id_graph_builder) = self.local_def_id_graph_builder.as_mut() { + let old = local_def_id_graph_builder.record_mode; + let old_from = local_def_id_graph_builder.from; + local_def_id_graph_builder.record_mode = mode; + local_def_id_graph_builder.from = Some(LocalDefId { local_def_index: from }); + let res = f(self); + self.local_def_id_graph_builder.as_mut().unwrap().record_mode = old; + self.local_def_id_graph_builder.as_mut().unwrap().from = old_from; + res + } else { + f(self) + } + } + fn record_encoded_local_def_id(&mut self, to: DefIndex) { + if let Some(local_def_id_graph_builder) = self.local_def_id_graph_builder.as_mut() { + local_def_id_graph_builder.record(to); + } + } + fn emit_lazy_distance(&mut self, position: NonZero) { let pos = position.get(); let distance = match self.lazy_state { @@ -1908,10 +1967,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { 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()), hcx, |def_id| 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()), hcx); + module_children.iter().filter(|child| !child.reexport_chain.is_empty()), hcx + ); let ambig_module_children = tcx .resolutions(()) @@ -2848,8 +2909,7 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, symbol_index_table: Default::default(), - encoded_crate_nums: hash_public_api - .then(|| IndexVec::from_elem_n(false, tcx.crates(()).len() + 1)), + local_def_id_graph_builder: hash_public_api.then(|| LocalDefIdGraphBuilder::new(tcx)), }; // Encode the rustc version string in a predictable location. 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 5c0fadd9335b1..d4271e77e1a7d 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -1,23 +1,25 @@ use std::fmt; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::svh::Svh; use rustc_hir::LangItem; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def_id::DefIndex; -use rustc_index::Idx; +use rustc_index::{Idx, IndexVec}; use rustc_macros::StableHash; use rustc_middle::ich::StableHashingContext; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::middle::lib_features::FeatureStability; +use rustc_middle::middle::privacy::{EffectiveVisibilities, Level}; use rustc_middle::ty::TyCtxt; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; use rustc_span::Symbol; -use rustc_span::def_id::{LOCAL_CRATE, StableCrateId}; +use rustc_span::def_id::{LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_span::edition::Edition; use rustc_target::spec::PanicStrategy; use tracing::debug; @@ -493,3 +495,52 @@ impl HashableCrateRoot { } } } + +#[derive(Clone, Copy)] +pub(crate) enum RecordMode { + All, + ReachableAtLevel(Level), + None, +} + +impl RecordMode { + pub(super) fn through_impl_trait() -> Self { + Self::ReachableAtLevel(Level::ReachableThroughImplTrait) + } +} + +pub(super) struct LocalDefIdGraphBuilder<'tcx> { + pub(super) from: Option, + pub(super) record_mode: RecordMode, + graph: IndexVec>, + effective_visibilities: &'tcx EffectiveVisibilities, +} + +impl<'tcx> LocalDefIdGraphBuilder<'tcx> { + pub(super) fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { + from: None, + record_mode: RecordMode::All, + graph: IndexVec::from_elem_n( + Default::default(), + tcx.definitions_untracked().def_index_count(), + ), + effective_visibilities: tcx.effective_visibilities(()), + } + } + + pub(super) fn record(&mut self, to: DefIndex) { + let to = LocalDefId { local_def_index: to }; + match self.record_mode { + RecordMode::All => { + self.graph[self.from.unwrap()].insert(to); + } + RecordMode::ReachableAtLevel(level) => { + if self.effective_visibilities.is_public_at_level(to, level) { + self.graph[self.from.unwrap()].insert(to); + } + } + RecordMode::None => (), + } + } +} From 8895a17c04b5b9d4d2917f965872a9bed6eb524c Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 11 May 2026 15:00:57 +0200 Subject: [PATCH 23/50] rdr: document what exportable_items is used for --- compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs | 3 ++- 1 file changed, 2 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 d4271e77e1a7d..2061659a3c663 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -341,7 +341,8 @@ pub(crate) struct HashableCrateRoot { pub(crate) debugger_visualizers: Hashed>, // FIXME do we need to hash this? - // what is this, the ones marked a `pub`? + // this is used to do symbol mangling in downstream crates. We should only include ones that + // are somehow reachable. pub(crate) exportable_items: Hashed>, // FIXME do we need to hash this? // what is this extactly, used for diagnostics From 646aeef470f67a5b58d804e81aacde3bf0b6e139 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 12 May 2026 08:52:29 +0200 Subject: [PATCH 24/50] rdr: save TraitImpls defids as (u32,u32) to avoid encoding them as DefIndex, which is interpreted as LocalDefId for now --- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 ++-- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index eb8a1ba579d5f..14a5acf2893d4 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -98,7 +98,7 @@ pub(crate) struct CrateMetadata { /// Trait impl data. /// FIXME: Used only from queries and can use query cache, /// so pre-decoding can probably be avoided. - trait_impls: FxIndexMap<(u32, DefIndex), LazyArray<(DefIndex, Option)>>, + trait_impls: FxIndexMap<(u32, u32), LazyArray<(DefIndex, Option)>>, /// Inherent impls which do not follow the normal coherence rules. /// /// These can be introduced using either `#![rustc_coherence_is_core]` @@ -1447,7 +1447,7 @@ impl CrateMetadata { // Do a reverse lookup beforehand to avoid touching the crate_num // hash map in the loop below. let key = match self.reverse_translate_def_id(trait_def_id) { - Some(def_id) => (def_id.krate.as_u32(), def_id.index), + Some(def_id) => (def_id.krate.as_u32(), def_id.index.as_u32()), None => return &[], }; diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 4b054bcb7eddc..5803be19829a6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2548,7 +2548,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { hasher.digest(trait_def_id, hcx); hasher.digest(impls, hcx); TraitImpls { - trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index), + trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index.as_u32()), impls: self.lazy_array(impls.iter().map(|(id, ty)| (id.local_def_index, *ty))), } }) diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 1df0b3e9e8fff..32f63465e350a 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -358,7 +358,7 @@ pub(crate) struct CrateDep { #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct TraitImpls { - trait_id: (u32, DefIndex), + trait_id: (u32, u32), impls: LazyArray<(DefIndex, Option)>, } From f8f2b5809432e3a861a349f119056543a0041470 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 14 May 2026 10:11:26 +0200 Subject: [PATCH 25/50] use SyntaxContext instead of raw u32 in HygieneEncodeContext::encode --- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- compiler/rustc_middle/src/query/on_disk_cache.rs | 2 +- compiler/rustc_span/src/hygiene.rs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5803be19829a6..72a96fac460ff 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2222,7 +2222,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table), |(this, syntax_contexts, _, _), index, ctxt_data| { syntax_contexts.set_some_hashed( - index, + index.as_u32(), this.lazy(ctxt_data), ctxt_data, &mut hcx.borrow_mut(), diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 3bbbe3c184eb0..891ce8f1b9ee6 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -283,7 +283,7 @@ impl OnDiskCache { |encoder, index, ctxt_data| { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data); - syntax_contexts.insert(index, pos); + syntax_contexts.insert(index.as_u32(), pos); }, |encoder, expn_id, data, hash| { if expn_id.krate == LOCAL_CRATE { diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index d95c94513c1dd..6a36a6f199f54 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -700,7 +700,7 @@ impl SyntaxContext { } #[inline] - pub(crate) const fn as_u32(self) -> u32 { + pub const fn as_u32(self) -> u32 { self.0 } @@ -1299,7 +1299,7 @@ impl HygieneEncodeContext { pub fn encode( &self, encoder: &mut T, - mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextKey), + mut encode_ctxt: impl FnMut(&mut T, SyntaxContext, &SyntaxContextKey), mut encode_expn: impl FnMut(&mut T, ExpnId, &ExpnData, ExpnHash), ) { // When we serialize a `SyntaxContextData`, we may end up serializing @@ -1324,7 +1324,7 @@ impl HygieneEncodeContext { }); for (ctxt, ctxt_key) in all_ctxt_data { if self.serialized_ctxts.lock().insert(ctxt) { - encode_ctxt(encoder, ctxt.0, &ctxt_key); + encode_ctxt(encoder, ctxt, &ctxt_key); } } From 243669ce5fb3a7d915b8217a3b6db2ab6b5c0539 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Tue, 19 May 2026 19:25:00 +0200 Subject: [PATCH 26/50] hash stuff reachable through the API --- compiler/rustc_metadata/src/rmeta/encoder.rs | 464 +++++++++--------- .../src/rmeta/encoder/public_api_hasher.rs | 373 ++++++++++++-- compiler/rustc_metadata/src/rmeta/mod.rs | 4 +- compiler/rustc_metadata/src/rmeta/table.rs | 10 +- compiler/rustc_middle/src/ty/codec.rs | 38 +- 5 files changed, 600 insertions(+), 289 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 72a96fac460ff..5a745c6171014 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -24,15 +24,15 @@ use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; -use rustc_middle::ty::{AssocContainer, Visibility}; +use rustc_middle::ty::{AssocContainer, PredicateKind, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{CrateType, OptLevel, TargetModifier}; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ - ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, - Symbol, SyntaxContext, sym, + ByteSymbol, ExternalSource, FileName, SourceFile, SpanEncoder, StableSourceFileId, Symbol, + SyntaxContext, sym, }; use tracing::{debug, instrument, trace}; @@ -42,7 +42,7 @@ use self::public_api_hasher::{ }; use crate::eii::EiiMapEncodedKeyValue; use crate::errors::{FailCreateFileEncoder, FailWriteFile}; -use crate::rmeta::encoder::public_api_hasher::{LocalDefIdGraphBuilder, RecordMode}; +use crate::rmeta::encoder::public_api_hasher::{IndexGraphBuilder, LocalNode, Node, RecordMode}; use crate::rmeta::*; pub(super) mod public_api_hasher; @@ -54,7 +54,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { tables: TableBuilders, lazy_state: LazyState, - span_shorthands: FxHashMap, + span_shorthands: FxHashMap>)>, type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, @@ -75,7 +75,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: &'a HygieneEncodeContext, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, - local_def_id_graph_builder: Option>, + index_graph_builder: Option>, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -156,18 +156,18 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { fn encode_def_index(&mut self, def_index: DefIndex) { // This is treated as if we are encoding a LocalDefId for now self.emit_u32(def_index.as_u32()); - self.record_encoded_local_def_id(def_index); + self.record_encoded_index(LocalDefId { local_def_index: def_index }); } fn encode_def_id(&mut self, def_id: DefId) { def_id.krate.encode(self); + // this must not be encoded as a DefIndex, as that would record it as a LocalDefId self.emit_u32(def_id.index.as_u32()); - if def_id.is_local() { - self.record_encoded_local_def_id(def_id.index); - } + self.record_encoded_index(def_id); } fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { + self.record_encoded_index(syntax_context); rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self); } @@ -181,6 +181,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } expn_id.krate.encode(self); expn_id.local_id.encode(self); + self.record_encoded_index(expn_id); } fn encode_span(&mut self, span: Span) { @@ -188,7 +189,13 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { Entry::Occupied(o) => { // If an offset is smaller than the absolute position, we encode with the offset. // This saves space since smaller numbers encode in less bits. - let last_location = *o.get(); + let (last_location, edges) = o.get(); + if let Some(local_def_id_graph_builder) = self.index_graph_builder.as_mut() { + for edge in edges { + local_def_id_graph_builder.record(*edge); + } + } + let last_location = *last_location; // This cannot underflow. Metadata is written with increasing position(), so any // previously saved offset must be smaller than the current position. let offset = self.opaque.position() - last_location; @@ -208,11 +215,11 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { }); } } - Entry::Vacant(v) => { + Entry::Vacant(_v) => { let position = self.opaque.position(); - v.insert(position); // Data is encoded with a SpanTag prefix (see below). - span.data().encode(self); + let edges = encode_span_data(span, self); + self.span_shorthands.insert(span, (position, edges)); } } } @@ -232,150 +239,170 @@ fn bytes_needed(n: usize) -> usize { (usize::BITS - n.leading_zeros()).div_ceil(u8::BITS) as usize } -impl<'a, 'tcx> Encodable> for SpanData { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - // Don't serialize any `SyntaxContext`s from a proc-macro crate, - // since we don't load proc-macro dependencies during serialization. - // This means that any hygiene information from macros used *within* - // a proc-macro crate (e.g. invoking a macro that expands to a proc-macro - // definition) will be lost. - // - // This can show up in two ways: - // - // 1. Any hygiene information associated with identifier of - // a proc macro (e.g. `#[proc_macro] pub fn $name`) will be lost. - // Since proc-macros can only be invoked from a different crate, - // real code should never need to care about this. - // - // 2. Using `Span::def_site` or `Span::mixed_site` will not - // include any hygiene information associated with the definition - // site. This means that a proc-macro cannot emit a `$crate` - // identifier which resolves to one of its dependencies, - // which also should never come up in practice. - // - // Additionally, this affects `Span::parent`, and any other - // span inspection APIs that would otherwise allow traversing - // the `SyntaxContexts` associated with a span. - // - // None of these user-visible effects should result in any - // cross-crate inconsistencies (getting one behavior in the same - // crate, and a different behavior in another crate) due to the - // limited surface that proc-macros can expose. - // - // IMPORTANT: If this is ever changed, be sure to update - // `rustc_span::hygiene::raw_encode_expn_id` to handle - // encoding `ExpnData` for proc-macro crates. - let ctxt = if s.is_proc_macro { SyntaxContext::root() } else { self.ctxt }; +fn encode_span_data<'a, 'tcx>(span: Span, s: &mut EncodeContext<'a, 'tcx>) -> Vec> { + let this = span.data(); + let mut nodes = Vec::>::new(); + // Don't serialize any `SyntaxContext`s from a proc-macro crate, + // since we don't load proc-macro dependencies during serialization. + // This means that any hygiene information from macros used *within* + // a proc-macro crate (e.g. invoking a macro that expands to a proc-macro + // definition) will be lost. + // + // This can show up in two ways: + // + // 1. Any hygiene information associated with identifier of + // a proc macro (e.g. `#[proc_macro] pub fn $name`) will be lost. + // Since proc-macros can only be invoked from a different crate, + // real code should never need to care about this. + // + // 2. Using `Span::def_site` or `Span::mixed_site` will not + // include any hygiene information associated with the definition + // site. This means that a proc-macro cannot emit a `$crate` + // identifier which resolves to one of its dependencies, + // which also should never come up in practice. + // + // Additionally, this affects `Span::parent`, and any other + // span inspection APIs that would otherwise allow traversing + // the `SyntaxContexts` associated with a span. + // + // None of these user-visible effects should result in any + // cross-crate inconsistencies (getting one behavior in the same + // crate, and a different behavior in another crate) due to the + // limited surface that proc-macros can expose. + // + // IMPORTANT: If this is ever changed, be sure to update + // `rustc_span::hygiene::raw_encode_expn_id` to handle + // encoding `ExpnData` for proc-macro crates. + let ctxt = if s.is_proc_macro { SyntaxContext::root() } else { this.ctxt }; - if self.is_dummy() { - let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); - tag.encode(s); - if tag.context().is_none() { - ctxt.encode(s); - } - return; + if s.index_graph_builder.is_some() { + nodes.push(ctxt.into()); + } + if this.is_dummy() { + let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); + tag.encode(s); + if tag.context().is_none() { + ctxt.encode(s); + } else { + s.record_encoded_index(ctxt); } + return nodes; + } - // The Span infrastructure should make sure that this invariant holds: - debug_assert!(self.lo <= self.hi); + // The Span infrastructure should make sure that this invariant holds: + debug_assert!(this.lo <= this.hi); - if !s.source_file_cache.0.contains(self.lo) { - let source_map = s.tcx.sess.source_map(); - let source_file_index = source_map.lookup_source_file_idx(self.lo); - s.source_file_cache = - (Arc::clone(&source_map.files()[source_file_index]), source_file_index); - } - let (ref source_file, source_file_index) = s.source_file_cache; - debug_assert!(source_file.contains(self.lo)); + if !s.source_file_cache.0.contains(this.lo) { + let source_map = s.tcx.sess.source_map(); + let source_file_index = source_map.lookup_source_file_idx(this.lo); + s.source_file_cache = + (Arc::clone(&source_map.files()[source_file_index]), source_file_index); + } + let (ref source_file, source_file_index) = s.source_file_cache; + debug_assert!(source_file.contains(this.lo)); - if !source_file.contains(self.hi) { - // Unfortunately, macro expansion still sometimes generates Spans - // that malformed in this way. - let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); - tag.encode(s); - if tag.context().is_none() { - ctxt.encode(s); - } - return; + if !source_file.contains(this.hi) { + // Unfortunately, macro expansion still sometimes generates Spans + // that malformed in this way. + let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); + tag.encode(s); + if tag.context().is_none() { + ctxt.encode(s); + } else { + s.record_encoded_index(ctxt); } - - // There are two possible cases here: - // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the - // crate we are writing metadata for. When the metadata for *this* crate gets - // deserialized, the deserializer will need to know which crate it originally came - // from. We use `TAG_VALID_SPAN_FOREIGN` to indicate that a `CrateNum` should - // be deserialized after the rest of the span data, which tells the deserializer - // which crate contains the source map information. - // 2. This span comes from our own crate. No special handling is needed - we just - // write `TAG_VALID_SPAN_LOCAL` to let the deserializer know that it should use - // our own source map information. + return nodes; + } + + // There are two possible cases here: + // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the + // crate we are writing metadata for. When the metadata for *this* crate gets + // deserialized, the deserializer will need to know which crate it originally came + // from. We use `TAG_VALID_SPAN_FOREIGN` to indicate that a `CrateNum` should + // be deserialized after the rest of the span data, which tells the deserializer + // which crate contains the source map information. + // 2. This span comes from our own crate. No special handling is needed - we just + // write `TAG_VALID_SPAN_LOCAL` to let the deserializer know that it should use + // our own source map information. + // + // If we're a proc-macro crate, we always treat this as a local `Span`. + // In `encode_source_map`, we serialize foreign `SourceFile`s into our metadata + // if we're a proc-macro crate. + // This allows us to avoid loading the dependencies of proc-macro crates: all of + // the information we need to decode `Span`s is stored in the proc-macro crate. + let source_file_start = source_file.start_pos; + let (kind, metadata_index) = if source_file.is_imported() && !s.is_proc_macro { + // To simplify deserialization, we 'rebase' this span onto the crate it originally came + // from (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' + // values are relative to the source map information for the 'foreign' crate whose + // CrateNum we write into the metadata. This allows `imported_source_files` to binary + // search through the 'foreign' crate's source map information, using the + // deserialized 'lo' and 'hi' values directly. // - // If we're a proc-macro crate, we always treat this as a local `Span`. - // In `encode_source_map`, we serialize foreign `SourceFile`s into our metadata - // if we're a proc-macro crate. - // This allows us to avoid loading the dependencies of proc-macro crates: all of - // the information we need to decode `Span`s is stored in the proc-macro crate. - let (kind, metadata_index) = if source_file.is_imported() && !s.is_proc_macro { - // To simplify deserialization, we 'rebase' this span onto the crate it originally came - // from (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' - // values are relative to the source map information for the 'foreign' crate whose - // CrateNum we write into the metadata. This allows `imported_source_files` to binary - // search through the 'foreign' crate's source map information, using the - // deserialized 'lo' and 'hi' values directly. - // - // All of this logic ensures that the final result of deserialization is a 'normal' - // Span that can be used without any additional trouble. - let metadata_index = { - // Introduce a new scope so that we drop the 'read()' temporary - match &*source_file.external_src.read() { - ExternalSource::Foreign { metadata_index, .. } => *metadata_index, - src => panic!("Unexpected external source {src:?}"), - } - }; + // All of this logic ensures that the final result of deserialization is a 'normal' + // Span that can be used without any additional trouble. + let metadata_index = { + // Introduce a new scope so that we drop the 'read()' temporary + match &*source_file.external_src.read() { + ExternalSource::Foreign { metadata_index, .. } => *metadata_index, + src => panic!("Unexpected external source {src:?}"), + } + }; + (SpanKind::Foreign, metadata_index) + } else { + // Record the fact that we need to encode the data for this `SourceFile` + let source_files = s.required_source_files.as_mut().expect("Already encoded SourceMap!"); + let (metadata_index, _) = source_files.insert_full(source_file_index); + let metadata_index: u32 = + metadata_index.try_into().expect("cannot export more than U32_MAX files"); - (SpanKind::Foreign, metadata_index) + (SpanKind::Local, metadata_index) + }; + if s.index_graph_builder.is_some() { + if let Some(parent) = this.parent { + let node = Node::ParentSpan(parent); + nodes.push(node); + s.record_encoded_index(node); } else { - // Record the fact that we need to encode the data for this `SourceFile` - let source_files = - s.required_source_files.as_mut().expect("Already encoded SourceMap!"); - let (metadata_index, _) = source_files.insert_full(source_file_index); - let metadata_index: u32 = - metadata_index.try_into().expect("cannot export more than U32_MAX files"); - - (SpanKind::Local, metadata_index) - }; + let node = Node::ParentlessSpan(span); + nodes.push(node); + s.record_encoded_index(node); + } + } - // Encode the start position relative to the file start, so we profit more from the - // variable-length integer encoding. - // IMPORTANT: if this is ever changed, the public api span hashing must be updated. It - // currently uses the `hash_spans_as_parentless` option to make sure spans are hashed not - // relative to their parent, but relative to their file. - let lo = self.lo - source_file.start_pos; + // Encode the start position relative to the file start, so we profit more from the + // variable-length integer encoding. + // IMPORTANT: if this is ever changed, the public api span hashing must be updated. It + // currently uses the `hash_spans_as_parentless` option to make sure spans are hashed not + // relative to their parent, but relative to their file. + let lo = this.lo - source_file_start; - // Encode length which is usually less than span.hi and profits more - // from the variable-length integer encoding that we use. - let len = self.hi - self.lo; + // Encode length which is usually less than span.hi and profits more + // from the variable-length integer encoding that we use. + let len = this.hi - this.lo; - let tag = SpanTag::new(kind, ctxt, len.0 as usize); - tag.encode(s); - if tag.context().is_none() { - ctxt.encode(s); - } - lo.encode(s); - if tag.length().is_none() { - len.encode(s); - } + let tag = SpanTag::new(kind, ctxt, len.0 as usize); + tag.encode(s); + if tag.context().is_none() { + ctxt.encode(s); + } else { + s.record_encoded_index(ctxt); + } + lo.encode(s); + if tag.length().is_none() { + len.encode(s); + } - // Encode the index of the `SourceFile` for the span, in order to make decoding faster. - metadata_index.encode(s); + // Encode the index of the `SourceFile` for the span, in order to make decoding faster. + metadata_index.encode(s); - if kind == SpanKind::Foreign { - // This needs to be two lines to avoid holding the `s.source_file_cache` - // while calling `cnum.encode(s)` - let cnum = s.source_file_cache.0.cnum; - cnum.encode(s); - } + if kind == SpanKind::Foreign { + // This needs to be two lines to avoid holding the `s.source_file_cache` + // while calling `cnum.encode(s)` + let cnum = s.source_file_cache.0.cnum; + cnum.encode(s); } + nodes } impl<'a, 'tcx> Encodable> for [u8] { @@ -392,10 +419,36 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { self.opaque.position() } + fn type_encode_begin(&mut self, ty: Ty<'tcx>) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record(Node::Ty(ty)); + index_graph_builder.record_mode.push(RecordMode::from(LocalNode::Ty(ty))); + } + } + + fn type_encode_end(&mut self) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record_mode.pop(); + } + } + fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.type_shorthands } + fn predicate_encode_begin(&mut self, pred: PredicateKind<'tcx>) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record(Node::Predicate(pred)); + index_graph_builder.record_mode.push(RecordMode::from(LocalNode::Predicate(pred))); + } + } + + fn predicate_encode_end(&mut self) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record_mode.pop(); + } + } + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.predicate_shorthands } @@ -411,18 +464,18 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { // 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, $hcx:ident) => {{ - record!($self.$tables.$table[$def_id] <- $value, $hcx, $value, RecordMode::All) + record!($self.$tables.$table[$def_id] <- $value, $hcx, $value, RecordMode::from) }}; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, RecordMode::$mode:tt ) => {{ record!($self.$tables.$table[$def_id] <- $value, $hcx, $value, RecordMode::$mode) }}; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr ) => {{ - record!($self.$tables.$table[$def_id] <- $value, $hcx, $hashed_value, RecordMode::All) + record!($self.$tables.$table[$def_id] <- $value, $hcx, $hashed_value, RecordMode::from) }}; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr, $record_mode:expr) => {{ { let value = $value; - let lazy = $self.with_record_mode_and_index($record_mode, $def_id.index, |this| + let lazy = $self.with_record_mode($record_mode(LocalNode::DefId($def_id.expect_local())), |this| this.lazy(value) ); $self.$tables.$table.set_some_hashed( @@ -439,16 +492,16 @@ macro_rules! record { // 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, $hcx:ident) => { - record_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v, RecordMode::All) + record_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v, RecordMode::from) }; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => { - record_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::All) + record_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::from) }; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr, $record_mode:expr) => {{ { let value = $value; let mut hasher = $self.$tables.$table.iter_hasher(); - let lazy = $self.with_record_mode_and_index($record_mode, $def_id.index, |this| + let lazy = $self.with_record_mode($record_mode(LocalNode::DefId($def_id.expect_local())), |this| this.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)) ); $self.$tables.$table.set_some_hashed( @@ -463,16 +516,16 @@ macro_rules! record_array { macro_rules! record_defaulted_array { ($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, RecordMode::All) + record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, |v| v, RecordMode::from) }; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => { - record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::All) + record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::from) }; ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr, $record_mode:expr) => {{ { let value = $value; let mut hasher = $self.$tables.$table.iter_hasher(); - let lazy = $self.with_record_mode_and_index($record_mode, $def_id.index, |this| + let lazy = $self.with_record_mode($record_mode(LocalNode::DefId($def_id.expect_local())), |this| this.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)) ); $self.$tables.$table.set_hashed( @@ -517,41 +570,23 @@ macro_rules! hashed_lazy_array { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - fn with_record_mode(&mut self, mode: RecordMode, f: F) -> T + fn with_record_mode(&mut self, mode: RecordMode<'tcx>, f: F) -> T where F: FnOnce(&mut Self) -> T, { - if let Some(local_def_id_graph_builder) = self.local_def_id_graph_builder.as_mut() { - let old = local_def_id_graph_builder.record_mode; - local_def_id_graph_builder.record_mode = mode; + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record_mode.push(mode); let res = f(self); - self.local_def_id_graph_builder.as_mut().unwrap().record_mode = old; + self.index_graph_builder.as_mut().unwrap().record_mode.pop(); res } else { f(self) } } - fn with_record_mode_and_index(&mut self, mode: RecordMode, from: DefIndex, f: F) -> T - where - F: FnOnce(&mut Self) -> T, - { - if let Some(local_def_id_graph_builder) = self.local_def_id_graph_builder.as_mut() { - let old = local_def_id_graph_builder.record_mode; - let old_from = local_def_id_graph_builder.from; - local_def_id_graph_builder.record_mode = mode; - local_def_id_graph_builder.from = Some(LocalDefId { local_def_index: from }); - let res = f(self); - self.local_def_id_graph_builder.as_mut().unwrap().record_mode = old; - self.local_def_id_graph_builder.as_mut().unwrap().from = old_from; - res - } else { - f(self) - } - } - fn record_encoded_local_def_id(&mut self, to: DefIndex) { - if let Some(local_def_id_graph_builder) = self.local_def_id_graph_builder.as_mut() { - local_def_id_graph_builder.record(to); + fn record_encoded_index(&mut self, index: impl Into>) { + if let Some(local_def_id_graph_builder) = self.index_graph_builder.as_mut() { + local_def_id_graph_builder.record(index.into()); } } @@ -689,7 +724,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // is done. let required_source_files = self.required_source_files.take().unwrap(); - let mut adapted = TableBuilder::, _, _>::default(); + // we don't hash the files here. What we need to hash for the public api comes from + let mut adapted = TableBuilder::, _, _>::default(); let local_crate_stable_id = self.tcx.stable_crate_id(LOCAL_CRATE); @@ -742,39 +778,7 @@ 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_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.set_some_unhashed(on_disk_index, self.lazy(&adapted_source_file)); } adapted.encode(&mut self.opaque, hcx) @@ -965,7 +969,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_path_hash_map, specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), }; - let crate_root = crate_root.into_crate_root(self.tcx, hcx); + let crate_root = crate_root.into_crate_root(self, hcx); let hashes = crate_root.header.hashes; let root = stat!("final", || { self.lazy(crate_root) }); @@ -2221,23 +2225,27 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { 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_hashed( - index.as_u32(), - 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_hashed( - index.as_raw(), - this.lazy(expn_data), + this.with_record_mode(RecordMode::From(LocalNode::SyntaxContext(index)), |this| { + syntax_contexts.set_some_hashed( index, + this.lazy(ctxt_data), + ctxt_data, &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)); + }); + }, + |(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| { + if let Some(index) = index.as_local() { + this.with_record_mode(RecordMode::From(LocalNode::ExpnId(index)), |this| { + expn_data_table.set_some_hashed( + index, + 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)); + }); } }, ); @@ -2263,7 +2271,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { 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 encoded_span = self.lazy(span); - self.tables.proc_macro_quoted_spans.set_some_hashed(i, encoded_span, span, hcx); + self.tables.proc_macro_quoted_spans.set_some_unhashed(i, encoded_span); } self.tables.def_kind.set_some_local_hashed(CRATE_DEF_ID, DefKind::Mod, hcx); @@ -2909,7 +2917,7 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, symbol_index_table: Default::default(), - local_def_id_graph_builder: hash_public_api.then(|| LocalDefIdGraphBuilder::new(tcx)), + index_graph_builder: hash_public_api.then(|| Default::default()), }; // Encode the rustc version string in a predictable location. 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 2061659a3c663..aaf9592fa1b9b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -1,9 +1,14 @@ +use core::iter::Iterator; use std::fmt; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::graph::scc::{Annotation, Annotations, Sccs}; +use rustc_data_structures::graph::{DirectedGraph, Successors}; +use rustc_data_structures::indexmap::map::Entry; use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; use rustc_data_structures::svh::Svh; +use rustc_data_structures::unord::UnordMap; use rustc_hir::LangItem; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def_id::DefIndex; @@ -13,21 +18,22 @@ use rustc_middle::ich::StableHashingContext; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::middle::lib_features::FeatureStability; -use rustc_middle::middle::privacy::{EffectiveVisibilities, Level}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::data_structures::IndexMap; +use rustc_middle::ty::{PredicateKind, Ty}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; -use rustc_span::Symbol; -use rustc_span::def_id::{LOCAL_CRATE, LocalDefId, StableCrateId}; +use rustc_span::def_id::{DefId, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_span::edition::Edition; +use rustc_span::hygiene::ExpnIndex; +use rustc_span::{ExpnId, LocalExpnId, Span, Symbol, SyntaxContext}; use rustc_target::spec::PanicStrategy; use tracing::debug; use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; use crate::rmeta::{ - CrateHashes, DefPathHashMapRef, EiiMapEncodedKeyValue, ExpnDataTable, ExpnHashTable, - IncoherentImpls, LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, + CrateHashes, DefPathHashMapRef, EiiMapEncodedKeyValue, EncodeContext, ExpnDataTable, + ExpnHashTable, IncoherentImpls, LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, SyntaxContextTable, TraitImpls, }; @@ -64,8 +70,12 @@ impl PublicApiHasher { pub(crate) trait TablePublicApiHasher: Default { type IterHasher; - fn digest(&mut self, index: I, value: V, hcx: &mut PublicApiHashingContext<'_>) - where + fn digest( + &mut self, + index: impl TableIndex, + value: V, + hcx: &mut PublicApiHashingContext<'_>, + ) where V: StableHash; fn finish(&self, hcx: &mut PublicApiHashingContext<'_>) -> Option; @@ -86,28 +96,35 @@ impl Default for RDRHashAll { pub(crate) struct PublicApiHashingContext<'a> { pub(crate) hcx: StableHashingContext<'a>, hash_public_api: bool, + def_id_hashes: IndexGraphHashes, } impl<'a> PublicApiHashingContext<'a> { pub(crate) fn new(hash_public_api: bool, hcx: StableHashingContext<'a>) -> Self { - Self { hash_public_api, hcx } + Self { hash_public_api, hcx, def_id_hashes: Default::default() } } } impl TablePublicApiHasher for RDRHashAll { type IterHasher = OrderedIterHasher; - fn digest(&mut self, index: I, value: V, hcx: &mut PublicApiHashingContext<'_>) - where + fn digest( + &mut self, + index: impl TableIndex, + value: V, + hcx: &mut PublicApiHashingContext<'_>, + ) where V: StableHash, { 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).stable_hash(&mut hcx.hcx, &mut hasher); + value.stable_hash(&mut hcx.hcx, &mut hasher); let hash: Fingerprint = hasher.finish(); - self.hash = self.hash.combine_commutative(hash); + let idx_hash = hcx.def_id_hashes.get_mut(index); + *idx_hash = idx_hash.combine_commutative(hash); + // remove this later, not needed anymore + self.hash.combine_commutative(hash); } fn finish(&self, hcx: &mut PublicApiHashingContext<'_>) -> Option { @@ -154,8 +171,12 @@ impl Default for RDRHashNone { impl TablePublicApiHasher for RDRHashNone { type IterHasher = RDRHashNone<()>; - fn digest(&mut self, _index: I, _value: V, _hcx: &mut PublicApiHashingContext<'_>) - where + fn digest( + &mut self, + _index: impl TableIndex, + _value: V, + _hcx: &mut PublicApiHashingContext<'_>, + ) where V: StableHash, { } @@ -413,11 +434,14 @@ pub(crate) struct HashableCrateRoot { impl HashableCrateRoot { pub(super) fn into_crate_root( self, - tcx: TyCtxt<'_>, + ecx: &mut EncodeContext<'_, '_>, hcx: &mut PublicApiHashingContext<'_>, ) -> CrateRoot { + let tcx = ecx.tcx; let hashes = if hcx.hash_public_api { assert!(!self.header.is_proc_macro_crate); + let graph = ecx.index_graph_builder.take().unwrap().build_graph(&mut hcx.hcx); + let mut hasher = StableHasher::default(); self.stable_hash(&mut hcx.hcx, &mut hasher); let public_hash = Svh::new(hasher.finish()); @@ -497,51 +521,300 @@ impl HashableCrateRoot { } } -#[derive(Clone, Copy)] -pub(crate) enum RecordMode { - All, - ReachableAtLevel(Level), - None, +pub(super) enum RecordMode<'tcx> { + From(LocalNode<'tcx>), } -impl RecordMode { - pub(super) fn through_impl_trait() -> Self { - Self::ReachableAtLevel(Level::ReachableThroughImplTrait) +impl<'tcx> RecordMode<'tcx> { + pub(super) fn from(from: LocalNode<'tcx>) -> Self { + Self::From(from) } } -pub(super) struct LocalDefIdGraphBuilder<'tcx> { - pub(super) from: Option, - pub(super) record_mode: RecordMode, - graph: IndexVec>, - effective_visibilities: &'tcx EffectiveVisibilities, +struct IndexGraph<'tcx> { + nodes: IndexMap, Fingerprint>, + edges: IndexVec>, + roots: Vec, +} + +pub(super) struct IndexGraphBuilder<'tcx> { + pub(super) record_mode: Vec>, + edges: FxHashMap, FxHashSet>>, + roots: FxHashSet>, } -impl<'tcx> LocalDefIdGraphBuilder<'tcx> { - pub(super) fn new(tcx: TyCtxt<'tcx>) -> Self { +impl Default for IndexGraphBuilder<'_> { + fn default() -> Self { Self { - from: None, - record_mode: RecordMode::All, - graph: IndexVec::from_elem_n( - Default::default(), - tcx.definitions_untracked().def_index_count(), - ), - effective_visibilities: tcx.effective_visibilities(()), + record_mode: Default::default(), + edges: Default::default(), + roots: [Node::DefId(rustc_hir::def_id::CRATE_DEF_ID.into())].into_iter().collect(), } } +} - pub(super) fn record(&mut self, to: DefIndex) { - let to = LocalDefId { local_def_index: to }; - match self.record_mode { - RecordMode::All => { - self.graph[self.from.unwrap()].insert(to); - } - RecordMode::ReachableAtLevel(level) => { - if self.effective_visibilities.is_public_at_level(to, level) { - self.graph[self.from.unwrap()].insert(to); +fn stable_hash<'a, T: StableHash>(hcx: &mut StableHashingContext<'a>, val: &T) -> Fingerprint { + let mut hasher = StableHasher::new(); + val.stable_hash(hcx, &mut hasher); + hasher.finish() +} + +impl<'tcx> IndexGraphBuilder<'tcx> { + fn build_graph(self, hcx: &mut StableHashingContext<'_>) -> IndexGraph<'tcx> { + let mut hashes = IndexMap::default(); + // iterating over FxHashSet and FxHashMap is fine here, as it is only used to build the + // hashes map, which is never returned or iterated + #[allow(rustc::potential_query_instability)] + for node in self + .edges + .iter() + .flat_map(|(node, edges)| { + std::iter::once(node.into_node()).chain(edges.iter().copied()) + }) + .chain(self.roots.iter().copied()) + { + match hashes.entry(node) { + Entry::Vacant(v) => { + let hash = stable_hash(hcx, &node); + v.insert(hash); } + Entry::Occupied(_o) => {} + } + } + hashes.sort_by_key(|_, v| *v); + + // iterating here is fine, as we stable sort right after + #[allow(rustc::potential_query_instability)] + let mut roots: Vec<_> = + self.roots.into_iter().map(|node| hashes.get_index_of(&node).unwrap()).collect(); + roots.sort(); + let mut edges = IndexVec::from_elem_n(Vec::default(), hashes.len()); + // iterating here is fine, as we stable when saving to `edges` + #[allow(rustc::potential_query_instability)] + for (node, node_edges) in self.edges { + // iterating here is fine, as we stable sort right after + #[allow(rustc::potential_query_instability)] + let mut node_edges: Vec<_> = + node_edges.into_iter().map(|node| hashes.get_index_of(&node).unwrap()).collect(); + node_edges.sort(); + edges[hashes.get_index_of(&node.into_node()).unwrap()] = node_edges; + } + + IndexGraph { roots, edges, nodes: hashes } + } + + pub(super) fn record(&mut self, to: Node<'tcx>) { + match self.record_mode.last() { + Some(RecordMode::From(from)) => { + self.edges.entry(*from).or_insert(Default::default()).insert(to); } - RecordMode::None => (), + None => { + self.roots.insert(to); + } + } + } +} + +#[derive(Default)] +pub(crate) struct IndexGraphHashes { + local: UnordMap, + expn: UnordMap, + syntax: UnordMap, +} + +impl IndexGraphHashes { + fn get_mut(&mut self, i: I) -> &mut Fingerprint { + i.index_mut(self) + } + + fn get_node(&self, node: &Node<'_>) -> Option { + match node { + Node::DefId(id) => id.as_local().and_then(|local| self.local.get(&local)).copied(), + Node::ExpnId(id) => id.as_local().and_then(|local| self.expn.get(&local)).copied(), + Node::SyntaxContext(id) => self.syntax.get(id).copied(), + Node::ParentlessSpan(_) => None, + Node::ParentSpan(_) => None, + Node::Ty(_) => None, + Node::Predicate(_) => None, + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Hash)] +pub(super) enum LocalNode<'tcx> { + DefId(LocalDefId), + ExpnId(LocalExpnId), + SyntaxContext(SyntaxContext), + Ty(Ty<'tcx>), + Predicate(PredicateKind<'tcx>), +} + +#[derive(PartialEq, Eq, Clone, Copy, Hash, StableHash)] +pub(super) enum Node<'tcx> { + DefId(DefId), + ExpnId(ExpnId), + SyntaxContext(SyntaxContext), + ParentlessSpan(Span), + ParentSpan(LocalDefId), + Ty(Ty<'tcx>), + Predicate(PredicateKind<'tcx>), +} + +impl<'tcx> LocalNode<'tcx> { + fn into_node(self) -> Node<'tcx> { + match self { + LocalNode::DefId(id) => Node::DefId(id.into()), + LocalNode::ExpnId(id) => Node::ExpnId(id.to_expn_id()), + LocalNode::SyntaxContext(id) => Node::SyntaxContext(id), + LocalNode::Ty(id) => Node::Ty(id), + LocalNode::Predicate(id) => Node::Predicate(id), + } + } +} + +impl From for Node<'_> { + fn from(value: LocalDefId) -> Self { + Self::DefId(value.into()) + } +} +impl From for Node<'_> { + fn from(value: DefId) -> Self { + Self::DefId(value) + } +} +impl From for Node<'_> { + fn from(value: ExpnId) -> Self { + Self::ExpnId(value) + } +} +impl From for Node<'_> { + fn from(value: SyntaxContext) -> Self { + Self::SyntaxContext(value) + } +} + +pub(crate) trait TableIndex: Copy { + type Encoded: Idx; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint; + fn into_encoded(self) -> Self::Encoded; +} + +impl TableIndex for DefIndex { + type Encoded = DefIndex; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.local.entry(LocalDefId { local_def_index: self }).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self + } +} + +impl TableIndex for LocalDefId { + type Encoded = DefIndex; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.local.entry(self).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self.local_def_index + } +} + +impl TableIndex for LocalExpnId { + type Encoded = ExpnIndex; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.expn.entry(self).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self.as_raw() + } +} + +impl TableIndex for SyntaxContext { + type Encoded = u32; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.syntax.entry(self).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self.as_u32() + } +} + +impl DirectedGraph for IndexGraph<'_> { + type Node = NodeIdx; + + fn num_nodes(&self) -> usize { + self.nodes.len() + } +} + +impl Successors for IndexGraph<'_> { + fn successors(&self, node: Self::Node) -> impl Iterator { + self.edges[node].iter().copied() + } +} + +pub type NodeIdx = usize; +pub type SccIdx = u32; + +#[derive(Debug, Copy, Clone)] +pub struct FingerprintAnnotation { + pub fingerprint: Fingerprint, +} + +impl Annotation for FingerprintAnnotation { + fn update_scc(&mut self, other: &Self) { + self.fingerprint = self.fingerprint.combine_commutative(other.fingerprint); + } + + fn update_reachable(&mut self, other: &Self) { + self.fingerprint = self.fingerprint.combine_commutative(other.fingerprint); + } +} + +pub struct FingerprintAnnotations<'a, 'tcx> { + graph: &'a IndexGraph<'tcx>, + hashes: &'a IndexGraphHashes, + scc_fingerprints: IndexVec, +} + +impl<'a, 'tcx> FingerprintAnnotations<'a, 'tcx> { + pub fn new(graph: &'a IndexGraph<'tcx>, hashes: &'a IndexGraphHashes) -> Self { + Self { graph, hashes, scc_fingerprints: IndexVec::with_capacity(graph.nodes.len()) } + } +} + +impl<'a, 'tcx> Annotations for FingerprintAnnotations<'a, 'tcx> { + type Ann = FingerprintAnnotation; + type SccIdx = SccIdx; + + fn new(&self, node: NodeIdx) -> FingerprintAnnotation { + FingerprintAnnotation { + fingerprint: if let Some(encoded_data_hash) = + self.hashes.get_node(self.graph.nodes.get_index(node).unwrap().0) + { + self.graph.nodes.get_index(node).unwrap().1.combine_commutative(encoded_data_hash) + } else { + *self.graph.nodes.get_index(node).unwrap().1 + }, } } + + fn annotate_scc(&mut self, scc: SccIdx, annotation: FingerprintAnnotation) { + debug_assert_eq!(self.scc_fingerprints.len(), scc.index()); + self.scc_fingerprints.push(annotation.fingerprint); + } +} + +fn build_public_hashes(graph: &IndexGraph<'_>, hashes: &IndexGraphHashes) -> ItemPublicHashes { + let mut roots = graph.roots.clone(); + let mut annotations = FingerprintAnnotations::new(graph, hashes); + let sccs = Sccs::<_, SccIdx>::new_with_annotation(graph, &mut annotations); + todo!() +} + +#[derive(Default)] +pub(crate) struct ItemPublicHashes { + local: UnordMap, + expn: UnordMap, + syntax: UnordMap, } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 32f63465e350a..6d90363bf3154 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -575,8 +575,8 @@ define_tables! { // // We don't need to include this in the hash, hashing `def_path_hash_map` takes care of it. def_keys: Table>, - // FIXME do we need to hash this? - proc_macro_quoted_spans: Table>, + // only used for proc macros, we don't calculate public hash for proc macros + proc_macro_quoted_spans: Table>, // FIXME do we need to hash this? variant_data: Table>, // FIXME do we need to hash this? diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index f84ac0efb313a..c4b37d28ffb32 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -5,7 +5,7 @@ use rustc_index::Idx; use crate::rmeta::decoder::MetaBlob; use crate::rmeta::encoder::public_api_hasher::{ - Hashed, PublicApiHashingContext, TablePublicApiHasher, + Hashed, PublicApiHashingContext, TableIndex, TablePublicApiHasher, }; use crate::rmeta::*; @@ -465,7 +465,7 @@ where { pub(crate) fn set_some_hashed<'a, HashedT>( &mut self, - i: I, + i: impl TableIndex, value: T, hashed: HashedT, hcx: &mut PublicApiHashingContext<'a>, @@ -473,7 +473,7 @@ where HashedT: StableHash, { self.hasher.digest(i, hashed, hcx); - self.set(i, Some(value)); + self.set(i.into_encoded(), Some(value)); } } @@ -536,7 +536,7 @@ impl, I: Idx, const N: usize, T: FixedSizeEncoding( &mut self, - i: I, + i: impl TableIndex, value: T, hashed: HashedT, hcx: &mut PublicApiHashingContext<'_>, @@ -544,7 +544,7 @@ impl, I: Idx, const N: usize, T: FixedSizeEncoding: SpanEncoder { fn position(&self) -> usize; + #[inline] + fn type_encode_begin(&mut self, _ty: Ty<'tcx>) {} + #[inline] + fn type_encode_end(&mut self) {} fn type_shorthands(&mut self) -> &mut FxHashMap, usize>; + #[inline] + fn predicate_encode_begin(&mut self, _pred: ty::PredicateKind<'tcx>) {} + #[inline] + fn predicate_encode_end(&mut self) {} fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; fn encode_alloc_id(&mut self, alloc_id: &AllocId); @@ -100,17 +108,26 @@ pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>>: PointeeSized { } /// Encode the given value or a previously cached shorthand. -pub fn encode_with_shorthand<'tcx, E, T, M>(encoder: &mut E, value: &T, cache: M) -where +pub fn encode_with_shorthand<'tcx, E, T, M, Begin, End>( + encoder: &mut E, + value: &T, + cache: M, + begin: Begin, + end: End, +) where E: TyEncoder<'tcx>, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, + Begin: Fn(&mut E, T), + End: Fn(&mut E), T: EncodableWithShorthand<'tcx, E>, // The discriminant and shorthand must have the same size. T::Variant: DiscriminantKind, { let existing_shorthand = cache(encoder).get(value).copied(); + begin(encoder, *value); if let Some(shorthand) = existing_shorthand { encoder.emit_usize(shorthand); + end(encoder); return; } @@ -136,11 +153,18 @@ where if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { cache(encoder).insert(*value, shorthand); } + end(encoder); } impl<'tcx, E: TyEncoder<'tcx>> Encodable for Ty<'tcx> { fn encode(&self, e: &mut E) { - encode_with_shorthand(e, self, TyEncoder::type_shorthands); + encode_with_shorthand( + e, + self, + TyEncoder::type_shorthands, + TyEncoder::type_encode_begin, + TyEncoder::type_encode_end, + ); } } @@ -148,7 +172,13 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Predicate<'tcx> { fn encode(&self, e: &mut E) { let kind = self.kind(); kind.bound_vars().encode(e); - encode_with_shorthand(e, &kind.skip_binder(), TyEncoder::predicate_shorthands); + encode_with_shorthand( + e, + &kind.skip_binder(), + TyEncoder::predicate_shorthands, + TyEncoder::predicate_encode_begin, + TyEncoder::predicate_encode_end, + ); } } From f20423301e338dca0315f37a3f0e677b9ed627cb Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Thu, 21 May 2026 16:01:21 +0200 Subject: [PATCH 27/50] rdr: save public api hashes of DefId-s and ExpnId-s to the metadata --- .../src/rmeta/decoder/cstore_impl.rs | 34 +++- compiler/rustc_metadata/src/rmeta/encoder.rs | 12 +- .../src/rmeta/encoder/public_api_hasher.rs | 155 ++++++++++++++---- compiler/rustc_metadata/src/rmeta/mod.rs | 3 +- .../rustc_metadata/src/rmeta/parameterized.rs | 1 + compiler/rustc_metadata/src/rmeta/table.rs | 26 +++ compiler/rustc_middle/src/queries.rs | 17 +- compiler/rustc_middle/src/query/erase.rs | 1 + compiler/rustc_middle/src/query/keys.rs | 17 +- compiler/rustc_span/src/hygiene.rs | 5 + 10 files changed, 226 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 01143533dd8b8..db92e407e91c0 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -186,18 +186,29 @@ macro_rules! provide { // small trait to work around different signature queries all being defined via // the macro above. trait IntoArgs { + type This; type Other; - fn into_args(self) -> (DefId, Self::Other); + fn into_args(self) -> (Self::This, Self::Other); } impl IntoArgs for DefId { + type This = DefId; type Other = (); fn into_args(self) -> (DefId, ()) { (self, ()) } } +impl IntoArgs for ExpnId { + type This = ExpnId; + type Other = (); + fn into_args(self) -> (ExpnId, ()) { + (self, ()) + } +} + impl IntoArgs for CrateNum { + type This = DefId; type Other = (); fn into_args(self) -> (DefId, ()) { (self.as_def_id(), ()) @@ -205,6 +216,7 @@ impl IntoArgs for CrateNum { } impl IntoArgs for (CrateNum, DefId) { + type This = DefId; type Other = DefId; fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) @@ -212,6 +224,7 @@ impl IntoArgs for (CrateNum, DefId) { } impl<'tcx> IntoArgs for ty::InstanceKind<'tcx> { + type This = DefId; type Other = (); fn into_args(self) -> (DefId, ()) { (self.def_id(), ()) @@ -219,6 +232,7 @@ impl<'tcx> IntoArgs for ty::InstanceKind<'tcx> { } impl IntoArgs for (CrateNum, SimplifiedType) { + type This = DefId; type Other = SimplifiedType; fn into_args(self) -> (DefId, SimplifiedType) { (self.0.as_def_id(), self.1) @@ -255,7 +269,7 @@ provide! { tcx, def_id, other, cdata, lookup_default_body_stability => { table } lookup_deprecation_entry => { table } params_in_repr => { table } - def_kind => { cdata.def_kind(def_id.index) } + def_kind => { table_direct } impl_parent => { table } defaultness => { table_direct } constness => { table_direct } @@ -436,6 +450,22 @@ provide! { tcx, def_id, other, cdata, } anon_const_kind => { table } const_of_item => { table } + extern_def_public_hash => { + cdata + .root + .rdr_hashes + .local + .get(cdata, def_id.index) + .unwrap_or_else(|| bug!("Trying to read public hash of definition categoriazed as private!")) + } + extern_expn_public_hash => { + cdata + .root + .rdr_hashes + .expn + .get(cdata, def_id.local_id) + .unwrap_or_else(|| bug!("Trying to read public hash of expnasion categoriazed as private!")) + } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5a745c6171014..3054b937f26f4 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -359,15 +359,9 @@ fn encode_span_data<'a, 'tcx>(span: Span, s: &mut EncodeContext<'a, 'tcx>) -> Ve (SpanKind::Local, metadata_index) }; if s.index_graph_builder.is_some() { - if let Some(parent) = this.parent { - let node = Node::ParentSpan(parent); - nodes.push(node); - s.record_encoded_index(node); - } else { - let node = Node::ParentlessSpan(span); - nodes.push(node); - s.record_encoded_index(node); - } + let node = Node::Span(span); + nodes.push(node); + s.record_encoded_index(node); } // Encode the start position relative to the file start, so we profit more from the 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 aaf9592fa1b9b..b209e314bbe0c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -1,3 +1,4 @@ +use core::cell::RefCell; use core::iter::Iterator; use std::fmt; @@ -13,13 +14,13 @@ use rustc_hir::LangItem; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def_id::DefIndex; use rustc_index::{Idx, IndexVec}; -use rustc_macros::StableHash; +use rustc_macros::{LazyDecodable, MetadataEncodable, StableHash}; use rustc_middle::ich::StableHashingContext; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use rustc_middle::middle::lib_features::FeatureStability; use rustc_middle::ty::data_structures::IndexMap; -use rustc_middle::ty::{PredicateKind, Ty}; +use rustc_middle::ty::{PredicateKind, Ty, TyCtxt}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; @@ -33,8 +34,8 @@ use tracing::debug; use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; use crate::rmeta::{ CrateHashes, DefPathHashMapRef, EiiMapEncodedKeyValue, EncodeContext, ExpnDataTable, - ExpnHashTable, IncoherentImpls, LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, - SyntaxContextTable, TraitImpls, + ExpnHashTable, IncoherentImpls, LazyArray, LazyDecoder, LazyTable, LazyTables, LazyValue, + ProcMacroData, SyntaxContextTable, TableBuilder, TraitImpls, }; #[derive(Default)] @@ -438,19 +439,22 @@ impl HashableCrateRoot { hcx: &mut PublicApiHashingContext<'_>, ) -> CrateRoot { let tcx = ecx.tcx; - let hashes = if hcx.hash_public_api { + let (rdr_hashes, hashes) = if hcx.hash_public_api { assert!(!self.header.is_proc_macro_crate); let graph = ecx.index_graph_builder.take().unwrap().build_graph(&mut hcx.hcx); + let public_hashes = build_public_hashes(&graph, &hcx.def_id_hashes, ecx.tcx); + let rdr_hashes = public_hashes.encode(ecx, hcx); let mut hasher = StableHasher::default(); self.stable_hash(&mut hcx.hcx, &mut hasher); let public_hash = Svh::new(hasher.finish()); debug!("Hashed crate root: {self:#x?}"); debug!("public api hash: {}", public_hash); - CrateHashes { public_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) } + (rdr_hashes, CrateHashes { public_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) }) } else { let hash = tcx.crate_hash(LOCAL_CRATE); - CrateHashes { public_hash: hash, private_hash: hash } + let rdr_hashes = ItemPublicHashesBuilder::default().encode(ecx, hcx); + (rdr_hashes, CrateHashes { public_hash: hash, private_hash: hash }) }; let header = self.header; let header = CrateHeader { @@ -517,6 +521,7 @@ impl HashableCrateRoot { symbol_mangling_version: self.symbol_mangling_version, specialization_enabled_in: self.specialization_enabled_in, + rdr_hashes, } } } @@ -537,6 +542,23 @@ struct IndexGraph<'tcx> { roots: Vec, } +impl<'tcx> IndexGraph<'tcx> { + fn reachable_set(&self) -> IndexVec { + let mut reachable = IndexVec::from_elem_n(false, self.edges.len()); + let mut stack = self.roots.clone(); + + while let Some(node) = stack.pop() { + if reachable[node] { + continue; + } + reachable[node] = true; + stack.extend_from_slice(&self.edges[node]); + } + + reachable + } +} + pub(super) struct IndexGraphBuilder<'tcx> { pub(super) record_mode: Vec>, edges: FxHashMap, FxHashSet>>, @@ -553,7 +575,10 @@ impl Default for IndexGraphBuilder<'_> { } } -fn stable_hash<'a, T: StableHash>(hcx: &mut StableHashingContext<'a>, val: &T) -> Fingerprint { +fn stable_hash<'a, T: StableHash + ?Sized>( + hcx: &mut StableHashingContext<'a>, + val: &T, +) -> Fingerprint { let mut hasher = StableHasher::new(); val.stable_hash(hcx, &mut hasher); hasher.finish() @@ -627,13 +652,32 @@ impl IndexGraphHashes { i.index_mut(self) } - fn get_node(&self, node: &Node<'_>) -> Option { + fn get_node(&self, node: &Node<'_>, tcx: TyCtxt<'_>) -> Option { match node { - Node::DefId(id) => id.as_local().and_then(|local| self.local.get(&local)).copied(), - Node::ExpnId(id) => id.as_local().and_then(|local| self.expn.get(&local)).copied(), + Node::DefId(id) => { + if let Some(local) = id.as_local() { + self.local.get(&local).copied() + } else { + Some(tcx.extern_def_public_hash(*id)) + } + } + Node::ExpnId(id) => { + if let Some(local) = id.as_local() { + self.expn.get(&local).copied() + } else { + Some(tcx.extern_expn_public_hash(*id)) + } + } Node::SyntaxContext(id) => self.syntax.get(id).copied(), - Node::ParentlessSpan(_) => None, - Node::ParentSpan(_) => None, + Node::Span(span) => tcx.with_stable_hashing_context(|hcx| { + let hcx = RefCell::new(hcx); + tcx.sess + .source_map() + .span_to_source(*span, |source, start, end| { + Ok(stable_hash(&mut hcx.borrow_mut(), &source[start..end])) + }) + .ok() + }), Node::Ty(_) => None, Node::Predicate(_) => None, } @@ -654,8 +698,7 @@ pub(super) enum Node<'tcx> { DefId(DefId), ExpnId(ExpnId), SyntaxContext(SyntaxContext), - ParentlessSpan(Span), - ParentSpan(LocalDefId), + Span(Span), Ty(Ty<'tcx>), Predicate(PredicateKind<'tcx>), } @@ -753,12 +796,12 @@ impl Successors for IndexGraph<'_> { } } -pub type NodeIdx = usize; -pub type SccIdx = u32; +type NodeIdx = usize; +type SccIdx = u32; #[derive(Debug, Copy, Clone)] -pub struct FingerprintAnnotation { - pub fingerprint: Fingerprint, +struct FingerprintAnnotation { + fingerprint: Fingerprint, } impl Annotation for FingerprintAnnotation { @@ -771,15 +814,16 @@ impl Annotation for FingerprintAnnotation { } } -pub struct FingerprintAnnotations<'a, 'tcx> { +struct FingerprintAnnotations<'a, 'tcx> { graph: &'a IndexGraph<'tcx>, hashes: &'a IndexGraphHashes, + tcx: TyCtxt<'tcx>, scc_fingerprints: IndexVec, } impl<'a, 'tcx> FingerprintAnnotations<'a, 'tcx> { - pub fn new(graph: &'a IndexGraph<'tcx>, hashes: &'a IndexGraphHashes) -> Self { - Self { graph, hashes, scc_fingerprints: IndexVec::with_capacity(graph.nodes.len()) } + fn new(graph: &'a IndexGraph<'tcx>, hashes: &'a IndexGraphHashes, tcx: TyCtxt<'tcx>) -> Self { + Self { graph, hashes, scc_fingerprints: IndexVec::with_capacity(graph.nodes.len()), tcx } } } @@ -790,7 +834,7 @@ impl<'a, 'tcx> Annotations for FingerprintAnnotations<'a, 'tcx> { fn new(&self, node: NodeIdx) -> FingerprintAnnotation { FingerprintAnnotation { fingerprint: if let Some(encoded_data_hash) = - self.hashes.get_node(self.graph.nodes.get_index(node).unwrap().0) + self.hashes.get_node(self.graph.nodes.get_index(node).unwrap().0, self.tcx) { self.graph.nodes.get_index(node).unwrap().1.combine_commutative(encoded_data_hash) } else { @@ -800,21 +844,70 @@ impl<'a, 'tcx> Annotations for FingerprintAnnotations<'a, 'tcx> { } fn annotate_scc(&mut self, scc: SccIdx, annotation: FingerprintAnnotation) { - debug_assert_eq!(self.scc_fingerprints.len(), scc.index()); + assert_eq!(self.scc_fingerprints.len(), scc.index()); self.scc_fingerprints.push(annotation.fingerprint); } } -fn build_public_hashes(graph: &IndexGraph<'_>, hashes: &IndexGraphHashes) -> ItemPublicHashes { - let mut roots = graph.roots.clone(); - let mut annotations = FingerprintAnnotations::new(graph, hashes); +fn build_public_hashes<'tcx>( + graph: &IndexGraph<'tcx>, + hashes: &IndexGraphHashes, + tcx: TyCtxt<'tcx>, +) -> ItemPublicHashesBuilder { + let mut annotations = FingerprintAnnotations::new(graph, hashes, tcx); let sccs = Sccs::<_, SccIdx>::new_with_annotation(graph, &mut annotations); - todo!() + let mut public_hashes = ItemPublicHashesBuilder::default(); + for (node_index, reachable) in graph.reachable_set().iter_enumerated() { + if !reachable { + continue; + } + match graph.nodes.get_index(node_index).unwrap().0 { + Node::DefId(id) => { + if let Some(local) = id.as_local() { + public_hashes.local.set_some_unhashed( + local.local_def_index, + annotations.scc_fingerprints[sccs.scc(node_index)], + ); + } + } + Node::ExpnId(id) => { + if let Some(local) = id.as_local() { + public_hashes.expn.set_some_unhashed( + local.as_raw(), + annotations.scc_fingerprints[sccs.scc(node_index)], + ); + } + } + Node::SyntaxContext(_) => (), + Node::Ty(_) => (), + Node::Predicate(_) => (), + Node::Span(_) => (), + } + } + public_hashes } #[derive(Default)] +pub(crate) struct ItemPublicHashesBuilder { + local: TableBuilder, DefIndex, Option>, + expn: TableBuilder, ExpnIndex, Option>, +} + +impl ItemPublicHashesBuilder { + fn encode<'a, 'tcx>( + &self, + ecx: &mut EncodeContext<'a, 'tcx>, + hcx: &mut PublicApiHashingContext<'_>, + ) -> ItemPublicHashes { + ItemPublicHashes { + local: self.local.encode(&mut ecx.opaque, hcx).value, + expn: self.expn.encode(&mut ecx.opaque, hcx).value, + } + } +} + +#[derive(LazyDecodable, MetadataEncodable)] pub(crate) struct ItemPublicHashes { - local: UnordMap, - expn: UnordMap, - syntax: UnordMap, + pub(crate) local: LazyTable>, + pub(crate) expn: LazyTable>, } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 6d90363bf3154..69a6c9a3e8b8f 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -48,7 +48,7 @@ use table::TableBuilder; use crate::eii::EiiMapEncodedKeyValue; use crate::rmeta::encoder::public_api_hasher::{ - Hashed, PublicApiHasher, PublicApiHashingContext, RDRHashAll, RDRHashNone, + Hashed, ItemPublicHashes, PublicApiHasher, PublicApiHashingContext, RDRHashAll, RDRHashNone, }; mod decoder; @@ -301,6 +301,7 @@ pub(crate) struct CrateRoot { symbol_mangling_version: SymbolManglingVersion, specialization_enabled_in: bool, + rdr_hashes: ItemPublicHashes, } /// All hashes here are equal to the hash from the crate header (the `crate_hash` query) when the public-api-hash unstable feature is disabled. diff --git a/compiler/rustc_metadata/src/rmeta/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 1531584e99788..fc97634067fb8 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -75,6 +75,7 @@ trivially_parameterized_over_tcx! { crate::rmeta::VariantData, rustc_abi::ReprOptions, rustc_ast::DelimArgs, + rustc_data_structures::fingerprint::Fingerprint, rustc_hir::Attribute, rustc_hir::ConstStability, rustc_hir::Constness, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index c4b37d28ffb32..344cec9ff2e45 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::StableHash; use rustc_hir::def::CtorOf; use rustc_hir::def_id::LocalDefId; @@ -78,6 +79,31 @@ impl FixedSizeEncoding for u64 { } } +impl FixedSizeEncoding for Option { + type ByteArray = [u8; 17]; + + fn from_bytes(b: &Self::ByteArray) -> Self { + if b[0] == 0 { + None + } else { + Some(Fingerprint::from_le_bytes(b[1..17].try_into().unwrap())) + } + } + + fn write_to_bytes(self, b: &mut Self::ByteArray) { + match self { + Some(fingerprint) => { + b[0] = 1; + let buf: &mut [u8; 16] = (&mut b[1..17]).try_into().unwrap(); + *buf = fingerprint.to_le_bytes(); + } + None => { + b[0] = 0; + } + } + } +} + macro_rules! fixed_size_enum { ($ty:ty { $(($($pat:tt)*))* } $( unreachable { $(($($upat:tt)*))+ } )?) => { impl FixedSizeEncoding for Option<$ty> { diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index c15f86a2f7a7c..0eb78c5a49cf8 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -55,6 +55,7 @@ use rustc_arena::TypedArena; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; @@ -77,7 +78,7 @@ use rustc_session::cstore::{ }; use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{DUMMY_SP, LocalExpnId, Span, Spanned, Symbol}; +use rustc_span::{DUMMY_SP, ExpnId, LocalExpnId, Span, Spanned, Symbol}; use rustc_target::spec::PanicStrategy; use crate::hir::Crate; @@ -2050,6 +2051,20 @@ rustc_queries! { separate_provide_extern } + /// Returns the public api hash of an extern DefId, panics for the local crate + query extern_def_public_hash(_: DefId) -> Fingerprint { + eval_always + desc { "looking up public hash of a definition" } + separate_provide_extern + } + + /// Returns the public api hash of an extern ExpnId, panics for the local crate + query extern_expn_public_hash(_: ExpnId) -> Fingerprint { + eval_always + desc { "looking up public hash of an expansion" } + 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 diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index a6ff238ad6f0b..7c4b072f0c12c 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -206,6 +206,7 @@ impl_erasable_for_types_with_no_type_params! { Result, traits::query::NoSolution>, Ty<'_>, bool, + rustc_data_structures::fingerprint::Fingerprint, rustc_data_structures::svh::Svh, rustc_hir::Constness, rustc_hir::Defaultness, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 71bd08861b8d5..8bf23c5ca5530 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -9,7 +9,7 @@ use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stable_hasher::StableHash; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::OwnerId; -use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol}; +use rustc_span::{DUMMY_SP, ExpnId, Ident, LocalExpnId, Span, Symbol}; use crate::dep_graph::DepNodeIndex; use crate::infer::canonical::CanonicalQueryInput; @@ -173,6 +173,21 @@ impl QueryKey for SimplifiedType { } } +impl QueryKey for ExpnId { + fn default_span(&self, _: TyCtxt<'_>) -> Span { + self.expn_data().call_site + } +} + +impl AsLocalQueryKey for ExpnId { + type LocalQueryKey = LocalExpnId; + + #[inline(always)] + fn as_local_key(&self) -> Option { + self.as_local() + } +} + impl QueryKey for (DefId, DefId) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 6a36a6f199f54..6958b195a8e73 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -333,6 +333,11 @@ impl ExpnId { } last_macro } + + #[inline] + pub fn is_local(&self) -> bool { + self.krate == LOCAL_CRATE + } } #[derive(Debug)] From baaaef7da3d28497d0903aa01cd8de338848f02a Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 22 May 2026 08:15:00 +0200 Subject: [PATCH 28/50] rdr: hash the public hashes, make sure hash retrieval works for metadatas without rdr --- .../src/rmeta/decoder/cstore_impl.rs | 29 +++++++++------- compiler/rustc_metadata/src/rmeta/encoder.rs | 2 +- .../src/rmeta/encoder/public_api_hasher.rs | 34 +++++++++++-------- compiler/rustc_metadata/src/rmeta/mod.rs | 2 +- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index db92e407e91c0..bdd1c5c22291b 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::mem; use std::sync::Arc; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::attrs::Deprecation; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; @@ -451,20 +452,24 @@ provide! { tcx, def_id, other, cdata, anon_const_kind => { table } const_of_item => { table } extern_def_public_hash => { - cdata - .root - .rdr_hashes - .local - .get(cdata, def_id.index) - .unwrap_or_else(|| bug!("Trying to read public hash of definition categoriazed as private!")) + if let Some(rdr_hashes) = cdata.root.rdr_hashes.as_ref() { + rdr_hashes + .local + .get(cdata, def_id.index) + .unwrap_or_else(|| bug!("Trying to read public hash of definition {def_id:?}, categoriazed as private!")) + } else { + Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) + } } extern_expn_public_hash => { - cdata - .root - .rdr_hashes - .expn - .get(cdata, def_id.local_id) - .unwrap_or_else(|| bug!("Trying to read public hash of expnasion categoriazed as private!")) + if let Some(rdr_hashes) = cdata.root.rdr_hashes.as_ref() { + rdr_hashes + .expn + .get(cdata, def_id.local_id) + .unwrap_or_else(|| bug!("Trying to read public hash of expnasion {def_id:?}, categoriazed as private!")) + } else { + Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) + } } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 3054b937f26f4..fe1709caa2ff3 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -963,7 +963,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_path_hash_map, specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), }; - let crate_root = crate_root.into_crate_root(self, hcx); + let crate_root = stat!("public hashes", || crate_root.into_crate_root(self, hcx)); let hashes = crate_root.header.hashes; 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 b209e314bbe0c..ee6bbb4376136 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -442,19 +442,23 @@ impl HashableCrateRoot { let (rdr_hashes, hashes) = if hcx.hash_public_api { assert!(!self.header.is_proc_macro_crate); let graph = ecx.index_graph_builder.take().unwrap().build_graph(&mut hcx.hcx); - let public_hashes = build_public_hashes(&graph, &hcx.def_id_hashes, ecx.tcx); + let public_hashes = + build_public_hashes(&graph, &hcx.def_id_hashes, ecx.tcx, &mut hcx.hcx); - let rdr_hashes = public_hashes.encode(ecx, hcx); let mut hasher = StableHasher::default(); self.stable_hash(&mut hcx.hcx, &mut hasher); + public_hashes.stable_hash(&mut hcx.hcx, &mut hasher); + let rdr_hashes = public_hashes.value.encode(ecx, hcx); let public_hash = Svh::new(hasher.finish()); debug!("Hashed crate root: {self:#x?}"); debug!("public api hash: {}", public_hash); - (rdr_hashes, CrateHashes { public_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) }) + ( + Some(rdr_hashes), + CrateHashes { public_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) }, + ) } else { let hash = tcx.crate_hash(LOCAL_CRATE); - let rdr_hashes = ItemPublicHashesBuilder::default().encode(ecx, hcx); - (rdr_hashes, CrateHashes { public_hash: hash, private_hash: hash }) + (None, CrateHashes { public_hash: hash, private_hash: hash }) }; let header = self.header; let header = CrateHeader { @@ -853,10 +857,12 @@ fn build_public_hashes<'tcx>( graph: &IndexGraph<'tcx>, hashes: &IndexGraphHashes, tcx: TyCtxt<'tcx>, -) -> ItemPublicHashesBuilder { + hcx: &mut StableHashingContext<'_>, +) -> Hashed { let mut annotations = FingerprintAnnotations::new(graph, hashes, tcx); let sccs = Sccs::<_, SccIdx>::new_with_annotation(graph, &mut annotations); let mut public_hashes = ItemPublicHashesBuilder::default(); + let mut hasher = StableHasher::new(); for (node_index, reachable) in graph.reachable_set().iter_enumerated() { if !reachable { continue; @@ -864,18 +870,16 @@ fn build_public_hashes<'tcx>( match graph.nodes.get_index(node_index).unwrap().0 { Node::DefId(id) => { if let Some(local) = id.as_local() { - public_hashes.local.set_some_unhashed( - local.local_def_index, - annotations.scc_fingerprints[sccs.scc(node_index)], - ); + let fingerprint = annotations.scc_fingerprints[sccs.scc(node_index)]; + (local, fingerprint).stable_hash(hcx, &mut hasher); + public_hashes.local.set_some_unhashed(local.local_def_index, fingerprint); } } Node::ExpnId(id) => { if let Some(local) = id.as_local() { - public_hashes.expn.set_some_unhashed( - local.as_raw(), - annotations.scc_fingerprints[sccs.scc(node_index)], - ); + let fingerprint = annotations.scc_fingerprints[sccs.scc(node_index)]; + (local, fingerprint).stable_hash(hcx, &mut hasher); + public_hashes.expn.set_some_unhashed(local.as_raw(), fingerprint); } } Node::SyntaxContext(_) => (), @@ -884,7 +888,7 @@ fn build_public_hashes<'tcx>( Node::Span(_) => (), } } - public_hashes + Hashed { value: public_hashes, hash: Some(hasher.finish()) } } #[derive(Default)] diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 69a6c9a3e8b8f..b5a691cc96865 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -301,7 +301,7 @@ pub(crate) struct CrateRoot { symbol_mangling_version: SymbolManglingVersion, specialization_enabled_in: bool, - rdr_hashes: ItemPublicHashes, + rdr_hashes: Option, } /// All hashes here are equal to the hash from the crate header (the `crate_hash` query) when the public-api-hash unstable feature is disabled. From f9137c01cd7b2211f91aed3fc4f22dd2a4dfd849 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 22 May 2026 10:24:37 +0200 Subject: [PATCH 29/50] rdr: remove stripped cfg items from metadata --- compiler/rustc_metadata/src/rmeta/encoder.rs | 7 +++++++ .../rustc_metadata/src/rmeta/encoder/public_api_hasher.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index fe1709caa2ff3..864b6d4e8784c 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -6,6 +6,7 @@ use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{par_for_each_in, par_join}; @@ -2463,6 +2464,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &mut self, hcx: &mut PublicApiHashingContext<'_>, ) -> Hashed>> { + if hcx.enabled() { + // FIXME: we should encode this while public api hashing is enabled, but it must not be + // part of the public hash. Its query should depend on the private hash of the crate + // if it is included. + return Hashed { value: LazyArray::default(), hash: Some(Fingerprint::ZERO) }; + } hashed_lazy_array!( self, self.tcx.stripped_cfg_items(LOCAL_CRATE), 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 ee6bbb4376136..1d92ff400f3b1 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -101,6 +101,9 @@ pub(crate) struct PublicApiHashingContext<'a> { } impl<'a> PublicApiHashingContext<'a> { + pub(crate) fn enabled(&self) -> bool { + self.hash_public_api + } pub(crate) fn new(hash_public_api: bool, hcx: StableHashingContext<'a>) -> Self { Self { hash_public_api, hcx, def_id_hashes: Default::default() } } From 3a7edd6f2a6575cf22454ee1acf1d788b493ccb2 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 22 May 2026 10:46:28 +0200 Subject: [PATCH 30/50] rdr: only record public module children in rmeta --- compiler/rustc_metadata/src/rmeta/encoder.rs | 61 ++++++++++++++++++-- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 864b6d4e8784c..997ca162d07bb 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -19,7 +19,9 @@ use rustc_hir::definitions::DefPathData; use rustc_hir::find_attr; use rustc_hir_pretty::id_to_string; use rustc_middle::dep_graph::WorkProductId; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; @@ -1584,6 +1586,17 @@ fn assoc_item_has_value<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool { } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { + fn public_module_children_local( + &self, + def_id: LocalDefId, + ) -> impl Iterator + use<'tcx> + Clone { + let effective_visibilities = self.tcx.effective_visibilities(()); + let remove_private_children = self.tcx.sess.opts.unstable_opts.public_api_hash; + self.tcx.module_children_local(def_id).iter().filter(move |child| { + is_public_mod_child(remove_private_children, effective_visibilities, child) + }) + } + fn encode_attrs(&mut self, def_id: LocalDefId, hcx: &mut PublicApiHashingContext<'_>) { let tcx = self.tcx; let mut state = AnalyzeAttrState { @@ -1736,9 +1749,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { 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(), hcx); - let module_children = self.tcx.module_children_local(local_id); + let module_children = self.public_module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().map(|child| child.res.def_id()), hcx, + module_children.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] @@ -1962,22 +1975,38 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // 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), hcx); } else { - let module_children = tcx.module_children_local(local_def_id); + let module_children = self.public_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()) + module_children.clone().filter(|child| child.reexport_chain.is_empty()) .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()), hcx + module_children.filter(|child| !child.reexport_chain.is_empty()), hcx ); + let effective_visibilities = self.tcx.effective_visibilities(()); + let remove_private_children = self.tcx.sess.opts.unstable_opts.public_api_hash; let ambig_module_children = tcx .resolutions(()) .ambig_module_children .get(&local_def_id) - .map_or_default(|v| &v[..]); + .map_or_default(|v| &v[..]) + .iter() + // only ambiguities where both main and second are public can cause actual + // ambiguity. + .filter(|ambig| { + is_public_mod_child( + remove_private_children, + &effective_visibilities, + &ambig.main, + ) && is_public_mod_child( + remove_private_children, + effective_visibilities, + &ambig.second, + ) + }); record_defaulted_array!(self.tables.ambig_module_children[def_id] <- ambig_module_children, hcx); } @@ -3100,3 +3129,23 @@ fn dylib_dependency_formats( }) }) } + +fn is_public_mod_child( + remove_private_children: bool, + effective_visibilities: &EffectiveVisibilities, + child: &ModChild, +) -> bool { + if !remove_private_children { + return true; + } + if child.reexport_chain.is_empty() { + effective_visibilities.is_reachable(child.res.def_id().expect_local()) + } else { + child.vis.is_public() + && child.reexport_chain.iter().all(|reexport| { + reexport.id().is_none_or(|id| { + !id.is_local() || effective_visibilities.is_reachable(id.expect_local()) + }) + }) + } +} From fdd50d516fe2bab93846a7124a3a028b9df631f5 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Fri, 22 May 2026 21:45:43 +0200 Subject: [PATCH 31/50] rdr: add VisibilityDefId which is only usable to check visibilities --- .../src/hir_ty_lowering/mod.rs | 8 +++---- compiler/rustc_middle/src/ty/mod.rs | 24 ++++++++++++++----- .../rustc_middle/src/ty/structural_impls.rs | 1 + compiler/rustc_resolve/src/lib.rs | 10 ++++++-- compiler/rustc_span/src/def_id.rs | 10 ++++++++ compiler/rustc_span/src/hygiene.rs | 8 +++---- .../src/error_reporting/infer/region.rs | 2 +- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5a86e8186a5aa..5aca21a573de1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -30,7 +30,7 @@ use rustc_errors::{ struct_span_code_err, }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, VisibilityDefId}; use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::DynCompatibilityViolation; @@ -117,7 +117,7 @@ pub enum RegionInferReason<'a> { pub struct InherentAssocCandidate { pub impl_: DefId, pub assoc_item: DefId, - pub scope: DefId, + pub scope: VisibilityDefId, } pub struct ResolvedStructPath<'tcx> { @@ -1882,7 +1882,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_tag: ty::AssocTag, block: HirId, scope: DefId, - ) -> Option<(ty::AssocItem, /*scope*/ DefId)> { + ) -> Option<(ty::AssocItem, /*scope*/ VisibilityDefId)> { let tcx = self.tcx(); let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); @@ -1902,7 +1902,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, item_def_id: DefId, ident: Ident, - scope: DefId, + scope: VisibilityDefId, block: HirId, span: Span, ) { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f68526724135c..af7b3cb728589 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -40,7 +40,7 @@ use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir as hir; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, VisibilityDefId}; use rustc_hir::definitions::PerParentDisambiguatorState; use rustc_hir::{LangItem, attrs as attr, find_attr}; use rustc_index::IndexVec; @@ -340,7 +340,13 @@ impl TyCtxt<'_> { /// Compare def-ids based on their position in def-id tree, ancestor def-ids are considered /// larger than descendant def-ids, and two different def-ids are considered unordered if /// neither of them is an ancestor of the other. - fn def_id_partial_cmp(self, lhs: DefId, rhs: DefId) -> Option { + fn def_id_partial_cmp( + self, + lhs: impl Into, + rhs: impl Into, + ) -> Option { + let lhs = lhs.into().0; + let rhs = rhs.into().0; // Def-ids from different crates are always unordered. if lhs.krate != rhs.krate { return None; @@ -365,7 +371,11 @@ impl TyCtxt<'_> { } } - pub fn is_descendant_of(self, descendant: DefId, ancestor: DefId) -> bool { + pub fn is_descendant_of( + self, + descendant: impl Into, + ancestor: impl Into, + ) -> bool { matches!( self.def_id_partial_cmp(descendant, ancestor), Some(Ordering::Less | Ordering::Equal) @@ -390,9 +400,11 @@ impl> Visibility { pub fn to_def_id(self) -> Visibility { self.map_id(Into::into) } +} +impl> Visibility { /// Returns `true` if an item with this visibility is accessible from the given module. - pub fn is_accessible_from(self, module: impl Into, tcx: TyCtxt<'_>) -> bool { + pub fn is_accessible_from(self, module: impl Into, tcx: TyCtxt<'_>) -> bool { match self { // Public items are visible everywhere. Visibility::Public => true, @@ -2060,12 +2072,12 @@ impl<'tcx> TyCtxt<'tcx> { mut ident: Ident, scope: DefId, block: hir::HirId, - ) -> (Ident, DefId) { + ) -> (Ident, VisibilityDefId) { let scope = ident .span .normalize_to_macros_2_0_and_adjust(self.expn_that_defined(scope)) .and_then(|actual_expansion| actual_expansion.expn_data().parent_module) - .unwrap_or_else(|| self.parent_module(block).to_def_id()); + .unwrap_or_else(|| self.parent_module(block).to_def_id().into()); (ident, scope) } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 08a8491bab6c5..a2927f11b1448 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -263,6 +263,7 @@ TrivialTypeTraversalAndLiftImpls! { crate::ty::ParamTy, crate::ty::instance::ReifyReason, rustc_hir::def_id::DefId, + rustc_hir::def_id::VisibilityDefId, // tidy-alphabetical-end } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 4352e10462901..ed06ea3a6992e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -56,7 +56,9 @@ use rustc_hir::def::{ self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, PerNS, }; -use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; +use rustc_hir::def_id::{ + CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap, VisibilityDefId, +}; use rustc_hir::definitions::{PerParentDisambiguatorState, PerParentDisambiguatorsMap}; use rustc_hir::{PrimTy, TraitCandidate, find_attr}; use rustc_index::bit_set::DenseBitSet; @@ -2389,7 +2391,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.pat_span_map.insert(node, span); } - fn is_accessible_from(&self, vis: Visibility>, module: Module<'ra>) -> bool { + fn is_accessible_from( + &self, + vis: Visibility>, + module: Module<'ra>, + ) -> bool { vis.is_accessible_from(module.nearest_parent_mod(), self.tcx) } diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index 6ce3d6b309f0e..d1fdd58524db5 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -564,3 +564,13 @@ impl LocalModDefId { self.0.is_top_level_module() } } + +/// DefId which can only be used to check visibilities. +#[derive(Clone, Copy, PartialEq, Eq, Hash, StableHash, Encodable, Decodable, Debug)] +pub struct VisibilityDefId(pub DefId); + +impl> From for VisibilityDefId { + fn from(value: T) -> Self { + Self(value.into()) + } +} diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 6958b195a8e73..ba7b32d2bb4a1 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -41,7 +41,7 @@ use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::{debug, trace}; -use crate::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, StableCrateId}; +use crate::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, StableCrateId, VisibilityDefId}; use crate::edition::Edition; use crate::source_map::SourceMap; use crate::symbol::{Symbol, kw, sym}; @@ -1017,7 +1017,7 @@ pub struct ExpnData { /// if this `ExpnData` corresponds to a macro invocation pub macro_def_id: Option, /// The normal module (`mod`) in which the expanded macro was defined. - pub parent_module: Option, + pub parent_module: Option, /// Suppresses the `unsafe_code` lint for code produced by this macro. pub(crate) allow_internal_unsafe: bool, /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro. @@ -1055,7 +1055,7 @@ impl ExpnData { allow_internal_unstable, edition, macro_def_id, - parent_module, + parent_module: parent_module.map(Into::into), disambiguator: 0, allow_internal_unsafe, local_inner_macros, @@ -1080,7 +1080,7 @@ impl ExpnData { allow_internal_unstable: None, edition, macro_def_id, - parent_module, + parent_module: parent_module.map(Into::into), disambiguator: 0, allow_internal_unsafe: false, local_inner_macros: false, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 2e9dcae700d2a..a92ec1de41c48 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -818,7 +818,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None => generic_param_scope, }, }; - match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { + match self.tcx.is_descendant_of(type_scope, lifetime_scope) { true => type_scope, false => lifetime_scope, } From a785d8089acb1105696b9c011c1930a0767e852a Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 07:47:56 +0200 Subject: [PATCH 32/50] rdr: do not include VisibilityDefId in the public hash graph --- compiler/rustc_metadata/src/rmeta/encoder.rs | 10 ++++++++++ compiler/rustc_span/src/def_id.rs | 2 +- compiler/rustc_span/src/lib.rs | 12 +++++++++++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 997ca162d07bb..bf7ed4cd1fca8 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -32,6 +32,7 @@ use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{CrateType, OptLevel, TargetModifier}; +use rustc_span::def_id::VisibilityDefId; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ ByteSymbol, ExternalSource, FileName, SourceFile, SpanEncoder, StableSourceFileId, Symbol, @@ -169,6 +170,15 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { self.record_encoded_index(def_id); } + fn encode_visibility_def_id(&mut self, def_id: VisibilityDefId) { + // we don't record VisibilityDefId-s with `record_encoded_index` into the publicly reachable + // graph, as it cannot be used to query more data. Only to check visibility from other DefId-s + + def_id.0.krate.encode(self); + // this must not be encoded as a DefIndex, as that would record it as a LocalDefId + self.emit_u32(def_id.0.index.as_u32()); + } + fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { self.record_encoded_index(syntax_context); rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self); diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs index d1fdd58524db5..6a2a5dcc890ef 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -566,7 +566,7 @@ impl LocalModDefId { } /// DefId which can only be used to check visibilities. -#[derive(Clone, Copy, PartialEq, Eq, Hash, StableHash, Encodable, Decodable, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, StableHash, Decodable, Debug)] pub struct VisibilityDefId(pub DefId); impl> From for VisibilityDefId { diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 2b58dc8b6a708..1857d194e04e3 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -44,6 +44,7 @@ pub mod source_map; use source_map::{SourceMap, SourceMapInputs}; pub use self::caching_source_map_view::CachingSourceMapView; +use crate::def_id::VisibilityDefId; use crate::fatal_error::FatalError; pub mod edition; @@ -1348,7 +1349,7 @@ rustc_index::newtype_index! { /// This trait is used to allow encoder specific encodings of certain types. /// It is similar to rustc_type_ir's TyEncoder. -pub trait SpanEncoder: Encoder { +pub trait SpanEncoder: Encoder + Sized { fn encode_span(&mut self, span: Span); fn encode_symbol(&mut self, sym: Symbol); fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol); @@ -1359,6 +1360,9 @@ pub trait SpanEncoder: Encoder { fn encode_crate_num(&mut self, crate_num: CrateNum); fn encode_def_index(&mut self, def_index: DefIndex); fn encode_def_id(&mut self, def_id: DefId); + fn encode_visibility_def_id(&mut self, def_id: VisibilityDefId) { + def_id.0.encode(self); + } } impl SpanEncoder for FileEncoder { @@ -1446,6 +1450,12 @@ impl Encodable for DefId { } } +impl Encodable for VisibilityDefId { + fn encode(&self, s: &mut E) { + s.encode_visibility_def_id(*self) + } +} + impl Encodable for AttrId { fn encode(&self, _s: &mut E) { // A fresh id will be generated when decoding From ed3bebe85d8de6436bbeaf3684af6e6c36036156 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 08:00:40 +0200 Subject: [PATCH 33/50] rdr: debug log the hash graph --- .../src/rmeta/encoder/public_api_hasher.rs | 22 ++++++++++++++++++- 1 file changed, 21 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 1d92ff400f3b1..525c0301facf6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -445,6 +445,7 @@ impl HashableCrateRoot { let (rdr_hashes, hashes) = if hcx.hash_public_api { assert!(!self.header.is_proc_macro_crate); let graph = ecx.index_graph_builder.take().unwrap().build_graph(&mut hcx.hcx); + debug!("Hash graph {graph:?}"); let public_hashes = build_public_hashes(&graph, &hcx.def_id_hashes, ecx.tcx, &mut hcx.hcx); @@ -549,6 +550,25 @@ struct IndexGraph<'tcx> { roots: Vec, } +impl fmt::Debug for IndexGraph<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let node = |idx| self.nodes.get_index(idx).unwrap().0; + let fingerprint = |idx| self.nodes.get_index(idx).unwrap().1; + writeln!(f, "roots:")?; + for root in &self.roots { + writeln!(f, " {:?}", node(*root))?; + } + writeln!(f, "nodes:")?; + for (from, edges) in self.edges.iter_enumerated() { + writeln!(f, "{:?}: {:x?}", node(from), fingerprint(from))?; + for to in edges { + writeln!(f, " {:?}", node(*to))?; + } + } + Ok(()) + } +} + impl<'tcx> IndexGraph<'tcx> { fn reachable_set(&self) -> IndexVec { let mut reachable = IndexVec::from_elem_n(false, self.edges.len()); @@ -700,7 +720,7 @@ pub(super) enum LocalNode<'tcx> { Predicate(PredicateKind<'tcx>), } -#[derive(PartialEq, Eq, Clone, Copy, Hash, StableHash)] +#[derive(PartialEq, Eq, Clone, Copy, Hash, StableHash, Debug)] pub(super) enum Node<'tcx> { DefId(DefId), ExpnId(ExpnId), From 86f06ecb345877b521fc3b1c4bc718dab5fd5ebe Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 12:47:08 +0200 Subject: [PATCH 34/50] add -Zls=public_hash for printing the metadata public hash --- compiler/rustc_metadata/src/rmeta/decoder.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 14a5acf2893d4..1f25044e7be7d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -749,11 +749,15 @@ impl MetadataBlob { "lang_items".to_owned(), "features".to_owned(), "items".to_owned(), + "public_hash".to_owned(), ]; let ls_kinds = if ls_kinds.contains(&"all".to_owned()) { &all_ls_kinds } else { ls_kinds }; for kind in ls_kinds { match &**kind { + "public_hash" => { + writeln!(out, "Public hash: {}", root.public_hash())?; + } "root" => { writeln!(out, "Crate info:")?; writeln!(out, "name {}{}", root.name(), root.extra_filename)?; From a4868f789875af80ccb4d06c411d4a43b4a404eb Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 12:50:44 +0200 Subject: [PATCH 35/50] rdr: do not hash the def_path_hash_map --- compiler/rustc_metadata/src/rmeta/encoder.rs | 13 +++---------- .../src/rmeta/encoder/public_api_hasher.rs | 11 ----------- 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index bf7ed4cd1fca8..90aac08b4c4e9 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -706,17 +706,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - fn encode_def_path_hash_map( - &mut self, - hcx: &mut PublicApiHashingContext<'_>, - ) -> Hashed>> { + fn encode_def_path_hash_map(&mut self) -> Hashed>> { let value = self .lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())); - // an ordered hash of all local defids encapsulates all information contained in a reverse - // mapping as well. - let mut hasher = PublicApiHasher::default(); - hasher.digest_iter(self.tcx.iter_local_def_id(), hcx); - Hashed { hash: hasher.finish(hcx), value } + Hashed { hash: Some(Fingerprint::ZERO), value } } fn encode_source_map( @@ -911,7 +904,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let (syntax_contexts, expn_data, expn_hashes) = stat!("hygiene", || self.encode_hygiene(hcx)); - let def_path_hash_map = stat!("def-path-hash-map", || self.encode_def_path_hash_map(hcx)); + 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. 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 525c0301facf6..be37e1758de68 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -55,17 +55,6 @@ impl PublicApiHasher { value.stable_hash(&mut hcx.hcx, &mut self.0); } } - pub(crate) fn digest_iter<'a, I>(&mut self, values: I, hcx: &mut PublicApiHashingContext<'a>) - where - I: IntoIterator, - I::Item: StableHash, - { - if hcx.hash_public_api { - for value in values { - self.digest(value, hcx); - } - } - } } pub(crate) trait TablePublicApiHasher: Default { From 3d506a40420df563fa2a6ca72266bd37daf9f0e7 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 12:51:52 +0200 Subject: [PATCH 36/50] rdr: remove LocalExpnId::ROOT -> CRATE_DEF_ID edge from the public hash graph --- .../src/rmeta/encoder/public_api_hasher.rs | 11 +++++++++-- 1 file changed, 9 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 be37e1758de68..103db59b3b522 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -24,7 +24,7 @@ use rustc_middle::ty::{PredicateKind, Ty, TyCtxt}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{SymbolManglingVersion, TargetModifier}; use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; -use rustc_span::def_id::{DefId, LOCAL_CRATE, LocalDefId, StableCrateId}; +use rustc_span::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId, StableCrateId}; use rustc_span::edition::Edition; use rustc_span::hygiene::ExpnIndex; use rustc_span::{ExpnId, LocalExpnId, Span, Symbol, SyntaxContext}; @@ -601,7 +601,14 @@ fn stable_hash<'a, T: StableHash + ?Sized>( } impl<'tcx> IndexGraphBuilder<'tcx> { - fn build_graph(self, hcx: &mut StableHashingContext<'_>) -> IndexGraph<'tcx> { + fn build_graph(mut self, hcx: &mut StableHashingContext<'_>) -> IndexGraph<'tcx> { + // ExpnId::root() contains CRATE_DEF_ID as its macro_def_id, but that isn't used for + // anything. + // TODO add newtype that ensures it really isn't used + self.edges + .get_mut(&LocalNode::ExpnId(LocalExpnId::ROOT)) + .unwrap() + .remove(&Node::DefId(CRATE_DEF_ID.into())); let mut hashes = IndexMap::default(); // iterating over FxHashSet and FxHashMap is fine here, as it is only used to build the // hashes map, which is never returned or iterated From f6906bf3fceb4d482b53cf4e5316287c84fd029a Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 12:52:30 +0200 Subject: [PATCH 37/50] rdr: add reachablity to the debug representation of IndexGraph --- .../src/rmeta/encoder/public_api_hasher.rs | 9 ++++++++- 1 file changed, 8 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 103db59b3b522..3003d952c7c7b 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -543,13 +543,20 @@ impl fmt::Debug for IndexGraph<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let node = |idx| self.nodes.get_index(idx).unwrap().0; let fingerprint = |idx| self.nodes.get_index(idx).unwrap().1; + let reachable = self.reachable_set(); writeln!(f, "roots:")?; for root in &self.roots { writeln!(f, " {:?}", node(*root))?; } writeln!(f, "nodes:")?; for (from, edges) in self.edges.iter_enumerated() { - writeln!(f, "{:?}: {:x?}", node(from), fingerprint(from))?; + writeln!( + f, + "{:?}: {:x?} reachable: {}", + node(from), + fingerprint(from), + reachable[from] + )?; for to in edges { writeln!(f, " {:?}", node(*to))?; } From 6d8b170d52a25f3f53a3883c1d2417f784d0ec96 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sat, 23 May 2026 17:55:39 +0200 Subject: [PATCH 38/50] rdr: only include public_global_hash instead of the full public hash when hashing crate dependencies --- .../src/rmeta/decoder/cstore_impl.rs | 7 ++++++ compiler/rustc_metadata/src/rmeta/encoder.rs | 24 +++++++++++++++---- .../src/rmeta/encoder/public_api_hasher.rs | 8 +++++-- compiler/rustc_metadata/src/rmeta/mod.rs | 5 ++-- compiler/rustc_middle/src/queries.rs | 15 ++++++++++++ 5 files changed, 50 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index bdd1c5c22291b..a31531f639b17 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -471,6 +471,13 @@ provide! { tcx, def_id, other, cdata, Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) } } + public_global_hash => { + if let Some(rdr_hashes) = cdata.root.rdr_hashes.as_ref() { + rdr_hashes.public_global_hash + } else { + Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) + } + } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 90aac08b4c4e9..5ecdee4726c07 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2402,7 +2402,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { { // Sanity-check the crate numbers let mut expected_cnum = 1; - for &(n, _) in &deps { + for &(n, _, _) in &deps { assert_eq!(n, CrateNum::new(expected_cnum)); expected_cnum += 1; } @@ -2412,7 +2412,23 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // the assumption that they are numbered 1 to n. // FIXME (#2166): This is not nearly enough to support correct versioning // but is enough to get transitive crate dependencies working. - hashed_lazy_array!(self, deps.iter().map(|(_, dep)| dep), hcx) + let mut hasher = PublicApiHasher::default(); + let array = self.lazy_array(deps.into_iter().map(|(cnum, dep, public_global_hash)| { + let CrateDep { name, hash, host_hash, kind, extra_filename, is_private } = &dep; + hasher.digest(cnum, hcx); + hasher.digest(name, hcx); + // We only include the global part of dependency hashes, not the full public hash. + // The remaining, item specific, part of public_hash should only be included when those + // items are reachable in the public hash graph. + let _ = hash; + hasher.digest(public_global_hash, hcx); + hasher.digest(host_hash, hcx); + hasher.digest(kind, hcx); + hasher.digest(extra_filename, hcx); + hasher.digest(is_private, hcx); + dep + })); + Hashed { value: array, hash: hasher.finish(hcx) } } fn encode_target_modifiers( @@ -3100,7 +3116,7 @@ pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: &hir::Body<'_>, def_id: Loc } } -fn crate_deps(tcx: TyCtxt<'_>) -> impl Iterator + '_ { +fn crate_deps(tcx: TyCtxt<'_>) -> impl Iterator + '_ { tcx.crates(()).iter().map(move |&cnum| { let dep = CrateDep { name: tcx.crate_name(cnum), @@ -3110,7 +3126,7 @@ fn crate_deps(tcx: TyCtxt<'_>) -> impl Iterator + ' extra_filename: tcx.extra_filename(cnum).clone(), is_private: tcx.is_private_dep(cnum), }; - (cnum, dep) + (cnum, dep, tcx.public_global_hash(cnum)) }) } 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 3003d952c7c7b..f1e49a16db9f6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -439,9 +439,10 @@ impl HashableCrateRoot { build_public_hashes(&graph, &hcx.def_id_hashes, ecx.tcx, &mut hcx.hcx); let mut hasher = StableHasher::default(); - self.stable_hash(&mut hcx.hcx, &mut hasher); + let public_global_hash = stable_hash(&mut hcx.hcx, &self); + public_global_hash.stable_hash(&mut hcx.hcx, &mut hasher); public_hashes.stable_hash(&mut hcx.hcx, &mut hasher); - let rdr_hashes = public_hashes.value.encode(ecx, hcx); + let rdr_hashes = public_hashes.value.encode(ecx, hcx, public_global_hash); let public_hash = Svh::new(hasher.finish()); debug!("Hashed crate root: {self:#x?}"); debug!("public api hash: {}", public_hash); @@ -928,10 +929,12 @@ impl ItemPublicHashesBuilder { &self, ecx: &mut EncodeContext<'a, 'tcx>, hcx: &mut PublicApiHashingContext<'_>, + public_global_hash: Fingerprint, ) -> ItemPublicHashes { ItemPublicHashes { local: self.local.encode(&mut ecx.opaque, hcx).value, expn: self.expn.encode(&mut ecx.opaque, hcx).value, + public_global_hash, } } } @@ -940,4 +943,5 @@ impl ItemPublicHashesBuilder { pub(crate) struct ItemPublicHashes { pub(crate) local: LazyTable>, pub(crate) expn: LazyTable>, + pub(crate) public_global_hash: Fingerprint, } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index b5a691cc96865..c5d8cbb1b9446 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -21,8 +21,7 @@ use rustc_hir::{PreciseCapturingArgKind, attrs}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_macros::{ - BlobDecodable, Decodable, Encodable, LazyDecodable, MetadataEncodable, StableHash, TyDecodable, - TyEncodable, + BlobDecodable, Decodable, Encodable, LazyDecodable, MetadataEncodable, TyDecodable, TyEncodable, }; use rustc_middle::metadata::{AmbigModChild, ModChild}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; @@ -347,7 +346,7 @@ impl RawDefId { } } -#[derive(Encodable, BlobDecodable, StableHash)] +#[derive(Encodable, BlobDecodable)] pub(crate) struct CrateDep { pub name: Symbol, pub hash: Svh, diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 0eb78c5a49cf8..9050b966b3dae 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2065,6 +2065,21 @@ rustc_queries! { separate_provide_extern } + /// Returns the global part of the public api hash. This covers the items in the rmeta which + /// requires a recompile of all down crates. + /// + /// For example adding lang items requires all downstream crates to check that there are no + /// conflicing implementations for that lang item. But adding a regular public item only + /// requires the direct downstream crates to recompile, since those need to reexport it for + /// the change to be visible to their dependents. + /// + /// Panics for the local crate. + query public_global_hash(_: CrateNum) -> Fingerprint { + 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 98e4c4a9f6c66459d06e919e15b9effa7c62ffc1 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 24 May 2026 09:21:48 +0200 Subject: [PATCH 39/50] rdr: remove traits from rmeta --- compiler/rustc_metadata/src/rmeta/encoder.rs | 6 ++++++ .../rustc_metadata/src/rmeta/encoder/public_api_hasher.rs | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5ecdee4726c07..d98985a75e096 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2530,6 +2530,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { &mut self, hcx: &mut PublicApiHashingContext<'_>, ) -> Hashed> { + if hcx.enabled() { + // FIXME: we should encode this while public api hashing is enabled, but it must not be + // part of the public hash. Its query should depend on the private hash of the crate + // if it is included. + return Hashed { value: LazyArray::default(), hash: Some(Fingerprint::ZERO) }; + } empty_proc_macro!(self); hashed_lazy_array!( self, 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 f1e49a16db9f6..574aacb18b485 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -331,6 +331,10 @@ pub(crate) struct HashableCrateRoot { // FIXME do we need to hash this? // the traits defined in this crate. Definitely not needed in everything_downstream as is // maybe we can leak private traits through MIR? + // According to the docs, and some personal digging, this is used by rustdoc and error reporting + // rustdoc - problem for another day + // error reporting: this should not be included in the public hash, as it is only read when the + // compiler errors. Just like stripped_cfg_items pub(crate) traits: Hashed>, // FIXME do we need to hash this? // the traits impls in this crate. Definitely not needed in everything_downstream as is From 1458d187a4c82a49d426662fa20a13924dcab70d Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 24 May 2026 09:22:52 +0200 Subject: [PATCH 40/50] rdr: add some comments to the impls field of HashableCrateRoot --- .../src/rmeta/encoder/public_api_hasher.rs | 12 ++++++++++++ 1 file changed, 12 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 574aacb18b485..b7a4012bdba78 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -339,6 +339,18 @@ pub(crate) struct HashableCrateRoot { // FIXME do we need to hash this? // the traits impls in this crate. Definitely not needed in everything_downstream as is // how is it needed? + // + // this is exposed as a full query with `trait_impls_of_crate`. Which is only used in + // rustc_public. Otherwise the `implementations_of_trait` is the only other consumer of this + // field. `implementations_of_trait` works as a map. You need to provide a trait DefId to get + // its impls, so this should be intergrated into the IndexGraph. + // + // When a trait is local: + // if it isn't reachable, the impls can should be left out from the hash + // When a trait is from another crate: + // even if this crate does not reexport that trait, a downstream dependency can import it + // from another crate, then invoke its methods. So all of those impls must be included, but + // only the ones that can be applied to publicly reachable types pub(crate) impls: Hashed>, // FIXME do we need to hash this? // what is this used for From 5a9be6cdb4c06702a30cc727ad26c8354c80afc8 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 24 May 2026 17:15:59 +0200 Subject: [PATCH 41/50] rdr: disable upstream_monomorphizations when public_api_hash is enabled --- compiler/rustc_codegen_gcc/src/callee.rs | 1 + compiler/rustc_codegen_llvm/src/callee.rs | 1 + compiler/rustc_codegen_ssa/src/back/symbol_export.rs | 1 + compiler/rustc_middle/src/ty/instance.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/compiler/rustc_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 00f095ed54371..dede4f0b49655 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -107,6 +107,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_instance_attrs(instance.def).inline == rustc_hir::attrs::InlineAttr::Never) + || tcx.sess.opts.unstable_opts.public_api_hash { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 9215273eed17d..2e808f7c6f53c 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -104,6 +104,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_instance_attrs(instance.def).inline == rustc_hir::attrs::InlineAttr::Never) + || tcx.sess.opts.unstable_opts.public_api_hash { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 014fd5cf3a0e5..64147215520ed 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -302,6 +302,7 @@ fn exported_generic_symbols_provider_local<'tcx>( if !tcx.sess.opts.share_generics() { if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_hir::attrs::InlineAttr::Never + && !tcx.sess.opts.unstable_opts.public_api_hash { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 1d761c2f43b3d..114c110b59be7 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -209,6 +209,7 @@ impl<'tcx> Instance<'tcx> { // However, if the def_id is marked inline(never), then it's fine to just reuse the // upstream monomorphization. && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_hir::attrs::InlineAttr::Never + || tcx.sess.opts.unstable_opts.public_api_hash { return None; } From 3ae97121f3424c9d415fec60d02bade746d79c5f Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Sun, 24 May 2026 17:18:21 +0200 Subject: [PATCH 42/50] rdr: document exported_non_generic_symbols and exported_generic_symbols hashing --- .../src/rmeta/encoder/public_api_hasher.rs | 24 +++++++++++++++++-- 1 file changed, 22 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 b7a4012bdba78..af45c7b95e9aa 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -378,11 +378,31 @@ pub(crate) struct HashableCrateRoot { // what is this extactly, used for diagnostics pub(crate) stable_order_of_exportable_impls: Hashed>, // FIXME do we need to hash this? - // what is this + // exported_non_generic_symbols and exported_symbols are used by the linker, which likely means + // that we should put them behind the private hash pub(crate) exported_non_generic_symbols: Hashed, SymbolExportInfo)>>, + // FIXME do we need to hash this? - // i think this is used to find upstream_monomorphizations or what + // I think this is used to find upstream_monomorphizations or what + // So this is tricky, and kind of makes this feature useless. For example, using a + // Vec::::push in private code will add that monomorphized function to + // exported_generic_symbols. + // On one hand, removing this will slow down the codegen for the current crate, as it needs to + // codegen these generics. On the other hand, keeping this forces a rebuild of all upstream + // crates when any new monomorphized item (where the monomorphized generic is public or from + // another crate). + // Let's review this from an incremental recompilation perspecitve. The goal of + // relink-dont-rebuild is mostly speeding that up. + // Let's say we remove this, the first build was done + // 1. recompile of a dependency that contains a monomorphization which could be used upstream. + // What changes? Nothing, we don't have to export the monomorphization for + // `upstream_monomorphizations`, but its is also used by the linker, so it has to be included + // anyways + // 2. recompile of crate that could use an upstream monomorphization. Since it is disabled, the + // monomorphization is already in its local incremental cache. It will be pulled from there. + // Given these two points, if the goal is fast incremental recompiles, then disabling upstream + // monomorphizations, and moving this behind private_hash might be the best (maybe linking_hash) pub(crate) exported_generic_symbols: Hashed, SymbolExportInfo)>>, From 3e4f2c73297f1127274edfe5ccbcc1c7a71c49c8 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 17:20:25 +0200 Subject: [PATCH 43/50] rdr: move is_reachable_non_generic into a table --- .../src/back/symbol_export.rs | 5 ----- .../src/rmeta/decoder/cstore_impl.rs | 7 +++++++ compiler/rustc_metadata/src/rmeta/encoder.rs | 21 +++++++++++++++---- compiler/rustc_metadata/src/rmeta/mod.rs | 2 ++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 64147215520ed..999d7b0ccf996 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -161,10 +161,6 @@ fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: LocalDefId) } } -fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) -} - fn exported_non_generic_symbols_provider_local<'tcx>( tcx: TyCtxt<'tcx>, _: LocalCrate, @@ -491,7 +487,6 @@ pub(crate) fn provide(providers: &mut Providers) { providers.queries.upstream_drop_glue_for = upstream_drop_glue_for_provider; providers.queries.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider; providers.queries.wasm_import_module_map = wasm_import_module_map; - providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern; providers.extern_queries.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index a31531f639b17..c6198278b213d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -386,6 +386,13 @@ provide! { tcx, def_id, other, cdata, reachable_non_generics } + is_reachable_non_generic => { + if tcx.sess.opts.unstable_opts.public_api_hash { + cdata.root.tables.is_reachable_non_generic.get(cdata, def_id.index) + } else { + tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) + } + } native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } crate_hash => { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d98985a75e096..8410ce1f210e5 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -887,10 +887,23 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let (exported_non_generic_symbols, exported_generic_symbols) = stat!("exported-symbols", || { ( - self.encode_exported_symbols( - tcx.exported_non_generic_symbols(LOCAL_CRATE), - hcx, - ), + { + if tcx.sess.opts.unstable_opts.public_api_hash { + for (symbol, _info) in tcx.exported_non_generic_symbols(LOCAL_CRATE) { + let def_id = match symbol { + ExportedSymbol::NonGeneric(id) => id.expect_local(), + _ => continue, + }; + self.tables + .is_reachable_non_generic + .set_local_hashed(def_id, true, hcx); + } + } + self.encode_exported_symbols( + tcx.exported_non_generic_symbols(LOCAL_CRATE), + hcx, + ) + }, self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE), hcx), ) }); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c5d8cbb1b9446..350e3118f4186 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -473,6 +473,8 @@ define_tables! { safety: Table, // FIXME do we need to hash this? defaultness: Table, + // FIXME do we need to hash this? + is_reachable_non_generic: Table, - optional: // FIXME do we need to hash this? From bedb5cfbeaae7fd43aebfeb2880274a8132a0e13 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 17:32:13 +0200 Subject: [PATCH 44/50] rdr: add the is_reachable_non_generic_with_export_level_c query --- .../rustc_codegen_ssa/src/back/symbol_export.rs | 12 ++++++++++++ .../src/rmeta/decoder/cstore_impl.rs | 4 ++++ compiler/rustc_metadata/src/rmeta/encoder.rs | 11 ++++++++++- compiler/rustc_metadata/src/rmeta/mod.rs | 2 ++ compiler/rustc_middle/src/queries.rs | 5 +++++ compiler/rustc_monomorphize/src/partitioning.rs | 14 +++++++++++--- 6 files changed, 44 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 999d7b0ccf996..4f2490a4e1f13 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -161,6 +161,16 @@ fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: LocalDefId) } } +fn is_reachable_non_generic_with_export_level_c_provider( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> bool { + match tcx.reachable_non_generics(LOCAL_CRATE).get(&def_id.to_def_id()) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => true, + _ => false, + } +} + fn exported_non_generic_symbols_provider_local<'tcx>( tcx: TyCtxt<'tcx>, _: LocalCrate, @@ -487,6 +497,8 @@ pub(crate) fn provide(providers: &mut Providers) { providers.queries.upstream_drop_glue_for = upstream_drop_glue_for_provider; providers.queries.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider; providers.queries.wasm_import_module_map = wasm_import_module_map; + providers.queries.is_reachable_non_generic_with_export_level_c = + is_reachable_non_generic_with_export_level_c_provider; providers.extern_queries.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c6198278b213d..d6c3097d4be5d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -393,6 +393,10 @@ provide! { tcx, def_id, other, cdata, tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) } } + is_reachable_non_generic_with_export_level_c => { + assert!(tcx.sess.opts.unstable_opts.public_api_hash); + cdata.root.tables.is_reachable_non_generic_with_export_level_c.get(cdata, def_id.index) + } native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } crate_hash => { diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8410ce1f210e5..0f7660783c8a3 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -21,6 +21,7 @@ use rustc_hir_pretty::id_to_string; use rustc_middle::dep_graph::WorkProductId; use rustc_middle::metadata::ModChild; use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; @@ -889,11 +890,19 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { ( { if tcx.sess.opts.unstable_opts.public_api_hash { - for (symbol, _info) in tcx.exported_non_generic_symbols(LOCAL_CRATE) { + for (symbol, info) in tcx.exported_non_generic_symbols(LOCAL_CRATE) { let def_id = match symbol { ExportedSymbol::NonGeneric(id) => id.expect_local(), _ => continue, }; + if matches!( + info, + SymbolExportInfo { level: SymbolExportLevel::C, .. } + ) { + self.tables + .is_reachable_non_generic_with_export_level_c + .set_local_hashed(def_id, true, hcx); + } self.tables .is_reachable_non_generic .set_local_hashed(def_id, true, hcx); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 350e3118f4186..c131cf65b5f31 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -475,6 +475,8 @@ define_tables! { defaultness: Table, // FIXME do we need to hash this? is_reachable_non_generic: Table, + // FIXME do we need to hash this? + is_reachable_non_generic_with_export_level_c: Table, - optional: // FIXME do we need to hash this? diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 9050b966b3dae..98473d700b34a 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -1935,6 +1935,11 @@ rustc_queries! { tcx.def_path_str(def_id), } } + query is_reachable_non_generic_with_export_level_c(def_id: DefId) -> bool { + desc { "checking whether `{}` is an exported symbol with export level ExportLevel::C", tcx.def_path_str(def_id) } + cache_on_disk + separate_provide_extern + } /// The entire set of monomorphizations the local crate can safely /// link to because they are exported from upstream crates. Do diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 6058cb892dc14..f854fabc9e8fc 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -940,9 +940,17 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit // Generic functions never have export-level C. SymbolExportLevel::Rust } else { - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, - _ => SymbolExportLevel::Rust, + if tcx.sess.opts.unstable_opts.public_api_hash { + if tcx.is_reachable_non_generic_with_export_level_c(id) { + SymbolExportLevel::C + } else { + SymbolExportLevel::Rust + } + } else { + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, + _ => SymbolExportLevel::Rust, + } } }; From 9c1fc784425032579dcb205f0b75b2f772a631ba Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 17:39:07 +0200 Subject: [PATCH 45/50] rdr: move exported_generic_symbols and exported_non_generic_symbols behind the private hash, as these are only used when during linking --- .../src/rmeta/decoder/cstore_impl.rs | 15 ++++++++++----- compiler/rustc_metadata/src/rmeta/encoder.rs | 11 ++++------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index d6c3097d4be5d..9c38d92a48a9e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -153,11 +153,16 @@ macro_rules! provide_one { // The `crate_hash` query must not depend on public_api_hash, since it might change when // the `public_api_hash` does not change. use rustc_middle::dep_graph::DepKind; - if ((DepKind::$name != DepKind::public_api_hash) - & (DepKind::$name != DepKind::crate_hash)) - && $tcx.dep_graph.is_fully_enabled() - { - $tcx.ensure_ok().public_api_hash($def_id.krate); + match DepKind::$name { + DepKind::public_api_hash | DepKind::crate_hash => (), + DepKind::exported_generic_symbols | DepKind::exported_non_generic_symbols => { + // FIXME should this be some kind of linking_hash? These should only be read + // when we are doing linking + $tcx.ensure_ok().crate_hash($def_id.krate); + } + _ => { + $tcx.ensure_ok().public_api_hash($def_id.krate); + } } let cstore = CStore::from_tcx($tcx); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 0f7660783c8a3..6deaa37713c26 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -908,12 +908,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .set_local_hashed(def_id, true, hcx); } } - self.encode_exported_symbols( - tcx.exported_non_generic_symbols(LOCAL_CRATE), - hcx, - ) + self.encode_exported_symbols(tcx.exported_non_generic_symbols(LOCAL_CRATE)) }, - self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE), hcx), + self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE)), ) }); @@ -2708,11 +2705,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_exported_symbols( &mut self, exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)], - hcx: &mut PublicApiHashingContext<'_>, ) -> Hashed, SymbolExportInfo)>> { empty_proc_macro!(self); - hashed_lazy_array!(self, exported_symbols, hcx) + let array = self.lazy_array(exported_symbols); + Hashed { value: array, hash: Some(Fingerprint::ZERO) } } fn encode_dylib_dependency_formats( From 23fd17c4503e9b4452248c40922533ca2f53e3f6 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 17:40:32 +0200 Subject: [PATCH 46/50] rdr: assert that reachable_non_generics is not used when public api hashing is enabled --- compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 9c38d92a48a9e..c1c74d7d03777 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -377,6 +377,7 @@ provide! { tcx, def_id, other, cdata, symbol_mangling_version => { cdata.root.symbol_mangling_version } specialization_enabled_in => { cdata.root.specialization_enabled_in } reachable_non_generics => { + assert!(!tcx.sess.opts.unstable_opts.public_api_hash, "reachable_non_generics is not available while public_api_hash is enabled!"); let reachable_non_generics = tcx .exported_non_generic_symbols(cdata.cnum) .iter() From 2127868f81b8e951839245ca83c1d637f8c50653 Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 18:26:09 +0200 Subject: [PATCH 47/50] rdr: save into the rmeta whether public_api_hash was enabled, use it to return the correct reachable_non_generics implementations --- .../src/rmeta/decoder/cstore_impl.rs | 16 +++++++++++----- compiler/rustc_metadata/src/rmeta/encoder.rs | 1 + .../src/rmeta/encoder/public_api_hasher.rs | 2 ++ compiler/rustc_metadata/src/rmeta/mod.rs | 1 + 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index c1c74d7d03777..6b4fc20f7a32d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -10,7 +10,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild}; -use rustc_middle::middle::exported_symbols::ExportedSymbol; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel}; use rustc_middle::middle::stability::DeprecationEntry; use rustc_middle::queries::ExternProviders; use rustc_middle::query::LocalCrate; @@ -377,7 +377,7 @@ provide! { tcx, def_id, other, cdata, symbol_mangling_version => { cdata.root.symbol_mangling_version } specialization_enabled_in => { cdata.root.specialization_enabled_in } reachable_non_generics => { - assert!(!tcx.sess.opts.unstable_opts.public_api_hash, "reachable_non_generics is not available while public_api_hash is enabled!"); + assert!(!cdata.root.public_api_hash_opt_enabled, "reachable_non_generics is not available from rmeatas where public_api_hash is enabled!"); let reachable_non_generics = tcx .exported_non_generic_symbols(cdata.cnum) .iter() @@ -393,15 +393,21 @@ provide! { tcx, def_id, other, cdata, reachable_non_generics } is_reachable_non_generic => { - if tcx.sess.opts.unstable_opts.public_api_hash { + if cdata.root.public_api_hash_opt_enabled { cdata.root.tables.is_reachable_non_generic.get(cdata, def_id.index) } else { tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) } } is_reachable_non_generic_with_export_level_c => { - assert!(tcx.sess.opts.unstable_opts.public_api_hash); - cdata.root.tables.is_reachable_non_generic_with_export_level_c.get(cdata, def_id.index) + if cdata.root.public_api_hash_opt_enabled { + cdata.root.tables.is_reachable_non_generic_with_export_level_c.get(cdata, def_id.index) + } else { + match tcx.reachable_non_generics(def_id.krate).get(&def_id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => true, + _ => false, + } + } } native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 6deaa37713c26..8f686355ded6f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -987,6 +987,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { expn_hashes, def_path_hash_map, specialization_enabled_in: tcx.specialization_enabled_in(LOCAL_CRATE), + public_api_hash_opt_enabled: tcx.sess.opts.unstable_opts.public_api_hash, }; let crate_root = stat!("public hashes", || crate_root.into_crate_root(self, hcx)); let hashes = crate_root.header.hashes; 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 af45c7b95e9aa..34a0c7ed87ea4 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -458,6 +458,7 @@ pub(crate) struct HashableCrateRoot { // FIXME do we need to hash this? // everything_downstream? pub(crate) specialization_enabled_in: bool, + pub(crate) public_api_hash_opt_enabled: bool, } impl HashableCrateRoot { @@ -556,6 +557,7 @@ impl HashableCrateRoot { specialization_enabled_in: self.specialization_enabled_in, rdr_hashes, + public_api_hash_opt_enabled: self.public_api_hash_opt_enabled, } } } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index c131cf65b5f31..39e31376c402b 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -301,6 +301,7 @@ pub(crate) struct CrateRoot { specialization_enabled_in: bool, rdr_hashes: Option, + public_api_hash_opt_enabled: bool, } /// All hashes here are equal to the hash from the crate header (the `crate_hash` query) when the public-api-hash unstable feature is disabled. From f5ac7fc49c064dca32a7f0c0bb0bdcb02ed6d23c Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 18:41:33 +0200 Subject: [PATCH 48/50] rdr: allow access to private hash for rmetas compiled without public_api_hash, and in sessions requiring a linking step --- .../src/rmeta/decoder/cstore_impl.rs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 6b4fc20f7a32d..2e3f098b8f4aa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -413,10 +413,9 @@ provide! { tcx, def_id, other, cdata, foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } crate_hash => { assert!( - // We could allow all binary targets, they should always get a rustc invocation for - // linking, but there is no reason to (yet). !tcx.sess.opts.unstable_opts.public_api_hash - || tcx.crate_types().contains(&CrateType::ProcMacro), + || !cdata.root.public_api_hash_opt_enabled + || will_have_link_step(tcx), "Calling the crate_hash query for dependencies outside of proc-macro crates is unsound!" ); cdata.root.header.hashes.private_hash @@ -838,3 +837,15 @@ fn provide_cstore_hooks(providers: &mut Providers) { } }; } + +fn will_have_link_step(tcx: TyCtxt<'_>) -> bool { + if !tcx.sess.opts.output_types.should_link() { + return false; + } + tcx.crate_types().iter().any(|ct| { + matches!( + ct, + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro + ) + }) +} From 621b5c4ae1e6477c6a5a3a84d3450eb3254c21ac Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 19:11:07 +0200 Subject: [PATCH 49/50] rdr: add test testing that changes to a private file does not change the public api hash --- .../rdr/private_module_file/auxiliary/dep.rs | 13 +++++++++++++ .../rdr/private_module_file/auxiliary/private.rs | 9 +++++++++ tests/incremental/rdr/private_module_file/main.rs | 15 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 tests/incremental/rdr/private_module_file/auxiliary/dep.rs create mode 100644 tests/incremental/rdr/private_module_file/auxiliary/private.rs create mode 100644 tests/incremental/rdr/private_module_file/main.rs diff --git a/tests/incremental/rdr/private_module_file/auxiliary/dep.rs b/tests/incremental/rdr/private_module_file/auxiliary/dep.rs new file mode 100644 index 0000000000000..20aa0f230d2d8 --- /dev/null +++ b/tests/incremental/rdr/private_module_file/auxiliary/dep.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Z public-api-hash + +#![crate_name = "dep"] +#![crate_type = "rlib"] + +mod private; + +pub fn call_private() { + private::print(); +} + +#[cfg(any(cpass3))] +pub fn new_public() {} diff --git a/tests/incremental/rdr/private_module_file/auxiliary/private.rs b/tests/incremental/rdr/private_module_file/auxiliary/private.rs new file mode 100644 index 0000000000000..65b5dff046907 --- /dev/null +++ b/tests/incremental/rdr/private_module_file/auxiliary/private.rs @@ -0,0 +1,9 @@ +#[cfg(any(cpass1))] +pub(super) fn print() { + println!("a"); +} + +#[cfg(any(cpass2, cpass3))] +pub(super) fn print() { + println!("b"); +} diff --git a/tests/incremental/rdr/private_module_file/main.rs b/tests/incremental/rdr/private_module_file/main.rs new file mode 100644 index 0000000000000..4d53700cb487d --- /dev/null +++ b/tests/incremental/rdr/private_module_file/main.rs @@ -0,0 +1,15 @@ +//@ revisions: cpass1 cpass2 cpass3 +//@ 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")] + +extern crate dep; + +fn main() { + dep::call_private(); +} From 33aec4a0509016689af9e216be09c2b85006bb3c Mon Sep 17 00:00:00 2001 From: Matyas Susits Date: Mon, 25 May 2026 19:13:44 +0200 Subject: [PATCH 50/50] rdr: test that changing public items in crate does not change the public hash of downstream crates, unless they reexport that item --- .../auxiliary/dep.rs | 9 +++++++++ .../auxiliary/transitive_dep.rs | 19 +++++++++++++++++++ .../main.rs | 15 +++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs create mode 100644 tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs create mode 100644 tests/incremental/rdr/reexported_transitive_dependencies/main.rs diff --git a/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs new file mode 100644 index 0000000000000..232763d6d05c4 --- /dev/null +++ b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs @@ -0,0 +1,9 @@ +//@ aux-build: transitive_dep.rs +//@ compile-flags: -Z public-api-hash + +#![crate_name = "dep"] +#![crate_type = "rlib"] + +extern crate transitive_dep; + +pub use transitive_dep::print; diff --git a/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs new file mode 100644 index 0000000000000..e4d31ff58f308 --- /dev/null +++ b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Z public-api-hash + +#![crate_name = "transitive_dep"] +#![crate_type = "rlib"] + +#[cfg(any(cpass1, cpass2))] +pub fn print() { + println!("5"); +} + +#[cfg(any(cpass3))] +pub fn print() { + println!("5"); +} + +#[cfg(any(cpass2))] +pub fn print2() { + println!("5"); +} diff --git a/tests/incremental/rdr/reexported_transitive_dependencies/main.rs b/tests/incremental/rdr/reexported_transitive_dependencies/main.rs new file mode 100644 index 0000000000000..a708d87f832fb --- /dev/null +++ b/tests/incremental/rdr/reexported_transitive_dependencies/main.rs @@ -0,0 +1,15 @@ +//@ revisions: cpass1 cpass2 cpass3 +//@ 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")] + +extern crate dep; + +fn main() { + dep::print(); +}