diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs index 7ac00b4609aaf..88795679943fb 100644 --- a/compiler/rustc_incremental/src/persist/dirty_clean.rs +++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs @@ -9,6 +9,13 @@ //! - `#[rustc_clean(cfg="rev2")]` same as above, except that the //! fingerprints must be the SAME (along with all other fingerprints). //! +//! - `#[rustc_clean(cfg="rev2", loaded_from_disk='typeck")]` asserts that +//! the query result for `DepNode::typeck(X)` was actually +//! loaded from disk (not just marked green). This can be useful +//! to ensure that a test is actually exercising the deserialization +//! logic for a particular query result. This can be combined with +//! `except` +//! //! Errors are reported if we are in the suitable configuration but //! the required condition is not met. @@ -28,6 +35,7 @@ use rustc_span::Span; use std::iter::FromIterator; use std::vec::Vec; +const LOADED_FROM_DISK: Symbol = sym::loaded_from_disk; const EXCEPT: Symbol = sym::except; const CFG: Symbol = sym::cfg; @@ -124,6 +132,7 @@ type Labels = FxHashSet; struct Assertion { clean: Labels, dirty: Labels, + loaded_from_disk: Labels, } pub fn check_dirty_clean_annotations(tcx: TyCtxt<'_>) { @@ -174,6 +183,7 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { fn assertion_auto(&mut self, item_id: LocalDefId, attr: &Attribute) -> Assertion { let (name, mut auto) = self.auto_labels(item_id, attr); let except = self.except(attr); + let loaded_from_disk = self.loaded_from_disk(attr); for e in except.iter() { if !auto.remove(e) { let msg = format!( @@ -183,7 +193,19 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { self.tcx.sess.span_fatal(attr.span, &msg); } } - Assertion { clean: auto, dirty: except } + Assertion { clean: auto, dirty: except, loaded_from_disk } + } + + /// `loaded_from_disk=` attribute value + fn loaded_from_disk(&self, attr: &Attribute) -> Labels { + for item in attr.meta_item_list().unwrap_or_else(Vec::new) { + if item.has_name(LOADED_FROM_DISK) { + let value = expect_associated_value(self.tcx, &item); + return self.resolve_labels(&item, value); + } + } + // If `loaded_from_disk=` is not specified, don't assert anything + Labels::default() } /// `except=` attribute value @@ -332,6 +354,18 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { } } + fn assert_loaded_from_disk(&self, item_span: Span, dep_node: DepNode) { + debug!("assert_loaded_from_disk({:?})", dep_node); + + if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) { + let dep_node_str = self.dep_node_str(&dep_node); + self.tcx.sess.span_err( + item_span, + &format!("`{}` should have been loaded from disk but it was not", dep_node_str), + ); + } + } + fn check_item(&mut self, item_id: LocalDefId, item_span: Span) { let def_path_hash = self.tcx.def_path_hash(item_id.to_def_id()); for attr in self.tcx.get_attrs(item_id.to_def_id()).iter() { @@ -348,6 +382,10 @@ impl<'tcx> DirtyCleanVisitor<'tcx> { let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); self.assert_dirty(item_span, dep_node); } + for label in assertion.loaded_from_disk { + let dep_node = DepNode::from_label_string(self.tcx, &label, def_path_hash).unwrap(); + self.assert_loaded_from_disk(item_span, dep_node); + } } } } @@ -382,7 +420,7 @@ fn check_config(tcx: TyCtxt<'_>, attr: &Attribute) -> bool { let value = expect_associated_value(tcx, &item); debug!("check_config: searching for cfg {:?}", value); cfg = Some(config.contains(&(value, None))); - } else if !item.has_name(EXCEPT) { + } else if !(item.has_name(EXCEPT) || item.has_name(LOADED_FROM_DISK)) { tcx.sess.span_err(attr.span, &format!("unknown item `{}`", item.name_or_empty())); } } diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index a8be1ca34c04f..7bc3fd718e009 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -88,6 +88,11 @@ struct DepGraphData { previous_work_products: FxHashMap, dep_node_debug: Lock, String>>, + + /// Used by incremental compilation tests to assert that + /// a particular query result was decoded from disk + /// (not just marked green) + debug_loaded_from_disk: Lock>>, } pub fn hash_result(hcx: &mut StableHashingContext<'_>, result: &R) -> Fingerprint @@ -135,6 +140,7 @@ impl DepGraph { processed_side_effects: Default::default(), previous: prev_graph, colors: DepNodeColorMap::new(prev_graph_node_count), + debug_loaded_from_disk: Default::default(), })), virtual_dep_node_index: Lrc::new(AtomicU32::new(0)), } @@ -438,6 +444,14 @@ impl DepGraph { &self.data.as_ref().unwrap().previous_work_products } + pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) { + self.data.as_ref().unwrap().debug_loaded_from_disk.lock().insert(dep_node); + } + + pub fn debug_was_loaded_from_disk(&self, dep_node: DepNode) -> bool { + self.data.as_ref().unwrap().debug_loaded_from_disk.lock().contains(&dep_node) + } + #[inline(always)] pub fn register_dep_node_debug_str(&self, dep_node: DepNode, debug_str_gen: F) where diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index b08db39e245a8..33732f9df7348 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -519,6 +519,10 @@ where prof_timer.finish_with_query_invocation_id(dep_node_index.into()); if let Some(result) = result { + if unlikely!(tcx.dep_context().sess().opts.debugging_opts.query_dep_graph) { + dep_graph.mark_debug_loaded_from_disk(*dep_node) + } + let prev_fingerprint = tcx .dep_context() .dep_graph() diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 51a7a2644f695..84cf8878af809 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -788,6 +788,7 @@ symbols! { literal, llvm_asm, load, + loaded_from_disk, local, local_inner_macros, log10f32, diff --git a/src/test/incremental/change_private_fn/struct_point.rs b/src/test/incremental/change_private_fn/struct_point.rs index 1791c089cfa8b..d57267adc6bdc 100644 --- a/src/test/incremental/change_private_fn/struct_point.rs +++ b/src/test/incremental/change_private_fn/struct_point.rs @@ -51,7 +51,11 @@ pub mod point { pub mod fn_calls_methods_in_same_impl { use point::Point; - #[rustc_clean(cfg="cfail2")] + // The cached result should actually be loaded from disk + // (not just marked green) - for example, `DeadVisitor` + // always runs during compilation as a "pass", and loads + // the typeck results for bodies. + #[rustc_clean(cfg="cfail2", loaded_from_disk="typeck")] pub fn check() { let x = Point { x: 2.0, y: 2.0 }; x.distance_from_origin();