Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 94 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 @@ -250,11 +250,11 @@ fn parse_cgu_fields<S: Stage>(
}

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 @@ -274,11 +274,7 @@ fn parse_cgu_fields<S: Stage>(
} 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 @@ -288,6 +284,95 @@ fn parse_cgu_fields<S: Stage>(
Some((cfg, module, kind))
}

fn parse_rdr_fields<S: Stage>(
cx: &mut AcceptContext<'_, '_, S>,
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(arg) = arg.meta_item() else {
cx.adcx().expected_name_value(args.span, None);
continue;
};

let res = match arg.ident().map(|i| i.name) {
Some(sym::cfg) => &mut cfg,
Some(sym::crate_name) => &mut crate_name,
_ => {
cx.adcx()
.expected_specific_argument(arg.path().span(), &[sym::cfg, sym::crate_name]);
continue;
}
};

let Some(i) = arg.args().name_value() else {
cx.adcx().expected_name_value(arg.span(), None);
continue;
};

let Some(str) = i.value_as_str() else {
cx.adcx().expected_string_literal(i.value_span, Some(i.value_as_lit()));
continue;
};

if res.is_some() {
cx.adcx().duplicate_key(arg.span(), arg.ident().unwrap().name);
continue;
}

*res = Some((str, i.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<S: Stage> AttributeParser<S> for RustcRDRTestAttributeParser {
const ATTRIBUTES: AcceptMapping<Self, S> = &[
(
&[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<'_, '_, S>) -> 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 @@ -162,6 +162,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_data_structures/src/stable_hasher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -601,4 +601,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 @@ -772,6 +772,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, HashStable_Generic, PrintAttribute)]
pub struct RDRFields {
pub crate_name: Symbol,
pub cfg: Symbol,
pub changed: bool,
}

#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)]
#[derive(HashStable_Generic, Encodable, Decodable)]
pub enum DivergingFallbackBehavior {
Expand Down Expand Up @@ -1562,6 +1569,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 @@ -177,6 +177,7 @@ impl AttributeKind {
RustcPreserveUbChecks => No,
RustcProcMacroDecls => No,
RustcPubTransparent(..) => Yes,
RustcRDRTestAttr(..) => No,
RustcReallocator => No,
RustcRegions => No,
RustcReservationImpl(..) => Yes,
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
12 changes: 8 additions & 4 deletions compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ macro_rules! provide_one {
let ($def_id, $other) = def_id_arg.into_args();
assert!(!$def_id.is_local());

// External query providers call `crate_hash` in order to register a dependency
// on the crate metadata. The exception is `crate_hash` itself, which obviously
// External query providers call `public_api_hash` in order to register a dependency
// on the crate metadata. The exception is `public_api_hash` itself, which obviously
// doesn't need to do this (and can't, as it would cause a query cycle).
use rustc_middle::dep_graph::DepKind;
if DepKind::$name != DepKind::crate_hash && $tcx.dep_graph.is_fully_enabled() {
$tcx.ensure_ok().crate_hash($def_id.krate);
if DepKind::$name != DepKind::public_api_hash && $tcx.dep_graph.is_fully_enabled() {
$tcx.ensure_ok().public_api_hash($def_id.krate);
}

let cstore = CStore::from_tcx($tcx);
Expand Down Expand Up @@ -366,6 +366,10 @@ provide! { tcx, def_id, other, cdata,
native_libraries => { cdata.get_native_libraries(tcx).collect() }
foreign_modules => { cdata.get_foreign_modules(tcx).map(|m| (m.def_id, m)).collect() }
crate_hash => { cdata.root.header.hash }
public_api_hash => { {
tracing::debug!("public api hash: {} {}", cdata.root.header.name, cdata.root.rdr_hashes.public_api_hash);
Comment thread
jdonszelmann marked this conversation as resolved.
cdata.root.rdr_hashes.public_api_hash
} }
crate_host_hash => { cdata.host_hash }
crate_name => { cdata.root.header.name }
num_extern_def_ids => { cdata.num_def_ids() }
Expand Down
Loading