Skip to content

Commit e96550d

Browse files
committed
Auto merge of #155871 - susitsm:relink-dont-rebuild-base, r=<try>
Relink don't rebuild: add a baseline, sound implementation that can be incrementally improved
2 parents 029c9e1 + f5bb135 commit e96550d

29 files changed

Lines changed: 2107 additions & 707 deletions

File tree

compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs

Lines changed: 86 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ use rustc_feature::AttributeStability;
55
use rustc_hir::LangItem;
66
use rustc_hir::attrs::{
77
BorrowckGraphvizFormatKind, CguFields, CguKind, DivergingBlockBehavior,
8-
DivergingFallbackBehavior, RustcCleanAttribute, RustcCleanQueries, RustcMirKind,
8+
DivergingFallbackBehavior, RDRFields, RustcCleanAttribute, RustcCleanQueries, RustcMirKind,
99
};
1010
use rustc_span::Symbol;
1111

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

1919
pub(crate) struct RustcMainParser;
@@ -226,11 +226,11 @@ fn parse_cgu_fields(
226226
}
227227

228228
let Some((cfg, _)) = cfg else {
229-
cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
229+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
230230
return None;
231231
};
232232
let Some((module, _)) = module else {
233-
cx.emit_err(CguFieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module });
233+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::module });
234234
return None;
235235
};
236236
let kind = if let Some((kind, span)) = kind {
@@ -250,11 +250,7 @@ fn parse_cgu_fields(
250250
} else {
251251
// return None so that an unwrap for the attributes that need it is ok.
252252
if accepts_kind {
253-
cx.emit_err(CguFieldsMissing {
254-
span: args.span,
255-
name: &cx.attr_path,
256-
field: sym::kind,
257-
});
253+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::kind });
258254
return None;
259255
};
260256

@@ -264,6 +260,87 @@ fn parse_cgu_fields(
264260
Some((cfg, module, kind))
265261
}
266262

