diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 59f0b67d67299..613b3c64efed2 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -248,7 +248,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; tls::with(|tcx| { // Accessing session globals is sound as they outlive `GlobalCtxt`. // They are needed to hash query keys containing spans or symbols. - let query_map = rustc_span::set_session_globals_then( + let job_map = rustc_span::set_session_globals_then( unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there were no errors collecting all active jobs. @@ -258,7 +258,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; ) }, ); - break_query_cycles(query_map, ®istry); + break_query_cycles(job_map, ®istry); }) }) }); diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index b246f077e747e..50fb4f29ed717 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -15,7 +15,7 @@ use rustc_query_system::query::{ use rustc_span::{DUMMY_SP, Span}; use crate::dep_graph::{DepContext, DepNode, DepNodeIndex}; -use crate::job::{QueryJobInfo, QueryMap, find_cycle_in_stack, report_cycle}; +use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle}; use crate::{QueryCtxt, QueryFlags, SemiDynamicQueryDispatcher}; #[inline] @@ -45,8 +45,8 @@ pub(crate) fn gather_active_jobs_inner<'tcx, K: Copy>( state: &QueryState<'tcx, K>, tcx: TyCtxt<'tcx>, make_frame: fn(TyCtxt<'tcx>, K) -> QueryStackFrame>, - jobs: &mut QueryMap<'tcx>, require_complete: bool, + job_map_out: &mut QueryJobMap<'tcx>, // Out-param; job info is gathered into this map ) -> Option<()> { let mut active = Vec::new(); @@ -77,7 +77,7 @@ pub(crate) fn gather_active_jobs_inner<'tcx, K: Copy>( // queries leading to a deadlock. for (key, job) in active { let frame = make_frame(tcx, key); - jobs.insert(job.id, QueryJobInfo { frame, job }); + job_map_out.insert(job.id, QueryJobInfo { frame, job }); } Some(()) @@ -213,12 +213,12 @@ fn cycle_error<'tcx, C: QueryCache, const FLAGS: QueryFlags>( ) -> (C::Value, Option) { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx + let job_map = qcx .collect_active_jobs_from_all_queries(false) .ok() .expect("failed to collect active queries"); - let error = find_cycle_in_stack(try_execute, query_map, &qcx.current_query_job(), span); + let error = find_cycle_in_stack(try_execute, job_map, &qcx.current_query_job(), span); (mk_cycle(query, qcx, error.lift()), None) } diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index f1eba0f76d171..19b8245b97e7a 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -17,39 +17,45 @@ use crate::dep_graph::DepContext; /// Map from query job IDs to job information collected by /// `collect_active_jobs_from_all_queries`. -pub type QueryMap<'tcx> = FxHashMap>; - -fn query_job_id_frame<'a, 'tcx>( - id: QueryJobId, - map: &'a QueryMap<'tcx>, -) -> QueryStackFrame> { - map.get(&id).unwrap().frame.clone() +#[derive(Debug, Default)] +pub struct QueryJobMap<'tcx> { + map: FxHashMap>, } -fn query_job_id_span<'a, 'tcx>(id: QueryJobId, map: &'a QueryMap<'tcx>) -> Span { - map.get(&id).unwrap().job.span -} +impl<'tcx> QueryJobMap<'tcx> { + /// Adds information about a job ID to the job map. + /// + /// Should only be called by `gather_active_jobs_inner`. + pub(crate) fn insert(&mut self, id: QueryJobId, info: QueryJobInfo<'tcx>) { + self.map.insert(id, info); + } -fn query_job_id_parent<'a, 'tcx>(id: QueryJobId, map: &'a QueryMap<'tcx>) -> Option { - map.get(&id).unwrap().job.parent -} + fn frame_of(&self, id: QueryJobId) -> &QueryStackFrame> { + &self.map[&id].frame + } -fn query_job_id_latch<'a, 'tcx>( - id: QueryJobId, - map: &'a QueryMap<'tcx>, -) -> Option<&'a QueryLatch<'tcx>> { - map.get(&id).unwrap().job.latch.as_ref() + fn span_of(&self, id: QueryJobId) -> Span { + self.map[&id].job.span + } + + fn parent_of(&self, id: QueryJobId) -> Option { + self.map[&id].job.parent + } + + fn latch_of(&self, id: QueryJobId) -> Option<&QueryLatch<'tcx>> { + self.map[&id].job.latch.as_ref() + } } #[derive(Clone, Debug)] -pub struct QueryJobInfo<'tcx> { - pub frame: QueryStackFrame>, - pub job: QueryJob<'tcx>, +pub(crate) struct QueryJobInfo<'tcx> { + pub(crate) frame: QueryStackFrame>, + pub(crate) job: QueryJob<'tcx>, } pub(crate) fn find_cycle_in_stack<'tcx>( id: QueryJobId, - query_map: QueryMap<'tcx>, + job_map: QueryJobMap<'tcx>, current_job: &Option, span: Span, ) -> CycleError> { @@ -58,7 +64,7 @@ pub(crate) fn find_cycle_in_stack<'tcx>( let mut current_job = Option::clone(current_job); while let Some(job) = current_job { - let info = query_map.get(&job).unwrap(); + let info = &job_map.map[&job]; cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); if job == id { @@ -70,11 +76,10 @@ pub(crate) fn find_cycle_in_stack<'tcx>( // Replace it with the span which caused the cycle to form cycle[0].span = span; // Find out why the cycle itself was used - let usage = info - .job - .parent - .as_ref() - .map(|parent| (info.job.span, query_job_id_frame(*parent, &query_map))); + let usage = try { + let parent = info.job.parent?; + (info.job.span, job_map.frame_of(parent).clone()) + }; return CycleError { usage, cycle }; } @@ -88,16 +93,16 @@ pub(crate) fn find_cycle_in_stack<'tcx>( #[inline(never)] pub(crate) fn find_dep_kind_root<'tcx>( id: QueryJobId, - query_map: QueryMap<'tcx>, + job_map: QueryJobMap<'tcx>, ) -> (QueryJobInfo<'tcx>, usize) { let mut depth = 1; - let info = query_map.get(&id).unwrap(); + let info = &job_map.map[&id]; let dep_kind = info.frame.dep_kind; let mut current_id = info.job.parent; let mut last_layout = (info.clone(), depth); while let Some(id) = current_id { - let info = query_map.get(&id).unwrap(); + let info = &job_map.map[&id]; if info.frame.dep_kind == dep_kind { depth += 1; last_layout = (info.clone(), depth); @@ -120,7 +125,7 @@ type Waiter = (QueryJobId, usize); /// required information to resume the waiter. /// If all `visit` calls returns None, this function also returns None. fn visit_waiters<'tcx, F>( - query_map: &QueryMap<'tcx>, + job_map: &QueryJobMap<'tcx>, query: QueryJobId, mut visit: F, ) -> Option> @@ -128,14 +133,14 @@ where F: FnMut(Span, QueryJobId) -> Option>, { // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = query_job_id_parent(query, query_map) - && let Some(cycle) = visit(query_job_id_span(query, query_map), parent) + if let Some(parent) = job_map.parent_of(query) + && let Some(cycle) = visit(job_map.span_of(query), parent) { return Some(cycle); } // Visit the explicit waiters which use condvars and are resumable - if let Some(latch) = query_job_id_latch(query, query_map) { + if let Some(latch) = job_map.latch_of(query) { for (i, waiter) in latch.info.lock().waiters.iter().enumerate() { if let Some(waiter_query) = waiter.query { if visit(waiter.span, waiter_query).is_some() { @@ -154,7 +159,7 @@ where /// If a cycle is detected, this initial value is replaced with the span causing /// the cycle. fn cycle_check<'tcx>( - query_map: &QueryMap<'tcx>, + job_map: &QueryJobMap<'tcx>, query: QueryJobId, span: Span, stack: &mut Vec<(Span, QueryJobId)>, @@ -178,8 +183,8 @@ fn cycle_check<'tcx>( stack.push((span, query)); // Visit all the waiters - let r = visit_waiters(query_map, query, |span, successor| { - cycle_check(query_map, successor, span, stack, visited) + let r = visit_waiters(job_map, query, |span, successor| { + cycle_check(job_map, successor, span, stack, visited) }); // Remove the entry in our stack if we didn't find a cycle @@ -194,7 +199,7 @@ fn cycle_check<'tcx>( /// from `query` without going through any of the queries in `visited`. /// This is achieved with a depth first search. fn connected_to_root<'tcx>( - query_map: &QueryMap<'tcx>, + job_map: &QueryJobMap<'tcx>, query: QueryJobId, visited: &mut FxHashSet, ) -> bool { @@ -204,18 +209,18 @@ fn connected_to_root<'tcx>( } // This query is connected to the root (it has no query parent), return true - if query_job_id_parent(query, query_map).is_none() { + if job_map.parent_of(query).is_none() { return true; } - visit_waiters(query_map, query, |_, successor| { - connected_to_root(query_map, successor, visited).then_some(None) + visit_waiters(job_map, query, |_, successor| { + connected_to_root(job_map, successor, visited).then_some(None) }) .is_some() } // Deterministically pick an query from a list -fn pick_query<'a, 'tcx, T, F>(query_map: &QueryMap<'tcx>, queries: &'a [T], f: F) -> &'a T +fn pick_query<'a, 'tcx, T, F>(job_map: &QueryJobMap<'tcx>, queries: &'a [T], f: F) -> &'a T where F: Fn(&T) -> (Span, QueryJobId), { @@ -225,7 +230,7 @@ where .iter() .min_by_key(|v| { let (span, query) = f(v); - let hash = query_job_id_frame(query, query_map).hash; + let hash = job_map.frame_of(query).hash; // Prefer entry points which have valid spans for nicer error messages // We add an integer to the tuple ensuring that entry points // with valid spans are picked first @@ -241,7 +246,7 @@ where /// If a cycle was not found, the starting query is removed from `jobs` and /// the function returns false. fn remove_cycle<'tcx>( - query_map: &QueryMap<'tcx>, + job_map: &QueryJobMap<'tcx>, jobs: &mut Vec, wakelist: &mut Vec>>, ) -> bool { @@ -249,7 +254,7 @@ fn remove_cycle<'tcx>( let mut stack = Vec::new(); // Look for a cycle starting with the last query in `jobs` if let Some(waiter) = - cycle_check(query_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + cycle_check(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 @@ -273,17 +278,17 @@ fn remove_cycle<'tcx>( let entry_points = stack .iter() .filter_map(|&(span, query)| { - if query_job_id_parent(query, query_map).is_none() { + if job_map.parent_of(query).is_none() { // This query is connected to the root (it has no query parent) Some((span, query, None)) } else { let mut waiters = Vec::new(); // Find all the direct waiters who lead to the root - visit_waiters(query_map, query, |span, waiter| { + visit_waiters(job_map, query, |span, waiter| { // Mark all the other queries in the cycle as already visited let mut visited = FxHashSet::from_iter(stack.iter().map(|q| q.1)); - if connected_to_root(query_map, waiter, &mut visited) { + if connected_to_root(job_map, waiter, &mut visited) { waiters.push((span, waiter)); } @@ -293,7 +298,7 @@ fn remove_cycle<'tcx>( None } else { // Deterministically pick one of the waiters to show to the user - let waiter = *pick_query(query_map, &waiters, |s| *s); + let waiter = *pick_query(job_map, &waiters, |s| *s); Some((span, query, Some(waiter))) } } @@ -301,7 +306,7 @@ fn remove_cycle<'tcx>( .collect::)>>(); // Deterministically pick an entry point - let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1)); + let (_, entry_point, usage) = pick_query(job_map, &entry_points, |e| (e.0, e.1)); // Shift the stack so that our entry point is first let entry_point_pos = stack.iter().position(|(_, query)| query == entry_point); @@ -309,15 +314,14 @@ fn remove_cycle<'tcx>( stack.rotate_left(pos); } - let usage = - usage.as_ref().map(|(span, query)| (*span, query_job_id_frame(*query, query_map))); + let usage = usage.map(|(span, job)| (span, job_map.frame_of(job).clone())); // Create the cycle error let error = CycleError { usage, cycle: stack .iter() - .map(|&(s, ref q)| QueryInfo { span: s, frame: query_job_id_frame(*q, query_map) }) + .map(|&(span, job)| QueryInfo { span, frame: job_map.frame_of(job).clone() }) .collect(), }; @@ -326,8 +330,7 @@ fn remove_cycle<'tcx>( let (waitee_query, waiter_idx) = waiter.unwrap(); // Extract the waiter we want to resume - let waiter = - query_job_id_latch(waitee_query, query_map).unwrap().extract_waiter(waiter_idx); + let waiter = job_map.latch_of(waitee_query).unwrap().extract_waiter(waiter_idx); // Set the cycle error so it will be picked up when resumed *waiter.cycle.lock() = Some(error); @@ -346,18 +349,21 @@ fn remove_cycle<'tcx>( /// 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>(query_map: QueryMap<'tcx>, registry: &rustc_thread_pool::Registry) { +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 = query_map.keys().cloned().collect(); + let mut jobs: Vec = job_map.map.keys().copied().collect(); let mut found_cycle = false; while jobs.len() > 0 { - if remove_cycle(&query_map, &mut jobs, &mut wakelist) { + if remove_cycle(&job_map, &mut jobs, &mut wakelist) { found_cycle = true; } } @@ -372,8 +378,7 @@ pub fn break_query_cycles<'tcx>(query_map: QueryMap<'tcx>, registry: &rustc_thre if !found_cycle { panic!( "deadlock detected as we're unable to find a query cycle to break\n\ - current query map:\n{:#?}", - query_map + current query map:\n{job_map:#?}", ); } @@ -402,17 +407,16 @@ pub fn print_query_stack<'tcx>( let mut count_printed = 0; let mut count_total = 0; - // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs_from_all_queries(false) { - Ok(query_map) => query_map, - Err(query_map) => query_map, - }; + // Make use of a partial query job map if we fail to take locks collecting active queries. + let job_map: QueryJobMap<'_> = qcx + .collect_active_jobs_from_all_queries(false) + .unwrap_or_else(|partial_job_map| partial_job_map); if let Some(ref mut file) = file { let _ = writeln!(file, "\n\nquery stack during panic:"); } while let Some(query) = current_query { - let Some(query_info) = query_map.get(&query) else { + let Some(query_info) = job_map.map.get(&query) else { break; }; let query_extra = query_info.frame.info.extract(); diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index e1c22c187b230..9b2078275aae5 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -6,6 +6,7 @@ #![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(rustc_attrs)] +#![feature(try_blocks)] // tidy-alphabetical-end use std::marker::ConstParamTy; @@ -26,7 +27,7 @@ use rustc_query_system::query::{ }; use rustc_span::{ErrorGuaranteed, Span}; -pub use crate::job::{QueryMap, break_query_cycles, print_query_stack}; +pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; use crate::plumbing::{encode_all_query_results, try_mark_green}; use crate::profiling_support::QueryKeyStringCache; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 9a07df361800c..9804e6b217567 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -36,7 +36,7 @@ use rustc_span::def_id::LOCAL_CRATE; use crate::error::{QueryOverflow, QueryOverflowNote}; use crate::execution::{all_inactive, force_query}; -use crate::job::{QueryMap, find_dep_kind_root}; +use crate::job::{QueryJobMap, find_dep_kind_root}; use crate::{QueryDispatcherUnerased, QueryFlags, SemiDynamicQueryDispatcher}; /// Implements [`QueryContext`] for use by [`rustc_query_system`], since that @@ -53,10 +53,10 @@ impl<'tcx> QueryCtxt<'tcx> { } fn depth_limit_error(self, job: QueryJobId) { - let query_map = self + let job_map = self .collect_active_jobs_from_all_queries(true) .expect("failed to collect active queries"); - let (info, depth) = find_dep_kind_root(job, query_map); + let (info, depth) = find_dep_kind_root(job, job_map); let suggested_limit = match self.tcx.recursion_limit() { Limit(0) => Limit(2), @@ -131,17 +131,17 @@ impl<'tcx> QueryCtxt<'tcx> { pub fn collect_active_jobs_from_all_queries( self, require_complete: bool, - ) -> Result, QueryMap<'tcx>> { - let mut jobs = QueryMap::default(); + ) -> Result, QueryJobMap<'tcx>> { + let mut job_map_out = QueryJobMap::default(); let mut complete = true; for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { - if gather_fn(self.tcx, &mut jobs, require_complete).is_none() { + if gather_fn(self.tcx, require_complete, &mut job_map_out).is_none() { complete = false; } } - if complete { Ok(jobs) } else { Err(jobs) } + if complete { Ok(job_map_out) } else { Err(job_map_out) } } } @@ -753,8 +753,8 @@ macro_rules! define_queries { /// Should only be called through `PER_QUERY_GATHER_ACTIVE_JOBS_FNS`. pub(crate) fn gather_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, - qmap: &mut QueryMap<'tcx>, require_complete: bool, + job_map_out: &mut QueryJobMap<'tcx>, ) -> Option<()> { let make_frame = |tcx: TyCtxt<'tcx>, key| { let vtable = &tcx.query_system.query_vtables.$name; @@ -765,8 +765,8 @@ macro_rules! define_queries { let res = crate::execution::gather_active_jobs_inner(&tcx.query_system.states.$name, tcx, make_frame, - qmap, require_complete, + job_map_out, ); // this can be called during unwinding, and the function has a `try_`-prefix, so @@ -849,9 +849,13 @@ macro_rules! define_queries { /// each individual query, so that we have distinct function names to /// grep for.) const PER_QUERY_GATHER_ACTIVE_JOBS_FNS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, require_complete: bool) -> Option<()> + for<'tcx> fn( + tcx: TyCtxt<'tcx>, + require_complete: bool, + job_map_out: &mut QueryJobMap<'tcx>, + ) -> Option<()> ] = &[ - $(query_impl::$name::gather_active_jobs),* + $( $crate::query_impl::$name::gather_active_jobs ),* ]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[