Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
7db1240
rdr: add RDRHashes field to CrateRoot
susitsm Apr 8, 2026
aac2ec2
rdr: add public_api_hash unstable option
susitsm Apr 20, 2026
2e274d5
derive HashStable for TargetModifier
susitsm Apr 20, 2026
ab1fd6c
derive HashStable for DeniedPartialMitigation
susitsm Apr 27, 2026
b3c3fda
implement StableHash for TargetTuple
susitsm May 7, 2026
c46e842
tidy: move TargetTuple to a new file
susitsm May 8, 2026
346cfd4
rdr: implement rmeta public api hash as the stable hash of (almost) a…
susitsm Apr 27, 2026
04c1362
Add public api hash debug logs
susitsm May 8, 2026
cfdfcc5
rdr: elaborate on how EII-s should be included in the public api hash
susitsm May 8, 2026
5989a84
rdr: document the process of hashing new fields added to CrateRoot
susitsm May 8, 2026
f8e21df
rdr: document hashed_lazy_array
susitsm May 8, 2026
504f859
rdr: hash spans as if they had no parents (spans are encoded into the…
susitsm Apr 25, 2026
294e8a3
rdr: add the public_api_hash query. Queries provided by rmeta now dep…
susitsm Apr 21, 2026
f3a45e4
rdr: add rustc_public_hash_unchanged and rustc_public_hash_changed at…
susitsm Apr 30, 2026
53ced96
rdr: add incremental test exercising rustc_public_hash_changed and ru…
susitsm Apr 26, 2026
949b4d4
rdr: use public_api_hash when hashing dependencies
susitsm Apr 29, 2026
bdf7e0b
rdr: use public_api_hash in the rmeta headers
susitsm May 5, 2026
5fe704b
rdr: move the hashes to crate header, properly handle private and pub…
susitsm May 8, 2026
d292f0b
rdr: add hash FIXME comments to LazyTables fields
susitsm May 9, 2026
0380de9
record encoded crate nums
susitsm May 10, 2026
aa0a2b8
rdr: more comments of what to include where
susitsm May 10, 2026
69d2f86
rdr: start building reachable graph
susitsm May 11, 2026
8895a17
rdr: document what exportable_items is used for
susitsm May 11, 2026
646aeef
rdr: save TraitImpls defids as (u32,u32) to avoid encoding them as De…
susitsm May 12, 2026
f8f2b58
use SyntaxContext instead of raw u32 in HygieneEncodeContext::encode
susitsm May 14, 2026
243669c
hash stuff reachable through the API
susitsm May 19, 2026
f204233
rdr: save public api hashes of DefId-s and ExpnId-s to the metadata
susitsm May 21, 2026
baaaef7
rdr: hash the public hashes, make sure hash retrieval works for metad…
susitsm May 22, 2026
f9137c0
rdr: remove stripped cfg items from metadata
susitsm May 22, 2026
3a7edd6
rdr: only record public module children in rmeta
susitsm May 22, 2026
fdd50d5
rdr: add VisibilityDefId which is only usable to check visibilities
susitsm May 22, 2026
a785d80
rdr: do not include VisibilityDefId in the public hash graph
susitsm May 23, 2026
ed3bebe
rdr: debug log the hash graph
susitsm May 23, 2026
86f06ec
add -Zls=public_hash for printing the metadata public hash
susitsm May 23, 2026
a4868f7
rdr: do not hash the def_path_hash_map
susitsm May 23, 2026
3d506a4
rdr: remove LocalExpnId::ROOT -> CRATE_DEF_ID edge from the public ha…
susitsm May 23, 2026
f6906bf
rdr: add reachablity to the debug representation of IndexGraph
susitsm May 23, 2026
6d8b170
rdr: only include public_global_hash instead of the full public hash …
susitsm May 23, 2026
98e4c4a
rdr: remove traits from rmeta
susitsm May 24, 2026
1458d18
rdr: add some comments to the impls field of HashableCrateRoot
susitsm May 24, 2026
5a9be6c
rdr: disable upstream_monomorphizations when public_api_hash is enabled
susitsm May 24, 2026
3ae9712
rdr: document exported_non_generic_symbols and exported_generic_symbo…
susitsm May 24, 2026
3e4f2c7
rdr: move is_reachable_non_generic into a table
susitsm May 25, 2026
bedb5cf
rdr: add the is_reachable_non_generic_with_export_level_c query
susitsm May 25, 2026
9c1fc78
rdr: move exported_generic_symbols and exported_non_generic_symbols b…
susitsm May 25, 2026
23fd17c
rdr: assert that reachable_non_generics is not used when public api h…
susitsm May 25, 2026
2127868
rdr: save into the rmeta whether public_api_hash was enabled, use it …
susitsm May 25, 2026
f5ac7fc
rdr: allow access to private hash for rmetas compiled without public_…
susitsm May 25, 2026
621b5c4
rdr: add test testing that changes to a private file does not change …
susitsm May 25, 2026
33aec4a
rdr: test that changing public items in crate does not change the pub…
susitsm May 25, 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
93 changes: 84 additions & 9 deletions compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ 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;

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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
};

Expand All @@ -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<Self> = &[
(
&[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<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 @@ -145,6 +145,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/session_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
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 @@ -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.
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 @@ -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>(
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_data_structures/src/stable_hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
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 @@ -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),
Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ impl AttributeKind {
RustcPreserveUbChecks => No,
RustcProcMacroDecls => No,
RustcPubTransparent(..) => Yes,
RustcRDRTestAttr(..) => No,
RustcReallocator => No,
RustcRegions => No,
RustcReservationImpl(..) => Yes,
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 @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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);
Expand All @@ -1902,7 +1902,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&self,
item_def_id: DefId,
ident: Ident,
scope: DefId,
scope: VisibilityDefId,
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
88 changes: 88 additions & 0 deletions compiler/rustc_incremental/src/persist/rdr_hashes.rs
Original file line number Diff line number Diff line change
@@ -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)",
);
}
}
3 changes: 2 additions & 1 deletion compiler/rustc_incremental/src/persist/save.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 || {
Expand Down
Loading