Skip to content

Commit f1d6823

Browse files
committed
Add #[rustc_clean(loaded_from_disk)] to assert loading of query result
Currently, you can use `#[rustc_clean]` to assert to that a particular query (technically, a `DepNode`) is green or red. However, a green `DepNode` does not mean that the query result was actually deserialized from disk - we might have never re-run a query that needed the result. Some incremental tests are written as regression tests for ICEs that occured during query result decoding. Using `#[rustc_clean(loaded_from_disk="typeck")]`, you can now assert that the result of a particular query (e.g. `typeck`) was actually loaded from disk, in addition to being green.
1 parent e100ec5 commit f1d6823

File tree

5 files changed

+64
-3
lines changed

5 files changed

+64
-3
lines changed

Diff for: compiler/rustc_incremental/src/persist/dirty_clean.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
//! - `#[rustc_clean(cfg="rev2")]` same as above, except that the
1010
//! fingerprints must be the SAME (along with all other fingerprints).
1111
//!
12+
//! - `#[rustc_clean(cfg="rev2", loaded_from_disk='typeck")]` asserts that
13+
//! the query result for `DepNode::typeck(X)` was actually
14+
//! loaded from disk (not just marked green). This can be useful
15+
//! to ensure that a test is actually exercising the deserialization
16+
//! logic for a particular query result. This can be combined with
17+
//! `except`
18+
//!
1219
//! Errors are reported if we are in the suitable configuration but
1320
//! the required condition is not met.
1421
@@ -28,6 +35,7 @@ use rustc_span::Span;
2835
use std::iter::FromIterator;
2936
use std::vec::Vec;
3037

38+
const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk;
3139
const EXCEPT: Symbol = sym::except;
3240
const CFG: Symbol = sym::cfg;
3341

@@ -124,6 +132,7 @@ type Labels = FxHashSet<String>;
124132
struct Assertion {
125133
clean: Labels,
126134
dirty: Labels,
135+
loaded_from_disk: Labels,
127136
}
128137

129138
pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) {
@@ -174,6 +183,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
174183
fn assertion_auto(&mut self, item_id: LocalDefId, attr: &Attribute) -> Assertion {
175184
let (name, mut auto) = self.auto_labels(item_id, attr);
176185
let except = self.except(attr);
186+
let loaded_from_disk = self.loaded_from_disk(attr);
177187
for e in except.iter() {
178188
if !auto.remove(e) {
179189
let msg = format!(
@@ -183,7 +193,19 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
183193
self.tcx.sess.span_fatal(attr.span, &msg);
184194
}
185195
}
186-
Assertion { clean: auto, dirty: except }
196+
Assertion { clean: auto, dirty: except, loaded_from_disk }
197+
}
198+
199+
/// `loaded_from_disk=` attribute value
200+
fn loaded_from_disk(&self, attr: &Attribute) -> Labels {
201+
for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
202+
if item.has_name(LOADED_FROM_DISK) {
203+
let value = expect_associated_value(self.tcx, &item);
204+
return self.resolve_labels(&item, value);
205+
}
206+
}
207+
// If `loaded_from_disk=` is not specified, don't assert anything
208+
Labels::default()
187209
}
188210

189211
/// `except=` attribute value
@@ -332,6 +354,18 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
332354
}
333355
}
334356

