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_codegen_gcc/src/callee.rs b/compiler/rustc_codegen_gcc/src/callee.rs index 00f095ed54371..dede4f0b49655 100644 --- a/compiler/rustc_codegen_gcc/src/callee.rs +++ b/compiler/rustc_codegen_gcc/src/callee.rs @@ -107,6 +107,7 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_instance_attrs(instance.def).inline == rustc_hir::attrs::InlineAttr::Never) + || tcx.sess.opts.unstable_opts.public_api_hash { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_llvm/src/callee.rs b/compiler/rustc_codegen_llvm/src/callee.rs index 9215273eed17d..2e808f7c6f53c 100644 --- a/compiler/rustc_codegen_llvm/src/callee.rs +++ b/compiler/rustc_codegen_llvm/src/callee.rs @@ -104,6 +104,7 @@ pub(crate) fn get_fn<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'t if !(cx.tcx.sess.opts.share_generics() || tcx.codegen_instance_attrs(instance.def).inline == rustc_hir::attrs::InlineAttr::Never) + || tcx.sess.opts.unstable_opts.public_api_hash { // When not sharing generics, all instances are in the same // crate and have hidden visibility. diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 014fd5cf3a0e5..4f2490a4e1f13 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -161,8 +161,14 @@ fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: LocalDefId) } } -fn is_reachable_non_generic_provider_extern(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) +fn is_reachable_non_generic_with_export_level_c_provider( + tcx: TyCtxt<'_>, + def_id: LocalDefId, +) -> bool { + match tcx.reachable_non_generics(LOCAL_CRATE).get(&def_id.to_def_id()) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => true, + _ => false, + } } fn exported_non_generic_symbols_provider_local<'tcx>( @@ -302,6 +308,7 @@ fn exported_generic_symbols_provider_local<'tcx>( if !tcx.sess.opts.share_generics() { if tcx.codegen_fn_attrs(mono_item.def_id()).inline == rustc_hir::attrs::InlineAttr::Never + && !tcx.sess.opts.unstable_opts.public_api_hash { // this is OK, we explicitly allow sharing inline(never) across crates even // without share-generics. @@ -490,7 +497,8 @@ pub(crate) fn provide(providers: &mut Providers) { providers.queries.upstream_drop_glue_for = upstream_drop_glue_for_provider; providers.queries.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider; providers.queries.wasm_import_module_map = wasm_import_module_map; - providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern; + providers.queries.is_reachable_non_generic_with_export_level_c = + is_reachable_non_generic_with_export_level_c_provider; providers.extern_queries.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; } diff --git a/compiler/rustc_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_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 5a86e8186a5aa..5aca21a573de1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -30,7 +30,7 @@ use rustc_errors::{ struct_span_code_err, }; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::{DefId, LocalDefId, VisibilityDefId}; use rustc_hir::{self as hir, AnonConst, GenericArg, GenericArgs, HirId}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::DynCompatibilityViolation; @@ -117,7 +117,7 @@ pub enum RegionInferReason<'a> { pub struct InherentAssocCandidate { pub impl_: DefId, pub assoc_item: DefId, - pub scope: DefId, + pub scope: VisibilityDefId, } pub struct ResolvedStructPath<'tcx> { @@ -1882,7 +1882,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { assoc_tag: ty::AssocTag, block: HirId, scope: DefId, - ) -> Option<(ty::AssocItem, /*scope*/ DefId)> { + ) -> Option<(ty::AssocItem, /*scope*/ VisibilityDefId)> { let tcx = self.tcx(); let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, block); @@ -1902,7 +1902,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, item_def_id: DefId, ident: Ident, - scope: DefId, + scope: VisibilityDefId, block: HirId, span: Span, ) { diff --git a/compiler/rustc_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..1f25044e7be7d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -98,7 +98,7 @@ pub(crate) struct CrateMetadata { /// Trait impl data. /// FIXME: Used only from queries and can use query cache, /// so pre-decoding can probably be avoided. - trait_impls: FxIndexMap<(u32, DefIndex), LazyArray<(DefIndex, Option)>>, + trait_impls: FxIndexMap<(u32, u32), LazyArray<(DefIndex, Option)>>, /// Inherent impls which do not follow the normal coherence rules. /// /// These can be introduced using either `#![rustc_coherence_is_core]` @@ -749,20 +749,25 @@ impl MetadataBlob { "lang_items".to_owned(), "features".to_owned(), "items".to_owned(), + "public_hash".to_owned(), ]; let ls_kinds = if ls_kinds.contains(&"all".to_owned()) { &all_ls_kinds } else { ls_kinds }; for kind in ls_kinds { match &**kind { + "public_hash" => { + writeln!(out, "Public hash: {}", root.public_hash())?; + } "root" => { writeln!(out, "Crate info:")?; writeln!(out, "name {}{}", root.name(), root.extra_filename)?; 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 +946,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 { @@ -1442,7 +1451,7 @@ impl CrateMetadata { // Do a reverse lookup beforehand to avoid touching the crate_num // hash map in the loop below. let key = match self.reverse_translate_def_id(trait_def_id) { - Some(def_id) => (def_id.krate.as_u32(), def_id.index), + Some(def_id) => (def_id.krate.as_u32(), def_id.index.as_u32()), None => return &[], }; @@ -2033,8 +2042,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..2e3f098b8f4aa 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -2,6 +2,7 @@ use std::any::Any; use std::mem; use std::sync::Arc; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_hir::attrs::Deprecation; use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE}; @@ -9,7 +10,7 @@ use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; use rustc_middle::arena::ArenaAllocatable; use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild}; -use rustc_middle::middle::exported_symbols::ExportedSymbol; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportLevel}; use rustc_middle::middle::stability::DeprecationEntry; use rustc_middle::queries::ExternProviders; use rustc_middle::query::LocalCrate; @@ -18,6 +19,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 +146,23 @@ 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); + match DepKind::$name { + DepKind::public_api_hash | DepKind::crate_hash => (), + DepKind::exported_generic_symbols | DepKind::exported_non_generic_symbols => { + // FIXME should this be some kind of linking_hash? These should only be read + // when we are doing linking + $tcx.ensure_ok().crate_hash($def_id.krate); + } + _ => { + $tcx.ensure_ok().public_api_hash($def_id.krate); + } } let cstore = CStore::from_tcx($tcx); @@ -179,18 +192,29 @@ macro_rules! provide { // small trait to work around different signature queries all being defined via // the macro above. trait IntoArgs { + type This; type Other; - fn into_args(self) -> (DefId, Self::Other); + fn into_args(self) -> (Self::This, Self::Other); } impl IntoArgs for DefId { + type This = DefId; type Other = (); fn into_args(self) -> (DefId, ()) { (self, ()) } } +impl IntoArgs for ExpnId { + type This = ExpnId; + type Other = (); + fn into_args(self) -> (ExpnId, ()) { + (self, ()) + } +} + impl IntoArgs for CrateNum { + type This = DefId; type Other = (); fn into_args(self) -> (DefId, ()) { (self.as_def_id(), ()) @@ -198,6 +222,7 @@ impl IntoArgs for CrateNum { } impl IntoArgs for (CrateNum, DefId) { + type This = DefId; type Other = DefId; fn into_args(self) -> (DefId, DefId) { (self.0.as_def_id(), self.1) @@ -205,6 +230,7 @@ impl IntoArgs for (CrateNum, DefId) { } impl<'tcx> IntoArgs for ty::InstanceKind<'tcx> { + type This = DefId; type Other = (); fn into_args(self) -> (DefId, ()) { (self.def_id(), ()) @@ -212,6 +238,7 @@ impl<'tcx> IntoArgs for ty::InstanceKind<'tcx> { } impl IntoArgs for (CrateNum, SimplifiedType) { + type This = DefId; type Other = SimplifiedType; fn into_args(self) -> (DefId, SimplifiedType) { (self.0.as_def_id(), self.1) @@ -248,7 +275,7 @@ provide! { tcx, def_id, other, cdata, lookup_default_body_stability => { table } lookup_deprecation_entry => { table } params_in_repr => { table } - def_kind => { cdata.def_kind(def_id.index) } + def_kind => { table_direct } impl_parent => { table } defaultness => { table_direct } constness => { table_direct } @@ -350,6 +377,7 @@ provide! { tcx, def_id, other, cdata, symbol_mangling_version => { cdata.root.symbol_mangling_version } specialization_enabled_in => { cdata.root.specialization_enabled_in } reachable_non_generics => { + assert!(!cdata.root.public_api_hash_opt_enabled, "reachable_non_generics is not available from rmeatas where public_api_hash is enabled!"); let reachable_non_generics = tcx .exported_non_generic_symbols(cdata.cnum) .iter() @@ -364,9 +392,38 @@ provide! { tcx, def_id, other, cdata, reachable_non_generics } + is_reachable_non_generic => { + if cdata.root.public_api_hash_opt_enabled { + cdata.root.tables.is_reachable_non_generic.get(cdata, def_id.index) + } else { + tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) + } + } + is_reachable_non_generic_with_export_level_c => { + if cdata.root.public_api_hash_opt_enabled { + cdata.root.tables.is_reachable_non_generic_with_export_level_c.get(cdata, def_id.index) + } else { + match tcx.reachable_non_generics(def_id.krate).get(&def_id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => true, + _ => false, + } + } + } native_libraries => { cdata.get_native_libraries(tcx).collect() } foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() } - crate_hash => { cdata.root.header.hash } + crate_hash => { + assert!( + !tcx.sess.opts.unstable_opts.public_api_hash + || !cdata.root.public_api_hash_opt_enabled + || will_have_link_step(tcx), + "Calling the crate_hash query for dependencies outside of proc-macro crates is unsound!" + ); + cdata.root.header.hashes.private_hash + } + 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() } @@ -416,6 +473,33 @@ provide! { tcx, def_id, other, cdata, } anon_const_kind => { table } const_of_item => { table } + extern_def_public_hash => { + if let Some(rdr_hashes) = cdata.root.rdr_hashes.as_ref() { + rdr_hashes + .local + .get(cdata, def_id.index) + .unwrap_or_else(|| bug!("Trying to read public hash of definition {def_id:?}, categoriazed as private!")) + } else { + Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) + } + } + extern_expn_public_hash => { + if let Some(rdr_hashes) = cdata.root.rdr_hashes.as_ref() { + rdr_hashes + .expn + .get(cdata, def_id.local_id) + .unwrap_or_else(|| bug!("Trying to read public hash of expnasion {def_id:?}, categoriazed as private!")) + } else { + Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) + } + } + public_global_hash => { + if let Some(rdr_hashes) = cdata.root.rdr_hashes.as_ref() { + rdr_hashes.public_global_hash + } else { + Fingerprint::from_le_bytes(cdata.root.header.hashes.public_hash.as_u128().to_le_bytes()) + } + } } pub(in crate::rmeta) fn provide(providers: &mut Providers) { @@ -753,3 +837,15 @@ fn provide_cstore_hooks(providers: &mut Providers) { } }; } + +fn will_have_link_step(tcx: TyCtxt<'_>) -> bool { + if !tcx.sess.opts.output_types.should_link() { + return false; + } + tcx.crate_types().iter().any(|ct| { + matches!( + ct, + CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro + ) + }) +} diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index a0db004b7f4c4..8f686355ded6f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1,10 +1,12 @@ use std::borrow::Borrow; +use std::cell::RefCell; use std::collections::hash_map::Entry; use std::fs::File; use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{par_for_each_in, par_join}; @@ -17,28 +19,39 @@ use rustc_hir::definitions::DefPathData; use rustc_hir::find_attr; use rustc_hir_pretty::id_to_string; use rustc_middle::dep_graph::WorkProductId; +use rustc_middle::metadata::ModChild; use rustc_middle::middle::dependency_format::Linkage; +use rustc_middle::middle::exported_symbols::SymbolExportLevel; +use rustc_middle::middle::privacy::EffectiveVisibilities; 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, PredicateKind, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; use rustc_session::config::{CrateType, OptLevel, TargetModifier}; +use rustc_span::def_id::VisibilityDefId; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ - ByteSymbol, ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, - Symbol, SyntaxContext, sym, + ByteSymbol, ExternalSource, FileName, SourceFile, SpanEncoder, StableSourceFileId, Symbol, + SyntaxContext, sym, }; use tracing::{debug, instrument, trace}; +use self::public_api_hasher::{ + HashableCrateHeader, HashableCrateRoot, Hashed, NoneIfHashed, PublicApiHasher, + PublicApiHashingContext, +}; use crate::eii::EiiMapEncodedKeyValue; use crate::errors::{FailCreateFileEncoder, FailWriteFile}; +use crate::rmeta::encoder::public_api_hasher::{IndexGraphBuilder, LocalNode, Node, RecordMode}; use crate::rmeta::*; +pub(super) mod public_api_hasher; + pub(super) struct EncodeContext<'a, 'tcx> { opaque: opaque::FileEncoder, tcx: TyCtxt<'tcx>, @@ -46,7 +59,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { tables: TableBuilders, lazy_state: LazyState, - span_shorthands: FxHashMap, + span_shorthands: FxHashMap>)>, type_shorthands: FxHashMap, usize>, predicate_shorthands: FxHashMap, usize>, @@ -67,6 +80,7 @@ pub(super) struct EncodeContext<'a, 'tcx> { hygiene_ctxt: &'a HygieneEncodeContext, // Used for both `Symbol`s and `ByteSymbol`s. symbol_index_table: FxHashMap, + index_graph_builder: Option>, } /// If the current crate is a proc-macro, returns early with `LazyArray::default()`. @@ -75,7 +89,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(); } }; } @@ -145,15 +159,29 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } fn encode_def_index(&mut self, def_index: DefIndex) { + // This is treated as if we are encoding a LocalDefId for now self.emit_u32(def_index.as_u32()); + self.record_encoded_index(LocalDefId { local_def_index: def_index }); } fn encode_def_id(&mut self, def_id: DefId) { def_id.krate.encode(self); - def_id.index.encode(self); + // this must not be encoded as a DefIndex, as that would record it as a LocalDefId + self.emit_u32(def_id.index.as_u32()); + self.record_encoded_index(def_id); + } + + fn encode_visibility_def_id(&mut self, def_id: VisibilityDefId) { + // we don't record VisibilityDefId-s with `record_encoded_index` into the publicly reachable + // graph, as it cannot be used to query more data. Only to check visibility from other DefId-s + + def_id.0.krate.encode(self); + // this must not be encoded as a DefIndex, as that would record it as a LocalDefId + self.emit_u32(def_id.0.index.as_u32()); } fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) { + self.record_encoded_index(syntax_context); rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self); } @@ -167,6 +195,7 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { } expn_id.krate.encode(self); expn_id.local_id.encode(self); + self.record_encoded_index(expn_id); } fn encode_span(&mut self, span: Span) { @@ -174,7 +203,13 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { Entry::Occupied(o) => { // If an offset is smaller than the absolute position, we encode with the offset. // This saves space since smaller numbers encode in less bits. - let last_location = *o.get(); + let (last_location, edges) = o.get(); + if let Some(local_def_id_graph_builder) = self.index_graph_builder.as_mut() { + for edge in edges { + local_def_id_graph_builder.record(*edge); + } + } + let last_location = *last_location; // This cannot underflow. Metadata is written with increasing position(), so any // previously saved offset must be smaller than the current position. let offset = self.opaque.position() - last_location; @@ -194,11 +229,11 @@ impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> { }); } } - Entry::Vacant(v) => { + Entry::Vacant(_v) => { let position = self.opaque.position(); - v.insert(position); // Data is encoded with a SpanTag prefix (see below). - span.data().encode(self); + let edges = encode_span_data(span, self); + self.span_shorthands.insert(span, (position, edges)); } } } @@ -218,147 +253,164 @@ fn bytes_needed(n: usize) -> usize { (usize::BITS - n.leading_zeros()).div_ceil(u8::BITS) as usize } -impl<'a, 'tcx> Encodable> for SpanData { - fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { - // Don't serialize any `SyntaxContext`s from a proc-macro crate, - // since we don't load proc-macro dependencies during serialization. - // This means that any hygiene information from macros used *within* - // a proc-macro crate (e.g. invoking a macro that expands to a proc-macro - // definition) will be lost. - // - // This can show up in two ways: - // - // 1. Any hygiene information associated with identifier of - // a proc macro (e.g. `#[proc_macro] pub fn $name`) will be lost. - // Since proc-macros can only be invoked from a different crate, - // real code should never need to care about this. - // - // 2. Using `Span::def_site` or `Span::mixed_site` will not - // include any hygiene information associated with the definition - // site. This means that a proc-macro cannot emit a `$crate` - // identifier which resolves to one of its dependencies, - // which also should never come up in practice. - // - // Additionally, this affects `Span::parent`, and any other - // span inspection APIs that would otherwise allow traversing - // the `SyntaxContexts` associated with a span. - // - // None of these user-visible effects should result in any - // cross-crate inconsistencies (getting one behavior in the same - // crate, and a different behavior in another crate) due to the - // limited surface that proc-macros can expose. - // - // IMPORTANT: If this is ever changed, be sure to update - // `rustc_span::hygiene::raw_encode_expn_id` to handle - // encoding `ExpnData` for proc-macro crates. - let ctxt = if s.is_proc_macro { SyntaxContext::root() } else { self.ctxt }; - - if self.is_dummy() { - let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); - tag.encode(s); - if tag.context().is_none() { - ctxt.encode(s); - } - return; - } - - // The Span infrastructure should make sure that this invariant holds: - debug_assert!(self.lo <= self.hi); - - if !s.source_file_cache.0.contains(self.lo) { - let source_map = s.tcx.sess.source_map(); - let source_file_index = source_map.lookup_source_file_idx(self.lo); - s.source_file_cache = - (Arc::clone(&source_map.files()[source_file_index]), source_file_index); - } - let (ref source_file, source_file_index) = s.source_file_cache; - debug_assert!(source_file.contains(self.lo)); - - if !source_file.contains(self.hi) { - // Unfortunately, macro expansion still sometimes generates Spans - // that malformed in this way. - let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); - tag.encode(s); - if tag.context().is_none() { - ctxt.encode(s); - } - return; - } - - // There are two possible cases here: - // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the - // crate we are writing metadata for. When the metadata for *this* crate gets - // deserialized, the deserializer will need to know which crate it originally came - // from. We use `TAG_VALID_SPAN_FOREIGN` to indicate that a `CrateNum` should - // be deserialized after the rest of the span data, which tells the deserializer - // which crate contains the source map information. - // 2. This span comes from our own crate. No special handling is needed - we just - // write `TAG_VALID_SPAN_LOCAL` to let the deserializer know that it should use - // our own source map information. - // - // If we're a proc-macro crate, we always treat this as a local `Span`. - // In `encode_source_map`, we serialize foreign `SourceFile`s into our metadata - // if we're a proc-macro crate. - // This allows us to avoid loading the dependencies of proc-macro crates: all of - // the information we need to decode `Span`s is stored in the proc-macro crate. - let (kind, metadata_index) = if source_file.is_imported() && !s.is_proc_macro { - // To simplify deserialization, we 'rebase' this span onto the crate it originally came - // from (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' - // values are relative to the source map information for the 'foreign' crate whose - // CrateNum we write into the metadata. This allows `imported_source_files` to binary - // search through the 'foreign' crate's source map information, using the - // deserialized 'lo' and 'hi' values directly. - // - // All of this logic ensures that the final result of deserialization is a 'normal' - // Span that can be used without any additional trouble. - let metadata_index = { - // Introduce a new scope so that we drop the 'read()' temporary - match &*source_file.external_src.read() { - ExternalSource::Foreign { metadata_index, .. } => *metadata_index, - src => panic!("Unexpected external source {src:?}"), - } - }; +fn encode_span_data<'a, 'tcx>(span: Span, s: &mut EncodeContext<'a, 'tcx>) -> Vec> { + let this = span.data(); + let mut nodes = Vec::>::new(); + // Don't serialize any `SyntaxContext`s from a proc-macro crate, + // since we don't load proc-macro dependencies during serialization. + // This means that any hygiene information from macros used *within* + // a proc-macro crate (e.g. invoking a macro that expands to a proc-macro + // definition) will be lost. + // + // This can show up in two ways: + // + // 1. Any hygiene information associated with identifier of + // a proc macro (e.g. `#[proc_macro] pub fn $name`) will be lost. + // Since proc-macros can only be invoked from a different crate, + // real code should never need to care about this. + // + // 2. Using `Span::def_site` or `Span::mixed_site` will not + // include any hygiene information associated with the definition + // site. This means that a proc-macro cannot emit a `$crate` + // identifier which resolves to one of its dependencies, + // which also should never come up in practice. + // + // Additionally, this affects `Span::parent`, and any other + // span inspection APIs that would otherwise allow traversing + // the `SyntaxContexts` associated with a span. + // + // None of these user-visible effects should result in any + // cross-crate inconsistencies (getting one behavior in the same + // crate, and a different behavior in another crate) due to the + // limited surface that proc-macros can expose. + // + // IMPORTANT: If this is ever changed, be sure to update + // `rustc_span::hygiene::raw_encode_expn_id` to handle + // encoding `ExpnData` for proc-macro crates. + let ctxt = if s.is_proc_macro { SyntaxContext::root() } else { this.ctxt }; - (SpanKind::Foreign, metadata_index) + if s.index_graph_builder.is_some() { + nodes.push(ctxt.into()); + } + if this.is_dummy() { + let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); + tag.encode(s); + if tag.context().is_none() { + ctxt.encode(s); } else { - // Record the fact that we need to encode the data for this `SourceFile` - let source_files = - s.required_source_files.as_mut().expect("Already encoded SourceMap!"); - let (metadata_index, _) = source_files.insert_full(source_file_index); - let metadata_index: u32 = - metadata_index.try_into().expect("cannot export more than U32_MAX files"); - - (SpanKind::Local, metadata_index) - }; + s.record_encoded_index(ctxt); + } + return nodes; + } - // Encode the start position relative to the file start, so we profit more from the - // variable-length integer encoding. - let lo = self.lo - source_file.start_pos; + // The Span infrastructure should make sure that this invariant holds: + debug_assert!(this.lo <= this.hi); - // Encode length which is usually less than span.hi and profits more - // from the variable-length integer encoding that we use. - let len = self.hi - self.lo; + if !s.source_file_cache.0.contains(this.lo) { + let source_map = s.tcx.sess.source_map(); + let source_file_index = source_map.lookup_source_file_idx(this.lo); + s.source_file_cache = + (Arc::clone(&source_map.files()[source_file_index]), source_file_index); + } + let (ref source_file, source_file_index) = s.source_file_cache; + debug_assert!(source_file.contains(this.lo)); - let tag = SpanTag::new(kind, ctxt, len.0 as usize); + if !source_file.contains(this.hi) { + // Unfortunately, macro expansion still sometimes generates Spans + // that malformed in this way. + let tag = SpanTag::new(SpanKind::Partial, ctxt, 0); tag.encode(s); if tag.context().is_none() { ctxt.encode(s); + } else { + s.record_encoded_index(ctxt); } - lo.encode(s); - if tag.length().is_none() { - len.encode(s); - } + return nodes; + } + + // There are two possible cases here: + // 1. This span comes from a 'foreign' crate - e.g. some crate upstream of the + // crate we are writing metadata for. When the metadata for *this* crate gets + // deserialized, the deserializer will need to know which crate it originally came + // from. We use `TAG_VALID_SPAN_FOREIGN` to indicate that a `CrateNum` should + // be deserialized after the rest of the span data, which tells the deserializer + // which crate contains the source map information. + // 2. This span comes from our own crate. No special handling is needed - we just + // write `TAG_VALID_SPAN_LOCAL` to let the deserializer know that it should use + // our own source map information. + // + // If we're a proc-macro crate, we always treat this as a local `Span`. + // In `encode_source_map`, we serialize foreign `SourceFile`s into our metadata + // if we're a proc-macro crate. + // This allows us to avoid loading the dependencies of proc-macro crates: all of + // the information we need to decode `Span`s is stored in the proc-macro crate. + let source_file_start = source_file.start_pos; + let (kind, metadata_index) = if source_file.is_imported() && !s.is_proc_macro { + // To simplify deserialization, we 'rebase' this span onto the crate it originally came + // from (the crate that 'owns' the file it references. These rebased 'lo' and 'hi' + // values are relative to the source map information for the 'foreign' crate whose + // CrateNum we write into the metadata. This allows `imported_source_files` to binary + // search through the 'foreign' crate's source map information, using the + // deserialized 'lo' and 'hi' values directly. + // + // All of this logic ensures that the final result of deserialization is a 'normal' + // Span that can be used without any additional trouble. + let metadata_index = { + // Introduce a new scope so that we drop the 'read()' temporary + match &*source_file.external_src.read() { + ExternalSource::Foreign { metadata_index, .. } => *metadata_index, + src => panic!("Unexpected external source {src:?}"), + } + }; + (SpanKind::Foreign, metadata_index) + } else { + // Record the fact that we need to encode the data for this `SourceFile` + let source_files = s.required_source_files.as_mut().expect("Already encoded SourceMap!"); + let (metadata_index, _) = source_files.insert_full(source_file_index); + let metadata_index: u32 = + metadata_index.try_into().expect("cannot export more than U32_MAX files"); - // Encode the index of the `SourceFile` for the span, in order to make decoding faster. - metadata_index.encode(s); + (SpanKind::Local, metadata_index) + }; + if s.index_graph_builder.is_some() { + let node = Node::Span(span); + nodes.push(node); + s.record_encoded_index(node); + } + + // Encode the start position relative to the file start, so we profit more from the + // variable-length integer encoding. + // IMPORTANT: if this is ever changed, the public api span hashing must be updated. It + // currently uses the `hash_spans_as_parentless` option to make sure spans are hashed not + // relative to their parent, but relative to their file. + let lo = this.lo - source_file_start; + + // Encode length which is usually less than span.hi and profits more + // from the variable-length integer encoding that we use. + let len = this.hi - this.lo; + + let tag = SpanTag::new(kind, ctxt, len.0 as usize); + tag.encode(s); + if tag.context().is_none() { + ctxt.encode(s); + } else { + s.record_encoded_index(ctxt); + } + lo.encode(s); + if tag.length().is_none() { + len.encode(s); + } - if kind == SpanKind::Foreign { - // This needs to be two lines to avoid holding the `s.source_file_cache` - // while calling `cnum.encode(s)` - let cnum = s.source_file_cache.0.cnum; - cnum.encode(s); - } + // Encode the index of the `SourceFile` for the span, in order to make decoding faster. + metadata_index.encode(s); + + if kind == SpanKind::Foreign { + // This needs to be two lines to avoid holding the `s.source_file_cache` + // while calling `cnum.encode(s)` + let cnum = s.source_file_cache.0.cnum; + cnum.encode(s); } + nodes } impl<'a, 'tcx> Encodable> for [u8] { @@ -375,10 +427,36 @@ impl<'a, 'tcx> TyEncoder<'tcx> for EncodeContext<'a, 'tcx> { self.opaque.position() } + fn type_encode_begin(&mut self, ty: Ty<'tcx>) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record(Node::Ty(ty)); + index_graph_builder.record_mode.push(RecordMode::from(LocalNode::Ty(ty))); + } + } + + fn type_encode_end(&mut self) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record_mode.pop(); + } + } + fn type_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.type_shorthands } + fn predicate_encode_begin(&mut self, pred: PredicateKind<'tcx>) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record(Node::Predicate(pred)); + index_graph_builder.record_mode.push(RecordMode::from(LocalNode::Predicate(pred))); + } + } + + fn predicate_encode_end(&mut self) { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record_mode.pop(); + } + } + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize> { &mut self.predicate_shorthands } @@ -393,11 +471,27 @@ 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, RecordMode::from) + }}; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, RecordMode::$mode:tt ) => {{ + record!($self.$tables.$table[$def_id] <- $value, $hcx, $value, RecordMode::$mode) + }}; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr ) => {{ + record!($self.$tables.$table[$def_id] <- $value, $hcx, $hashed_value, RecordMode::from) + }}; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $hashed_value:expr, $record_mode:expr) => {{ { let value = $value; - let lazy = $self.lazy(value); - $self.$tables.$table.set_some($def_id.index, lazy); + let lazy = $self.with_record_mode($record_mode(LocalNode::DefId($def_id.expect_local())), |this| + this.lazy(value) + ); + $self.$tables.$table.set_some_hashed( + $def_id.index, + lazy, + ($def_id, $hashed_value), + $hcx, + ); } }}; } @@ -405,26 +499,105 @@ 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, RecordMode::from) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => { + record_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::from) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr, $record_mode: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.with_record_mode($record_mode(LocalNode::DefId($def_id.expect_local())), |this| + this.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)) + ); + $self.$tables.$table.set_some_hashed( + $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, RecordMode::from) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr) => { + record_defaulted_array!($self.$tables.$table[$def_id] <- $value, $hcx, $encode_map, RecordMode::from) + }; + ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr, $hcx:ident, $encode_map:expr, $record_mode: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.with_record_mode($record_mode(LocalNode::DefId($def_id.expect_local())), |this| + this.lazy_array(hasher.inspect_digest(value, $hcx).map($encode_map)) + ); + $self.$tables.$table.set_hashed( + $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> { + fn with_record_mode(&mut self, mode: RecordMode<'tcx>, f: F) -> T + where + F: FnOnce(&mut Self) -> T, + { + if let Some(index_graph_builder) = self.index_graph_builder.as_mut() { + index_graph_builder.record_mode.push(mode); + let res = f(self); + self.index_graph_builder.as_mut().unwrap().record_mode.pop(); + res + } else { + f(self) + } + } + + fn record_encoded_index(&mut self, index: impl Into>) { + if let Some(local_def_id_graph_builder) = self.index_graph_builder.as_mut() { + local_def_id_graph_builder.record(index.into()); + } + } + fn emit_lazy_distance(&mut self, position: NonZero) { let pos = position.get(); let distance = match self.lazy_state { @@ -510,6 +683,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 +691,32 @@ 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_def_path_hash_map(&mut self) -> Hashed>> { + let value = self + .lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map())); + Hashed { hash: Some(Fingerprint::ZERO), value } } - fn encode_source_map(&mut self) -> LazyTable>> { + 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 +725,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // is done. let required_source_files = self.required_source_files.take().unwrap(); - let mut adapted = TableBuilder::default(); + // we don't hash the files here. What we need to hash for the public api comes from + let mut adapted = TableBuilder::, _, _>::default(); let local_crate_stable_id = self.tcx.stable_crate_id(LOCAL_CRATE); @@ -595,13 +779,16 @@ 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_unhashed(on_disk_index, self.lazy(&adapted_source_file)); } - 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 +805,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,33 +861,55 @@ 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)), + { + if tcx.sess.opts.unstable_opts.public_api_hash { + for (symbol, info) in tcx.exported_non_generic_symbols(LOCAL_CRATE) { + let def_id = match symbol { + ExportedSymbol::NonGeneric(id) => id.expect_local(), + _ => continue, + }; + if matches!( + info, + SymbolExportInfo { level: SymbolExportLevel::C, .. } + ) { + self.tables + .is_reachable_non_generic_with_export_level_c + .set_local_hashed(def_id, true, hcx); + } + self.tables + .is_reachable_non_generic + .set_local_hashed(def_id, true, hcx); + } + } + self.encode_exported_symbols(tcx.exported_non_generic_symbols(LOCAL_CRATE)) + }, self.encode_exported_symbols(tcx.exported_generic_symbols(LOCAL_CRATE)), ) }); @@ -707,76 +920,79 @@ 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()); // 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), + public_api_hash_opt_enabled: tcx.sess.opts.unstable_opts.public_api_hash, + }; + let crate_root = stat!("public hashes", || crate_root.into_crate_root(self, hcx)); + let hashes = crate_root.header.hashes; + + let root = stat!("final", || { self.lazy(crate_root) }); let total_bytes = self.position(); @@ -841,7 +1057,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { eprint!("{s}"); } - root + (root, hashes) } } @@ -1393,7 +1609,18 @@ 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 public_module_children_local( + &self, + def_id: LocalDefId, + ) -> impl Iterator + use<'tcx> + Clone { + let effective_visibilities = self.tcx.effective_visibilities(()); + let remove_private_children = self.tcx.sess.opts.unstable_opts.public_api_hash; + self.tcx.module_children_local(def_id).iter().filter(move |child| { + is_public_mod_child(remove_private_children, effective_visibilities, child) + }) + } + + fn encode_attrs(&mut self, def_id: LocalDefId, hcx: &mut PublicApiHashingContext<'_>) { let tcx = self.tcx; let mut state = AnalyzeAttrState { is_exported: tcx.effective_visibilities(()).is_exported(def_id), @@ -1404,17 +1631,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 +1659,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 +1692,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()); - let module_children = self.tcx.module_children_local(local_id); + self.tcx.explicit_implied_predicates_of(def_id).skip_binder(), hcx); + let module_children = self.public_module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().map(|child| child.res.def_id().index)); + module_children.map(|child| child.res.def_id()), hcx, + |def_id| def_id.index); if self.tcx.is_const_trait(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] - <- 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 +1949,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,81 +1996,115 @@ 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); + let module_children = self.public_module_children_local(local_def_id); record_array!(self.tables.module_children_non_reexports[def_id] <- - module_children.iter().filter(|child| child.reexport_chain.is_empty()) - .map(|child| child.res.def_id().index)); + module_children.clone().filter(|child| child.reexport_chain.is_empty()) + .map(|child| child.res.def_id()), hcx, |def_id| def_id.index + ); record_defaulted_array!(self.tables.module_children_reexports[def_id] <- - module_children.iter().filter(|child| !child.reexport_chain.is_empty())); + module_children.filter(|child| !child.reexport_chain.is_empty()), hcx + ); + let effective_visibilities = self.tcx.effective_visibilities(()); + let remove_private_children = self.tcx.sess.opts.unstable_opts.public_api_hash; let ambig_module_children = tcx .resolutions(()) .ambig_module_children .get(&local_def_id) - .map_or_default(|v| &v[..]); + .map_or_default(|v| &v[..]) + .iter() + // only ambiguities where both main and second are public can cause actual + // ambiguity. + .filter(|ambig| { + is_public_mod_child( + remove_private_children, + &effective_visibilities, + &ambig.main, + ) && is_public_mod_child( + remove_private_children, + effective_visibilities, + &ambig.second, + ) + }); record_defaulted_array!(self.tables.ambig_module_children[def_id] <- - ambig_module_children); + 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 +2121,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 +2183,132 @@ 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)); + this.with_record_mode(RecordMode::From(LocalNode::SyntaxContext(index)), |this| { + syntax_contexts.set_some_hashed( + index, + this.lazy(ctxt_data), + ctxt_data, + &mut hcx.borrow_mut(), + ); + }); }, |(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)); + this.with_record_mode(RecordMode::From(LocalNode::ExpnId(index)), |this| { + expn_data_table.set_some_hashed( + index, + this.lazy(expn_data), + index, + &mut hcx.borrow_mut(), + ); + // don't need to hash it since it is already included with `expn_data_table` + expn_hash_table.set_some_unhashed(index.as_raw(), this.lazy(hash)); + }); } }, ); + 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 +2317,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_unhashed(i, encoded_span); } - 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 +2365,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 +2391,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,33 +2407,22 @@ 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 let mut expected_cnum = 1; - for &(n, _) in &deps { + for &(n, _, _) in &deps { assert_eq!(n, CrateNum::new(expected_cnum)); expected_cnum += 1; } @@ -2098,77 +2432,148 @@ 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)) - } - - fn encode_target_modifiers(&mut self) -> LazyArray { + let mut hasher = PublicApiHasher::default(); + let array = self.lazy_array(deps.into_iter().map(|(cnum, dep, public_global_hash)| { + let CrateDep { name, hash, host_hash, kind, extra_filename, is_private } = &dep; + hasher.digest(cnum, hcx); + hasher.digest(name, hcx); + // We only include the global part of dependency hashes, not the full public hash. + // The remaining, item specific, part of public_hash should only be included when those + // items are reachable in the public hash graph. + let _ = hash; + hasher.digest(public_global_hash, hcx); + hasher.digest(host_hash, hcx); + hasher.digest(kind, hcx); + hasher.digest(extra_filename, hcx); + hasher.digest(is_private, hcx); + dep + })); + Hashed { value: array, hash: hasher.finish(hcx) } + } + + fn encode_target_modifiers( + &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>> { + if hcx.enabled() { + // FIXME: we should encode this while public api hashing is enabled, but it must not be + // part of the public hash. Its query should depend on the private hash of the crate + // if it is included. + return Hashed { value: LazyArray::default(), hash: Some(Fingerprint::ZERO) }; + } + hashed_lazy_array!( + self, + self.tcx.stripped_cfg_items(LOCAL_CRATE), + 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> { + if hcx.enabled() { + // FIXME: we should encode this while public api hashing is enabled, but it must not be + // part of the public hash. Its query should depend on the private hash of the crate + // if it is included. + return Hashed { value: LazyArray::default(), hash: Some(Fingerprint::ZERO) }; + } empty_proc_macro!(self); - 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 +2584,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 +2598,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.as_u32()), + 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 +2706,20 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_exported_symbols( &mut self, exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)], - ) -> LazyArray<(ExportedSymbol<'static>, SymbolExportInfo)> { + ) -> Hashed, SymbolExportInfo)>> { empty_proc_macro!(self); - self.lazy_array(exported_symbols.iter().cloned()) + let array = self.lazy_array(exported_symbols); + Hashed { value: array, hash: Some(Fingerprint::ZERO) } } - 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 +2865,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,36 +2899,68 @@ 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, hash_public_api, |ecx| { + // Encode all the entries and extra information in the crate, + // culminating in the `CrateRoot` which points to all of it. + let (root, crate_hashes) = ecx.encode_crate_root(&mut hcx); + 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, false, |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( tcx: TyCtxt<'_>, path: &Path, + hash_public_api: bool, f: impl FnOnce(&mut EncodeContext<'_, '_>) -> usize, ) { let mut encoder = opaque::FileEncoder::new(path) @@ -2539,6 +2992,7 @@ fn with_encode_metadata_header( is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, symbol_index_table: Default::default(), + index_graph_builder: hash_public_api.then(|| Default::default()), }; // Encode the rustc version string in a predictable location. @@ -2687,3 +3141,56 @@ 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, tcx.public_global_hash(cnum)) + }) +} + +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), + }, + ) + }) + }) +} + +fn is_public_mod_child( + remove_private_children: bool, + effective_visibilities: &EffectiveVisibilities, + child: &ModChild, +) -> bool { + if !remove_private_children { + return true; + } + if child.reexport_chain.is_empty() { + effective_visibilities.is_reachable(child.res.def_id().expect_local()) + } else { + child.vis.is_public() + && child.reexport_chain.iter().all(|reexport| { + reexport.id().is_none_or(|id| { + !id.is_local() || effective_visibilities.is_reachable(id.expect_local()) + }) + }) + } +} 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..34a0c7ed87ea4 --- /dev/null +++ b/compiler/rustc_metadata/src/rmeta/encoder/public_api_hasher.rs @@ -0,0 +1,985 @@ +use core::cell::RefCell; +use core::iter::Iterator; +use std::fmt; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::graph::scc::{Annotation, Annotations, Sccs}; +use rustc_data_structures::graph::{DirectedGraph, Successors}; +use rustc_data_structures::indexmap::map::Entry; +use rustc_data_structures::stable_hasher::{StableHash, StableHashCtxt, StableHasher}; +use rustc_data_structures::svh::Svh; +use rustc_data_structures::unord::UnordMap; +use rustc_hir::LangItem; +use rustc_hir::attrs::StrippedCfgItem; +use rustc_hir::def_id::DefIndex; +use rustc_index::{Idx, IndexVec}; +use rustc_macros::{LazyDecodable, MetadataEncodable, StableHash}; +use rustc_middle::ich::StableHashingContext; +use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; +use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; +use rustc_middle::middle::lib_features::FeatureStability; +use rustc_middle::ty::data_structures::IndexMap; +use rustc_middle::ty::{PredicateKind, Ty, TyCtxt}; +use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; +use rustc_session::config::{SymbolManglingVersion, TargetModifier}; +use rustc_session::cstore::{ForeignModule, LinkagePreference, NativeLib}; +use rustc_span::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId, StableCrateId}; +use rustc_span::edition::Edition; +use rustc_span::hygiene::ExpnIndex; +use rustc_span::{ExpnId, LocalExpnId, Span, Symbol, SyntaxContext}; +use rustc_target::spec::PanicStrategy; +use tracing::debug; + +use super::{CrateDep, CrateHeader, CrateRoot, TargetTuple}; +use crate::rmeta::{ + CrateHashes, DefPathHashMapRef, EiiMapEncodedKeyValue, EncodeContext, ExpnDataTable, + ExpnHashTable, IncoherentImpls, LazyArray, LazyDecoder, LazyTable, LazyTables, LazyValue, + ProcMacroData, SyntaxContextTable, TableBuilder, 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) trait TablePublicApiHasher: Default { + type IterHasher; + + fn digest( + &mut self, + index: impl TableIndex, + 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, + def_id_hashes: IndexGraphHashes, +} + +impl<'a> PublicApiHashingContext<'a> { + pub(crate) fn enabled(&self) -> bool { + self.hash_public_api + } + pub(crate) fn new(hash_public_api: bool, hcx: StableHashingContext<'a>) -> Self { + Self { hash_public_api, hcx, def_id_hashes: Default::default() } + } +} + +impl TablePublicApiHasher for RDRHashAll { + type IterHasher = OrderedIterHasher; + fn digest( + &mut self, + index: impl TableIndex, + value: V, + hcx: &mut PublicApiHashingContext<'_>, + ) where + V: StableHash, + { + if !hcx.hash_public_api { + return; + } + let mut hasher = StableHasher::default(); + value.stable_hash(&mut hcx.hcx, &mut hasher); + let hash: Fingerprint = hasher.finish(); + let idx_hash = hcx.def_id_hashes.get_mut(index); + *idx_hash = idx_hash.combine_commutative(hash); + // remove this later, not needed anymore + self.hash.combine_commutative(hash); + } + + fn finish(&self, hcx: &mut PublicApiHashingContext<'_>) -> Option { + 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: impl TableIndex, + _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? + // everything_downstream + pub(crate) header: HashableCrateHeader, + + // FIXME do we need to hash this? + // everything downstream + pub(crate) extra_filename: String, + // FIXME do we need to hash this? + // everything_downstream + pub(crate) stable_crate_id: StableCrateId, + // FIXME do we need to hash this? + // everything_downstream, right? + pub(crate) required_panic_strategy: Option, + // FIXME do we need to hash this? + // everything_downstream, right? + pub(crate) panic_in_drop_strategy: PanicStrategy, + // FIXME do we need to hash this? + // macro expansion in downstream crates uses it, only relevant for publicly reachable macros + pub(crate) edition: Edition, + // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally + pub(crate) has_global_allocator: bool, + // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally + pub(crate) has_alloc_error_handler: bool, + // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally + pub(crate) has_panic_handler: bool, + // FIXME do we need to hash this? + // everything_downstream, there can only be a single one globally + pub(crate) has_default_lib_allocator: bool, + // Changing externally implementable items should cause recompiles in all downstream crates. + // FIXME EiiDecl and EiiImpl contain spans. Should changing the span of these items cause + // 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) + // everything_downstream, there can only be a single one of each eii globally + pub(crate) externally_implementable_items: Hashed>, + + // FIXME do we need to hash this? + // we only need the everything_downstream hashes from here included in our everything_downstream + pub(crate) crate_deps: Hashed>, + // FIXME do we need to hash this? + // lets say everything_downstream, but not sure + pub(crate) dylib_dependency_formats: Hashed>>, + // FIXME do we need to hash this? + // lets say everything_downstream, but not sure + pub(crate) lib_features: Hashed>, + // FIXME do we need to hash this? + // lets say everything_downstream, but not sure + pub(crate) stability_implications: Hashed>, + // FIXME do we need to hash this? + // everything_downstream, lang items impls are checked for uniqueness globally + pub(crate) lang_items: Hashed>, + // FIXME do we need to hash this? + // everything_downstream, lang items impls are checked for uniqueness globally + pub(crate) lang_items_missing: Hashed>, + // FIXME do we need to hash this? + // private_hash, only used when we are creating error diagnostics + pub(crate) stripped_cfg_items: Hashed>>, + // FIXME do we need to hash this? + // lets say everything_downstream for now, I think this is mostly used in the std + pub(crate) diagnostic_items: Hashed>, + // FIXME do we need to hash this? + // let say everything_downstream + pub(crate) native_libraries: Hashed>, + // FIXME do we need to hash this? + // dont know what this is used for. doc says `extern` blocks + pub(crate) foreign_modules: Hashed>, + // FIXME do we need to hash this? + // the traits defined in this crate. Definitely not needed in everything_downstream as is + // maybe we can leak private traits through MIR? + // According to the docs, and some personal digging, this is used by rustdoc and error reporting + // rustdoc - problem for another day + // error reporting: this should not be included in the public hash, as it is only read when the + // compiler errors. Just like stripped_cfg_items + pub(crate) traits: Hashed>, + // FIXME do we need to hash this? + // the traits impls in this crate. Definitely not needed in everything_downstream as is + // how is it needed? + // + // this is exposed as a full query with `trait_impls_of_crate`. Which is only used in + // rustc_public. Otherwise the `implementations_of_trait` is the only other consumer of this + // field. `implementations_of_trait` works as a map. You need to provide a trait DefId to get + // its impls, so this should be intergrated into the IndexGraph. + // + // When a trait is local: + // if it isn't reachable, the impls can should be left out from the hash + // When a trait is from another crate: + // even if this crate does not reexport that trait, a downstream dependency can import it + // from another crate, then invoke its methods. So all of those impls must be included, but + // only the ones that can be applied to publicly reachable types + pub(crate) impls: Hashed>, + // FIXME do we need to hash this? + // what is this used for + pub(crate) incoherent_impls: Hashed>, + // FIXME do we need to hash this? + // what is this used for, some kind of const eval? + pub(crate) interpret_alloc_index: Hashed>, + // FIXME do we need to hash this? + // proc macro, ignored + pub(crate) proc_macro_data: NoneIfHashed, + + // FIXME do we need to hash this? + // do it by table + pub(crate) tables: Hashed, + // FIXME do we need to hash this? + // likely don't only private. Do debuggers use this when debugging the linked binary? Then this + // is private_hash + pub(crate) debugger_visualizers: Hashed>, + + // FIXME do we need to hash this? + // this is used to do symbol mangling in downstream crates. We should only include ones that + // are somehow reachable. + pub(crate) exportable_items: Hashed>, + // FIXME do we need to hash this? + // what is this extactly, used for diagnostics + pub(crate) stable_order_of_exportable_impls: Hashed>, + // FIXME do we need to hash this? + // exported_non_generic_symbols and exported_symbols are used by the linker, which likely means + // that we should put them behind the private hash + pub(crate) exported_non_generic_symbols: + Hashed, SymbolExportInfo)>>, + + // FIXME do we need to hash this? + // I think this is used to find upstream_monomorphizations or what + // So this is tricky, and kind of makes this feature useless. For example, using a + // Vec::::push in private code will add that monomorphized function to + // exported_generic_symbols. + // On one hand, removing this will slow down the codegen for the current crate, as it needs to + // codegen these generics. On the other hand, keeping this forces a rebuild of all upstream + // crates when any new monomorphized item (where the monomorphized generic is public or from + // another crate). + // Let's review this from an incremental recompilation perspecitve. The goal of + // relink-dont-rebuild is mostly speeding that up. + // Let's say we remove this, the first build was done + // 1. recompile of a dependency that contains a monomorphization which could be used upstream. + // What changes? Nothing, we don't have to export the monomorphization for + // `upstream_monomorphizations`, but its is also used by the linker, so it has to be included + // anyways + // 2. recompile of crate that could use an upstream monomorphization. Since it is disabled, the + // monomorphization is already in its local incremental cache. It will be pulled from there. + // Given these two points, if the goal is fast incremental recompiles, then disabling upstream + // monomorphizations, and moving this behind private_hash might be the best (maybe linking_hash) + pub(crate) exported_generic_symbols: + Hashed, SymbolExportInfo)>>, + + // FIXME do we need to hash this? + // some kind of macro expansion stuff? + pub(crate) syntax_contexts: Hashed, + // FIXME do we need to hash this? + // some kind of macro expansion stuff? + pub(crate) expn_data: Hashed, + // FIXME do we need to hash this? + // some kind of macro expansion stuff? + pub(crate) expn_hashes: Hashed, + + // FIXME do we need to hash this? + // we stbale hash all localdefids currently to get this. Likely only need the ones that are + // somehow reachable + pub(crate) def_path_hash_map: Hashed>>, + + // FIXME do we need to hash this? + // used for diagnostics and line info generation in codegen? To map span data to line/column + // data + // we need this while the spans are encoded like that. + pub(crate) source_map: Hashed>>>, + // FIXME do we need to hash this? + // everything_downstream, prbably not changing much + pub(crate) target_modifiers: Hashed>, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) denied_partial_mitigations: Hashed>, + + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) compiler_builtins: bool, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) needs_allocator: bool, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) needs_panic_runtime: bool, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) no_builtins: bool, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) panic_runtime: bool, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) profiler_runtime: bool, + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) symbol_mangling_version: SymbolManglingVersion, + + // FIXME do we need to hash this? + // everything_downstream? + pub(crate) specialization_enabled_in: bool, + pub(crate) public_api_hash_opt_enabled: bool, +} + +impl HashableCrateRoot { + pub(super) fn into_crate_root( + self, + ecx: &mut EncodeContext<'_, '_>, + hcx: &mut PublicApiHashingContext<'_>, + ) -> CrateRoot { + let tcx = ecx.tcx; + let (rdr_hashes, hashes) = if hcx.hash_public_api { + assert!(!self.header.is_proc_macro_crate); + let graph = ecx.index_graph_builder.take().unwrap().build_graph(&mut hcx.hcx); + debug!("Hash graph {graph:?}"); + let public_hashes = + build_public_hashes(&graph, &hcx.def_id_hashes, ecx.tcx, &mut hcx.hcx); + + let mut hasher = StableHasher::default(); + let public_global_hash = stable_hash(&mut hcx.hcx, &self); + public_global_hash.stable_hash(&mut hcx.hcx, &mut hasher); + public_hashes.stable_hash(&mut hcx.hcx, &mut hasher); + let rdr_hashes = public_hashes.value.encode(ecx, hcx, public_global_hash); + let public_hash = Svh::new(hasher.finish()); + debug!("Hashed crate root: {self:#x?}"); + debug!("public api hash: {}", public_hash); + ( + Some(rdr_hashes), + CrateHashes { public_hash, private_hash: tcx.crate_hash(LOCAL_CRATE) }, + ) + } else { + let hash = tcx.crate_hash(LOCAL_CRATE); + (None, 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, + rdr_hashes, + public_api_hash_opt_enabled: self.public_api_hash_opt_enabled, + } + } +} + +pub(super) enum RecordMode<'tcx> { + From(LocalNode<'tcx>), +} + +impl<'tcx> RecordMode<'tcx> { + pub(super) fn from(from: LocalNode<'tcx>) -> Self { + Self::From(from) + } +} + +struct IndexGraph<'tcx> { + nodes: IndexMap, Fingerprint>, + edges: IndexVec>, + roots: Vec, +} + +impl fmt::Debug for IndexGraph<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let node = |idx| self.nodes.get_index(idx).unwrap().0; + let fingerprint = |idx| self.nodes.get_index(idx).unwrap().1; + let reachable = self.reachable_set(); + writeln!(f, "roots:")?; + for root in &self.roots { + writeln!(f, " {:?}", node(*root))?; + } + writeln!(f, "nodes:")?; + for (from, edges) in self.edges.iter_enumerated() { + writeln!( + f, + "{:?}: {:x?} reachable: {}", + node(from), + fingerprint(from), + reachable[from] + )?; + for to in edges { + writeln!(f, " {:?}", node(*to))?; + } + } + Ok(()) + } +} + +impl<'tcx> IndexGraph<'tcx> { + fn reachable_set(&self) -> IndexVec { + let mut reachable = IndexVec::from_elem_n(false, self.edges.len()); + let mut stack = self.roots.clone(); + + while let Some(node) = stack.pop() { + if reachable[node] { + continue; + } + reachable[node] = true; + stack.extend_from_slice(&self.edges[node]); + } + + reachable + } +} + +pub(super) struct IndexGraphBuilder<'tcx> { + pub(super) record_mode: Vec>, + edges: FxHashMap, FxHashSet>>, + roots: FxHashSet>, +} + +impl Default for IndexGraphBuilder<'_> { + fn default() -> Self { + Self { + record_mode: Default::default(), + edges: Default::default(), + roots: [Node::DefId(rustc_hir::def_id::CRATE_DEF_ID.into())].into_iter().collect(), + } + } +} + +fn stable_hash<'a, T: StableHash + ?Sized>( + hcx: &mut StableHashingContext<'a>, + val: &T, +) -> Fingerprint { + let mut hasher = StableHasher::new(); + val.stable_hash(hcx, &mut hasher); + hasher.finish() +} + +impl<'tcx> IndexGraphBuilder<'tcx> { + fn build_graph(mut self, hcx: &mut StableHashingContext<'_>) -> IndexGraph<'tcx> { + // ExpnId::root() contains CRATE_DEF_ID as its macro_def_id, but that isn't used for + // anything. + // TODO add newtype that ensures it really isn't used + self.edges + .get_mut(&LocalNode::ExpnId(LocalExpnId::ROOT)) + .unwrap() + .remove(&Node::DefId(CRATE_DEF_ID.into())); + let mut hashes = IndexMap::default(); + // iterating over FxHashSet and FxHashMap is fine here, as it is only used to build the + // hashes map, which is never returned or iterated + #[allow(rustc::potential_query_instability)] + for node in self + .edges + .iter() + .flat_map(|(node, edges)| { + std::iter::once(node.into_node()).chain(edges.iter().copied()) + }) + .chain(self.roots.iter().copied()) + { + match hashes.entry(node) { + Entry::Vacant(v) => { + let hash = stable_hash(hcx, &node); + v.insert(hash); + } + Entry::Occupied(_o) => {} + } + } + hashes.sort_by_key(|_, v| *v); + + // iterating here is fine, as we stable sort right after + #[allow(rustc::potential_query_instability)] + let mut roots: Vec<_> = + self.roots.into_iter().map(|node| hashes.get_index_of(&node).unwrap()).collect(); + roots.sort(); + let mut edges = IndexVec::from_elem_n(Vec::default(), hashes.len()); + // iterating here is fine, as we stable when saving to `edges` + #[allow(rustc::potential_query_instability)] + for (node, node_edges) in self.edges { + // iterating here is fine, as we stable sort right after + #[allow(rustc::potential_query_instability)] + let mut node_edges: Vec<_> = + node_edges.into_iter().map(|node| hashes.get_index_of(&node).unwrap()).collect(); + node_edges.sort(); + edges[hashes.get_index_of(&node.into_node()).unwrap()] = node_edges; + } + + IndexGraph { roots, edges, nodes: hashes } + } + + pub(super) fn record(&mut self, to: Node<'tcx>) { + match self.record_mode.last() { + Some(RecordMode::From(from)) => { + self.edges.entry(*from).or_insert(Default::default()).insert(to); + } + None => { + self.roots.insert(to); + } + } + } +} + +#[derive(Default)] +pub(crate) struct IndexGraphHashes { + local: UnordMap, + expn: UnordMap, + syntax: UnordMap, +} + +impl IndexGraphHashes { + fn get_mut(&mut self, i: I) -> &mut Fingerprint { + i.index_mut(self) + } + + fn get_node(&self, node: &Node<'_>, tcx: TyCtxt<'_>) -> Option { + match node { + Node::DefId(id) => { + if let Some(local) = id.as_local() { + self.local.get(&local).copied() + } else { + Some(tcx.extern_def_public_hash(*id)) + } + } + Node::ExpnId(id) => { + if let Some(local) = id.as_local() { + self.expn.get(&local).copied() + } else { + Some(tcx.extern_expn_public_hash(*id)) + } + } + Node::SyntaxContext(id) => self.syntax.get(id).copied(), + Node::Span(span) => tcx.with_stable_hashing_context(|hcx| { + let hcx = RefCell::new(hcx); + tcx.sess + .source_map() + .span_to_source(*span, |source, start, end| { + Ok(stable_hash(&mut hcx.borrow_mut(), &source[start..end])) + }) + .ok() + }), + Node::Ty(_) => None, + Node::Predicate(_) => None, + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Hash)] +pub(super) enum LocalNode<'tcx> { + DefId(LocalDefId), + ExpnId(LocalExpnId), + SyntaxContext(SyntaxContext), + Ty(Ty<'tcx>), + Predicate(PredicateKind<'tcx>), +} + +#[derive(PartialEq, Eq, Clone, Copy, Hash, StableHash, Debug)] +pub(super) enum Node<'tcx> { + DefId(DefId), + ExpnId(ExpnId), + SyntaxContext(SyntaxContext), + Span(Span), + Ty(Ty<'tcx>), + Predicate(PredicateKind<'tcx>), +} + +impl<'tcx> LocalNode<'tcx> { + fn into_node(self) -> Node<'tcx> { + match self { + LocalNode::DefId(id) => Node::DefId(id.into()), + LocalNode::ExpnId(id) => Node::ExpnId(id.to_expn_id()), + LocalNode::SyntaxContext(id) => Node::SyntaxContext(id), + LocalNode::Ty(id) => Node::Ty(id), + LocalNode::Predicate(id) => Node::Predicate(id), + } + } +} + +impl From for Node<'_> { + fn from(value: LocalDefId) -> Self { + Self::DefId(value.into()) + } +} +impl From for Node<'_> { + fn from(value: DefId) -> Self { + Self::DefId(value) + } +} +impl From for Node<'_> { + fn from(value: ExpnId) -> Self { + Self::ExpnId(value) + } +} +impl From for Node<'_> { + fn from(value: SyntaxContext) -> Self { + Self::SyntaxContext(value) + } +} + +pub(crate) trait TableIndex: Copy { + type Encoded: Idx; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint; + fn into_encoded(self) -> Self::Encoded; +} + +impl TableIndex for DefIndex { + type Encoded = DefIndex; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.local.entry(LocalDefId { local_def_index: self }).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self + } +} + +impl TableIndex for LocalDefId { + type Encoded = DefIndex; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.local.entry(self).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self.local_def_index + } +} + +impl TableIndex for LocalExpnId { + type Encoded = ExpnIndex; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.expn.entry(self).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self.as_raw() + } +} + +impl TableIndex for SyntaxContext { + type Encoded = u32; + fn index_mut(self, hashes: &mut IndexGraphHashes) -> &mut Fingerprint { + hashes.syntax.entry(self).or_insert(Fingerprint::ZERO) + } + fn into_encoded(self) -> Self::Encoded { + self.as_u32() + } +} + +impl DirectedGraph for IndexGraph<'_> { + type Node = NodeIdx; + + fn num_nodes(&self) -> usize { + self.nodes.len() + } +} + +impl Successors for IndexGraph<'_> { + fn successors(&self, node: Self::Node) -> impl Iterator { + self.edges[node].iter().copied() + } +} + +type NodeIdx = usize; +type SccIdx = u32; + +#[derive(Debug, Copy, Clone)] +struct FingerprintAnnotation { + fingerprint: Fingerprint, +} + +impl Annotation for FingerprintAnnotation { + fn update_scc(&mut self, other: &Self) { + self.fingerprint = self.fingerprint.combine_commutative(other.fingerprint); + } + + fn update_reachable(&mut self, other: &Self) { + self.fingerprint = self.fingerprint.combine_commutative(other.fingerprint); + } +} + +struct FingerprintAnnotations<'a, 'tcx> { + graph: &'a IndexGraph<'tcx>, + hashes: &'a IndexGraphHashes, + tcx: TyCtxt<'tcx>, + scc_fingerprints: IndexVec, +} + +impl<'a, 'tcx> FingerprintAnnotations<'a, 'tcx> { + fn new(graph: &'a IndexGraph<'tcx>, hashes: &'a IndexGraphHashes, tcx: TyCtxt<'tcx>) -> Self { + Self { graph, hashes, scc_fingerprints: IndexVec::with_capacity(graph.nodes.len()), tcx } + } +} + +impl<'a, 'tcx> Annotations for FingerprintAnnotations<'a, 'tcx> { + type Ann = FingerprintAnnotation; + type SccIdx = SccIdx; + + fn new(&self, node: NodeIdx) -> FingerprintAnnotation { + FingerprintAnnotation { + fingerprint: if let Some(encoded_data_hash) = + self.hashes.get_node(self.graph.nodes.get_index(node).unwrap().0, self.tcx) + { + self.graph.nodes.get_index(node).unwrap().1.combine_commutative(encoded_data_hash) + } else { + *self.graph.nodes.get_index(node).unwrap().1 + }, + } + } + + fn annotate_scc(&mut self, scc: SccIdx, annotation: FingerprintAnnotation) { + assert_eq!(self.scc_fingerprints.len(), scc.index()); + self.scc_fingerprints.push(annotation.fingerprint); + } +} + +fn build_public_hashes<'tcx>( + graph: &IndexGraph<'tcx>, + hashes: &IndexGraphHashes, + tcx: TyCtxt<'tcx>, + hcx: &mut StableHashingContext<'_>, +) -> Hashed { + let mut annotations = FingerprintAnnotations::new(graph, hashes, tcx); + let sccs = Sccs::<_, SccIdx>::new_with_annotation(graph, &mut annotations); + let mut public_hashes = ItemPublicHashesBuilder::default(); + let mut hasher = StableHasher::new(); + for (node_index, reachable) in graph.reachable_set().iter_enumerated() { + if !reachable { + continue; + } + match graph.nodes.get_index(node_index).unwrap().0 { + Node::DefId(id) => { + if let Some(local) = id.as_local() { + let fingerprint = annotations.scc_fingerprints[sccs.scc(node_index)]; + (local, fingerprint).stable_hash(hcx, &mut hasher); + public_hashes.local.set_some_unhashed(local.local_def_index, fingerprint); + } + } + Node::ExpnId(id) => { + if let Some(local) = id.as_local() { + let fingerprint = annotations.scc_fingerprints[sccs.scc(node_index)]; + (local, fingerprint).stable_hash(hcx, &mut hasher); + public_hashes.expn.set_some_unhashed(local.as_raw(), fingerprint); + } + } + Node::SyntaxContext(_) => (), + Node::Ty(_) => (), + Node::Predicate(_) => (), + Node::Span(_) => (), + } + } + Hashed { value: public_hashes, hash: Some(hasher.finish()) } +} + +#[derive(Default)] +pub(crate) struct ItemPublicHashesBuilder { + local: TableBuilder, DefIndex, Option>, + expn: TableBuilder, ExpnIndex, Option>, +} + +impl ItemPublicHashesBuilder { + fn encode<'a, 'tcx>( + &self, + ecx: &mut EncodeContext<'a, 'tcx>, + hcx: &mut PublicApiHashingContext<'_>, + public_global_hash: Fingerprint, + ) -> ItemPublicHashes { + ItemPublicHashes { + local: self.local.encode(&mut ecx.opaque, hcx).value, + expn: self.expn.encode(&mut ecx.opaque, hcx).value, + public_global_hash, + } + } +} + +#[derive(LazyDecodable, MetadataEncodable)] +pub(crate) struct ItemPublicHashes { + pub(crate) local: LazyTable>, + pub(crate) expn: LazyTable>, + pub(crate) public_global_hash: Fingerprint, +} diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index a3645a5556bf3..39e31376c402b 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -46,6 +46,9 @@ use rustc_target::spec::{PanicStrategy, TargetTuple}; use table::TableBuilder; use crate::eii::EiiMapEncodedKeyValue; +use crate::rmeta::encoder::public_api_hasher::{ + Hashed, ItemPublicHashes, PublicApiHasher, PublicApiHashingContext, RDRHashAll, RDRHashNone, +}; mod decoder; mod def_path_hash_map; @@ -206,7 +209,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. /// @@ -297,6 +300,27 @@ pub(crate) struct CrateRoot { symbol_mangling_version: SymbolManglingVersion, specialization_enabled_in: bool, + rdr_hashes: Option, + public_api_hash_opt_enabled: bool, +} + +/// All hashes here are equal to the hash from the crate header (the `crate_hash` query) when the public-api-hash unstable feature is disabled. +#[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`. @@ -335,7 +359,7 @@ pub(crate) struct CrateDep { #[derive(MetadataEncodable, LazyDecodable)] pub(crate) struct TraitImpls { - trait_id: (u32, DefIndex), + trait_id: (u32, u32), impls: LazyArray<(DefIndex, Option)>, } @@ -348,8 +372,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 +383,231 @@ 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, + // FIXME do we need to hash this? + is_reachable_non_generic: Table, + // FIXME do we need to hash this? + is_reachable_non_generic_with_export_level_c: Table, - optional: - 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>, + // only used for proc macros, we don't calculate public hash for proc macros + proc_macro_quoted_spans: Table>, + // FIXME do we need to hash this? + variant_data: Table>, + // FIXME do we need to hash this? + 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 +620,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/parameterized.rs b/compiler/rustc_metadata/src/rmeta/parameterized.rs index 1531584e99788..fc97634067fb8 100644 --- a/compiler/rustc_metadata/src/rmeta/parameterized.rs +++ b/compiler/rustc_metadata/src/rmeta/parameterized.rs @@ -75,6 +75,7 @@ trivially_parameterized_over_tcx! { crate::rmeta::VariantData, rustc_abi::ReprOptions, rustc_ast::DelimArgs, + rustc_data_structures::fingerprint::Fingerprint, rustc_hir::Attribute, rustc_hir::ConstStability, rustc_hir::Constness, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 26c5908563777..344cec9ff2e45 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -1,7 +1,13 @@ +use rustc_data_structures::fingerprint::Fingerprint; +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, TableIndex, TablePublicApiHasher, +}; use crate::rmeta::*; pub(super) trait IsDefault: Default { @@ -73,6 +79,31 @@ impl FixedSizeEncoding for u64 { } } +impl FixedSizeEncoding for Option { + type ByteArray = [u8; 17]; + + fn from_bytes(b: &Self::ByteArray) -> Self { + if b[0] == 0 { + None + } else { + Some(Fingerprint::from_le_bytes(b[1..17].try_into().unwrap())) + } + } + + fn write_to_bytes(self, b: &mut Self::ByteArray) { + match self { + Some(fingerprint) => { + b[0] = 1; + let buf: &mut [u8; 16] = (&mut b[1..17]).try_into().unwrap(); + *buf = fingerprint.to_le_bytes(); + } + None => { + b[0] = 0; + } + } + } +} + macro_rules! fixed_size_enum { ($ty:ty { $(($($pat:tt)*))* } $( unreachable { $(($($upat:tt)*))+ } )?) => { impl FixedSizeEncoding for Option<$ty> { @@ -436,34 +467,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: impl TableIndex, + value: T, + hashed: HashedT, + hcx: &mut PublicApiHashingContext<'a>, + ) where + HashedT: StableHash, + { + self.hasher.digest(i, hashed, hcx); + self.set(i.into_encoded(), 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: impl TableIndex, + value: T, + hashed: HashedT, + hcx: &mut PublicApiHashingContext<'_>, + ) where + HashedT: StableHash, + { + self.hasher.digest(i, hashed, hcx); + self.set(i.into_encoded(), 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 +601,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 +615,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..98473d700b34a 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -55,6 +55,7 @@ use rustc_arena::TypedArena; use rustc_ast as ast; use rustc_ast::expand::allocator::AllocatorKind; use rustc_ast::tokenstream::TokenStream; +use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; @@ -77,7 +78,7 @@ use rustc_session::cstore::{ }; use rustc_session::lint::LintExpectationId; use rustc_span::def_id::LOCAL_CRATE; -use rustc_span::{DUMMY_SP, LocalExpnId, Span, Spanned, Symbol}; +use rustc_span::{DUMMY_SP, ExpnId, LocalExpnId, Span, Spanned, Symbol}; use rustc_target::spec::PanicStrategy; use crate::hir::Crate; @@ -1934,6 +1935,11 @@ rustc_queries! { tcx.def_path_str(def_id), } } + query is_reachable_non_generic_with_export_level_c(def_id: DefId) -> bool { + desc { "checking whether `{}` is an exported symbol with export level ExportLevel::C", tcx.def_path_str(def_id) } + cache_on_disk + separate_provide_extern + } /// The entire set of monomorphizations the local crate can safely /// link to because they are exported from upstream crates. Do @@ -2033,12 +2039,52 @@ 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 + } + + /// Returns the public api hash of an extern DefId, panics for the local crate + query extern_def_public_hash(_: DefId) -> Fingerprint { + eval_always + desc { "looking up public hash of a definition" } + separate_provide_extern + } + + /// Returns the public api hash of an extern ExpnId, panics for the local crate + query extern_expn_public_hash(_: ExpnId) -> Fingerprint { + eval_always + desc { "looking up public hash of an expansion" } + separate_provide_extern + } + + /// Returns the global part of the public api hash. This covers the items in the rmeta which + /// requires a recompile of all down crates. + /// + /// For example adding lang items requires all downstream crates to check that there are no + /// conflicing implementations for that lang item. But adding a regular public item only + /// requires the direct downstream crates to recompile, since those need to reexport it for + /// the change to be visible to their dependents. + /// + /// Panics for the local crate. + query public_global_hash(_: CrateNum) -> Fingerprint { + eval_always + desc { "looking up the hash a crate" } + separate_provide_extern + } + /// Gets the hash for the host proc macro. Used to support -Z dual-proc-macro. query crate_host_hash(_: CrateNum) -> Option { eval_always diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index a6ff238ad6f0b..7c4b072f0c12c 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -206,6 +206,7 @@ impl_erasable_for_types_with_no_type_params! { Result, traits::query::NoSolution>, Ty<'_>, bool, + rustc_data_structures::fingerprint::Fingerprint, rustc_data_structures::svh::Svh, rustc_hir::Constness, rustc_hir::Defaultness, diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs index 71bd08861b8d5..8bf23c5ca5530 100644 --- a/compiler/rustc_middle/src/query/keys.rs +++ b/compiler/rustc_middle/src/query/keys.rs @@ -9,7 +9,7 @@ use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stable_hasher::StableHash; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::OwnerId; -use rustc_span::{DUMMY_SP, Ident, LocalExpnId, Span, Symbol}; +use rustc_span::{DUMMY_SP, ExpnId, Ident, LocalExpnId, Span, Symbol}; use crate::dep_graph::DepNodeIndex; use crate::infer::canonical::CanonicalQueryInput; @@ -173,6 +173,21 @@ impl QueryKey for SimplifiedType { } } +impl QueryKey for ExpnId { + fn default_span(&self, _: TyCtxt<'_>) -> Span { + self.expn_data().call_site + } +} + +impl AsLocalQueryKey for ExpnId { + type LocalQueryKey = LocalExpnId; + + #[inline(always)] + fn as_local_key(&self) -> Option { + self.as_local() + } +} + impl QueryKey for (DefId, DefId) { fn default_span(&self, tcx: TyCtxt<'_>) -> Span { self.1.default_span(tcx) diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 3bbbe3c184eb0..891ce8f1b9ee6 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -283,7 +283,7 @@ impl OnDiskCache { |encoder, index, ctxt_data| { let pos = AbsoluteBytePos::new(encoder.position()); encoder.encode_tagged(TAG_SYNTAX_CONTEXT, ctxt_data); - syntax_contexts.insert(index, pos); + syntax_contexts.insert(index.as_u32(), pos); }, |encoder, expn_id, data, hash| { if expn_id.krate == LOCAL_CRATE { diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index 8b1e812582783..4ed9a0a589540 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -34,8 +34,16 @@ pub trait TyEncoder<'tcx>: SpanEncoder { fn position(&self) -> usize; + #[inline] + fn type_encode_begin(&mut self, _ty: Ty<'tcx>) {} + #[inline] + fn type_encode_end(&mut self) {} fn type_shorthands(&mut self) -> &mut FxHashMap, usize>; + #[inline] + fn predicate_encode_begin(&mut self, _pred: ty::PredicateKind<'tcx>) {} + #[inline] + fn predicate_encode_end(&mut self) {} fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; fn encode_alloc_id(&mut self, alloc_id: &AllocId); @@ -100,17 +108,26 @@ pub trait RefDecodable<'tcx, D: TyDecoder<'tcx>>: PointeeSized { } /// Encode the given value or a previously cached shorthand. -pub fn encode_with_shorthand<'tcx, E, T, M>(encoder: &mut E, value: &T, cache: M) -where +pub fn encode_with_shorthand<'tcx, E, T, M, Begin, End>( + encoder: &mut E, + value: &T, + cache: M, + begin: Begin, + end: End, +) where E: TyEncoder<'tcx>, M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, + Begin: Fn(&mut E, T), + End: Fn(&mut E), T: EncodableWithShorthand<'tcx, E>, // The discriminant and shorthand must have the same size. T::Variant: DiscriminantKind, { let existing_shorthand = cache(encoder).get(value).copied(); + begin(encoder, *value); if let Some(shorthand) = existing_shorthand { encoder.emit_usize(shorthand); + end(encoder); return; } @@ -136,11 +153,18 @@ where if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { cache(encoder).insert(*value, shorthand); } + end(encoder); } impl<'tcx, E: TyEncoder<'tcx>> Encodable for Ty<'tcx> { fn encode(&self, e: &mut E) { - encode_with_shorthand(e, self, TyEncoder::type_shorthands); + encode_with_shorthand( + e, + self, + TyEncoder::type_shorthands, + TyEncoder::type_encode_begin, + TyEncoder::type_encode_end, + ); } } @@ -148,7 +172,13 @@ impl<'tcx, E: TyEncoder<'tcx>> Encodable for ty::Predicate<'tcx> { fn encode(&self, e: &mut E) { let kind = self.kind(); kind.bound_vars().encode(e); - encode_with_shorthand(e, &kind.skip_binder(), TyEncoder::predicate_shorthands); + encode_with_shorthand( + e, + &kind.skip_binder(), + TyEncoder::predicate_shorthands, + TyEncoder::predicate_encode_begin, + TyEncoder::predicate_encode_end, + ); } } diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 1d761c2f43b3d..114c110b59be7 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -209,6 +209,7 @@ impl<'tcx> Instance<'tcx> { // However, if the def_id is marked inline(never), then it's fine to just reuse the // upstream monomorphization. && tcx.codegen_fn_attrs(self.def_id()).inline != rustc_hir::attrs::InlineAttr::Never + || tcx.sess.opts.unstable_opts.public_api_hash { return None; } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index f68526724135c..af7b3cb728589 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -40,7 +40,7 @@ use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer}; use rustc_hir as hir; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, VisibilityDefId}; use rustc_hir::definitions::PerParentDisambiguatorState; use rustc_hir::{LangItem, attrs as attr, find_attr}; use rustc_index::IndexVec; @@ -340,7 +340,13 @@ impl TyCtxt<'_> { /// Compare def-ids based on their position in def-id tree, ancestor def-ids are considered /// larger than descendant def-ids, and two different def-ids are considered unordered if /// neither of them is an ancestor of the other. - fn def_id_partial_cmp(self, lhs: DefId, rhs: DefId) -> Option { + fn def_id_partial_cmp( + self, + lhs: impl Into, + rhs: impl Into, + ) -> Option { + let lhs = lhs.into().0; + let rhs = rhs.into().0; // Def-ids from different crates are always unordered. if lhs.krate != rhs.krate { return None; @@ -365,7 +371,11 @@ impl TyCtxt<'_> { } } - pub fn is_descendant_of(self, descendant: DefId, ancestor: DefId) -> bool { + pub fn is_descendant_of( + self, + descendant: impl Into, + ancestor: impl Into, + ) -> bool { matches!( self.def_id_partial_cmp(descendant, ancestor), Some(Ordering::Less | Ordering::Equal) @@ -390,9 +400,11 @@ impl> Visibility { pub fn to_def_id(self) -> Visibility { self.map_id(Into::into) } +} +impl> Visibility { /// Returns `true` if an item with this visibility is accessible from the given module. - pub fn is_accessible_from(self, module: impl Into, tcx: TyCtxt<'_>) -> bool { + pub fn is_accessible_from(self, module: impl Into, tcx: TyCtxt<'_>) -> bool { match self { // Public items are visible everywhere. Visibility::Public => true, @@ -2060,12 +2072,12 @@ impl<'tcx> TyCtxt<'tcx> { mut ident: Ident, scope: DefId, block: hir::HirId, - ) -> (Ident, DefId) { + ) -> (Ident, VisibilityDefId) { let scope = ident .span .normalize_to_macros_2_0_and_adjust(self.expn_that_defined(scope)) .and_then(|actual_expansion| actual_expansion.expn_data().parent_module) - .unwrap_or_else(|| self.parent_module(block).to_def_id()); + .unwrap_or_else(|| self.parent_module(block).to_def_id().into()); (ident, scope) } diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 08a8491bab6c5..a2927f11b1448 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -263,6 +263,7 @@ TrivialTypeTraversalAndLiftImpls! { crate::ty::ParamTy, crate::ty::instance::ReifyReason, rustc_hir::def_id::DefId, + rustc_hir::def_id::VisibilityDefId, // tidy-alphabetical-end } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 6058cb892dc14..f854fabc9e8fc 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -940,9 +940,17 @@ fn default_visibility(tcx: TyCtxt<'_>, id: DefId, is_generic: bool) -> Visibilit // Generic functions never have export-level C. SymbolExportLevel::Rust } else { - match tcx.reachable_non_generics(id.krate).get(&id) { - Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, - _ => SymbolExportLevel::Rust, + if tcx.sess.opts.unstable_opts.public_api_hash { + if tcx.is_reachable_non_generic_with_export_level_c(id) { + SymbolExportLevel::C + } else { + SymbolExportLevel::Rust + } + } else { + match tcx.reachable_non_generics(id.krate).get(&id) { + Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => SymbolExportLevel::C, + _ => SymbolExportLevel::Rust, + } } }; 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_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 4352e10462901..ed06ea3a6992e 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -56,7 +56,9 @@ use rustc_hir::def::{ self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, PerNS, }; -use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; +use rustc_hir::def_id::{ + CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap, VisibilityDefId, +}; use rustc_hir::definitions::{PerParentDisambiguatorState, PerParentDisambiguatorsMap}; use rustc_hir::{PrimTy, TraitCandidate, find_attr}; use rustc_index::bit_set::DenseBitSet; @@ -2389,7 +2391,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.pat_span_map.insert(node, span); } - fn is_accessible_from(&self, vis: Visibility>, module: Module<'ra>) -> bool { + fn is_accessible_from( + &self, + vis: Visibility>, + module: Module<'ra>, + ) -> bool { vis.is_accessible_from(module.nearest_parent_mod(), self.tcx) } diff --git a/compiler/rustc_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/def_id.rs b/compiler/rustc_span/src/def_id.rs index 6ce3d6b309f0e..6a2a5dcc890ef 100644 --- a/compiler/rustc_span/src/def_id.rs +++ b/compiler/rustc_span/src/def_id.rs @@ -564,3 +564,13 @@ impl LocalModDefId { self.0.is_top_level_module() } } + +/// DefId which can only be used to check visibilities. +#[derive(Clone, Copy, PartialEq, Eq, Hash, StableHash, Decodable, Debug)] +pub struct VisibilityDefId(pub DefId); + +impl> From for VisibilityDefId { + fn from(value: T) -> Self { + Self(value.into()) + } +} diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index d95c94513c1dd..ba7b32d2bb4a1 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -41,7 +41,7 @@ use rustc_macros::{Decodable, Encodable, StableHash}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use tracing::{debug, trace}; -use crate::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, StableCrateId}; +use crate::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, StableCrateId, VisibilityDefId}; use crate::edition::Edition; use crate::source_map::SourceMap; use crate::symbol::{Symbol, kw, sym}; @@ -333,6 +333,11 @@ impl ExpnId { } last_macro } + + #[inline] + pub fn is_local(&self) -> bool { + self.krate == LOCAL_CRATE + } } #[derive(Debug)] @@ -700,7 +705,7 @@ impl SyntaxContext { } #[inline] - pub(crate) const fn as_u32(self) -> u32 { + pub const fn as_u32(self) -> u32 { self.0 } @@ -1012,7 +1017,7 @@ pub struct ExpnData { /// if this `ExpnData` corresponds to a macro invocation pub macro_def_id: Option, /// The normal module (`mod`) in which the expanded macro was defined. - pub parent_module: Option, + pub parent_module: Option, /// Suppresses the `unsafe_code` lint for code produced by this macro. pub(crate) allow_internal_unsafe: bool, /// Enables the macro helper hack (`ident!(...)` -> `$crate::ident!(...)`) for this macro. @@ -1050,7 +1055,7 @@ impl ExpnData { allow_internal_unstable, edition, macro_def_id, - parent_module, + parent_module: parent_module.map(Into::into), disambiguator: 0, allow_internal_unsafe, local_inner_macros, @@ -1075,7 +1080,7 @@ impl ExpnData { allow_internal_unstable: None, edition, macro_def_id, - parent_module, + parent_module: parent_module.map(Into::into), disambiguator: 0, allow_internal_unsafe: false, local_inner_macros: false, @@ -1299,7 +1304,7 @@ impl HygieneEncodeContext { pub fn encode( &self, encoder: &mut T, - mut encode_ctxt: impl FnMut(&mut T, u32, &SyntaxContextKey), + mut encode_ctxt: impl FnMut(&mut T, SyntaxContext, &SyntaxContextKey), mut encode_expn: impl FnMut(&mut T, ExpnId, &ExpnData, ExpnHash), ) { // When we serialize a `SyntaxContextData`, we may end up serializing @@ -1324,7 +1329,7 @@ impl HygieneEncodeContext { }); for (ctxt, ctxt_key) in all_ctxt_data { if self.serialized_ctxts.lock().insert(ctxt) { - encode_ctxt(encoder, ctxt.0, &ctxt_key); + encode_ctxt(encoder, ctxt, &ctxt_key); } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 2b58dc8b6a708..1857d194e04e3 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -44,6 +44,7 @@ pub mod source_map; use source_map::{SourceMap, SourceMapInputs}; pub use self::caching_source_map_view::CachingSourceMapView; +use crate::def_id::VisibilityDefId; use crate::fatal_error::FatalError; pub mod edition; @@ -1348,7 +1349,7 @@ rustc_index::newtype_index! { /// This trait is used to allow encoder specific encodings of certain types. /// It is similar to rustc_type_ir's TyEncoder. -pub trait SpanEncoder: Encoder { +pub trait SpanEncoder: Encoder + Sized { fn encode_span(&mut self, span: Span); fn encode_symbol(&mut self, sym: Symbol); fn encode_byte_symbol(&mut self, byte_sym: ByteSymbol); @@ -1359,6 +1360,9 @@ pub trait SpanEncoder: Encoder { fn encode_crate_num(&mut self, crate_num: CrateNum); fn encode_def_index(&mut self, def_index: DefIndex); fn encode_def_id(&mut self, def_id: DefId); + fn encode_visibility_def_id(&mut self, def_id: VisibilityDefId) { + def_id.0.encode(self); + } } impl SpanEncoder for FileEncoder { @@ -1446,6 +1450,12 @@ impl Encodable for DefId { } } +impl Encodable for VisibilityDefId { + fn encode(&self, s: &mut E) { + s.encode_visibility_def_id(*self) + } +} + impl Encodable for AttrId { fn encode(&self, _s: &mut E) { // A fresh id will be generated when decoding 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/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 2e9dcae700d2a..a92ec1de41c48 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -818,7 +818,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { None => generic_param_scope, }, }; - match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) { + match self.tcx.is_descendant_of(type_scope, lifetime_scope) { true => type_scope, false => lifetime_scope, } diff --git a/tests/incremental/rdr/private_module_file/auxiliary/dep.rs b/tests/incremental/rdr/private_module_file/auxiliary/dep.rs new file mode 100644 index 0000000000000..20aa0f230d2d8 --- /dev/null +++ b/tests/incremental/rdr/private_module_file/auxiliary/dep.rs @@ -0,0 +1,13 @@ +//@ compile-flags: -Z public-api-hash + +#![crate_name = "dep"] +#![crate_type = "rlib"] + +mod private; + +pub fn call_private() { + private::print(); +} + +#[cfg(any(cpass3))] +pub fn new_public() {} diff --git a/tests/incremental/rdr/private_module_file/auxiliary/private.rs b/tests/incremental/rdr/private_module_file/auxiliary/private.rs new file mode 100644 index 0000000000000..65b5dff046907 --- /dev/null +++ b/tests/incremental/rdr/private_module_file/auxiliary/private.rs @@ -0,0 +1,9 @@ +#[cfg(any(cpass1))] +pub(super) fn print() { + println!("a"); +} + +#[cfg(any(cpass2, cpass3))] +pub(super) fn print() { + println!("b"); +} diff --git a/tests/incremental/rdr/private_module_file/main.rs b/tests/incremental/rdr/private_module_file/main.rs new file mode 100644 index 0000000000000..4d53700cb487d --- /dev/null +++ b/tests/incremental/rdr/private_module_file/main.rs @@ -0,0 +1,15 @@ +//@ revisions: cpass1 cpass2 cpass3 +//@ compile-flags: -Z query-dep-graph -Z public-api-hash +//@ aux-build: dep.rs +//@ ignore-backends: gcc + +#![feature(rustc_attrs)] +#![crate_type = "bin"] +#![rustc_public_hash_unchanged(crate_name = "dep", cfg = "cpass2")] +#![rustc_public_hash_changed(crate_name = "dep", cfg = "cpass3")] + +extern crate dep; + +fn main() { + dep::call_private(); +} 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); +} diff --git a/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs new file mode 100644 index 0000000000000..232763d6d05c4 --- /dev/null +++ b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/dep.rs @@ -0,0 +1,9 @@ +//@ aux-build: transitive_dep.rs +//@ compile-flags: -Z public-api-hash + +#![crate_name = "dep"] +#![crate_type = "rlib"] + +extern crate transitive_dep; + +pub use transitive_dep::print; diff --git a/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs new file mode 100644 index 0000000000000..e4d31ff58f308 --- /dev/null +++ b/tests/incremental/rdr/reexported_transitive_dependencies/auxiliary/transitive_dep.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -Z public-api-hash + +#![crate_name = "transitive_dep"] +#![crate_type = "rlib"] + +#[cfg(any(cpass1, cpass2))] +pub fn print() { + println!("5"); +} + +#[cfg(any(cpass3))] +pub fn print() { + println!("5"); +} + +#[cfg(any(cpass2))] +pub fn print2() { + println!("5"); +} diff --git a/tests/incremental/rdr/reexported_transitive_dependencies/main.rs b/tests/incremental/rdr/reexported_transitive_dependencies/main.rs new file mode 100644 index 0000000000000..a708d87f832fb --- /dev/null +++ b/tests/incremental/rdr/reexported_transitive_dependencies/main.rs @@ -0,0 +1,15 @@ +//@ revisions: cpass1 cpass2 cpass3 +//@ compile-flags: -Z query-dep-graph -Z public-api-hash +//@ aux-build: dep.rs +//@ ignore-backends: gcc + +#![feature(rustc_attrs)] +#![crate_type = "bin"] +#![rustc_public_hash_unchanged(crate_name = "dep", cfg = "cpass2")] +#![rustc_public_hash_changed(crate_name = "dep", cfg = "cpass3")] + +extern crate dep; + +fn main() { + dep::print(); +}