Skip to content

Commit 08e9b32

Browse files
committed
rdr: add rustc_public_hash_unchanged and rustc_public_hash_changed attributes for testing
1 parent 101cadc commit 08e9b32

10 files changed

Lines changed: 203 additions & 23 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs

Lines changed: 94 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ use rustc_ast::{LitIntType, LitKind, MetaItemLit};
44
use rustc_hir::LangItem;
55
use rustc_hir::attrs::{
66
BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior,
7-
DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcMirKind,
7+
DivergingFallbackBehavior, RDRFields, RustcCleanAttribute, RustcCleanQueries, RustcMirKind,
88
};
99
use rustc_span::Symbol;
1010

1111
use super::prelude::*;
1212
use super::util::parse_single_integer;
1313
use crate::errors;
1414
use crate::session_diagnostics::{
15-
AttributeRequiresOpt, CguFieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem,
15+
AttributeRequiresOpt, FieldsMissing, RustcScalableVectorCountOutOfRange, UnknownLangItem,
1616
};
1717

1818
pub(crate) struct RustcMainParser;
@@ -250,11 +250,11 @@ fn parse_cgu_fields<S: Stage>(
250250
}
251251

252252
let Some((cfg, _)) = cfg else {
253-
cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
253+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
254254
return None;
255255
};
256256
let Some((module, _)) = module else {
257-
cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module });
257+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module });
258258
return None;
259259
};
260260
let kind = if let Some((kind, span)) = kind {
@@ -274,11 +274,7 @@ fn parse_cgu_fields<S: Stage>(
274274
} else {
275275
// return None so that an unwrap for the attributes that need it is ok.
276276
if accepts_kind {
277-
cx.emit_err(CguFieldsMissing {
278-
span: args.span,
279-
name: &cx.attr_path,
280-
field: sym::kind,
281-
});
277+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::kind });
282278
return None;
283279
};
284280

@@ -288,6 +284,95 @@ fn parse_cgu_fields<S: Stage>(
288284
Some((cfg, module, kind))
289285
}
290286

