diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 0cd0275f96bbb..24b23cc4199e9 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -184,7 +184,7 @@ pub(crate) fn run_in_thread_pool_with_globals< use rustc_data_structures::defer; use rustc_middle::ty::tls; - use rustc_query_impl::break_query_cycles; + use rustc_query_impl::break_query_cycle; let thread_stack_size = init_stack_size(thread_builder_diag); @@ -260,7 +260,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; ) }, ); - break_query_cycles(job_map, ®istry); + break_query_cycle(job_map, ®istry); }) }) }); diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index ff96f5044dc14..599ae61592032 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -60,7 +60,7 @@ use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::steal::Steal; use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; -use rustc_errors::ErrorGuaranteed; +use rustc_errors::{ErrorGuaranteed, catch_fatal_errors}; use rustc_hir as hir; use rustc_hir::attrs::{EiiDecl, EiiImpl, StrippedCfgItem}; use rustc_hir::def::{DefKind, DocLinkResMap}; diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index 402c448a1fa3a..5d916b047d450 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,12 +1,14 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. +use std::panic::Location; + use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; use crate::dep_graph::DepNodeKey; use crate::query::erase::{self, Erasable, Erased}; -use crate::query::{EnsureMode, QueryCache, QueryMode, QueryVTable}; +use crate::query::{EnsureMode, QueryCache, QueryCallContext, QueryMode, QueryVTable}; use crate::ty::TyCtxt; /// Checks whether there is already a value for this key in the in-memory @@ -31,6 +33,7 @@ where /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` /// for all queries. #[inline(always)] +#[track_caller] pub(crate) fn query_get_at<'tcx, C>( tcx: TyCtxt<'tcx>, span: Span, @@ -40,15 +43,17 @@ pub(crate) fn query_get_at<'tcx, C>( where C: QueryCache, { + let call_context = QueryCallContext { span, location: Some(Location::caller()) }; match try_get_cached(tcx, &query.cache, key) { Some(value) => value, - None => (query.execute_query_fn)(tcx, span, key, QueryMode::Get).unwrap(), + None => (query.execute_query_fn)(tcx, call_context, key, QueryMode::Get).unwrap(), } } /// Shared implementation of `tcx.ensure_ok().$query(..)` and /// `tcx.ensure_done().$query(..)` for all queries. #[inline] +#[track_caller] pub(crate) fn query_ensure_ok_or_done<'tcx, C>( tcx: TyCtxt<'tcx>, query: &'tcx QueryVTable<'tcx, C>, @@ -57,10 +62,11 @@ pub(crate) fn query_ensure_ok_or_done<'tcx, C>( ) where C: QueryCache, { + let call_context = QueryCallContext { span: DUMMY_SP, location: Some(Location::caller()) }; match try_get_cached(tcx, &query.cache, key) { Some(_value) => {} None => { - (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::Ensure { ensure_mode }); + (query.execute_query_fn)(tcx, call_context, key, QueryMode::Ensure { ensure_mode }); } } } @@ -68,6 +74,7 @@ pub(crate) fn query_ensure_ok_or_done<'tcx, C>( /// Implementation of `tcx.ensure_result().$query(..)` for queries that /// return `Result<_, ErrorGuaranteed>`. #[inline] +#[track_caller] pub(crate) fn query_ensure_result<'tcx, C, T>( tcx: TyCtxt<'tcx>, query: &'tcx QueryVTable<'tcx, C>, @@ -77,6 +84,7 @@ where C: QueryCache>>, Result: Erasable, { + let call_context = QueryCallContext { span: DUMMY_SP, location: Some(Location::caller()) }; let convert = |value: Erased>| -> Result<(), ErrorGuaranteed> { match erase::restore_val(value) { Ok(_) => Ok(()), @@ -89,7 +97,7 @@ where None => { match (query.execute_query_fn)( tcx, - DUMMY_SP, + call_context, key, QueryMode::Ensure { ensure_mode: EnsureMode::Ok }, ) { diff --git a/compiler/rustc_middle/src/query/job.rs b/compiler/rustc_middle/src/query/job.rs index 24c4daf9855d2..2c661fb905ac5 100644 --- a/compiler/rustc_middle/src/query/job.rs +++ b/compiler/rustc_middle/src/query/job.rs @@ -4,9 +4,8 @@ use std::num::NonZero; use std::sync::Arc; use parking_lot::{Condvar, Mutex}; -use rustc_span::Span; -use crate::query::Cycle; +use crate::query::{Cycle, QueryCallContext}; use crate::ty::TyCtxt; /// A value uniquely identifying an active query job. @@ -18,8 +17,8 @@ pub struct QueryJobId(pub NonZero); pub struct QueryJob<'tcx> { pub id: QueryJobId, - /// The span corresponding to the reason for which this query was required. - pub span: Span, + /// The span and call site corresponding to why this query was required. + pub call_context: QueryCallContext, /// The parent query job which created this job and is implicitly waiting on it. pub parent: Option, @@ -31,8 +30,8 @@ pub struct QueryJob<'tcx> { impl<'tcx> QueryJob<'tcx> { /// Creates a new query job. #[inline] - pub fn new(id: QueryJobId, span: Span, parent: Option) -> Self { - QueryJob { id, span, parent, latch: None } + pub fn new(id: QueryJobId, call_context: QueryCallContext, parent: Option) -> Self { + QueryJob { id, call_context, parent, latch: None } } pub fn latch(&mut self) -> QueryLatch<'tcx> { @@ -58,7 +57,7 @@ impl<'tcx> QueryJob<'tcx> { pub struct QueryWaiter<'tcx> { pub parent: Option, pub condvar: Condvar, - pub span: Span, + pub call_context: QueryCallContext, pub cycle: Mutex>>, } @@ -78,7 +77,7 @@ impl<'tcx> QueryLatch<'tcx> { &self, tcx: TyCtxt<'tcx>, query: Option, - span: Span, + call_context: QueryCallContext, ) -> Result<(), Cycle<'tcx>> { let mut waiters_guard = self.waiters.lock(); let Some(waiters) = &mut *waiters_guard else { @@ -87,7 +86,7 @@ impl<'tcx> QueryLatch<'tcx> { let waiter = Arc::new(QueryWaiter { parent: query, - span, + call_context, cycle: Mutex::new(None), condvar: Condvar::new(), }); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b7e5e9bcb5e32..17767a8bedd17 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -8,7 +8,7 @@ pub use self::plumbing::{ ActiveKeyStatus, Cycle, EnsureMode, QueryMode, QueryState, QuerySystem, QueryVTable, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult, }; -pub use self::stack::QueryStackFrame; +pub use self::stack::{QueryCallContext, QueryStackFrame}; pub use crate::queries::Providers; use crate::ty::TyCtxt; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index ef6259b1a0c1a..b83fdce569284 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -13,7 +13,7 @@ use crate::dep_graph::{DepKind, DepNodeIndex, QuerySideEffect, SerializedDepNode use crate::ich::StableHashingContext; use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables, TaggedQueryKey}; use crate::query::on_disk_cache::OnDiskCache; -use crate::query::{QueryCache, QueryJob, QueryStackFrame}; +use crate::query::{QueryCache, QueryCallContext, QueryJob, QueryStackFrame}; use crate::ty::TyCtxt; /// For a particular query, keeps track of "active" keys, i.e. keys whose @@ -130,7 +130,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// and putting the obtained value into the in-memory cache. /// /// [^1]: [`TyCtxt`], [`TyCtxtAt`], [`TyCtxtEnsureOk`], [`TyCtxtEnsureDone`] - pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, + pub execute_query_fn: fn(TyCtxt<'tcx>, QueryCallContext, C::Key, QueryMode) -> Option, } impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { @@ -166,6 +166,8 @@ pub struct QuerySystem<'tcx> { pub extern_providers: ExternProviders, pub jobs: AtomicU64, + + pub cycle_handler_nesting: Lock, } #[derive(Copy, Clone)] @@ -204,6 +206,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Returns a transparent wrapper for `TyCtxt` which uses /// `span` as the location of queries performed through it. #[inline(always)] + #[track_caller] pub fn at(self, span: Span) -> TyCtxtAt<'tcx> { TyCtxtAt { tcx: self, span } } @@ -231,6 +234,7 @@ impl<'tcx> TyCtxt<'tcx> { /// Therefore, this call mode is not appropriate for callers that want to /// ensure that the query is _never_ executed in the future. #[inline(always)] + #[track_caller] pub fn ensure_ok(self) -> TyCtxtEnsureOk<'tcx> { TyCtxtEnsureOk { tcx: self } } @@ -241,6 +245,7 @@ impl<'tcx> TyCtxt<'tcx> { /// but nothing else. As with `ensure_ok`, this can be more efficient than /// a normal query call. #[inline(always)] + #[track_caller] pub fn ensure_result(self) -> TyCtxtEnsureResult<'tcx> { TyCtxtEnsureResult { tcx: self } } @@ -262,6 +267,7 @@ impl<'tcx> TyCtxt<'tcx> { /// /// [`Steal`]: rustc_data_structures::steal::Steal #[inline(always)] + #[track_caller] pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> { TyCtxtEnsureDone { tcx: self } } @@ -431,6 +437,11 @@ macro_rules! define_callbacks { } } + /// Calls `self.description` or returns a fallback if there was a fatal error + pub fn catch_description(&self, tcx: TyCtxt<'tcx>) -> String { + catch_fatal_errors(|| self.description(tcx)).unwrap_or_else(|_| format!("", self.query_name())) + } + /// Returns the default span for this query if `span` is a dummy span. pub fn default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span { if !span.is_dummy() { @@ -449,28 +460,9 @@ macro_rules! define_callbacks { } } - pub fn def_kind(&self, tcx: TyCtxt<'tcx>) -> Option { - // This is used to reduce code generation as it - // can be reused for queries with the same key type. - fn inner<'tcx>(key: &impl $crate::query::QueryKey, tcx: TyCtxt<'tcx>) - -> Option - { - key - .key_as_def_id() - .and_then(|def_id| def_id.as_local()) - .map(|def_id| tcx.def_kind(def_id)) - } - - if let TaggedQueryKey::def_kind(..) = self { - // Try to avoid infinite recursion. - return None - } - - match self { - $( - TaggedQueryKey::$name(key) => inner(key, tcx), - )* - } + /// Calls `self.default_span` or returns `DUMMY_SP` if there was a fatal error + pub fn catch_default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span { + catch_fatal_errors(|| self.default_span(tcx, span)).unwrap_or(DUMMY_SP) } } @@ -554,6 +546,7 @@ macro_rules! define_callbacks { $( $(#[$attr])* #[inline(always)] + #[track_caller] #[must_use] pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { self.at(DUMMY_SP).$name(key) @@ -565,6 +558,7 @@ macro_rules! define_callbacks { $( $(#[$attr])* #[inline(always)] + #[track_caller] pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { use $crate::query::{erase, inner}; @@ -582,6 +576,7 @@ macro_rules! define_callbacks { $( $(#[$attr])* #[inline(always)] + #[track_caller] pub fn $name(self, key: maybe_into_query_key!($($K)*)) { $crate::query::inner::query_ensure_ok_or_done( self.tcx, @@ -599,6 +594,7 @@ macro_rules! define_callbacks { #[cfg($returns_error_guaranteed)] $(#[$attr])* #[inline(always)] + #[track_caller] pub fn $name( self, key: maybe_into_query_key!($($K)*), @@ -616,6 +612,7 @@ macro_rules! define_callbacks { $( $(#[$attr])* #[inline(always)] + #[track_caller] pub fn $name(self, key: maybe_into_query_key!($($K)*)) { $crate::query::inner::query_ensure_ok_or_done( self.tcx, diff --git a/compiler/rustc_middle/src/query/stack.rs b/compiler/rustc_middle/src/query/stack.rs index 9465fc87edf09..79df5436a4b45 100644 --- a/compiler/rustc_middle/src/query/stack.rs +++ b/compiler/rustc_middle/src/query/stack.rs @@ -1,13 +1,21 @@ +use std::panic::Location; + use rustc_span::Span; use crate::queries::TaggedQueryKey; +#[derive(Clone, Copy, Debug)] +pub struct QueryCallContext { + pub span: Span, + pub location: Option<&'static Location<'static>>, +} + /// Description of a frame in the query stack. /// /// This is mostly used in case of cycles for error reporting. #[derive(Debug)] pub struct QueryStackFrame<'tcx> { - pub span: Span, + pub call_context: QueryCallContext, /// The query and key of the query method call that this stack frame /// corresponds to. diff --git a/compiler/rustc_query_impl/src/error.rs b/compiler/rustc_query_impl/src/error.rs index 44d53f87aae29..5dd4500d10bc5 100644 --- a/compiler/rustc_query_impl/src/error.rs +++ b/compiler/rustc_query_impl/src/error.rs @@ -30,6 +30,8 @@ pub(crate) struct CycleStack { #[primary_span] pub span: Span, pub desc: String, + #[subdiagnostic] + pub location: Option, } #[derive(Subdiagnostic)] @@ -58,6 +60,14 @@ pub(crate) struct CycleUsage { #[primary_span] pub span: Span, pub usage: String, + #[subdiagnostic] + pub location: Option, +} + +#[derive(Subdiagnostic)] +#[note("at {$location}")] +pub(crate) struct QueryLocationNote { + pub location: String, } #[derive(Diagnostic)] @@ -71,6 +81,8 @@ pub(crate) struct Cycle { #[subdiagnostic] pub stack_count: StackCount, #[subdiagnostic] + pub stack_bottom_location: Option, + #[subdiagnostic] pub alias: Option, #[subdiagnostic] pub cycle_usage: Option, @@ -79,3 +91,23 @@ pub(crate) struct Cycle { )] pub note_span: (), } + +#[derive(Diagnostic)] +#[diag("cycle when printing cycle detected when {$stack_bottom}")] +pub(crate) struct NestedCycle { + #[primary_span] + pub span: Span, + pub stack_bottom: String, + #[subdiagnostic] + pub cycle_stack: Vec, + #[subdiagnostic] + pub stack_count: StackCount, + #[subdiagnostic] + pub stack_bottom_location: Option, + #[subdiagnostic] + pub cycle_usage: Option, + #[note( + "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information" + )] + pub note_span: (), +} diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 8844d40f49cf3..85b8b8706db56 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -4,19 +4,20 @@ use std::mem::ManuallyDrop; 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_data_structures::{defer, outline, sharded, sync}; use rustc_errors::FatalError; use rustc_middle::dep_graph::{DepGraphData, DepNodeKey, SerializedDepNodeIndex}; use rustc_middle::query::{ - ActiveKeyStatus, Cycle, EnsureMode, QueryCache, QueryJob, QueryJobId, QueryKey, QueryLatch, - QueryMode, QueryState, QueryVTable, + ActiveKeyStatus, Cycle, EnsureMode, QueryCache, QueryCallContext, QueryJob, QueryJobId, + QueryKey, QueryLatch, QueryMode, QueryState, QueryVTable, }; use rustc_middle::ty::TyCtxt; use rustc_middle::verify_ich::incremental_verify_ich; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::DUMMY_SP; use tracing::warn; use crate::dep_graph::{DepNode, DepNodeIndex}; +use crate::handle_cycle_error; use crate::job::{QueryJobInfo, QueryJobMap, create_cycle_error, find_cycle_in_stack}; use crate::plumbing::{current_query_job, loadable_from_disk, next_job_id, start_query}; use crate::query_impl::for_each_query_vtable; @@ -114,8 +115,29 @@ fn handle_cycle<'tcx, C: QueryCache>( key: C::Key, cycle: Cycle<'tcx>, ) -> C::Value { - let error = create_cycle_error(tcx, &cycle); - (query.handle_cycle_error_fn)(tcx, key, cycle, error) + let nested; + { + let mut nesting = tcx.query_system.cycle_handler_nesting.lock(); + nested = match *nesting { + 0 => false, + 1 => true, + _ => { + // Don't print further nested errors to avoid cases of infinite recursion + tcx.dcx().delayed_bug("doubly nested cycle error").raise_fatal() + } + }; + *nesting += 1; + } + let _guard = defer(|| *tcx.query_system.cycle_handler_nesting.lock() -= 1); + + let error = create_cycle_error(tcx, &cycle, nested); + + if nested { + // Avoid custom handlers and only use the robust `create_cycle_error` for nested cycle errors + handle_cycle_error::default(error) + } else { + (query.handle_cycle_error_fn)(tcx, key, cycle, error) + } } /// Guard object representing the responsibility to execute a query job and @@ -199,13 +221,13 @@ fn find_and_handle_cycle<'tcx, C: QueryCache>( tcx: TyCtxt<'tcx>, key: C::Key, try_execute: QueryJobId, - span: Span, + call_context: QueryCallContext, ) -> (C::Value, Option) { // Ensure there were no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. let job_map = collect_active_query_jobs(tcx, CollectActiveJobsKind::FullNoContention); - let cycle = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), span); + let cycle = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), call_context); (handle_cycle(query, tcx, key, cycle), None) } @@ -213,7 +235,7 @@ fn find_and_handle_cycle<'tcx, C: QueryCache>( fn wait_for_query<'tcx, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, - span: Span, + call_context: QueryCallContext, key: C::Key, key_hash: u64, latch: QueryLatch<'tcx>, @@ -225,7 +247,7 @@ fn wait_for_query<'tcx, C: QueryCache>( let query_blocked_prof_timer = tcx.prof.query_blocked(); // With parallel queries we might just have to wait on some other thread. - let result = latch.wait_on(tcx, current, span); + let result = latch.wait_on(tcx, current, call_context); match result { Ok(()) => { @@ -259,7 +281,7 @@ fn wait_for_query<'tcx, C: QueryCache>( fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, - span: Span, + call_context: QueryCallContext, key: C::Key, dep_node: Option, // `None` for non-incremental, `Some` for incremental ) -> (C::Value, Option) { @@ -286,7 +308,7 @@ fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>( // Nothing has computed or is computing the query, so we start a new job and insert it // in the state map. let id = next_job_id(tcx); - let job = QueryJob::new(id, span, current_job_id); + let job = QueryJob::new(id, call_context, current_job_id); entry.insert((key, ActiveKeyStatus::Started(job))); // Drop the lock before we start executing the query. @@ -323,14 +345,22 @@ 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. - wait_for_query(query, tcx, span, key, key_hash, latch, current_job_id) + wait_for_query( + query, + tcx, + call_context, + key, + key_hash, + 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. - find_and_handle_cycle(query, tcx, key, id, span) + find_and_handle_cycle(query, tcx, key, id, call_context) } } ActiveKeyStatus::Poisoned => FatalError.raise(), @@ -590,10 +620,10 @@ fn ensure_can_skip_execution<'tcx, C: QueryCache>( pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, - span: Span, + call_context: QueryCallContext, key: C::Key, ) -> C::Value { - ensure_sufficient_stack(|| try_execute_query::(query, tcx, span, key, None).0) + ensure_sufficient_stack(|| try_execute_query::(query, tcx, call_context, key, None).0) } /// Called by a macro-generated impl of [`QueryVTable::execute_query_fn`], @@ -602,7 +632,7 @@ pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>( pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, - span: Span, + call_context: QueryCallContext, key: C::Key, mode: QueryMode, ) -> Option { @@ -616,7 +646,7 @@ pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>( } let (result, dep_node_index) = ensure_sufficient_stack(|| { - try_execute_query::(query, tcx, span, key, Some(dep_node)) + try_execute_query::(query, tcx, call_context, key, Some(dep_node)) }); if let Some(dep_node_index) = dep_node_index { tcx.dep_graph.read_index(dep_node_index) @@ -640,7 +670,13 @@ pub(crate) fn force_query_dep_node<'tcx, C: QueryCache>( }; ensure_sufficient_stack(|| { - try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) + try_execute_query::( + query, + tcx, + QueryCallContext { span: DUMMY_SP, location: None }, + key, + Some(dep_node), + ) }); // We did manage to recover a key and force the node, though it's up to diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index 5676669bf1c0e..d8eb55c9b5173 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -165,8 +165,9 @@ fn layout_of<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> &'tcx ty::layout::L let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { continue; }; - let frame_span = - frame.tagged_key.default_span(tcx, frames[(i + 1) % frames.len()].span); + let frame_span = frame + .tagged_key + .default_span(tcx, frames[(i + 1) % frames.len()].call_context.span); if frame_span.is_dummy() { continue; } @@ -200,7 +201,7 @@ fn layout_of<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> &'tcx ty::layout::L ControlFlow::Continue(()) } }, - || create_cycle_error(tcx, &cycle), + || create_cycle_error(tcx, &cycle, false), ); let guar = diag.emit(); diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index a27a6f4ea322e..58c202098760c 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -7,10 +7,13 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{Diag, DiagCtxtHandle}; use rustc_hir::def::DefKind; use rustc_middle::queries::TaggedQueryKey; -use rustc_middle::query::{Cycle, QueryJob, QueryJobId, QueryLatch, QueryStackFrame, QueryWaiter}; +use rustc_middle::query::{ + Cycle, QueryCallContext, QueryJob, QueryJobId, QueryLatch, QueryStackFrame, QueryWaiter, +}; use rustc_middle::ty::TyCtxt; use rustc_span::{DUMMY_SP, Span}; +use crate::error::QueryLocationNote; use crate::{CollectActiveJobsKind, collect_active_query_jobs}; /// Map from query job IDs to job information collected by @@ -32,8 +35,8 @@ impl<'tcx> QueryJobMap<'tcx> { self.map[&id].tagged_key } - fn span_of(&self, id: QueryJobId) -> Span { - self.map[&id].job.span + fn call_context_of(&self, id: QueryJobId) -> QueryCallContext { + self.map[&id].job.call_context } fn parent_of(&self, id: QueryJobId) -> Option { @@ -55,7 +58,7 @@ pub(crate) fn find_cycle_in_stack<'tcx>( id: QueryJobId, job_map: QueryJobMap<'tcx>, current_job: &Option, - span: Span, + call_context: QueryCallContext, ) -> Cycle<'tcx> { // Find the waitee amongst `current_job` parents. let mut frames = Vec::new(); @@ -63,7 +66,10 @@ pub(crate) fn find_cycle_in_stack<'tcx>( while let Some(job) = current_job { let info = &job_map.map[&job]; - frames.push(QueryStackFrame { span: info.job.span, tagged_key: info.tagged_key }); + frames.push(QueryStackFrame { + call_context: info.job.call_context, + tagged_key: info.tagged_key, + }); if job == id { frames.reverse(); @@ -71,11 +77,14 @@ pub(crate) fn find_cycle_in_stack<'tcx>( // This is the end of the cycle. The span entry we included was for // the usage of the cycle itself, and not part of the cycle. // Replace it with the span which caused the cycle to form. - frames[0].span = span; + frames[0].call_context = call_context; // Find out why the cycle itself was used. let usage = try { let parent = info.job.parent?; - QueryStackFrame { span: info.job.span, tagged_key: job_map.tagged_key_of(parent) } + QueryStackFrame { + call_context: info.job.call_context, + tagged_key: job_map.tagged_key_of(parent), + } }; return Cycle { usage, frames }; } @@ -109,7 +118,7 @@ pub(crate) fn find_dep_kind_root<'tcx>( last_info = info; } } - (last_info.job.span, last_info.tagged_key.description(tcx), depth) + (last_info.job.call_context.span, last_info.tagged_key.description(tcx), depth) } /// The locaton of a resumable waiter. The usize is the index into waiters in the query's latch. @@ -119,8 +128,8 @@ type ResumableWaiterLocation = (QueryJobId, usize); /// This abstracts over non-resumable waiters which are found in `QueryJob`'s `parent` field /// and resumable waiters are in `latch` field. struct AbstractedWaiter { - /// The span corresponding to the reason for why we're waiting on this query. - span: Span, + /// The context corresponding to the reason for why we're waiting on this query. + call_context: QueryCallContext, /// The query which we are waiting from, if none the waiter is from a compiler root. parent: Option, resumable: Option, @@ -133,7 +142,7 @@ fn abstracted_waiters_of(job_map: &QueryJobMap<'_>, query: QueryJobId) -> Vec, query: QueryJobId) -> Vec, query: QueryJobId) -> Vec( job_map: &QueryJobMap<'tcx>, query: QueryJobId, - span: Span, - stack: &mut Vec<(Span, QueryJobId)>, + call_context: QueryCallContext, + stack: &mut Vec<(QueryCallContext, QueryJobId)>, visited: &mut FxHashSet, ) -> ControlFlow> { if !visited.insert(query) { @@ -169,8 +178,8 @@ fn find_cycle<'tcx>( // Remove previous stack entries stack.drain(0..pos); - // Replace the span for the first query with the cycle cause - stack[0].0 = span; + // Replace the context for the first query with the cycle cause + stack[0].0 = call_context; ControlFlow::Break(None) } else { ControlFlow::Continue(()) @@ -178,7 +187,7 @@ fn find_cycle<'tcx>( } // Query marked as visited is added it to the stack - stack.push((span, query)); + stack.push((call_context, query)); // Visit all the waiters for abstracted_waiter in abstracted_waiters_of(job_map, query) { @@ -187,7 +196,7 @@ fn find_cycle<'tcx>( continue; }; if let ControlFlow::Break(maybe_resumable) = - find_cycle(job_map, parent, abstracted_waiter.span, stack, visited) + find_cycle(job_map, parent, abstracted_waiter.call_context, stack, visited) { // Return the resumable waiter in `waiter.resumable` if present return ControlFlow::Break(abstracted_waiter.resumable.or(maybe_resumable)); @@ -229,103 +238,105 @@ fn connected_to_root<'tcx>( false } -/// Looks for a query cycle using the last query in `jobs`. -/// If a cycle is found, all queries in the cycle is removed from `jobs` and -/// the function return true. -/// If a cycle was not found, the starting query is removed from `jobs` and -/// the function returns false. -fn remove_cycle<'tcx>( +/// Processes a found query cycle into a `Cycle` +fn process_cycle<'tcx>( job_map: &QueryJobMap<'tcx>, - jobs: &mut Vec, - wakelist: &mut Vec>>, -) -> bool { - let mut visited = FxHashSet::default(); - let mut stack = Vec::new(); - // Look for a cycle starting with the last query in `jobs` - if let ControlFlow::Break(resumable) = - find_cycle(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) - { - // The stack is a vector of pairs of spans and queries; reverse it so that - // the earlier entries require later entries - let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); - - // Shift the spans so that queries are matched with the span for their waitee - spans.rotate_right(1); - - // Zip them back together - let mut stack: Vec<_> = iter::zip(spans, queries).collect(); - - // Remove the queries in our cycle from the list of jobs to look at - for r in &stack { - if let Some(pos) = jobs.iter().position(|j| j == &r.1) { - jobs.remove(pos); - } - } + stack: Vec<(QueryCallContext, QueryJobId)>, +) -> Cycle<'tcx> { + // The stack is a vector of call-context/query pairs; reverse it so that + // the earlier entries require later entries. + let (mut call_contexts, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); - struct EntryPoint { - query_in_cycle: QueryJobId, - query_waiting_on_cycle: Option<(Span, QueryJobId)>, - } + // Shift the spans so that queries are matched with the span for their waitee + call_contexts.rotate_right(1); - // Find the queries in the cycle which are - // connected to queries outside the cycle - let entry_points = stack - .iter() - .filter_map(|&(_, query_in_cycle)| { - let mut entrypoint = false; - let mut query_waiting_on_cycle = None; - - // Find a direct waiter who leads to the root - for abstracted_waiter in abstracted_waiters_of(job_map, query_in_cycle) { - let Some(parent) = abstracted_waiter.parent else { - // The query in the cycle is directly connected to root. - entrypoint = true; - continue; - }; - - // Mark all the other queries in the cycle as already visited, - // so paths to the root through the cycle itself won't count. - let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); - - if connected_to_root(job_map, parent, &mut visited) { - query_waiting_on_cycle = Some((abstracted_waiter.span, parent)); - entrypoint = true; - break; - } + // Zip them back together + let mut stack: Vec<_> = iter::zip(call_contexts, queries).collect(); + + struct EntryPoint { + query_in_cycle: QueryJobId, + query_waiting_on_cycle: Option<(QueryCallContext, QueryJobId)>, + } + + // Find the queries in the cycle which are + // connected to queries outside the cycle + let entry_points = stack + .iter() + .filter_map(|&(_, query_in_cycle)| { + let mut entrypoint = false; + let mut query_waiting_on_cycle = None; + + // Find a direct waiter who leads to the root + for abstracted_waiter in abstracted_waiters_of(job_map, query_in_cycle) { + let Some(parent) = abstracted_waiter.parent else { + // The query in the cycle is directly connected to root. + entrypoint = true; + continue; + }; + + // Mark all the other queries in the cycle as already visited, + // so paths to the root through the cycle itself won't count. + let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); + + if connected_to_root(job_map, parent, &mut visited) { + query_waiting_on_cycle = Some((abstracted_waiter.call_context, parent)); + entrypoint = true; + break; } + } - entrypoint.then_some(EntryPoint { query_in_cycle, query_waiting_on_cycle }) - }) - .collect::>(); + entrypoint.then_some(EntryPoint { query_in_cycle, query_waiting_on_cycle }) + }) + .collect::>(); - // Pick an entry point, preferring ones with waiters - let entry_point = entry_points - .iter() - .find(|entry_point| entry_point.query_waiting_on_cycle.is_some()) - .unwrap_or(&entry_points[0]); - - // Shift the stack so that our entry point is first - let entry_point_pos = - stack.iter().position(|(_, query)| *query == entry_point.query_in_cycle); - if let Some(pos) = entry_point_pos { - stack.rotate_left(pos); - } + // Pick an entry point, preferring ones with waiters + let entry_point = entry_points + .iter() + .find(|entry_point| entry_point.query_waiting_on_cycle.is_some()) + .unwrap_or(&entry_points[0]); - let usage = entry_point - .query_waiting_on_cycle - .map(|(span, job)| QueryStackFrame { span, tagged_key: job_map.tagged_key_of(job) }); + // Shift the stack so that our entry point is first + let entry_point_pos = stack.iter().position(|(_, query)| *query == entry_point.query_in_cycle); + if let Some(pos) = entry_point_pos { + stack.rotate_left(pos); + } + + let usage = entry_point.query_waiting_on_cycle.map(|(call_context, job)| QueryStackFrame { + call_context, + tagged_key: job_map.tagged_key_of(job), + }); + // Create the cycle error + Cycle { + usage, + frames: stack + .iter() + .map(|&(call_context, job)| QueryStackFrame { + call_context, + tagged_key: job_map.tagged_key_of(job), + }) + .collect(), + } +} + +/// Looks for a query cycle starting at `query`. +/// Returns a waiter to resume if a cycle is found. +fn find_and_process_cycle<'tcx>( + job_map: &QueryJobMap<'tcx>, + query: QueryJobId, +) -> Option>> { + let mut visited = FxHashSet::default(); + let mut stack = Vec::new(); + // Look for a cycle starting with the last query in `jobs` + if let ControlFlow::Break(resumable) = find_cycle( + job_map, + query, + QueryCallContext { span: DUMMY_SP, location: None }, + &mut stack, + &mut visited, + ) { // Create the cycle error - let error = Cycle { - usage, - frames: stack - .iter() - .map(|&(span, job)| QueryStackFrame { - span, - tagged_key: job_map.tagged_key_of(job), - }) - .collect(), - }; + let error = process_cycle(job_map, stack); // We unwrap `resumable` here since there must always be one // edge which is resumable / waited using a query latch @@ -338,62 +349,31 @@ fn remove_cycle<'tcx>( *waiter.cycle.lock() = Some(error); // Put the waiter on the list of things to resume - wakelist.push(waiter); - - true + Some(waiter) } else { - false + None } } /// Detects query cycles by using depth first search over all active query jobs. /// If a query cycle is found it will break the cycle by finding an edge which /// uses a query latch and then resuming that waiter. -/// There may be multiple cycles involved in a deadlock, so this searches -/// all active queries for cycles before finally resuming all the waiters at once. -pub fn break_query_cycles<'tcx>( - job_map: QueryJobMap<'tcx>, - registry: &rustc_thread_pool::Registry, -) { - let mut wakelist = Vec::new(); - // It is OK per the comments: - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798854932 - // - https://github.com/rust-lang/rust/pull/131200#issuecomment-2798866392 - #[allow(rustc::potential_query_instability)] - let mut jobs: Vec = job_map.map.keys().copied().collect(); - - let mut found_cycle = false; - - while jobs.len() > 0 { - if remove_cycle(&job_map, &mut jobs, &mut wakelist) { - found_cycle = true; - } - } - - // Check that a cycle was found. It is possible for a deadlock to occur without - // a query cycle if a query which can be waited on uses Rayon to do multithreading - // internally. Such a query (X) may be executing on 2 threads (A and B) and A may - // wait using Rayon on B. Rayon may then switch to executing another query (Y) - // which in turn will wait on X causing a deadlock. We have a false dependency from - // X to Y due to Rayon waiting and a true dependency from Y to X. The algorithm here - // only considers the true dependency and won't detect a cycle. - if !found_cycle { - panic!( - "deadlock detected as we're unable to find a query cycle to break\n\ - current query map:\n{job_map:#?}", - ); - } - - // Mark all the thread we're about to wake up as unblocked. This needs to be done before - // we wake the threads up as otherwise Rayon could detect a deadlock if a thread we - // resumed fell asleep and this thread had yet to mark the remaining threads as unblocked. - for _ in 0..wakelist.len() { - rustc_thread_pool::mark_unblocked(registry); - } - - for waiter in wakelist.into_iter() { - waiter.condvar.notify_one(); - } +/// +/// There may be multiple cycles involved in a deadlock, but this only breaks one at a time so +/// there will be multiple rounds through the deadlock handler if multiple cycles are present. +#[allow(rustc::potential_query_instability)] +pub fn break_query_cycle<'tcx>(job_map: QueryJobMap<'tcx>, registry: &rustc_thread_pool::Registry) { + // Look for a cycle starting at each query job + let waiter = job_map + .map + .keys() + .find_map(|query| find_and_process_cycle(&job_map, *query)) + .expect("unable to find a query cycle"); + + // Mark the thread we're about to wake up as unblocked. + rustc_thread_pool::mark_unblocked(registry); + + assert!(waiter.condvar.notify_one(), "unable to wake the waiter"); } pub fn print_query_stack<'tcx>( @@ -426,8 +406,11 @@ pub fn print_query_stack<'tcx>( "#{count_printed} [{query_name}] {description}", query_name = query_info.tagged_key.query_name(), )) - .with_span(query_info.job.span) + .with_span(query_info.job.call_context.span) .emit(); + if let Some(location) = query_info.job.call_context.location { + dcx.struct_failure_note(format!(" - {location}")).emit(); + } count_printed += 1; } @@ -437,6 +420,9 @@ pub fn print_query_stack<'tcx>( "#{count_total} [{query_name}] {description}", query_name = query_info.tagged_key.query_name(), ); + if let Some(location) = query_info.job.call_context.location { + let _ = writeln!(file, " - {location}"); + } } current_query = query_info.job.parent; @@ -454,15 +440,28 @@ pub fn print_query_stack<'tcx>( pub(crate) fn create_cycle_error<'tcx>( tcx: TyCtxt<'tcx>, Cycle { usage, frames }: &Cycle<'tcx>, + nested: bool, ) -> Diag<'tcx> { + let location = |frame: &QueryStackFrame<'tcx>| { + if tcx.sess.verbose_internals() { + frame + .call_context + .location + .map(|location| QueryLocationNote { location: location.to_string() }) + } else { + None + } + }; + assert!(!frames.is_empty()); - let span = frames[0].tagged_key.default_span(tcx, frames[1 % frames.len()].span); + let span = + frames[0].tagged_key.catch_default_span(tcx, frames[1 % frames.len()].call_context.span); let mut cycle_stack = Vec::new(); use crate::error::StackCount; - let stack_bottom = frames[0].tagged_key.description(tcx); + let stack_bottom = frames[0].tagged_key.catch_description(tcx); let stack_count = if frames.len() == 1 { StackCount::Single { stack_bottom: stack_bottom.clone() } } else { @@ -471,37 +470,68 @@ pub(crate) fn create_cycle_error<'tcx>( for i in 1..frames.len() { let frame = &frames[i]; - let span = frame.tagged_key.default_span(tcx, frames[(i + 1) % frames.len()].span); - cycle_stack - .push(crate::error::CycleStack { span, desc: frame.tagged_key.description(tcx) }); + let span = frame + .tagged_key + .catch_default_span(tcx, frames[(i + 1) % frames.len()].call_context.span); + cycle_stack.push(crate::error::CycleStack { + span, + desc: frame.tagged_key.catch_description(tcx), + location: location(frame), + }); } let cycle_usage = usage.as_ref().map(|usage| crate::error::CycleUsage { - span: usage.tagged_key.default_span(tcx, usage.span), - usage: usage.tagged_key.description(tcx), + span: usage.tagged_key.catch_default_span(tcx, usage.call_context.span), + usage: usage.tagged_key.catch_description(tcx), + location: location(usage), }); - let alias = if frames - .iter() - .all(|frame| frame.tagged_key.def_kind(tcx) == Some(DefKind::TyAlias)) - { - Some(crate::error::Alias::Ty) - } else if frames.iter().all(|frame| frame.tagged_key.def_kind(tcx) == Some(DefKind::TraitAlias)) - { - Some(crate::error::Alias::Trait) - } else { - None + let is_all_def_kind = |def_kind| { + // Trivial type alias and trait alias cycles consists of `type_of` and + // `explicit_implied_predicates_of` queries, so we just check just these here. + frames.iter().all(|frame| match frame.tagged_key { + TaggedQueryKey::type_of(def_id) + | TaggedQueryKey::explicit_implied_predicates_of(def_id) + if tcx.def_kind(def_id) == def_kind => + { + true + } + _ => false, + }) }; - let cycle_diag = crate::error::Cycle { - span, - cycle_stack, - stack_bottom, - alias, - cycle_usage, - stack_count, - note_span: (), + let alias = if !nested { + if is_all_def_kind(DefKind::TyAlias) { + Some(crate::error::Alias::Ty) + } else if is_all_def_kind(DefKind::TraitAlias) { + Some(crate::error::Alias::Trait) + } else { + None + } + } else { + None }; - tcx.sess.dcx().create_err(cycle_diag) + if nested { + tcx.sess.dcx().create_err(crate::error::NestedCycle { + span, + cycle_stack, + stack_bottom, + stack_bottom_location: location(&frames[0]), + cycle_usage, + stack_count, + note_span: (), + }) + } else { + tcx.sess.dcx().create_err(crate::error::Cycle { + span, + cycle_stack, + stack_bottom, + stack_bottom_location: location(&frames[0]), + alias, + cycle_usage, + stack_count, + note_span: (), + }) + } } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index de03b48394b10..b4b30d6412110 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -8,7 +8,7 @@ #![feature(try_blocks)] // tidy-alphabetical-end -use rustc_data_structures::sync::AtomicU64; +use rustc_data_structures::sync::{AtomicU64, Lock}; use rustc_middle::dep_graph; use rustc_middle::queries::{ExternProviders, Providers}; use rustc_middle::query::on_disk_cache::OnDiskCache; @@ -17,7 +17,7 @@ use rustc_middle::ty::TyCtxt; pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::execution::{CollectActiveJobsKind, collect_active_query_jobs}; -pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; +pub use crate::job::{QueryJobMap, break_query_cycle, print_query_stack}; mod dep_kind_vtables; mod error; @@ -58,6 +58,7 @@ pub fn query_system<'tcx>( local_providers, extern_providers, jobs: AtomicU64::new(1), + cycle_handler_nesting: Lock::new(0), } } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index ef4fff293bf65..29a594b45b4b3 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -8,7 +8,9 @@ use rustc_middle::dep_graph::DepKindVTable; use rustc_middle::dep_graph::{DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex}; use rustc_middle::query::erase::{Erasable, Erased}; use rustc_middle::query::on_disk_cache::{CacheDecoder, CacheEncoder}; -use rustc_middle::query::{QueryCache, QueryJobId, QueryMode, QueryVTable, erase}; +use rustc_middle::query::{ + QueryCache, QueryCallContext, QueryJobId, QueryMode, QueryVTable, erase, +}; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_serialize::{Decodable, Encodable}; @@ -168,7 +170,12 @@ pub(crate) fn promote_from_disk_inner<'tcx, C: QueryCache>( // FIXME(Zalathar): Is there a reasonable way to skip more of the // query bookkeeping when doing this? None => { - (query.execute_query_fn)(tcx, DUMMY_SP, key, QueryMode::Get); + (query.execute_query_fn)( + tcx, + QueryCallContext { span: DUMMY_SP, location: None }, + key, + QueryMode::Get, + ); } } } diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 60ddf3e4ad381..5e356c3ba0a9e 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -1,8 +1,7 @@ use rustc_middle::queries::TaggedQueryKey; use rustc_middle::query::erase::{self, Erased}; -use rustc_middle::query::{AsLocalQueryKey, QueryMode, QueryVTable}; +use rustc_middle::query::{AsLocalQueryKey, QueryCallContext, QueryMode, QueryVTable}; use rustc_middle::ty::TyCtxt; -use rustc_span::Span; use crate::GetQueryVTable; @@ -50,7 +49,7 @@ macro_rules! define_queries { #[inline(never)] pub(crate) fn __rust_end_short_backtrace<'tcx>( tcx: TyCtxt<'tcx>, - span: Span, + call_context: QueryCallContext, key: Key<'tcx>, mode: QueryMode, ) -> Option>> { @@ -59,7 +58,7 @@ macro_rules! define_queries { crate::execution::execute_query_incr_inner( &tcx.query_system.query_vtables.$name, tcx, - span, + call_context, key, mode ) @@ -73,14 +72,14 @@ macro_rules! define_queries { #[inline(never)] pub(crate) fn __rust_end_short_backtrace<'tcx>( tcx: TyCtxt<'tcx>, - span: Span, + call_context: QueryCallContext, key: Key<'tcx>, __mode: QueryMode, ) -> Option>> { Some(crate::execution::execute_query_non_incr_inner( &tcx.query_system.query_vtables.$name, tcx, - span, + call_context, key, )) } diff --git a/src/tools/miri/tests/panic/mir-validation.stderr b/src/tools/miri/tests/panic/mir-validation.stderr index 1d40c93d709e6..603d8ed7c70d3 100644 --- a/src/tools/miri/tests/panic/mir-validation.stderr +++ b/src/tools/miri/tests/panic/mir-validation.stderr @@ -15,6 +15,7 @@ stack backtrace: query stack during panic: #0 [optimized_mir] optimizing MIR for `main` + - compiler/rustc_middle/src/ty/mod.rs:LL:CC end of query stack Miri caused an ICE during evaluation. Here's the interpreter backtrace at the time of the panic: diff --git a/tests/ui/consts/const-eval/const-eval-query-stack.rs b/tests/ui/consts/const-eval/const-eval-query-stack.rs index 3728e2071d358..c94da433658c3 100644 --- a/tests/ui/consts/const-eval/const-eval-query-stack.rs +++ b/tests/ui/consts/const-eval/const-eval-query-stack.rs @@ -1,6 +1,7 @@ //@ compile-flags: -Ztreat-err-as-bug=1 //@ failure-status: 101 //@ rustc-env:RUST_BACKTRACE=1 +//@ normalize-stderr: "(compiler/[^ )]+\.rs):\d+:\d+" -> "$1:LL:CC" //@ normalize-stderr: "\nerror: .*unexpectedly panicked.*\n\n" -> "" //@ normalize-stderr: "note: we would appreciate a bug report.*\n\n" -> "" //@ normalize-stderr: "note: compiler flags.*\n\n" -> "" diff --git a/tests/ui/consts/const-eval/const-eval-query-stack.stderr b/tests/ui/consts/const-eval/const-eval-query-stack.stderr index 89f9c1e36920d..01961535272c4 100644 --- a/tests/ui/consts/const-eval/const-eval-query-stack.stderr +++ b/tests/ui/consts/const-eval/const-eval-query-stack.stderr @@ -1,5 +1,5 @@ error: internal compiler error[E0080]: attempt to divide `1_i32` by zero - --> $DIR/const-eval-query-stack.rs:16:16 + --> $DIR/const-eval-query-stack.rs:17:16 | LL | const X: i32 = 1 / 0; | ^^^^^ evaluation of `X` failed here @@ -9,6 +9,9 @@ note: please make sure that you have updated to the latest nightly query stack during panic: #0 [eval_to_allocation_raw] const-evaluating + checking `X` + - compiler/rustc_const_eval/src/const_eval/eval_queries.rs:LL:CC #1 [eval_to_const_value_raw] simplifying constant for the type system `X` + - compiler/rustc_hir_analysis/src/lib.rs:LL:CC #2 [analysis] running analysis passes on crate `const_eval_query_stack` + - compiler/rustc_driver_impl/src/lib.rs:LL:CC end of query stack diff --git a/tests/ui/layout/valid_range_oob.stderr b/tests/ui/layout/valid_range_oob.stderr index fc6ebcf1692fe..209e201516cfd 100644 --- a/tests/ui/layout/valid_range_oob.stderr +++ b/tests/ui/layout/valid_range_oob.stderr @@ -4,5 +4,7 @@ error: the compiler unexpectedly panicked. This is a bug query stack during panic: #0 [layout_of] computing layout of `Foo` + - compiler/rustc_middle/src/ty/layout.rs:704:17 #1 [eval_to_allocation_raw] const-evaluating + checking `FOO` + - compiler/rustc_const_eval/src/const_eval/eval_queries.rs:333:9 ... and 2 other queries... use `env RUST_BACKTRACE=1` to see the full query stack diff --git a/tests/ui/panics/default-backtrace-ice.rs b/tests/ui/panics/default-backtrace-ice.rs index e919e1a4ea625..60d6b822c65d3 100644 --- a/tests/ui/panics/default-backtrace-ice.rs +++ b/tests/ui/panics/default-backtrace-ice.rs @@ -2,6 +2,7 @@ //@ compile-flags:-Z treat-err-as-bug=1 //@ failure-status:101 //@ ignore-msvc +//@ normalize-stderr: "(compiler/[^ )]+\.rs):\d+:\d+" -> "$1:LL:CC" //@ normalize-stderr: "note: .*" -> "" //@ normalize-stderr: "thread 'rustc' .*" -> "" //@ normalize-stderr: " +\d+:.*__rust_begin_short_backtrace.*" -> "(begin_short_backtrace)" diff --git a/tests/ui/panics/default-backtrace-ice.stderr b/tests/ui/panics/default-backtrace-ice.stderr index 4e48e769d9d51..579f06c56c4ff 100644 --- a/tests/ui/panics/default-backtrace-ice.stderr +++ b/tests/ui/panics/default-backtrace-ice.stderr @@ -1,5 +1,5 @@ error: internal compiler error[E0425]: cannot find value `missing_ident` in this scope - --> $DIR/default-backtrace-ice.rs:20:13 + --> $DIR/default-backtrace-ice.rs:21:13 | LL | fn main() { missing_ident; } | ^^^^^^^^^^^^^ not found in this scope @@ -25,4 +25,5 @@ error: the compiler unexpectedly panicked. This is a bug query stack during panic: #0 [resolver_for_lowering_raw] getting the resolver for lowering + - compiler/rustc_middle/src/ty/context.rs:LL:CC end of query stack diff --git a/tests/ui/query-system/query-cycle-printing-issue-151358.rs b/tests/ui/query-system/query-cycle-printing-issue-151358.rs index 04d8664420be8..13f0aa25d8906 100644 --- a/tests/ui/query-system/query-cycle-printing-issue-151358.rs +++ b/tests/ui/query-system/query-cycle-printing-issue-151358.rs @@ -1,4 +1,6 @@ -//~ ERROR: cycle detected when looking up span for `Default` +//@ compile-flags: -Zverbose-internals +//@ dont-require-annotations: ERROR +//@ normalize-stderr: "(compiler/[^ )]+\.rs):\d+:\d+" -> "$1:LL:CC" trait Default {} use std::num::NonZero; fn main() { diff --git a/tests/ui/query-system/query-cycle-printing-issue-151358.stderr b/tests/ui/query-system/query-cycle-printing-issue-151358.stderr index 9c1d7b1de33a5..d71f0c3d0feff 100644 --- a/tests/ui/query-system/query-cycle-printing-issue-151358.stderr +++ b/tests/ui/query-system/query-cycle-printing-issue-151358.stderr @@ -1,9 +1,29 @@ -error[E0391]: cycle detected when looking up span for `Default` +error: cycle when printing cycle detected when getting HIR ID of `Default` ["local_def_id_to_hir_id"] | - = note: ...which immediately requires looking up span for `Default` again - = note: cycle used when perform lints prior to AST lowering + = note: at compiler/rustc_middle/src/hir/mod.rs:LL:CC + = note: ...which requires getting the crate HIR ["hir_crate"]... + = note: at compiler/rustc_ast_lowering/src/lib.rs:LL:CC + = note: ...which requires perform lints prior to AST lowering ["early_lint_checks"]... + = note: at compiler/rustc_middle/src/query/keys.rs:LL:CC + = note: ...which requires looking up span for `Default` ["def_span"]... + = note: ...which again requires getting HIR ID of `Default` ["local_def_id_to_hir_id"], completing the cycle + = note: at compiler/rustc_middle/src/hir/mod.rs:LL:CC + = note: at compiler/rustc_middle/src/ty/mod.rs:LL:CC + = note: cycle used when getting the resolver for lowering ["resolver_for_lowering_raw"] = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 1 previous error +error[E0391]: cycle detected when getting the resolver for lowering ["resolver_for_lowering_raw"] + | + = note: at compiler/rustc_middle/src/ty/mod.rs:LL:CC + = note: ...which requires getting HIR ID of `Default` ["local_def_id_to_hir_id"]... + = note: at compiler/rustc_middle/src/hir/mod.rs:LL:CC + = note: ...which requires getting the crate HIR ["hir_crate"]... + = note: at compiler/rustc_ast_lowering/src/lib.rs:LL:CC + = note: ...which requires perform lints prior to AST lowering ["early_lint_checks"]... + = note: ...which again requires getting the resolver for lowering ["resolver_for_lowering_raw"], completing the cycle + = note: at compiler/rustc_middle/src/ty/context.rs:LL:CC + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/resolve/multiple_definitions_attribute_merging.rs b/tests/ui/resolve/multiple_definitions_attribute_merging.rs index 9f1bff51a3d4c..79f51ac773181 100644 --- a/tests/ui/resolve/multiple_definitions_attribute_merging.rs +++ b/tests/ui/resolve/multiple_definitions_attribute_merging.rs @@ -4,6 +4,7 @@ //@known-bug: #120873 //@ failure-status: 101 +//@ normalize-stderr: "(compiler/[^ )]+\.rs):\d+:\d+" -> "$1:LL:CC" //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*\n" -> "" //@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " diff --git a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr index b8b33e3417bf7..31fb8e400afb6 100644 --- a/tests/ui/resolve/multiple_definitions_attribute_merging.stderr +++ b/tests/ui/resolve/multiple_definitions_attribute_merging.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `Dealigned` is defined multiple times - --> $DIR/multiple_definitions_attribute_merging.rs:18:1 + --> $DIR/multiple_definitions_attribute_merging.rs:19:1 | LL | struct Dealigned(u8, T); | --------------------------- previous definition of the type `Dealigned` here @@ -8,7 +8,7 @@ LL | struct Dealigned(u8, T); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Dealigned` redefined here | = error: internal compiler error: compiler/rustc_mir_transform/src/check_packed_ref.rs:LL:CC: builtin derive created an unaligned reference - --> $DIR/multiple_definitions_attribute_merging.rs:18:25 + --> $DIR/multiple_definitions_attribute_merging.rs:19:25 | LL | #[derive(PartialEq)] | --------- in this derive macro expansion @@ -19,8 +19,10 @@ LL | struct Dealigned(u8, T); Box query stack during panic: -#0 [mir_built] building MIR for `::eq` -#1 [check_unsafety] unsafety-checking `::eq` +#0 [mir_built] building MIR for `::eq` + - compiler/rustc_mir_build/src/check_unsafety.rs:LL:CC +#1 [check_unsafety] unsafety-checking `::eq` + - compiler/rustc_interface/src/passes.rs:LL:CC ... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/proc_macro_generated_packed.rs b/tests/ui/resolve/proc_macro_generated_packed.rs index e35f50911663c..bf92685258438 100644 --- a/tests/ui/resolve/proc_macro_generated_packed.rs +++ b/tests/ui/resolve/proc_macro_generated_packed.rs @@ -4,6 +4,7 @@ //@proc-macro: proc_macro_generate_packed.rs //@known-bug: #120873 //@ failure-status: 101 +//@ normalize-stderr: "(compiler/[^ )]+\.rs):\d+:\d+" -> "$1:LL:CC" //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*\n" -> "" //@ normalize-stderr: "(error: internal compiler error: [^:]+):\d+:\d+: " -> "$1:LL:CC: " diff --git a/tests/ui/resolve/proc_macro_generated_packed.stderr b/tests/ui/resolve/proc_macro_generated_packed.stderr index d8e160d0c6a03..68987c1fd1c68 100644 --- a/tests/ui/resolve/proc_macro_generated_packed.stderr +++ b/tests/ui/resolve/proc_macro_generated_packed.stderr @@ -1,5 +1,5 @@ error: internal compiler error: compiler/rustc_mir_transform/src/check_packed_ref.rs:LL:CC: builtin derive created an unaligned reference - --> $DIR/proc_macro_generated_packed.rs:19:25 + --> $DIR/proc_macro_generated_packed.rs:20:25 | LL | #[derive(PartialEq)] | --------- in this derive macro expansion @@ -10,8 +10,10 @@ LL | struct Dealigned(u8, T); Box query stack during panic: -#0 [mir_built] building MIR for `::eq` -#1 [check_unsafety] unsafety-checking `::eq` +#0 [mir_built] building MIR for `::eq` + - compiler/rustc_mir_build/src/check_unsafety.rs:LL:CC +#1 [check_unsafety] unsafety-checking `::eq` + - compiler/rustc_interface/src/passes.rs:LL:CC ... and 1 other queries... use `env RUST_BACKTRACE=1` to see the full query stack error: aborting due to 1 previous error diff --git a/tests/ui/resolve/query-cycle-issue-124901.rs b/tests/ui/resolve/query-cycle-issue-124901.rs index 6cb1e58b6258f..eacbf73755744 100644 --- a/tests/ui/resolve/query-cycle-issue-124901.rs +++ b/tests/ui/resolve/query-cycle-issue-124901.rs @@ -1,4 +1,5 @@ -//~ ERROR: cycle detected when looking up span for `Default` +//~ ERROR: cycle when printing cycle detected +//~^ ERROR: cycle detected trait Default { type Id; diff --git a/tests/ui/resolve/query-cycle-issue-124901.stderr b/tests/ui/resolve/query-cycle-issue-124901.stderr index 9c1d7b1de33a5..876b63a5f4594 100644 --- a/tests/ui/resolve/query-cycle-issue-124901.stderr +++ b/tests/ui/resolve/query-cycle-issue-124901.stderr @@ -1,9 +1,20 @@ -error[E0391]: cycle detected when looking up span for `Default` +error: cycle when printing cycle detected when getting HIR ID of `Default` | - = note: ...which immediately requires looking up span for `Default` again - = note: cycle used when perform lints prior to AST lowering + = note: ...which requires getting the crate HIR... + = note: ...which requires perform lints prior to AST lowering... + = note: ...which requires looking up span for `Default`... + = note: ...which again requires getting HIR ID of `Default`, completing the cycle + = note: cycle used when getting the resolver for lowering = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information -error: aborting due to 1 previous error +error[E0391]: cycle detected when getting the resolver for lowering + | + = note: ...which requires getting HIR ID of `Default`... + = note: ...which requires getting the crate HIR... + = note: ...which requires perform lints prior to AST lowering... + = note: ...which again requires getting the resolver for lowering, completing the cycle + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/track-diagnostics/track.stderr b/tests/ui/track-diagnostics/track.stderr index ba314c14b37ef..718976e6bd348 100644 --- a/tests/ui/track-diagnostics/track.stderr +++ b/tests/ui/track-diagnostics/track.stderr @@ -41,7 +41,9 @@ note: compiler flags: ... -Z ui-testing ... -Z track-diagnostics query stack during panic: #0 [typeck] type-checking `main` + - compiler/rustc_hir_analysis/src/lib.rs:LL:CC #1 [analysis] running analysis passes on crate `track` + - compiler/rustc_driver_impl/src/lib.rs:LL:CC end of query stack error: aborting due to 3 previous errors diff --git a/tests/ui/treat-err-as-bug/err.rs b/tests/ui/treat-err-as-bug/err.rs index f297a5773cb36..96bfa01f23662 100644 --- a/tests/ui/treat-err-as-bug/err.rs +++ b/tests/ui/treat-err-as-bug/err.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Ztreat-err-as-bug //@ failure-status: 101 +//@ normalize-stderr: "(compiler/[^ ]+\.rs):\d+:\d+" -> "$1:LL:CC" //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*:\n.*\n" -> "" //@ rustc-env:RUST_BACKTRACE=0 diff --git a/tests/ui/treat-err-as-bug/err.stderr b/tests/ui/treat-err-as-bug/err.stderr index 8ff267deacef5..b305474e5eb5c 100644 --- a/tests/ui/treat-err-as-bug/err.stderr +++ b/tests/ui/treat-err-as-bug/err.stderr @@ -1,5 +1,5 @@ error: internal compiler error[E0080]: attempt to compute `0_u32 - 1_u32`, which would overflow - --> $DIR/err.rs:9:21 + --> $DIR/err.rs:10:21 | LL | pub static C: u32 = 0 - 1; | ^^^^^ evaluation of `C` failed here @@ -9,5 +9,7 @@ error: the compiler unexpectedly panicked. This is a bug query stack during panic: #0 [eval_static_initializer] evaluating initializer of static `C` + - compiler/rustc_hir_analysis/src/lib.rs:LL:CC #1 [analysis] running analysis passes on crate `err` + - compiler/rustc_driver_impl/src/lib.rs:LL:CC end of query stack diff --git a/tests/ui/treat-err-as-bug/span_delayed_bug.rs b/tests/ui/treat-err-as-bug/span_delayed_bug.rs index ff5a595899a98..a55f1997d4b16 100644 --- a/tests/ui/treat-err-as-bug/span_delayed_bug.rs +++ b/tests/ui/treat-err-as-bug/span_delayed_bug.rs @@ -1,5 +1,6 @@ //@ compile-flags: -Ztreat-err-as-bug -Zeagerly-emit-delayed-bugs //@ failure-status: 101 +//@ normalize-stderr: "(compiler/[^ ]+\.rs):\d+:\d+" -> "$1:LL:CC" //@ normalize-stderr: "note: .*\n\n" -> "" //@ normalize-stderr: "thread 'rustc'.*panicked.*:\n.*\n" -> "" //@ rustc-env:RUST_BACKTRACE=0 diff --git a/tests/ui/treat-err-as-bug/span_delayed_bug.stderr b/tests/ui/treat-err-as-bug/span_delayed_bug.stderr index ae1bad55960ee..35b97773f9d00 100644 --- a/tests/ui/treat-err-as-bug/span_delayed_bug.stderr +++ b/tests/ui/treat-err-as-bug/span_delayed_bug.stderr @@ -1,5 +1,5 @@ error: internal compiler error: delayed bug triggered by #[rustc_delayed_bug_from_inside_query] - --> $DIR/span_delayed_bug.rs:10:1 + --> $DIR/span_delayed_bug.rs:11:1 | LL | fn main() {} | ^^^^^^^^^ @@ -9,4 +9,5 @@ error: the compiler unexpectedly panicked. This is a bug query stack during panic: #0 [trigger_delayed_bug] triggering a delayed bug for testing incremental + - compiler/rustc_interface/src/passes.rs:LL:CC end of query stack