Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
2cdfc7f
rdr: add RDRHashes field to CrateRoot
susitsm Apr 8, 2026
48b5691
rdr: add public_api_hash unstable option
susitsm Apr 20, 2026
2591aa9
derive HashStable for TargetModifier
susitsm Apr 20, 2026
6b0f54a
derive HashStable for DeniedPartialMitigation
susitsm Apr 27, 2026
b9e923c
implement StableHash for TargetTuple
susitsm May 7, 2026
272de14
rdr: implement rmeta public api hash as the stable hash of (almost) a…
susitsm Apr 27, 2026
08ad287
Add public api hash debug logs
susitsm May 8, 2026
2d632a7
rdr: elaborate on how EII-s should be included in the public api hash
susitsm May 8, 2026
a612498
rdr: document hashed_lazy_array
susitsm May 8, 2026
128afda
rdr: hash spans as if they had no parents (spans are encoded into the…
susitsm Apr 25, 2026
f2113af
rdr: add the public_api_hash query. Queries provided by rmeta now dep…
susitsm Apr 21, 2026
ab4f22b
rdr: add rustc_public_hash_unchanged and rustc_public_hash_changed at…
susitsm Apr 30, 2026
6ab0489
rdr: add incremental test exercising rustc_public_hash_changed and ru…
susitsm Apr 26, 2026
63b2175
rdr: use public_api_hash when hashing dependencies
susitsm Apr 29, 2026
2e60675
rdr: use public_api_hash in the rmeta headers
susitsm May 5, 2026
728fad4
rdr: move the hashes to crate header, properly handle private and pub…
susitsm May 8, 2026
2d0ae78
rdr-perf: make encode_crate_root const generic over public api hashing
susitsm May 28, 2026
ea8949e
rdr-perf: always inline public api hashing functions
susitsm May 29, 2026
05124c9
rdr-perf: reduce code size of parentless span stable hashing
susitsm May 29, 2026
9387db1
perf: merge span hi and lo hashing
susitsm Jun 1, 2026
110f25c
rdr-perf: only check rdr test attributes when the query_dep_graph opt…
susitsm Jun 7, 2026
c9ed6b4
rdr-perf: skip query calls when public api hashing is disabled
susitsm Jun 7, 2026
202526e
rdr-perf: make sure record! macros do not duplicate query invocations
susitsm Jun 7, 2026
b6ac399
merge stable_hash_span and stable_hash_span_data
susitsm Jun 7, 2026
55f4844
rdr-perf: make StableHashState generic over HASH_SPANS_AS_PARENTLESS
susitsm Jun 8, 2026
0473780
rdr: save whether public api hashing was used while encoding the meta…
susitsm May 25, 2026
5f0194e
rdr: encode the DefIndex part of DefIds as stable hash when public ap…
susitsm Jun 8, 2026
537fbbe
rdr: store trait impls with DefPathHash when public api hashing is en…
susitsm May 12, 2026
20587c4
rdr: Encode DefIndex part of RawDefId-s as a DefPathHash when public …
susitsm Jun 24, 2026
b56fe50
rdr: do not hash the def_path_hash_map
susitsm May 23, 2026
22ee24e
use SyntaxContext instead of raw u32 in HygieneEncodeContext::encode
susitsm May 14, 2026
3646ed6
rdr: add the metadata reachability graph
susitsm Jun 17, 2026
60be890
rdr: add edges to the reachability graph from tables containing RawDefId
susitsm Jun 24, 2026
fe1ddf0
TyEncoder: add Ty and Predicate encoding callbacks
susitsm Jun 17, 2026
bc7852a
perf: always inline tyencoder functions
susitsm Jun 26, 2026
be17d4b
rdr: add Ty and Predicate nodes to the reachability graph
susitsm Jun 17, 2026
a5054d9
rdr: add mir interpreter AllocId-s to the reachability graph
susitsm Jun 11, 2026
ff5768d
rdr: add Span nodes to the reachability graph
susitsm Jun 17, 2026
c9dfd03
rdr: remove LocalExpnId::ROOT -> CRATE_DEF_ID edge from the reachabil…
susitsm Jun 17, 2026
c4816f9
rdr: move hashed_lazy_array to the top of the file
susitsm Jun 18, 2026
8e46d44
rdr: include trait impls in the reachability graph
susitsm Jun 18, 2026
03141ad
perf: inline no-op functions used when public api hashing is disabled
susitsm Jun 26, 2026
7608860
rdr: remove stripped cfg items from metadata when public api hashing …
susitsm May 22, 2026
177f93d
rdr: only record public module children in the metadata when public a…
susitsm May 22, 2026
ee4c04c
rdr: add VisibilityDefId which is only usable to check visibilities
susitsm May 22, 2026
1e54722
rdr: do not include VisibilityDefId in the reachability graph
susitsm May 23, 2026
3fdfdc3
rdr: remove traits from metadata when public api hashing is enabled
susitsm May 24, 2026
b490621
rdr: disable upstream_monomorphizations when public_api_hash is enabled
susitsm May 24, 2026
99306e0
rdr: add is_exportable query, save is_exportable to a table instead o…
susitsm Jun 18, 2026
9bd64bb
rdr: document how exported_generic_symbols and exported_non_generic_s…
susitsm Jun 18, 2026
1125064
rdr: move is_reachable_non_generic into a table
susitsm May 25, 2026
2c8a96e
rdr: add the is_reachable_non_generic_with_export_level_c query
susitsm May 25, 2026
913586a
rdr: move exported_generic_symbols and exported_non_generic_symbols b…
susitsm May 25, 2026
174ff2f
rdr: assert that reachable_non_generics is not used when public api h…
susitsm May 25, 2026
952b5d6
rdr: allow access to private hash for metadata compiled without publi…
susitsm May 25, 2026
455bfbf
rdr: test that changing public items in a crate does not change the p…
susitsm May 25, 2026
a9bc3db
rdr: add test testing that changes to a private file does not change …
susitsm May 25, 2026
483df33
rdr: use EncodeContext instead of FileEncoder when encoding tables
susitsm Jun 18, 2026
8a1878e
rdr: allow public api hashing when incremental is disabled
susitsm Jun 19, 2026
7de98ba
rdr: add -Zls=public_hash for printing the metadata public hash
susitsm May 23, 2026
2a9ad3f
rdr: test that adding private items to the end of a file does not cha…
susitsm Jun 19, 2026
322b9c8
rdr: debug log global hash and root hash
susitsm Jun 21, 2026
eff88f0
rdr: add module level docs, update HashableCrateRoot docs
susitsm Jun 22, 2026
609520f
rdr: add tests for trait impls in the public hash
susitsm Jun 24, 2026
7adc27b
rdr: test that inlinable functions expose private callees to the publ…
susitsm Jun 24, 2026
3d54082
rdr: test that changing extra-filename changes the public hash
susitsm Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4417,6 +4417,7 @@ dependencies = [
"rustc_expand",
"rustc_feature",
"rustc_fs_util",
"rustc_hashes",
"rustc_hir",
"rustc_hir_pretty",
"rustc_index",
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_attr_ir/src/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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, Encodable, Decodable, Debug, StableHash, PrintAttribute)]
pub enum InlineAttr {
None,
Expand Down Expand Up @@ -1389,6 +1396,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,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_ir/src/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ impl AttributeKind {
RustcPreserveUbChecks => No,
RustcProcMacroDecls => No,
RustcPubTransparent(..) => Yes,
RustcRDRTestAttr(..) => No,
RustcReallocator => No,
RustcRegions => No,
RustcScalableVector { .. } => Yes,
Expand Down
99 changes: 88 additions & 11 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use rustc_ast::{LitIntType, LitKind, MetaItemLit};
use rustc_attr_ir::lang_items::LangItem;
use rustc_attr_ir::target::GenericParamKind;
use rustc_attr_ir::{
BorrowckGraphvizFormatKind, CguFields, CguKind, RustcCleanAttribute, RustcCleanQueries,
RustcMirKind,
BorrowckGraphvizFormatKind, CguFields, CguKind, RDRFields, RustcCleanAttribute,
RustcCleanQueries, RustcMirKind,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_feature::AttributeStability;
Expand All @@ -15,8 +15,8 @@ use super::prelude::*;
use super::util::parse_single_integer;
use crate::diagnostics;
use crate::diagnostics::{
AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange,
UnknownExternLangItem, UnknownLangItem,
AttributeRequiresOpt, FieldsMissing, RustcScalableVectorCountOutOfRange, UnknownExternLangItem,
UnknownLangItem,
};

pub(crate) struct RustcMainParser;
Expand Down Expand Up @@ -256,11 +256,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 {
Expand All @@ -280,11 +280,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;
};

Expand All @@ -294,6 +290,87 @@ 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<Self> = &[
(
&[sym::rustc_public_hash_changed],
template!(List: &[r#"cfg = "...", crate_name = "...""#]),
unstable!(rustc_attrs),
|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 = "...""#]),
unstable!(rustc_attrs),
|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<AttributeKind> {
Some(AttributeKind::RustcRDRTestAttr(self.items))
}
}

#[derive(Default)]
pub(crate) struct RustcCguTestAttributeParser {
items: ThinVec<(Span, CguFields)>,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ attribute_parsers!(
RustcAlignParser,
RustcAlignStaticParser,
RustcCguTestAttributeParser,
RustcRDRTestAttributeParser,
StabilityParser,
UsedParser,
// tidy-alphabetical-end
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_attr_parsing/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,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,
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_gcc/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/src/callee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,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.
Expand Down
14 changes: 11 additions & 3 deletions compiler/rustc_codegen_ssa/src/back/symbol_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,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>(
Expand Down Expand Up @@ -389,6 +395,7 @@ fn exported_generic_symbols_provider_local<'tcx>(
if !item_is_offload && !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.
Expand Down Expand Up @@ -598,7 +605,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;
}
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[
sym::rustc_partition_reused,
sym::rustc_partition_codegened,
sym::rustc_expected_cgu_reuse,
sym::rustc_public_hash_changed,
sym::rustc_public_hash_unchanged,
sym::rustc_dump_symbol_name,
sym::rustc_dump_def_path,
sym::rustc_mir,
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use rustc_middle::ty::{
};
use rustc_middle::{bug, span_bug};
use rustc_session::diagnostics::feature_err;
use rustc_span::def_id::ModId;
use rustc_span::def_id::VisibilityModId;
use rustc_span::{DUMMY_SP, Ident, Span, kw, sym};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{self, FulfillmentError};
Expand Down Expand Up @@ -123,7 +123,7 @@ pub enum RegionInferReason<'a> {
pub struct InherentAssocCandidate {
pub impl_: DefId,
pub assoc_item: DefId,
pub scope: ModId,
pub scope: VisibilityModId,
}

pub struct ResolvedStructPath<'tcx> {
Expand Down Expand Up @@ -1810,7 +1810,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
ident: Ident,
assoc_tag: ty::AssocTag,
scope: DefId,
) -> Option<(ty::AssocItem, /*scope*/ ModId)> {
) -> Option<(ty::AssocItem, /*scope*/ VisibilityModId)> {
let tcx = self.tcx();

let (ident, def_scope) = tcx.adjust_ident_and_get_scope(ident, scope, self.item_def_id());
Expand All @@ -1830,7 +1830,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&self,
item_def_id: DefId,
ident: Ident,
scope: ModId,
scope: VisibilityModId,
block: HirId,
span: Span,
) {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_incremental/src/persist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod data;
mod file_format;
mod fs;
mod load;
mod rdr_hashes;
mod save;
mod work_product;

Expand Down
91 changes: 91 additions & 0 deletions compiler/rustc_incremental/src/persist/rdr_hashes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
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<'_>) {
if !tcx.sess.opts.unstable_opts.query_dep_graph {
return;
}
// 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)",
);
}
}
Loading
Loading