From 72a001bc25217104f9de5ff4b35c280c95adcb28 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 10:09:39 +1100 Subject: [PATCH 01/10] Remove `DepKindVTable::is_anon`. It's unused. --- compiler/rustc_middle/src/dep_graph/dep_node.rs | 5 ----- compiler/rustc_query_impl/src/dep_kind_vtables.rs | 9 --------- 2 files changed, 14 deletions(-) diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 599c84597a8b9..c356d566626d1 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -174,11 +174,6 @@ impl fmt::Debug for DepNode { /// of the `DepKind`. Overall, this allows to implement `DepContext` using this manual /// jump table instead of large matches. pub struct DepKindVTable<'tcx> { - /// Anonymous queries cannot be replayed from one compiler invocation to the next. - /// When their result is needed, it is recomputed. They are useful for fine-grained - /// dependency tracking, and caching within one compiler invocation. - pub is_anon: bool, - /// Eval-always queries do not track their dependencies, and are always recomputed, even if /// their inputs have not changed since the last compiler invocation. The result is still /// cached within one compiler invocation. diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index fa82a0413b1ad..01e37ca3e3ee1 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -13,7 +13,6 @@ mod non_query { // We use this for most things when incr. comp. is turned off. pub(crate) fn Null<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Unit, force_from_dep_node_fn: Some(|_, dep_node, _| { @@ -26,7 +25,6 @@ mod non_query { // We use this for the forever-red node. pub(crate) fn Red<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Unit, force_from_dep_node_fn: Some(|_, dep_node, _| { @@ -38,7 +36,6 @@ mod non_query { pub(crate) fn SideEffect<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Unit, force_from_dep_node_fn: Some(|tcx, _, prev_index| { @@ -51,7 +48,6 @@ mod non_query { pub(crate) fn AnonZeroDeps<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: true, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Opaque, force_from_dep_node_fn: Some(|_, _, _| bug!("cannot force an anon node")), @@ -61,7 +57,6 @@ mod non_query { pub(crate) fn TraitSelect<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: true, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Unit, force_from_dep_node_fn: None, @@ -71,7 +66,6 @@ mod non_query { pub(crate) fn CompileCodegenUnit<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Opaque, force_from_dep_node_fn: None, @@ -81,7 +75,6 @@ mod non_query { pub(crate) fn CompileMonoItem<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Opaque, force_from_dep_node_fn: None, @@ -91,7 +84,6 @@ mod non_query { pub(crate) fn Metadata<'tcx>() -> DepKindVTable<'tcx> { DepKindVTable { - is_anon: false, is_eval_always: false, key_fingerprint_style: KeyFingerprintStyle::Unit, force_from_dep_node_fn: None, @@ -124,7 +116,6 @@ where } DepKindVTable { - is_anon, is_eval_always, key_fingerprint_style, force_from_dep_node_fn: can_recover.then_some(force_from_dep_node_inner::), From cc1b878386666be4b97ed21e053487f1ec0f6c0c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 10:27:31 +1100 Subject: [PATCH 02/10] Inline and remove `handle_cycle_error`. It has a single use. --- compiler/rustc_query_impl/src/execution.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 67054b415214b..8d5aa20da1508 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -5,7 +5,7 @@ use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_data_structures::{outline, sharded, sync}; -use rustc_errors::{Diag, FatalError, StashKey}; +use rustc_errors::{FatalError, StashKey}; use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex}; use rustc_middle::query::plumbing::QueryVTable; use rustc_middle::query::{ @@ -126,19 +126,10 @@ fn mk_cycle<'tcx, C: QueryCache>( cycle_error: CycleError, ) -> C::Value { let error = report_cycle(tcx.sess, &cycle_error); - handle_cycle_error(query, tcx, &cycle_error, error) -} - -fn handle_cycle_error<'tcx, C: QueryCache>( - query: &'tcx QueryVTable<'tcx, C>, - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - error: Diag<'_>, -) -> C::Value { match query.cycle_error_handling { CycleErrorHandling::Error => { let guar = error.emit(); - query.value_from_cycle_error(tcx, cycle_error, guar) + query.value_from_cycle_error(tcx, &cycle_error, guar) } CycleErrorHandling::Fatal => { error.emit(); @@ -147,7 +138,7 @@ fn handle_cycle_error<'tcx, C: QueryCache>( } CycleErrorHandling::DelayBug => { let guar = error.delay_as_bug(); - query.value_from_cycle_error(tcx, cycle_error, guar) + query.value_from_cycle_error(tcx, &cycle_error, guar) } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() @@ -157,7 +148,7 @@ fn handle_cycle_error<'tcx, C: QueryCache>( } else { error.emit() }; - query.value_from_cycle_error(tcx, cycle_error, guar) + query.value_from_cycle_error(tcx, &cycle_error, guar) } } } From c0770baed8b4158a063ed27493ff2eeacfd6681c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 10:31:32 +1100 Subject: [PATCH 03/10] Make `from_cycle_error` consume the `CycleError`. This makes it clear the `CycleError` is not used after the call. --- compiler/rustc_middle/src/query/plumbing.rs | 4 +-- compiler/rustc_query_impl/src/execution.rs | 6 ++-- compiler/rustc_query_impl/src/values.rs | 37 +++++++-------------- 3 files changed, 17 insertions(+), 30 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 072e29aaa9063..3832a961b4194 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -151,7 +151,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub hash_value_fn: Option, &C::Value) -> Fingerprint>, pub value_from_cycle_error: - fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, + fn(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> C::Value, pub format_value: fn(&C::Value) -> String, /// Formats a human-readable description of this query and its key, as @@ -216,7 +216,7 @@ impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { pub fn value_from_cycle_error( &self, tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, guar: ErrorGuaranteed, ) -> C::Value { (self.value_from_cycle_error)(tcx, cycle_error, guar) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 8d5aa20da1508..634015403a627 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -129,7 +129,7 @@ fn mk_cycle<'tcx, C: QueryCache>( match query.cycle_error_handling { CycleErrorHandling::Error => { let guar = error.emit(); - query.value_from_cycle_error(tcx, &cycle_error, guar) + query.value_from_cycle_error(tcx, cycle_error, guar) } CycleErrorHandling::Fatal => { error.emit(); @@ -138,7 +138,7 @@ fn mk_cycle<'tcx, C: QueryCache>( } CycleErrorHandling::DelayBug => { let guar = error.delay_as_bug(); - query.value_from_cycle_error(tcx, &cycle_error, guar) + query.value_from_cycle_error(tcx, cycle_error, guar) } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() @@ -148,7 +148,7 @@ fn mk_cycle<'tcx, C: QueryCache>( } else { error.emit() }; - query.value_from_cycle_error(tcx, &cycle_error, guar) + query.value_from_cycle_error(tcx, cycle_error, guar) } } } diff --git a/compiler/rustc_query_impl/src/values.rs b/compiler/rustc_query_impl/src/values.rs index 8f55e98df867e..6e739ee04a024 100644 --- a/compiler/rustc_query_impl/src/values.rs +++ b/compiler/rustc_query_impl/src/values.rs @@ -18,14 +18,13 @@ use rustc_span::{ErrorGuaranteed, Span}; use crate::job::report_cycle; pub(crate) trait Value<'tcx>: Sized { - fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) - -> Self; + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self; } impl<'tcx, T> Value<'tcx> for T { default fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> T { tcx.sess.dcx().abort_if_errors(); @@ -38,7 +37,7 @@ impl<'tcx, T> Value<'tcx> for T { } impl<'tcx> Value<'tcx> for Ty<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { std::mem::transmute::, Ty<'_>>(Ty::new_error(tcx, guar)) } @@ -46,13 +45,13 @@ impl<'tcx> Value<'tcx> for Ty<'_> { } impl<'tcx> Value<'tcx> for Result>, CyclePlaceholder> { - fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self { Err(CyclePlaceholder(guar)) } } impl<'tcx> Value<'tcx> for ty::SymbolName<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, _guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. unsafe { @@ -64,11 +63,7 @@ impl<'tcx> Value<'tcx> for ty::SymbolName<'_> { } impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { let err = Ty::new_error(tcx, guar); let arity = if let Some(info) = cycle_error.cycle.get(0) @@ -100,7 +95,7 @@ impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { impl<'tcx> Value<'tcx> for Representability { fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> Self { let mut item_and_field_ids = Vec::new(); @@ -134,21 +129,13 @@ impl<'tcx> Value<'tcx> for Representability { } impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, Ty<'_>> { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar)) } } impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { - fn from_cycle_error( - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> Self { + fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar)) } } @@ -156,7 +143,7 @@ impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { impl<'tcx> Value<'tcx> for &[ty::Variance] { fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> Self { search_for_cycle_permutation( @@ -204,7 +191,7 @@ fn search_for_cycle_permutation( impl<'tcx, T> Value<'tcx> for Result> { fn from_cycle_error( tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, + cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> Self { let diag = search_for_cycle_permutation( @@ -280,7 +267,7 @@ impl<'tcx, T> Value<'tcx> for Result> { ControlFlow::Continue(()) } }, - || report_cycle(tcx.sess, cycle_error), + || report_cycle(tcx.sess, &cycle_error), ); let guar = diag.emit(); From 7a4daa48b8f4dfe00deacea6f2ba950c76de8a39 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 10:35:45 +1100 Subject: [PATCH 04/10] Avoid an early return in `try_execute_query`. When there are two cases of equal size and importance, I find an if/else expression easier to read than an early return. --- compiler/rustc_query_impl/src/execution.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 634015403a627..3d1ebd7b23671 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -331,15 +331,15 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( // Only call `wait_for_query` if we're using a Rayon thread pool // as it will attempt to mark the worker thread as blocked. - return wait_for_query(query, tcx, span, key, latch, current_job_id); - } - - let id = job.id; - drop(state_lock); + wait_for_query(query, tcx, span, key, latch, current_job_id) + } else { + let id = job.id; + drop(state_lock); - // If we are single-threaded we know that we have cycle error, - // so we just return the error. - cycle_error(query, tcx, id, span) + // If we are single-threaded we know that we have cycle error, + // so we just return the error. + cycle_error(query, tcx, id, span) + } } ActiveKeyStatus::Poisoned => FatalError.raise(), } From fc395ed961fd0ff9b75baddb402b328aef5f99fd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 10:40:47 +1100 Subject: [PATCH 05/10] Merge two assertion blocks in `execute_job_non_incr`. It reads better that way. --- compiler/rustc_query_impl/src/execution.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 3d1ebd7b23671..dd929c99b1c15 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -421,12 +421,6 @@ fn execute_job_non_incr<'tcx, C: QueryCache>( ) -> (C::Value, DepNodeIndex) { debug_assert!(!tcx.dep_graph.is_fully_enabled()); - // Fingerprint the key, just to assert that it doesn't - // have anything we don't consider hashable - if cfg!(debug_assertions) { - let _ = key.to_fingerprint(tcx); - } - let prof_timer = tcx.prof.query_provider(); // Call the query provider. let value = @@ -434,14 +428,14 @@ fn execute_job_non_incr<'tcx, C: QueryCache>( let dep_node_index = tcx.dep_graph.next_virtual_depnode_index(); prof_timer.finish_with_query_invocation_id(dep_node_index.into()); - // Similarly, fingerprint the result to assert that - // it doesn't have anything not considered hashable. - if cfg!(debug_assertions) - && let Some(hash_value_fn) = query.hash_value_fn - { - tcx.with_stable_hashing_context(|mut hcx| { - hash_value_fn(&mut hcx, &value); - }); + // Sanity: Fingerprint the key and the result to assert they don't contain anything unhashable. + if cfg!(debug_assertions) { + let _ = key.to_fingerprint(tcx); + if let Some(hash_value_fn) = query.hash_value_fn { + tcx.with_stable_hashing_context(|mut hcx| { + hash_value_fn(&mut hcx, &value); + }); + } } (value, dep_node_index) From 8129da81e247cb21f8d7cacf486f4683cc291660 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 10:48:53 +1100 Subject: [PATCH 06/10] Remove three single-use type synonyms. It's an unnecessary level of indirection. --- compiler/rustc_middle/src/query/plumbing.rs | 27 +++++++++------------ 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 3832a961b4194..2dba891885404 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -62,18 +62,6 @@ pub enum CycleErrorHandling { Stash, } -pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; - -pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( - tcx: TyCtxt<'tcx>, - key: &Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, -) -> Option; - -pub type IsLoadableFromDiskFn<'tcx, Key> = - fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; - #[derive(Clone, Debug)] pub struct CycleError { /// The query and related span that uses the cycle. @@ -126,7 +114,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub cycle_error_handling: CycleErrorHandling, pub state: QueryState<'tcx, C::Key>, pub cache: C, - pub will_cache_on_disk_for_key_fn: Option>, + pub will_cache_on_disk_for_key_fn: Option, key: &C::Key) -> bool>, /// Function pointer that calls `tcx.$query(key)` for this query and /// discards the returned value. @@ -142,8 +130,17 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// This should be the only code that calls the provider function. pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - pub try_load_from_disk_fn: Option>, - pub is_loadable_from_disk_fn: Option>, + pub try_load_from_disk_fn: Option< + fn( + tcx: TyCtxt<'tcx>, + key: &C::Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, + ) -> Option, + >, + + pub is_loadable_from_disk_fn: + Option, key: &C::Key, index: SerializedDepNodeIndex) -> bool>, /// Function pointer that hashes this query's result values. /// From 2aaced63444987973c1205931ed3dda94f26a26b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 14:31:51 +1100 Subject: [PATCH 07/10] Replace two `abort_if_errors` calls. Calling `abort_if_errors` after emitting an error is guaranteed to call `raise_fatal`, so just do that directly instead. --- compiler/rustc_query_impl/src/execution.rs | 5 ++--- compiler/rustc_query_impl/src/values.rs | 14 ++++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index dd929c99b1c15..323d40fdbaabb 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -132,9 +132,8 @@ fn mk_cycle<'tcx, C: QueryCache>( query.value_from_cycle_error(tcx, cycle_error, guar) } CycleErrorHandling::Fatal => { - error.emit(); - tcx.dcx().abort_if_errors(); - unreachable!() + let guar = error.emit(); + guar.raise_fatal(); } CycleErrorHandling::DelayBug => { let guar = error.delay_as_bug(); diff --git a/compiler/rustc_query_impl/src/values.rs b/compiler/rustc_query_impl/src/values.rs index 6e739ee04a024..216f36bd6e847 100644 --- a/compiler/rustc_query_impl/src/values.rs +++ b/compiler/rustc_query_impl/src/values.rs @@ -27,12 +27,14 @@ impl<'tcx, T> Value<'tcx> for T { cycle_error: CycleError, _guar: ErrorGuaranteed, ) -> T { - tcx.sess.dcx().abort_if_errors(); - bug!( - "<{} as Value>::from_cycle_error called without errors: {:#?}", - std::any::type_name::(), - cycle_error.cycle, - ); + let Some(guar) = tcx.sess.dcx().has_errors() else { + bug!( + "<{} as Value>::from_cycle_error called without errors: {:#?}", + std::any::type_name::(), + cycle_error.cycle, + ); + }; + guar.raise_fatal(); } } From 6f111d21a5ab57069727be85fd94f910a49665aa Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 16:39:23 +1100 Subject: [PATCH 08/10] Remove a duplicated comment. This exact comment block also appears in `compiler/rustc_middle/src/queries.rs`, which is a better place for it. --- compiler/rustc_middle/src/query/plumbing.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 2dba891885404..e1a19e5ffec7f 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -647,18 +647,6 @@ macro_rules! define_callbacks { }; } -// Each of these queries corresponds to a function pointer field in the -// `Providers` struct for requesting a value of that type, and a method -// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way -// which memoizes and does dep-graph tracking, wrapping around the actual -// `Providers` that the driver creates (using several `rustc_*` crates). -// -// The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. -// Queries marked with `cycle_fatal` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. - mod sealed { use rustc_hir::def_id::{LocalModDefId, ModDefId}; From 5677f7c70676a99c6cae3b636098ffbf5f8c70f5 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 15:11:18 +1100 Subject: [PATCH 09/10] Rename trait `Value` as `FromCycleError`. `Value` is an unhelpfully generic name. Standard naming procedure for a trait with a single method is for the trait name to match the method name, which is what this commit does. Likewise, the enclosing module is renamed from `values` to `from_cycle_error`. Also add a comment about some non-obvious behaviour. --- compiler/rustc_middle/src/queries.rs | 6 ++-- .../src/{values.rs => from_cycle_error.rs} | 28 +++++++++++-------- compiler/rustc_query_impl/src/lib.rs | 4 +-- compiler/rustc_query_impl/src/plumbing.rs | 3 +- 4 files changed, 23 insertions(+), 18 deletions(-) rename compiler/rustc_query_impl/src/{values.rs => from_cycle_error.rs} (93%) diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 0ff38a0f36041..f54b291a2024f 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -150,10 +150,10 @@ use crate::{dep_graph, mir, thir}; // `Providers` that the driver creates (using several `rustc_*` crates). // // The result type of each query must implement `Clone`, and additionally -// `ty::query::values::Value`, which produces an appropriate placeholder -// (error) value if the query resulted in a query cycle. +// `ty::query::from_cycle_error::FromCycleError`, which produces an appropriate +// placeholder (error) value if the query resulted in a query cycle. // Queries marked with `cycle_fatal` do not need the latter implementation, -// as they will raise an fatal error on query cycles instead. +// as they will raise a fatal error on query cycles instead. rustc_queries! { /// Caches the expansion of a derive proc macro, e.g. `#[derive(Serialize)]`. /// The key is: diff --git a/compiler/rustc_query_impl/src/values.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs similarity index 93% rename from compiler/rustc_query_impl/src/values.rs rename to compiler/rustc_query_impl/src/from_cycle_error.rs index 216f36bd6e847..690eb022ea234 100644 --- a/compiler/rustc_query_impl/src/values.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -17,11 +17,15 @@ use rustc_span::{ErrorGuaranteed, Span}; use crate::job::report_cycle; -pub(crate) trait Value<'tcx>: Sized { +pub(crate) trait FromCycleError<'tcx>: Sized { + /// Try to produce a `Self` value that represents an error form (e.g. `TyKind::Error`). + /// + /// Note: the default impl calls `raise_fatal`, ending compilation immediately! Only a few + /// types override this with a non-fatal impl. fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self; } -impl<'tcx, T> Value<'tcx> for T { +impl<'tcx, T> FromCycleError<'tcx> for T { default fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: CycleError, @@ -29,7 +33,7 @@ impl<'tcx, T> Value<'tcx> for T { ) -> T { let Some(guar) = tcx.sess.dcx().has_errors() else { bug!( - "<{} as Value>::from_cycle_error called without errors: {:#?}", + "<{} as FromCycleError>::from_cycle_error called without errors: {:#?}", std::any::type_name::(), cycle_error.cycle, ); @@ -38,7 +42,7 @@ impl<'tcx, T> Value<'tcx> for T { } } -impl<'tcx> Value<'tcx> for Ty<'_> { +impl<'tcx> FromCycleError<'tcx> for Ty<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `Ty<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. @@ -46,13 +50,13 @@ impl<'tcx> Value<'tcx> for Ty<'_> { } } -impl<'tcx> Value<'tcx> for Result>, CyclePlaceholder> { +impl<'tcx> FromCycleError<'tcx> for Result>, CyclePlaceholder> { fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: CycleError, guar: ErrorGuaranteed) -> Self { Err(CyclePlaceholder(guar)) } } -impl<'tcx> Value<'tcx> for ty::SymbolName<'_> { +impl<'tcx> FromCycleError<'tcx> for ty::SymbolName<'_> { fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, _guar: ErrorGuaranteed) -> Self { // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. // FIXME: Represent the above fact in the trait system somehow. @@ -64,7 +68,7 @@ impl<'tcx> Value<'tcx> for ty::SymbolName<'_> { } } -impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { +impl<'tcx> FromCycleError<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { let err = Ty::new_error(tcx, guar); @@ -94,7 +98,7 @@ impl<'tcx> Value<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { } } -impl<'tcx> Value<'tcx> for Representability { +impl<'tcx> FromCycleError<'tcx> for Representability { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: CycleError, @@ -130,19 +134,19 @@ impl<'tcx> Value<'tcx> for Representability { } } -impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, Ty<'_>> { +impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, Ty<'_>> { fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar)) } } -impl<'tcx> Value<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { +impl<'tcx> FromCycleError<'tcx> for ty::EarlyBinder<'_, ty::Binder<'_, ty::FnSig<'_>>> { fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar)) } } -impl<'tcx> Value<'tcx> for &[ty::Variance] { +impl<'tcx> FromCycleError<'tcx> for &[ty::Variance] { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: CycleError, @@ -190,7 +194,7 @@ fn search_for_cycle_permutation( otherwise() } -impl<'tcx, T> Value<'tcx> for Result> { +impl<'tcx, T> FromCycleError<'tcx> for Result> { fn from_cycle_error( tcx: TyCtxt<'tcx>, cycle_error: CycleError, diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 875dd07ff25f0..5be636877fa50 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -18,10 +18,10 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Span; pub use crate::dep_kind_vtables::make_dep_kind_vtables; +use crate::from_cycle_error::FromCycleError; pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; use crate::plumbing::try_mark_green; use crate::profiling_support::QueryKeyStringCache; -use crate::values::Value; #[macro_use] mod plumbing; @@ -29,9 +29,9 @@ mod plumbing; mod dep_kind_vtables; mod error; mod execution; +mod from_cycle_error; mod job; mod profiling_support; -mod values; /// Trait that knows how to look up the [`QueryVTable`] for a particular query. /// diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index f005e55418294..f22b5be75cafa 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -554,7 +554,8 @@ macro_rules! define_queries { None }), value_from_cycle_error: |tcx, cycle, guar| { - let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); + let result: queries::$name::Value<'tcx> = + FromCycleError::from_cycle_error(tcx, cycle, guar); erase::erase_val(result) }, hash_value_fn: if_no_hash!( From 5bd28b85bd5cc0000f67035a1c37fd0b150eccd6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 27 Feb 2026 16:15:59 +1100 Subject: [PATCH 10/10] Remove `FromCycleError` impl for `SymbolName`. It has no effect. `symbol_name` is the only query that produces a `SymbolName`. If it was marked with `cycle_delayed_bug`/`cycle_stash` then this `FromCycleError` impl would make sense, but that's not the case. Maybe it was the case in the past. --- compiler/rustc_query_impl/src/from_cycle_error.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/compiler/rustc_query_impl/src/from_cycle_error.rs b/compiler/rustc_query_impl/src/from_cycle_error.rs index 690eb022ea234..a13db9004d678 100644 --- a/compiler/rustc_query_impl/src/from_cycle_error.rs +++ b/compiler/rustc_query_impl/src/from_cycle_error.rs @@ -56,18 +56,6 @@ impl<'tcx> FromCycleError<'tcx> for Result>, CyclePla } } -impl<'tcx> FromCycleError<'tcx> for ty::SymbolName<'_> { - fn from_cycle_error(tcx: TyCtxt<'tcx>, _: CycleError, _guar: ErrorGuaranteed) -> Self { - // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`. - // FIXME: Represent the above fact in the trait system somehow. - unsafe { - std::mem::transmute::, ty::SymbolName<'_>>(ty::SymbolName::new( - tcx, "", - )) - } - } -} - impl<'tcx> FromCycleError<'tcx> for ty::Binder<'_, ty::FnSig<'_>> { fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle_error: CycleError, guar: ErrorGuaranteed) -> Self { let err = Ty::new_error(tcx, guar);