From 7cec0df9e76a500ab55e909b4d957aa9606b9ecd Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Mon, 16 Mar 2026 15:29:45 +0300 Subject: [PATCH 1/2] Add coherency check for multi-threaded and single-threaded cycle breaking --- compiler/rustc_middle/src/query/plumbing.rs | 4 ++-- compiler/rustc_middle/src/query/stack.rs | 2 +- compiler/rustc_query_impl/src/execution.rs | 3 ++- compiler/rustc_query_impl/src/job.rs | 26 ++++++++++++++++----- compiler/rustc_span/src/lib.rs | 5 ++++ compiler/rustc_span/src/symbol.rs | 18 +++++++++++--- 6 files changed, 45 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 6f18c01577da2..777c9a30d25b0 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -50,7 +50,7 @@ pub enum ActiveKeyStatus<'tcx> { Poisoned, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct CycleError<'tcx> { /// The query and related span that uses the cycle. pub usage: Option>>, @@ -505,7 +505,7 @@ macro_rules! define_callbacks { /// Identifies a query by kind and key. This is in contrast to `QueryJobId` which is just a number. #[allow(non_camel_case_types)] - #[derive(Clone, Debug)] + #[derive(Clone, Debug, PartialEq, Eq)] pub enum TaggedQueryKey<'tcx> { $( $name($name::Key<'tcx>), diff --git a/compiler/rustc_middle/src/query/stack.rs b/compiler/rustc_middle/src/query/stack.rs index 6599ad3c6f54c..9aa9793909a25 100644 --- a/compiler/rustc_middle/src/query/stack.rs +++ b/compiler/rustc_middle/src/query/stack.rs @@ -8,7 +8,7 @@ use crate::queries::TaggedQueryKey; /// Description of a frame in the query stack. /// /// This is mostly used in case of cycles for error reporting. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct QueryStackFrame<'tcx> { pub tagged_key: TaggedQueryKey<'tcx>, pub dep_kind: DepKind, diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index eae91a2b2e566..0496b894cce74 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -221,7 +221,8 @@ fn cycle_error<'tcx, C: QueryCache>( let job_map = collect_active_jobs_from_all_queries(tcx, CollectActiveJobsKind::FullNoContention); - let error = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), span); + let error = find_cycle_in_stack(try_execute, &job_map, current_query_job(), span) + .expect("did not find a cycle"); (mk_cycle(query, tcx, key, error), None) } diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 6bd4c4af5daf7..1bc6646e4f547 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -54,13 +54,12 @@ pub(crate) struct QueryJobInfo<'tcx> { pub(crate) fn find_cycle_in_stack<'tcx>( id: QueryJobId, - job_map: QueryJobMap<'tcx>, - current_job: &Option, + job_map: &QueryJobMap<'tcx>, + mut current_job: Option, span: Span, -) -> CycleError<'tcx> { +) -> Option> { // Find the waitee amongst `current_job` parents let mut cycle = Vec::new(); - let mut current_job = Option::clone(current_job); while let Some(job) = current_job { let info = &job_map.map[&job]; @@ -79,13 +78,13 @@ pub(crate) fn find_cycle_in_stack<'tcx>( let parent = info.job.parent?; respan(info.job.span, job_map.frame_of(parent).clone()) }; - return CycleError { usage, cycle }; + return Some(CycleError { usage, cycle }); } current_job = info.job.parent; } - panic!("did not find a cycle") + None } /// Finds the job closest to the root with a `DepKind` matching the `DepKind` of `id` and returns @@ -324,6 +323,21 @@ fn remove_cycle<'tcx>( .collect(), }; + if cfg!(debug_assertions) + && let Some(query_waiting_on_cycle) = entry_point.query_waiting_on_cycle + && let Some(expected) = find_cycle_in_stack( + query_waiting_on_cycle.1, + job_map, + stack.last().map(|&(_, job)| job), + query_waiting_on_cycle.0, + ) + { + if error != expected { + panic!( + "CycleError coherency check failed:\n expected: {expected:#?}\n got: {error:#?}" + ); + } + } // We unwrap `resumable` here since there must always be one // edge which is resumable / waited using a query latch let (waitee_query, waiter_idx) = resumable.unwrap(); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index d7d2ecb5bbe79..38475d974b82c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -183,6 +183,11 @@ where SESSION_GLOBALS.with(f) } +#[inline] +pub fn are_session_globals_set() -> bool { + SESSION_GLOBALS.is_set() +} + /// Default edition, no source map. pub fn create_default_session_globals_then(f: impl FnOnce() -> R) -> R { create_session_globals_then(edition::DEFAULT_EDITION, &[], None, f) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 26ced79b822ac..29eca6ab74576 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -14,7 +14,7 @@ use rustc_data_structures::sync::Lock; use rustc_macros::{Decodable, Encodable, HashStable_Generic, symbols}; use crate::edit_distance::find_best_match_for_name; -use crate::{DUMMY_SP, Edition, Span, with_session_globals}; +use crate::{DUMMY_SP, Edition, Span, are_session_globals_set, with_session_globals}; #[cfg(test)] mod tests; @@ -2545,6 +2545,10 @@ impl Symbol { }) } + pub fn opt_as_str(&self) -> Option<&str> { + are_session_globals_set().then(|| self.as_str()) + } + pub fn as_u32(self) -> u32 { self.0.as_u32() } @@ -2586,13 +2590,21 @@ impl Symbol { impl fmt::Debug for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), f) + if let Some(s) = self.opt_as_str() { + fmt::Debug::fmt(s, f) + } else { + fmt::Debug::fmt(&self.0, f) + } } } impl fmt::Display for Symbol { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_str(), f) + if let Some(s) = self.opt_as_str() { + fmt::Display::fmt(s, f) + } else { + fmt::Debug::fmt(&self.0, f) + } } } From dec769a3cb3e7b9dc42170cf676285c5f4966434 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 17 Mar 2026 13:24:16 +0300 Subject: [PATCH 2/2] Fix cycle coherency check and cycle inconsistency --- compiler/rustc_query_impl/src/job.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 1bc6646e4f547..8f14b2f2cbead 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -314,6 +314,8 @@ fn remove_cycle<'tcx>( .query_waiting_on_cycle .map(|(span, job)| respan(span, job_map.frame_of(job).clone())); + let span = entry_point.query_waiting_on_cycle.map_or(DUMMY_SP, |(s, _)| s); + stack[0].0 = span; // Create the cycle error let error = CycleError { usage, @@ -324,17 +326,16 @@ fn remove_cycle<'tcx>( }; if cfg!(debug_assertions) - && let Some(query_waiting_on_cycle) = entry_point.query_waiting_on_cycle && let Some(expected) = find_cycle_in_stack( - query_waiting_on_cycle.1, + entry_point.query_in_cycle, job_map, stack.last().map(|&(_, job)| job), - query_waiting_on_cycle.0, + span, ) { if error != expected { panic!( - "CycleError coherency check failed:\n expected: {expected:#?}\n got: {error:#?}" + "CycleError coherency check failed:\nexpected: {expected:#?}\ngot: {error:#?}" ); } }