287+
fn parse_rdr_fields<S: Stage>(
288+
cx: &mut AcceptContext<'_, '_, S>,
289+
args: &ArgParser,
290+
) -> Option<(Symbol, Symbol)> {
291+
let args = cx.expect_list(args, cx.attr_span)?;
292+
293+
let mut cfg = None::<(Symbol, Span)>;
294+
let mut crate_name = None::<(Symbol, Span)>;
295+
296+
for arg in args.mixed() {
297+
let Some(arg) = arg.meta_item() else {
298+
cx.adcx().expected_name_value(args.span, None);
299+
continue;
300+
};
301+
302+
let res = match arg.ident().map(|i| i.name) {
303+
Some(sym::cfg) => &mut cfg,
304+
Some(sym::crate_name) => &mut crate_name,
305+
_ => {
306+
cx.adcx()
307+
.expected_specific_argument(arg.path().span(), &[sym::cfg, sym::crate_name]);
308+
continue;
309+
}
310+
};
311+
312+
let Some(i) = arg.args().name_value() else {
313+
cx.adcx().expected_name_value(arg.span(), None);
314+
continue;
315+
};
316+
317+
let Some(str) = i.value_as_str() else {
318+
cx.adcx().expected_string_literal(i.value_span, Some(i.value_as_lit()));
319+
continue;
320+
};
321+
322+
if res.is_some() {
323+
cx.adcx().duplicate_key(arg.span(), arg.ident().unwrap().name);
324+
continue;
325+
}
326+
327+
*res = Some((str, i.value_span));
328+
}
329+
330+
let Some((cfg, _)) = cfg else {
331+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
332+
return None;
333+
};
334+
let Some((crate_name, _)) = crate_name else {
335+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::crate_name });
336+
return None;
337+
};
338+
339+
Some((cfg, crate_name))
340+
}
341+
342+
#[derive(Default)]
343+
pub(crate) struct RustcRDRTestAttributeParser {
344+
items: ThinVec<(Span, RDRFields)>,
345+
}
346+
347+
impl<S: Stage> AttributeParser<S> for RustcRDRTestAttributeParser {
348+
const ATTRIBUTES: AcceptMapping<Self, S> = &[
349+
(
350+
&[sym::rustc_public_hash_changed],
351+
template!(List: &[r#"cfg = "...", crate_name = "...""#]),
352+
|this, cx, args| {
353+
this.items.extend(parse_rdr_fields(cx, args).map(|(cfg, crate_name)| {
354+
(cx.attr_span, RDRFields { cfg, crate_name, changed: false })
355+
}));
356+
},
357+
),
358+
(
359+
&[sym::rustc_public_hash_unchanged],
360+
template!(List: &[r#"cfg = "...", crate_name = "...""#]),
361+
|this, cx, args| {
362+
this.items.extend(parse_rdr_fields(cx, args).map(|(cfg, crate_name)| {
363+
(cx.attr_span, RDRFields { cfg, crate_name, changed: true })
364+
}));
365+
},
366+
),
367+
];
368+
369+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
370+
371+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
372+
Some(AttributeKind::RustcRDRTestAttr(self.items))
373+
}
374+
}
375+
291376
#[derive(Default)]
292377
pub(crate) struct RustcCguTestAttributeParser {
293378
items: ThinVec<(Span, CguFields)>,

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ attribute_parsers!(
164164
RustcAlignParser,
165165
RustcAlignStaticParser,
166166
RustcCguTestAttributeParser,
167+
RustcRDRTestAttributeParser,
167168
StabilityParser,
168169
UsedParser,
169170
// tidy-alphabetical-end

compiler/rustc_attr_parsing/src/session_diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub(crate) struct DocAliasStartEnd<'a> {
5050

5151
#[derive(Diagnostic)]
5252
#[diag("`#[{$name})]` is missing a `{$field}` argument")]
53-
pub(crate) struct CguFieldsMissing<'a> {
53+
pub(crate) struct FieldsMissing<'a> {
5454
#[primary_span]
5555
pub span: Span,
5656
pub name: &'a AttrPath,

compiler/rustc_codegen_ssa/src/assert_module_sources.rs

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,80 @@ use std::fmt;
2828

2929
use rustc_data_structures::unord::{UnordMap, UnordSet};
3030
use rustc_errors::{DiagArgValue, IntoDiagArg};
31-
use rustc_hir::attrs::{CguFields, CguKind};
31+
use rustc_hir::attrs::{CguFields, CguKind, RDRFields};
3232
use rustc_hir::def_id::LOCAL_CRATE;
3333
use rustc_hir::{self as hir, find_attr};
34+
use rustc_middle::dep_graph::{DepKind, DepNode};
3435
use rustc_middle::mono::CodegenUnitNameBuilder;
3536
use rustc_middle::ty::TyCtxt;
3637
use rustc_session::Session;
3738
use rustc_span::{Span, Symbol};
3839
use tracing::debug;
3940

4041
use crate::errors;
42+
fn check_rdr_test_attrs(tcx: TyCtxt<'_>) {
43+
for &(span, fields) in
44+
find_attr!(tcx.hir_attrs(rustc_hir::CRATE_HIR_ID), RustcRDRTestAttr(e) => e)
45+
.into_iter()
46+
.flatten()
47+
{
48+
assert_dependency_public_hash(tcx, span, fields);
49+
}
50+
}
51+
52+
fn assert_dependency_public_hash(tcx: TyCtxt<'_>, span: Span, fields: RDRFields) {
53+
let crate_num = tcx
54+
.crates(())
55+
.iter()
56+
.copied()
57+
.find(|&cnum| tcx.crate_name(cnum).as_str() == fields.crate_name.as_str())
58+
.unwrap_or_else(|| {
59+
tcx.dcx().span_fatal(
60+
span,
61+
format!("crate `{}` not found in dependencies", fields.crate_name),
62+
)
63+
});
64+
if crate_num == LOCAL_CRATE {
65+
tcx.dcx().span_fatal(span, "expected the name of a dependency crate");
66+
}
67+
68+
if !tcx.sess.opts.unstable_opts.query_dep_graph {
69+
tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span });
70+
}
71+
72+
if !check_config(tcx, fields.cfg) {
73+
debug!("check_attr: config does not match, ignoring attr");
74+
return;
75+
}
76+
77+
let green = !fields.changed;
78+
let dep_node = DepNode::construct(tcx, DepKind::public_api_hash, &crate_num);
79+
let is_green = tcx.dep_graph.is_green(&dep_node);
80+
let is_red = tcx.dep_graph.is_red(&dep_node);
81+
if !is_red && !is_green {
82+
tcx.dcx().span_fatal(span, "dependency color is neither red or green!");
83+
}
84+
85+
if green && !is_green {
86+
tcx.dcx().span_fatal(
87+
span,
88+
"expected dependency to be unchanged (green) but it was changed (red)",
89+
);
90+
} else if !green && is_green {
91+
tcx.dcx().span_fatal(
92+
span,
93+
"expected dependency to have changed (red) but it was unchanged (green)",
94+
);
95+
}
96+
}
4197

4298
#[allow(missing_docs)]
4399
pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTracker)) {
44100
tcx.dep_graph.with_ignore(|| {
45101
if tcx.sess.opts.incremental.is_none() {
46102
return;
47103
}
104+
check_rdr_test_attrs(tcx);
48105

49106
let available_cgus = tcx
50107
.collect_and_partition_mono_items(())
@@ -111,7 +168,7 @@ impl<'tcx> AssertModuleSource<'tcx> {
111168
self.tcx.dcx().emit_fatal(errors::MissingQueryDepGraph { span });
112169
}
113170

114-
if !self.check_config(cfg) {
171+
if !check_config(self.tcx, cfg) {
115172
debug!("check_attr: config does not match, ignoring attr");
116173
return;
117174
}
@@ -167,19 +224,19 @@ impl<'tcx> AssertModuleSource<'tcx> {
167224
);
168225
}
169226
}
227+
}
170228

171-
/// Scan for a `cfg="foo"` attribute and check whether we have a
172-
/// cfg flag called `foo`.
173-
fn check_config(&self, value: Symbol) -> bool {
174-
let config = &self.tcx.sess.psess.config;
175-
debug!("check_config(config={:?}, value={:?})", config, value);
176-
if config.iter().any(|&(name, _)| name == value) {
177-
debug!("check_config: matched");
178-
return true;
179-
}
180-
debug!("check_config: no match found");
181-
false
229+
/// Scan for a `cfg="foo"` attribute and check whether we have a
230+
/// cfg flag called `foo`.
231+
fn check_config(tcx: TyCtxt<'_>, value: Symbol) -> bool {
232+
let config = &tcx.sess.psess.config;
233+
debug!("check_config(config={:?}, value={:?})", config, value);
234+
if config.iter().any(|&(name, _)| name == value) {
235+
debug!("check_config: matched");
236+
return true;
182237
}
238+
debug!("check_config: no match found");
239+
false
183240
}
184241

185242
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
772772
rustc_attr!(TEST, rustc_partition_reused),
773773
rustc_attr!(TEST, rustc_partition_codegened),
774774
rustc_attr!(TEST, rustc_expected_cgu_reuse),
775+
rustc_attr!(TEST, rustc_public_hash_changed),
776+
rustc_attr!(TEST, rustc_public_hash_unchanged),
775777
rustc_attr!(TEST, rustc_dump_symbol_name),
776778
rustc_attr!(TEST, rustc_dump_def_path),
777779
rustc_attr!(TEST, rustc_mir),

compiler/rustc_hir/src/attrs/data_structures.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ pub enum CguFields {
6969
ExpectedCguReuse { cfg: Symbol, module: Symbol, kind: CguKind },
7070
}
7171

72+
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
73+
pub struct RDRFields {
74+
pub crate_name: Symbol,
75+
pub cfg: Symbol,
76+
pub changed: bool,
77+
}
78+
7279
#[derive(Copy, Clone, PartialEq, Debug, PrintAttribute)]
7380
#[derive(HashStable_Generic, Encodable, Decodable)]
7481
pub enum DivergingFallbackBehavior {
@@ -1558,6 +1565,9 @@ pub enum AttributeKind {
15581565
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
15591566
RustcPubTransparent(Span),
15601567

1568+
/// Represents `#[rustc_public_hash_changed]` and `#[rustc_public_hash_unchanged]`.
1569+
RustcRDRTestAttr(ThinVec<(Span, RDRFields)>),
1570+
15611571
/// Represents `#[rustc_reallocator]`
15621572
RustcReallocator,
15631573

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ impl AttributeKind {
177177
RustcPreserveUbChecks => No,
178178
RustcProcMacroDecls => No,
179179
RustcPubTransparent(..) => Yes,
180+
RustcRDRTestAttr(..) => No,
180181
RustcReallocator => No,
181182
RustcRegions => No,
182183
RustcReservationImpl(..) => Yes,

compiler/rustc_passes/src/check_attr.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
341341
| AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
342342
| AttributeKind::RustcPreserveUbChecks
343343
| AttributeKind::RustcProcMacroDecls
344+
| AttributeKind::RustcRDRTestAttr(..)
344345
| AttributeKind::RustcReallocator
345346
| AttributeKind::RustcRegions
346347
| AttributeKind::RustcReservationImpl(..)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//! ICH - Incremental Compilation Hash
2+
3+
use rustc_span::{Symbol, sym};
4+
5+
pub use self::hcx::StableHashingContext;
6+
7+
mod hcx;
8+
mod impls_syntax;
9+
10+
pub const IGNORED_ATTRIBUTES: &[Symbol] = &[
11+
sym::cfg_trace, // FIXME should this really be ignored?
12+
sym::rustc_if_this_changed,
13+
sym::rustc_then_this_would_need,
14+
sym::rustc_dirty,
15+
sym::rustc_clean,
16+
sym::rustc_partition_reused,
17+
sym::rustc_partition_codegened,
18+
sym::rustc_expected_cgu_reuse,
19+
sym::rustc_public_hash_unchanged,
20+
sym::rustc_public_hash_changed,
21+
];

compiler/rustc_span/src/symbol.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1794,6 +1794,8 @@ symbols! {
17941794
rustc_proc_macro_decls,
17951795
rustc_promotable,
17961796
rustc_pub_transparent,
1797+
rustc_public_hash_changed,
1798+
rustc_public_hash_unchanged,
17971799
rustc_reallocator,
17981800
rustc_regions,
17991801
rustc_reservation_impl,

0 commit comments

Comments
 (0)