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_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_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_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 cbd6afd68473a..01143533dd8b8 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}; @@ -144,12 +145,18 @@ 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). + // + // 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::crate_hash && $tcx.dep_graph.is_fully_enabled() { - $tcx.ensure_ok().crate_hash($def_id.krate); + 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); } let cstore = CStore::from_tcx($tcx); @@ -366,7 +373,20 @@ 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 } + 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), + "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.header.hashes.public_hash); + cdata.root.header.hashes.public_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 a0db004b7f4c4..cb2d15b170182 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(); } }; } @@ -333,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 @@ -393,11 +403,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,23 +423,72 @@ 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, + ); + } + }}; +} + +/// 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) => {{ + { + 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> { @@ -510,6 +577,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 +585,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 +626,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 +679,48 @@ 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, CrateHashes) { let tcx = self.tcx; let mut stats: Vec<(&'static str, usize)> = Vec::with_capacity(32); @@ -618,45 +737,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 +793,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,76 +834,78 @@ 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), - }) - }); + .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 hashes = crate_root.header.hashes; + + let root = stat!("final", || { self.lazy(crate_root) }); let total_bytes = self.position(); @@ -841,7 +970,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { eprint!("{s}"); } - root + (root, hashes) } } @@ -1393,7 +1522,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), @@ -1404,17 +1533,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` @@ -1427,7 +1561,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 @@ -1460,226 +1594,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); @@ -1694,35 +1851,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(); @@ -1733,16 +1898,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(()) @@ -1750,64 +1915,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; } @@ -1824,53 +2005,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); } } @@ -1884,99 +2067,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; @@ -1985,24 +2197,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 @@ -2033,15 +2245,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); } } @@ -2051,9 +2271,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() @@ -2062,28 +2287,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 @@ -2098,77 +2312,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() { @@ -2179,9 +2436,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( @@ -2189,68 +2450,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) ) } @@ -2263,26 +2558,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) } } @@ -2428,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(); @@ -2478,31 +2751,62 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { ); } + 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. tcx.dep_graph.with_task( 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(|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 + & 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, crate_hashes) = ecx.encode_crate_root(&mut hcx); + hashes = crate_hashes; + + // 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, ); + + // 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(), + hashes, + is_proc_macro_crate: false, + is_stub: true, + }); + header.position.get() + }) + } } fn with_encode_metadata_header( @@ -2687,3 +2991,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.public_api_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..75fe45353bfce --- /dev/null +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -0,0 +1,442 @@ +use std::fmt; + +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 tracing::debug; + +use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; +use crate::rmeta::{ + CrateHashes, DefPathHashMapRef, EiiMapEncodedKeyValue, ExpnDataTable, ExpnHashTable, + IncoherentImpls, LazyArray, LazyTable, LazyTables, LazyValue, ProcMacroData, + 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> { + pub(crate) 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 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); + } +} + +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()); + 0u32.stable_hash(hcx, hasher); + } +} + +#[derive(StableHash, Debug)] +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, +} + +/// 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? + 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, + // 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? + 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 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_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) } + } else { + let hash = tcx.crate_hash(LOCAL_CRATE); + CrateHashes { public_hash: hash, private_hash: hash } + }; + let header = self.header; + let header = CrateHeader { + triple: header.triple, + hashes, + 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, + } + } +} diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a3645a5556bf3..1df0b3e9e8fff 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; @@ -206,7 +210,7 @@ pub(crate) struct ProcMacroData { #[derive(MetadataEncodable, BlobDecodable)] pub(crate) struct CrateHeader { pub(crate) triple: TargetTuple, - pub(crate) hash: Svh, + pub(crate) hashes: CrateHashes, pub(crate) name: Symbol, /// Whether this is the header for a proc-macro crate. /// @@ -299,6 +303,25 @@ pub(crate) struct CrateRoot { specialization_enabled_in: 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. +#[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`. /// This creates a type-safe way to enforce that we remap the CrateNum between the on-disk /// representation and the compilation session. @@ -323,7 +346,7 @@ impl RawDefId { } } -#[derive(Encodable, BlobDecodable)] +#[derive(Encodable, BlobDecodable, StableHash)] pub(crate) struct CrateDep { pub name: Symbol, pub hash: Svh, @@ -348,8 +371,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 { @@ -359,121 +382,227 @@ 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); + 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 + },)+ + }; + Hashed { hash: hasher.finish(hcx), value: 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>>, - is_macro_rules: Table, - type_alias_is_lazy: Table, - attr_flags: Table, + // 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. // // 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, + // 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. - module_children_reexports: Table>, - ambig_module_children: Table>, - cross_crate_inlinable: Table, - asyncness: Table, - constness: Table, - safety: Table, - defaultness: Table, + // 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: - attributes: Table>, + // 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. - 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 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? - 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>, + // 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 // `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>, + // 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>>>, } #[derive(TyEncodable, TyDecodable)] @@ -486,7 +615,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), + } } } 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/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 diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index f8200badb345b..c15f86a2f7a7c 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2033,12 +2033,23 @@ 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" } 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 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_session/src/options.rs b/compiler/rustc_session/src/options.rs index 51b2635a4188e..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, )? @@ -2564,6 +2567,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], 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, 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, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index b1a9c4b3cde6a..321781e29f840 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -40,11 +40,11 @@ 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, @@ -52,9 +52,7 @@ use rustc_abi::{ }; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; 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; @@ -67,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)] @@ -3887,142 +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) - } - } - } -} - -// 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); 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); +}