263+
fn parse_rdr_fields(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<(Symbol, Symbol)> {
264+
let args = cx.expect_list(args, cx.attr_span)?;
265+
266+
let mut cfg = None::<(Symbol, Span)>;
267+
let mut crate_name = None::<(Symbol, Span)>;
268+
269+
for arg in args.mixed() {
270+
let Some((ident, arg)) = cx.expect_name_value(arg, arg.span(), None) else {
271+
continue;
272+
};
273+
274+
let res = match ident.name {
275+
sym::cfg => &mut cfg,
276+
sym::crate_name => &mut crate_name,
277+
_ => {
278+
cx.adcx().expected_specific_argument(ident.span, &[sym::cfg, sym::crate_name]);
279+
continue;
280+
}
281+
};
282+
283+
let Some(str) = arg.value_as_str() else {
284+
cx.adcx().expected_string_literal(arg.value_span, Some(arg.value_as_lit()));
285+
continue;
286+
};
287+
288+
if res.is_some() {
289+
cx.adcx().duplicate_key(ident.span.to(arg.args_span()), ident.name);
290+
continue;
291+
}
292+
293+
*res = Some((str, arg.value_span));
294+
}
295+
296+
let Some((cfg, _)) = cfg else {
297+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::cfg });
298+
return None;
299+
};
300+
let Some((crate_name, _)) = crate_name else {
301+
cx.emit_err(FieldsMissing { span: args.span, name: &cx.attr_path, field: sym::crate_name });
302+
return None;
303+
};
304+
305+
Some((cfg, crate_name))
306+
}
307+
308+
#[derive(Default)]
309+
pub(crate) struct RustcRDRTestAttributeParser {
310+
items: ThinVec<(Span, RDRFields)>,
311+
}
312+
313+
impl AttributeParser for RustcRDRTestAttributeParser {
314+
const ATTRIBUTES: AcceptMapping<Self> = &[
315+
(
316+
&[sym::rustc_public_hash_changed],
317+
template!(List: &[r#"cfg = "...", crate_name = "...""#]),
318+
unstable!(rustc_attrs),
319+
|this, cx, args| {
320+
this.items.extend(parse_rdr_fields(cx, args).map(|(cfg, crate_name)| {
321+
(cx.attr_span, RDRFields { cfg, crate_name, changed: true })
322+
}));
323+
},
324+
),
325+
(
326+
&[sym::rustc_public_hash_unchanged],
327+
template!(List: &[r#"cfg = "...", crate_name = "...""#]),
328+
unstable!(rustc_attrs),
329+
|this, cx, args| {
330+
this.items.extend(parse_rdr_fields(cx, args).map(|(cfg, crate_name)| {
331+
(cx.attr_span, RDRFields { cfg, crate_name, changed: false })
332+
}));
333+
},
334+
),
335+
];
336+
337+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
338+
339+
fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
340+
Some(AttributeKind::RustcRDRTestAttr(self.items))
341+
}
342+
}
343+
267344
#[derive(Default)]
268345
pub(crate) struct RustcCguTestAttributeParser {
269346
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
@@ -150,6 +150,7 @@ attribute_parsers!(
150150
RustcAlignParser,
151151
RustcAlignStaticParser,
152152
RustcCguTestAttributeParser,
153+
RustcRDRTestAttributeParser,
153154
StabilityParser,
154155
UsedParser,
155156
// 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
@@ -49,7 +49,7 @@ pub(crate) struct DocAliasStartEnd<'a> {
4949

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

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,8 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[
503503
sym::rustc_partition_reused,
504504
sym::rustc_partition_codegened,
505505
sym::rustc_expected_cgu_reuse,
506+
sym::rustc_public_hash_changed,
507+
sym::rustc_public_hash_unchanged,
506508
sym::rustc_dump_symbol_name,
507509
sym::rustc_dump_def_path,
508510
sym::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, StableHash, 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(StableHash, Encodable, Decodable)]
7481
pub enum DivergingFallbackBehavior {
@@ -1520,6 +1527,9 @@ pub enum AttributeKind {
15201527
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
15211528
RustcPubTransparent(Span),
15221529

1530+
/// Represents `#[rustc_public_hash_changed]` and `#[rustc_public_hash_unchanged]`.
1531+
RustcRDRTestAttr(ThinVec<(Span, RDRFields)>),
1532+
15231533
/// Represents `#[rustc_reallocator]`
15241534
RustcReallocator,
15251535

compiler/rustc_hir/src/attrs/encode_cross_crate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ impl AttributeKind {
175175
RustcPreserveUbChecks => No,
176176
RustcProcMacroDecls => No,
177177
RustcPubTransparent(..) => Yes,
178+
RustcRDRTestAttr(..) => No,
178179
RustcReallocator => No,
179180
RustcRegions => No,
180181
RustcReservationImpl(..) => Yes,

compiler/rustc_incremental/src/persist/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod data;
77
mod file_format;
88
mod fs;
99
mod load;
10+
mod rdr_hashes;
1011
mod save;
1112
mod work_product;
1213

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use rustc_hir::attrs::RDRFields;
2+
use rustc_hir::def_id::LOCAL_CRATE;
3+
use rustc_hir::find_attr;
4+
use rustc_macros::Diagnostic;
5+
use rustc_middle::dep_graph::{DepKind, DepNode};
6+
use rustc_middle::ty::TyCtxt;
7+
use rustc_span::{Span, Symbol};
8+
use tracing::debug;
9+
10+
pub(crate) fn check_rdr_test_attrs(tcx: TyCtxt<'_>) {
11+
if !tcx.sess.opts.unstable_opts.query_dep_graph {
12+
return;
13+
}
14+
// can't add the attributes without opting into this feature
15+
if !tcx.features().rustc_attrs() {
16+
return;
17+
}
18+
for &(span, fields) in
19+
find_attr!(tcx.hir_attrs(rustc_hir::CRATE_HIR_ID), RustcRDRTestAttr(e) => e)
20+
.into_iter()
21+
.flatten()
22+
{
23+
assert_dependency_public_hash(tcx, span, fields);
24+
}
25+
}
26+
27+
#[derive(Diagnostic)]
28+
#[diag("found rdr hash attribute but `-Zquery-dep-graph` was not specified")]
29+
pub(crate) struct MissingQueryDepGraph {
30+
#[primary_span]
31+
pub span: Span,
32+
}
33+
34+
/// Scan for a `cfg="foo"` attribute and check whether we have a
35+
/// cfg flag called `foo`.
36+
fn check_config(tcx: TyCtxt<'_>, value: Symbol) -> bool {
37+
let config = &tcx.sess.config;
38+
debug!("check_config(config={:?}, value={:?})", config, value);
39+
if config.iter().any(|&(name, _)| name == value) {
40+
debug!("check_config: matched");
41+
return true;
42+
}
43+
debug!("check_config: no match found");
44+
false
45+
}
46+
47+
fn assert_dependency_public_hash(tcx: TyCtxt<'_>, span: Span, fields: RDRFields) {
48+
if !tcx.sess.opts.unstable_opts.query_dep_graph {
49+
tcx.dcx().emit_fatal(MissingQueryDepGraph { span });
50+
}
51+
52+
let crate_num = tcx
53+
.crates(())
54+
.iter()
55+
.copied()
56+
.find(|&cnum| tcx.crate_name(cnum).as_str() == fields.crate_name.as_str())
57+
.unwrap_or_else(|| {
58+
tcx.dcx().span_fatal(
59+
span,
60+
format!("crate `{}` not found in dependencies", fields.crate_name),
61+
)
62+
});
63+
if crate_num == LOCAL_CRATE {
64+
tcx.dcx().span_fatal(span, "expected the name of a dependency crate");
65+
}
66+
67+
if !check_config(tcx, fields.cfg) {
68+
debug!("check_attr: config does not match, ignoring attr");
69+
return;
70+
}
71+
72+
let green = !fields.changed;
73+
let dep_node = DepNode::construct(tcx, DepKind::public_api_hash, &crate_num);
74+
let is_green = tcx.dep_graph.is_green(&dep_node);
75+
let is_red = tcx.dep_graph.is_red(&dep_node);
76+
if !is_red && !is_green {
77+
tcx.dcx().span_fatal(span, "dependency color is neither red or green!");
78+
}
79+
80+
if green && !is_green {
81+
tcx.dcx().span_fatal(
82+
span,
83+
"expected dependency to be unchanged (green) but it was changed (red)",
84+
);
85+
} else if !green && is_green {
86+
tcx.dcx().span_fatal(
87+
span,
88+
"expected dependency to have changed (red) but it was unchanged (green)",
89+
);
90+
}
91+
}

compiler/rustc_incremental/src/persist/save.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use tracing::debug;
1111

1212
use super::data::*;
1313
use super::fs::*;
14-
use super::{clean, file_format, work_product};
14+
use super::{clean, file_format, rdr_hashes, work_product};
1515
use crate::assert_dep_graph::assert_dep_graph;
1616
use crate::errors;
1717

@@ -40,6 +40,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) {
4040

4141
sess.time("assert_dep_graph", || assert_dep_graph(tcx));
4242
sess.time("check_clean", || clean::check_clean_annotations(tcx));
43+
sess.time("check_rdr_hashes", || rdr_hashes::check_rdr_test_attrs(tcx));
4344

4445
par_join(
4546
move || {

compiler/rustc_metadata/src/creader.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
119119
for (cnum, data) in self.0.iter_crate_data() {
120120
writeln!(fmt, " name: {}", data.name())?;
121121
writeln!(fmt, " cnum: {cnum}")?;
122-
writeln!(fmt, " hash: {}", data.hash())?;
122+
writeln!(fmt, " private hash: {}", data.private_hash())?;
123+
writeln!(fmt, " public hash: {}", data.public_hash())?;
123124
writeln!(fmt, " reqd: {:?}", data.dep_kind())?;
124125
writeln!(fmt, " priv: {:?}", data.is_private_dep())?;
125126
let CrateSource { dylib, rlib, rmeta, sdylib_interface } = data.source();
@@ -554,19 +555,23 @@ impl CStore {
554555
}
555556
}
556557

557-
fn existing_match(&self, name: Symbol, hash: Option<Svh>) -> Option<CrateNum> {
558-
let hash = hash?;
558+
fn existing_match(&self, name: Symbol, public_hash: Option<Svh>) -> Option<CrateNum> {
559+
let public_hash = public_hash?;
559560

560561
for (cnum, data) in self.iter_crate_data() {
561562
if data.name() != name {
562563
trace!("{} did not match {}", data.name(), name);
563564
continue;
564565
}
565566

566-
if hash == data.hash() {
567+
if public_hash == data.public_hash() {
567568
return Some(cnum);
568569
} else {
569-
debug!("actual hash {} did not match expected {}", hash, data.hash());
570+
debug!(
571+
"actual public hash {} did not match expected {}",
572+
public_hash,
573+
data.public_hash()
574+
);
570575
}
571576
}
572577

@@ -609,7 +614,15 @@ impl CStore {
609614

610615
let Library { source, metadata } = lib;
611616
let crate_root = metadata.get_root();
612-
let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash());
617+
let host_hash = host_lib.as_ref().map(|lib| {
618+
let host_root = lib.metadata.get_root();
619+
assert_eq!(
620+
host_root.public_hash(),
621+
host_root.private_hash(),
622+
"Mismatched public and private hash for proc macro!"
623+
);
624+
host_root.public_hash()
625+
});
613626
let private_dep = self.is_private_dep(&tcx.sess.opts.externs, name, private_dep);
614627

615628
// Claim this crate number and cache it
@@ -880,7 +893,8 @@ impl CStore {
880893
let root = library.metadata.get_root();
881894
let mut result = LoadResult::Loaded(library);
882895
for (cnum, data) in self.iter_crate_data() {
883-
if data.name() == root.name() && root.hash() == data.hash() {
896+
if data.name() == root.name() && root.public_hash() == data.public_hash() {
897+
assert!(root.private_hash() == data.private_hash());
884898
assert!(locator.hash.is_none());
885899
info!("load success, going to previous cnum: {}", cnum);
886900
result = LoadResult::Previous(cnum);

0 commit comments

Comments
 (0)