357+
fn assert_loaded_from_disk(&self, item_span: Span, dep_node: DepNode) {
358+
debug!("assert_loaded_from_disk({:?})", dep_node);
359+
360+
if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
361+
let dep_node_str = self.dep_node_str(&dep_node);
362+
self.tcx.sess.span_err(
363+
item_span,
364+
&format!("`{}` should have been loaded from disk but it was not", dep_node_str),
365+
);
366+
}
367+
}
368+
335369
fn check_item(&mut self, item_id: LocalDefId, item_span: Span) {
336370
let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id());
337371
for attr in self.tcx.get_attrs(item_id.to_def_id()).iter() {
@@ -348,6 +382,10 @@ impl<'tcx> DirtyCleanVisitor<'tcx> {
348382
let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
349383
self.assert_dirty(item_span, dep_node);
350384
}
385+
for label in assertion.loaded_from_disk {
386+
let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap();
387+
self.assert_loaded_from_disk(item_span, dep_node);
388+
}
351389
}
352390
}
353391
}
@@ -382,7 +420,7 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool {
382420
let value = expect_associated_value(tcx, &item);
383421
debug!("check_config: searching for cfg {:?}", value);
384422
cfg = Some(config.contains(&(value, None)));
385-
} else if !item.has_name(EXCEPT) {
423+
} else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) {
386424
tcx.sess.span_err(attr.span, &format!("unknown item `{}`", item.name_or_empty()));
387425
}
388426
}

Diff for: compiler/rustc_query_system/src/dep_graph/graph.rs

+14
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ struct DepGraphData<K: DepKind> {
8888
previous_work_products: FxHashMap<WorkProductId, WorkProduct>,
8989

9090
dep_node_debug: Lock<FxHashMap<DepNode<K>, String>>,
91+
92+
/// Used by incremental compilation tests to assert that
93+
/// a particular query result was decoded from disk
94+
/// (not just marked green)
95+
debug_loaded_from_disk: Lock<FxHashSet<DepNode<K>>>,
9196
}
9297

9398
pub fn hash_result<R>(hcx: &mut StableHashingContext<'_>, result: &R) -> Fingerprint
@@ -135,6 +140,7 @@ impl<K: DepKind> DepGraph<K> {
135140
processed_side_effects: Default::default(),
136141
previous: prev_graph,
137142
colors: DepNodeColorMap::new(prev_graph_node_count),
143+
debug_loaded_from_disk: Default::default(),
138144
})),
139145
virtual_dep_node_index: Lrc::new(AtomicU32::new(0)),
140146
}
@@ -438,6 +444,14 @@ impl<K: DepKind> DepGraph<K> {
438444
&self.data.as_ref().unwrap().previous_work_products
439445
}
440446

447+
pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode<K>) {
448+
self.data.as_ref().unwrap().debug_loaded_from_disk.lock().insert(dep_node);
449+
}
450+
451+
pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode<K>) -> bool {
452+
self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node)
453+
}
454+
441455
#[inline(always)]
442456
pub fn register_dep_node_debug_str<F>(&self, dep_node: DepNode<K>, debug_str_gen: F)
443457
where

Diff for: compiler/rustc_query_system/src/query/plumbing.rs

+4
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,10 @@ where
519519
prof_timer.finish_with_query_invocation_id(dep_node_index.into());
520520

521521
if let Some(result) = result {
522+
if unlikely!(tcx.dep_context().sess().opts.debugging_opts.query_dep_graph) {
523+
dep_graph.mark_debug_loaded_from_disk(*dep_node)
524+
}
525+
522526
let prev_fingerprint = tcx
523527
.dep_context()
524528
.dep_graph()

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ symbols! {
788788
literal,
789789
llvm_asm,
790790
load,
791+
loaded_from_disk,
791792
local,
792793
local_inner_macros,
793794
log10f32,

Diff for: src/test/incremental/change_private_fn/struct_point.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ pub mod point {
5151
pub mod fn_calls_methods_in_same_impl {
5252
use point::Point;
5353

54-
#[rustc_clean(cfg="cfail2")]
54+
// The cached result should actually be loaded from disk
55+
// (not just marked green) - for example, `DeadVisitor`
56+
// always runs during compilation as a "pass", and loads
57+
// the typeck results for bodies.
58+
#[rustc_clean(cfg="cfail2", loaded_from_disk="typeck")]
5559
pub fn check() {
5660
let x = Point { x: 2.0, y: 2.0 };
5761
x.distance_from_origin();

0 commit comments

Comments
 (0)