diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 1ad9bbc3b4b30..b13361b3e6a5e 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -373,6 +373,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { no_hash, anon, eval_always, + feedable, depth_limit, separate_provide_extern, return_result_from_ensure_ok, diff --git a/compiler/rustc_middle/src/dep_graph/mod.rs b/compiler/rustc_middle/src/dep_graph/mod.rs index 049e868879e96..37b0d2d8a695f 100644 --- a/compiler/rustc_middle/src/dep_graph/mod.rs +++ b/compiler/rustc_middle/src/dep_graph/mod.rs @@ -52,6 +52,7 @@ impl Deps for DepsType { const DEP_KIND_RED: DepKind = dep_kinds::Red; const DEP_KIND_SIDE_EFFECT: DepKind = dep_kinds::SideEffect; const DEP_KIND_ANON_ZERO_DEPS: DepKind = dep_kinds::AnonZeroDeps; + const DEP_KIND_TRAIT_SELECT: DepKind = dep_kinds::TraitSelect; const DEP_KIND_MAX: u16 = dep_node::DEP_KIND_VARIANTS - 1; } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index d6d1dc781f3e2..293522afc96fa 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -489,7 +489,11 @@ where } } -pub(crate) fn query_callback<'tcx, Q>(is_anon: bool, is_eval_always: bool) -> DepKindStruct<'tcx> +pub(crate) fn query_callback<'tcx, Q>( + is_anon: bool, + is_eval_always: bool, + is_feedable: bool, +) -> DepKindStruct<'tcx> where Q: QueryConfigRestored<'tcx>, { @@ -499,6 +503,7 @@ where return DepKindStruct { is_anon, is_eval_always, + is_feedable, fingerprint_style, force_from_dep_node: None, try_load_from_on_disk_cache: None, @@ -509,6 +514,7 @@ where DepKindStruct { is_anon, is_eval_always, + is_feedable, fingerprint_style, force_from_dep_node: Some(|tcx, dep_node, _| { force_from_dep_node(Q::config(tcx), tcx, dep_node) @@ -822,6 +828,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: false, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Unit, force_from_dep_node: Some(|_, dep_node, _| bug!("force_from_dep_node: encountered {:?}", dep_node)), try_load_from_on_disk_cache: None, @@ -834,6 +841,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: false, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Unit, force_from_dep_node: Some(|_, dep_node, _| bug!("force_from_dep_node: encountered {:?}", dep_node)), try_load_from_on_disk_cache: None, @@ -845,6 +853,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: false, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Unit, force_from_dep_node: Some(|tcx, _, prev_index| { tcx.dep_graph.force_diagnostic_node(QueryCtxt::new(tcx), prev_index); @@ -859,6 +868,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: true, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Opaque, force_from_dep_node: Some(|_, _, _| bug!("cannot force an anon node")), try_load_from_on_disk_cache: None, @@ -870,6 +880,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: true, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Unit, force_from_dep_node: None, try_load_from_on_disk_cache: None, @@ -881,6 +892,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: false, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Opaque, force_from_dep_node: None, try_load_from_on_disk_cache: None, @@ -892,6 +904,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: false, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Opaque, force_from_dep_node: None, try_load_from_on_disk_cache: None, @@ -903,6 +916,7 @@ macro_rules! define_queries { DepKindStruct { is_anon: false, is_eval_always: false, + is_feedable: false, fingerprint_style: FingerprintStyle::Unit, force_from_dep_node: None, try_load_from_on_disk_cache: None, @@ -914,6 +928,7 @@ macro_rules! define_queries { $crate::plumbing::query_callback::>( is_anon!([$($modifiers)*]), is_eval_always!([$($modifiers)*]), + feedable!([$($modifiers)*]), ) })* } diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index bdd1d5f3e88a9..b6a6198e2afce 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -237,6 +237,8 @@ pub struct DepKindStruct { /// cached within one compiler invocation. pub is_eval_always: bool, + pub is_feedable: bool, + /// Whether the query key can be recovered from the hashed fingerprint. /// See [DepNodeParams] trait for the behaviour of each key type. pub fingerprint_style: FingerprintStyle, diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index f0cc9636b75c2..230005fdaf5ee 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -356,7 +356,7 @@ impl DepGraphData { (with_deps(TaskDepsRef::Allow(&task_deps)), task_deps.into_inner().reads) }; - let dcx = cx.dep_context(); + let dcx = *cx.dep_context(); let dep_node_index = self.hash_result_and_alloc_node(dcx, key, edges, &result, hash_result); (result, dep_node_index) @@ -444,7 +444,7 @@ impl DepGraphData { /// Intern the new `DepNode` with the dependencies up-to-now. fn hash_result_and_alloc_node, R>( &self, - cx: &Ctxt, + cx: Ctxt, node: DepNode, edges: EdgesVec, result: &R, @@ -454,7 +454,7 @@ impl DepGraphData { let current_fingerprint = hash_result.map(|hash_result| { cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result)) }); - let dep_node_index = self.alloc_and_color_node(node, edges, current_fingerprint); + let dep_node_index = self.alloc_and_color_node(cx, node, edges, current_fingerprint); hashing_timer.finish_with_query_invocation_id(dep_node_index.into()); dep_node_index } @@ -608,7 +608,7 @@ impl DepGraph { } }); - data.hash_result_and_alloc_node(&cx, node, edges, result, hash_result) + data.hash_result_and_alloc_node(cx, node, edges, result, hash_result) } else { // Incremental compilation is turned off. We just execute the task // without tracking. We still provide a dep-node index that uniquely @@ -726,13 +726,101 @@ impl DepGraphData { }) } - fn alloc_and_color_node( + // Checks if the node is expected to be green after execution. + fn post_exec_expect_green>( &self, + cx: Ctxt, + kind: DepKind, + dep_node_index: SerializedDepNodeIndex, + frame: Option<&MarkFrame<'_>>, + ) -> bool { + let frame = MarkFrame { index: dep_node_index, parent: frame }; + + if cx.is_eval_always(kind) { + // We don't know the dependencies of these. + return false; + } + + if cx.dep_kind_info(kind).is_feedable { + // Avoid assumptions about feedable nodes, possibly an implementation error. + return false; + } + + let prev_deps = self.previous.edge_targets_from(dep_node_index); + + for dep_dep_node_index in prev_deps { + match self.colors.get(dep_dep_node_index) { + DepNodeColor::Green(_) => {} + DepNodeColor::Red => return false, + DepNodeColor::Unknown => { + let dep_node = self.previous.index_to_node(dep_dep_node_index); + + if cx.dep_kind_info(dep_node.kind).is_anon { + if dep_node.kind == ::Deps::DEP_KIND_TRAIT_SELECT { + // TraitSelect doesn't behave like other anon queries. Maybe a bug? + return false; + } + + if !self.post_exec_expect_green( + cx, + dep_node.kind, + dep_dep_node_index, + Some(&frame), + ) { + return false; + } + // The anon node is expected to be green, continue checking dependencies + } else { + if cx.sess().seen_cycle_error.load(Ordering::Acquire) { + // Cycle can leave uncolored, but execution still continues. + // Unsure how this happens. + return false; + } + + // Unexpected uncolored dependency, we expect either all green, or a red node + // to be present before any uncolored node. + + // Check if it's possible to color it currently. + let _ = self.try_mark_parent_green(cx, dep_dep_node_index, &frame); + let color = self.colors.get(dep_dep_node_index); + + eprintln!("post_exec_expect_green dep node stack:"); + + let mut i = 0; + let mut current = Some(&frame); + while let Some(frame) = current { + let node = self.previous.index_to_node(frame.index); + eprintln!("#{i} {node:?}"); + current = frame.parent; + i += 1; + } + + eprintln!("end of post_exec_expect_green dep node stack"); + + panic!( + "unexpected uncolored dependency {:?} found, can be marked as: {:?}", + self.previous.index_to_node(dep_dep_node_index), + color + ) + } + } + } + } + + // All dependencies were green or were expected to be, so this node is expected to be green too + true + } + + fn alloc_and_color_node>( + &self, + cx: Ctxt, key: DepNode, edges: EdgesVec, fingerprint: Option, ) -> DepNodeIndex { if let Some(prev_index) = self.previous.node_to_index_opt(&key) { + let expect_green = self.post_exec_expect_green(cx, key.kind, prev_index, None); + // Determine the color and index of the new `DepNode`. let is_green = if let Some(fingerprint) = fingerprint { if fingerprint == self.previous.fingerprint_by_index(prev_index) { @@ -740,6 +828,11 @@ impl DepGraphData { // its query was re-executed, and it has the same result as before. true } else { + assert!( + !expect_green, + "expected {:?} to be green, but fingerprint changed", + key, + ); // This is a red node: it existed in the previous compilation, its query // was re-executed, but it has a different result from before. false @@ -749,7 +842,7 @@ impl DepGraphData { // session, its query was re-executed, but it doesn't compute a result hash // (i.e. it represents a `no_hash` query), so we have no way of determining // whether or not the result was the same as before. - false + expect_green }; let fingerprint = fingerprint.unwrap_or(Fingerprint::ZERO); @@ -876,16 +969,16 @@ impl DepGraphData { // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its // dependencies green. - self.try_mark_previous_green(qcx, prev_index, dep_node, None) + self.try_mark_previous_green(*qcx.dep_context(), prev_index, dep_node, None) .map(|dep_node_index| (prev_index, dep_node_index)) } } } - #[instrument(skip(self, qcx, parent_dep_node_index, frame), level = "debug")] - fn try_mark_parent_green>( + #[instrument(skip(self, cx, parent_dep_node_index, frame), level = "debug")] + fn try_mark_parent_green>( &self, - qcx: Qcx, + cx: Ctxt, parent_dep_node_index: SerializedDepNodeIndex, frame: &MarkFrame<'_>, ) -> Option<()> { @@ -918,14 +1011,14 @@ impl DepGraphData { // We don't know the state of this dependency. If it isn't // an eval_always node, let's try to mark it green recursively. - if !qcx.dep_context().is_eval_always(dep_dep_node.kind) { + if !cx.is_eval_always(dep_dep_node.kind) { debug!( "state of dependency {:?} ({}) is unknown, trying to mark it green", dep_dep_node, dep_dep_node.hash, ); let node_index = - self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, Some(frame)); + self.try_mark_previous_green(cx, parent_dep_node_index, dep_dep_node, Some(frame)); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green"); @@ -935,7 +1028,7 @@ impl DepGraphData { // We failed to mark it green, so we try to force the query. debug!("trying to force dependency {dep_dep_node:?}"); - if !qcx.dep_context().try_force_from_dep_node(*dep_dep_node, parent_dep_node_index, frame) { + if !cx.try_force_from_dep_node(*dep_dep_node, parent_dep_node_index, frame) { // The DepNode could not be forced. debug!("dependency {dep_dep_node:?} could not be forced"); return None; @@ -953,7 +1046,7 @@ impl DepGraphData { DepNodeColor::Unknown => {} } - if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() { + if let None = cx.sess().dcx().has_errors_or_delayed_bugs() { panic!("try_mark_previous_green() - Forcing the DepNode should have set its color") } @@ -972,10 +1065,10 @@ impl DepGraphData { } /// Try to mark a dep-node which existed in the previous compilation session as green. - #[instrument(skip(self, qcx, prev_dep_node_index, frame), level = "debug")] - fn try_mark_previous_green>( + #[instrument(skip(self, cx, prev_dep_node_index, frame), level = "debug")] + fn try_mark_previous_green>( &self, - qcx: Qcx, + cx: Ctxt, prev_dep_node_index: SerializedDepNodeIndex, dep_node: &DepNode, frame: Option<&MarkFrame<'_>>, @@ -983,14 +1076,14 @@ impl DepGraphData { let frame = MarkFrame { index: prev_dep_node_index, parent: frame }; // We never try to mark eval_always nodes as green - debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); + debug_assert!(!cx.is_eval_always(dep_node.kind)); debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), *dep_node); let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); for dep_dep_node_index in prev_deps { - self.try_mark_parent_green(qcx, dep_dep_node_index, &frame)?; + self.try_mark_parent_green(cx, dep_dep_node_index, &frame)?; } // If we got here without hitting a `return` that means that all diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 8b9e4fe1bf29b..f920a7fc2168e 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -117,6 +117,8 @@ pub trait Deps: DynSync { /// We use this to create the anon node with zero dependencies. const DEP_KIND_ANON_ZERO_DEPS: DepKind; + const DEP_KIND_TRAIT_SELECT: DepKind; + /// This is the highest value a `DepKind` can have. It's used during encoding to /// pack information into the unused bits. const DEP_KIND_MAX: u16; diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 79d08d33c0b1c..f48361768ed74 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -3,6 +3,7 @@ use std::io::Write; use std::iter; use std::num::NonZero; use std::sync::Arc; +use std::sync::atomic::Ordering; use parking_lot::{Condvar, Mutex}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -539,6 +540,8 @@ pub fn report_cycle<'a>( sess: &'a Session, CycleError { usage, cycle: stack }: &CycleError, ) -> Diag<'a> { + sess.seen_cycle_error.store(true, Ordering::Release); + assert!(!stack.is_empty()); let span = stack[0].query.default_span(stack[1 % stack.len()].span); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 27d6efb8b5490..cbb2297554f14 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -97,6 +97,8 @@ pub struct Session { incr_comp_session: RwLock, + pub seen_cycle_error: AtomicBool, + /// Used by `-Z self-profile`. pub prof: SelfProfilerRef, @@ -1076,6 +1078,7 @@ pub fn build_session( target_tlib_path, psess, io, + seen_cycle_error: AtomicBool::new(false), incr_comp_session: RwLock::new(IncrCompSession::NotInitialized), prof, timings,